Intereting Posts
Android-opencv конвертирует мат в оттенки серого с использованием matToBitmap / bitmapToMat NFC Offhost маршрутизирует UICC на Nexus 5X и Nexus 6P Выбрать несколько изображений в галерее Android Кордова / Телефонная связь Все внешние запросы Ajax Возвраты 404 Почему Android NoSuchMethodException происходит в AlertDialog.Builder's setOnDismissListener Как объединить несколько изображений в одно изображение в Android? Селекторы ListView с использованием CursorAdapter Каков максимальный объем данных, который может содержать строка в java? Динамически менять язык с помощью многоязычной поддержки androids? Android – Исправлена ​​ориентация экрана только для 1 страницы (телефонная задержка) Является ли Robotium надежным для тестирования того, как начинаются быстрые действия и фрагменты? Как вы можете зарезервировать имя для приложения? Android getListView () в ошибке фрагмента Проблема с дополнительным пространством в нижней части Android-браузера Eclipse застревает при попытке запустить приложение для Android

Кодирование H.264 с камеры с Android MediaCodec

Я пытаюсь заставить это работать на Android 4.1 (используя обновленный планшет Asus Transformer). Благодаря ответу Алекса на мой предыдущий вопрос , я уже смог записать некоторые необработанные данные H.264 в файл, но этот файл можно воспроизводить только с помощью ffplay -f h264 , и кажется, что он потерял всю информацию о частоте кадров (чрезвычайно Быстрое воспроизведение). Также цветовое пространство выглядит некорректно (atm с использованием камеры по умолчанию на стороне энкодера).

 public class AvcEncoder { private MediaCodec mediaCodec; private BufferedOutputStream outputStream; public AvcEncoder() { File f = new File(Environment.getExternalStorageDirectory(), "Download/video_encoded.264"); touch (f); try { outputStream = new BufferedOutputStream(new FileOutputStream(f)); Log.i("AvcEncoder", "outputStream initialized"); } catch (Exception e){ e.printStackTrace(); } mediaCodec = MediaCodec.createEncoderByType("video/avc"); MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 320, 240); mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 125000); mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15); mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar); mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); mediaCodec.start(); } public void close() { try { mediaCodec.stop(); mediaCodec.release(); outputStream.flush(); outputStream.close(); } catch (Exception e){ e.printStackTrace(); } } // called from Camera.setPreviewCallbackWithBuffer(...) in other class public void offerEncoder(byte[] input) { try { ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers(); int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1); if (inputBufferIndex >= 0) { ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; inputBuffer.clear(); inputBuffer.put(input); mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, 0, 0); } MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0); while (outputBufferIndex >= 0) { ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; byte[] outData = new byte[bufferInfo.size]; outputBuffer.get(outData); outputStream.write(outData, 0, outData.length); Log.i("AvcEncoder", outData.length + " bytes written"); mediaCodec.releaseOutputBuffer(outputBufferIndex, false); outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0); } } catch (Throwable t) { t.printStackTrace(); } } 

Изменение типа кодировщика на «video / mp4», по-видимому, решает проблему с частотой кадров, но поскольку главная цель состоит в создании службы потоковой передачи, это не является хорошим решением.

Я знаю, что я отбросил код Alex, рассматривая SPS и PPS NALU, но я надеялся, что это не будет необходимо, поскольку эта информация также поступает из outData и я предположил, что кодировщик будет отформатировать это правильно. Если это не так, как мне организовать различные типы NALU в моем файле / потоке?

Итак, что мне здесь не хватает, чтобы создать действующий рабочий поток H.264? И какие настройки следует использовать для согласования между цветовым пространством камеры и цветовым пространством кодировщика?

У меня такое ощущение, что это скорее вопрос, связанный с H.264, чем тема Android / MediaCodec. Или я до сих пор не использую API MediaCodec правильно?

Заранее спасибо.

Solutions Collecting From Web of "Кодирование H.264 с камеры с Android MediaCodec"

Для вашего быстрого воспроизведения – проблема с частотой кадров, вам здесь нечего делать. Так как это потоковое решение, другой стороне нужно сообщить частоту кадров заранее или временные метки с каждым кадром. Оба они не являются частью элементарного потока. Выбрана либо заранее определенная частота кадров, либо вы передаете какой-либо sdp или что-то в этом роде, или используете существующие протоколы, такие как rtsp. Во втором случае отметки времени являются частью потока, отправленного в виде чего-то вроде rtp. Затем клиент должен затухать поток rtp и играть в него. Вот как работает элементарная потоковая передача. [Исправить частоту кадров, если у вас есть кодировщик с фиксированной скоростью или указать временные метки]

