Intereting Posts
Android, класс не найден из импортированного файла jar Espresso: Как проверить SwipeRefreshLayout? Android Studio: как создать второй тип сборки отладки Снижение производительности анимации Android, когда устройство остается неподвижным, но тронуто Как узнать, когда GridView полностью нарисован и готов? Студия Android не устанавливается, даже если установлен JDK 8 Таблица SQLite с целыми столбцами хранит строку Как я могу установить тень представления только по сторонам для просмотра с увеличением Android-браузер завораживает события для двух кранов в быстрой последовательности Продолжайте получать 404 для apple-touch-icon.png Сессия Предоставляется для запроса в неоткрытом состоянии Эквалайзер не всегда поддерживается, даже если api> = 9? Требуется Gradle версия 2.10. Текущая версия 2.8 Ошибка Получить ширину и высоту растрового изображения без загрузки в память Как разместить текст по горизонтали и вертикали в TextView на 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.