Локальное воспроизведение ПК будет быстрым, потому что он не будет знать fps. Предоставляя параметр fps перед входом, например

 ffplay -fps 30 in.264 

Вы можете управлять воспроизведением на ПК.

Что касается файла, который не воспроизводится: имеет ли он SPS и PPS. Также у вас должны быть включены заголовки NAL – формат приложения b. Я мало знаю об андроиде, но это требование для того, чтобы любой элементарный поток h.264 мог воспроизводиться, когда они не находятся в каких-либо контейнерах, и их нужно сбрасывать и воспроизводить позже. Если по умолчанию андроид – mp4, но заголовки приложений по умолчанию будут отключены, поэтому, возможно, есть переключатель, чтобы включить его. Или, если вы получаете данные по кадре, просто добавьте их самостоятельно.

Что касается цветового формата: я бы предположил, что дефолт должен работать. Поэтому постарайтесь не устанавливать его. Если не попробовать 422 Planar или UVYV / VYUY, чередующиеся форматы. Обычно камеры являются одними из них. (Но не обязательно, это могут быть те, с которыми я столкнулся чаще).

Android 4.3 (API 18) обеспечивает простое решение. Класс MediaCodec теперь принимает входные данные от Surfaces, что означает, что вы можете подключить предварительный просмотр камеры к кодировщику и обходить все странные проблемы формата YUV.

Существует также новый класс MediaMuxer , который преобразует ваш необработанный поток H.264 в файл .mp4 (возможно, смешение в аудиопотоке).

См. Источник CameraToMpegTest для примера того, как это делается. (Он также демонстрирует использование шейдера фрагмента OpenGL ES для выполнения тривиального редактирования видео по мере его записи).

Вы можете преобразовать цветовые пространства, подобные этому, если вы задали цветовое пространство предварительного просмотра для YV12:

 public static byte[] YV12toYUV420PackedSemiPlanar(final byte[] input, final byte[] output, final int width, final int height) { /* * COLOR_TI_FormatYUV420PackedSemiPlanar is NV12 * We convert by putting the corresponding U and V bytes together (interleaved). */ final int frameSize = width * height; final int qFrameSize = frameSize/4; System.arraycopy(input, 0, output, 0, frameSize); // Y for (int i = 0; i < qFrameSize; i++) { output[frameSize + i*2] = input[frameSize + i + qFrameSize]; // Cb (U) output[frameSize + i*2 + 1] = input[frameSize + i]; // Cr (V) } return output; } 

Или

  public static byte[] YV12toYUV420Planar(byte[] input, byte[] output, int width, int height) { /* * COLOR_FormatYUV420Planar is I420 which is like YV12, but with U and V reversed. * So we just have to reverse U and V. */ final int frameSize = width * height; final int qFrameSize = frameSize/4; System.arraycopy(input, 0, output, 0, frameSize); // Y System.arraycopy(input, frameSize, output, frameSize + qFrameSize, qFrameSize); // Cr (V) System.arraycopy(input, frameSize + qFrameSize, output, frameSize, qFrameSize); // Cb (U) return output; } 

Вы можете запросить MediaCodec для поддерживаемого формата растрового изображения и запросить предварительный просмотр. Проблема в том, что некоторые MediaCodecs поддерживают только запатентованные форматы YUV, которые вы не можете получить из предварительного просмотра. В частности, 2130706688 = 0x7F000100 = COLOR_TI_FormatYUV420PackedSemiPlanar. Формат по умолчанию для предварительного просмотра: 17 = NV21 = MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV411Planar = YCbCr 420 Полупланарный

Если вы явно не запрашивали другой формат пикселей, буферы предварительного просмотра камеры будут поступать в формат YUV 420, известный как NV21 , для которого COLOR_FormatYCrYCb является эквивалентом MediaCodec.

К сожалению, как упоминают другие ответы на этой странице, нет гарантии, что на вашем устройстве AVC-кодер поддерживает этот формат. Обратите внимание, что существуют некоторые странные устройства, которые не поддерживают NV21, но я не знаю, какие могут быть обновлены до API 16 (следовательно, есть MediaCodec).

Документация Google также утверждает, что YV12 планарный YUV должен поддерживаться как формат предварительного просмотра камеры для всех устройств с API> = 12. Поэтому может быть полезно попробовать его (эквивалент MediaCodec – COLOR_FormatYUV420Planar, который вы используете в своем фрагменте кода).

Обновление : как напомнил мне Андрей Коттрелл, YV12 по-прежнему нуждается в обмене цветности, чтобы стать COLOR_FormatYUV420Planar.