Как использовать MediaCodec без MediaExtractor для H264

Мне нужно использовать MediaCodec без MediaExtractor, и я читаю файл, используя FileInputStream. В настоящее время он не работает, он показывает зеленоватое скремблированное изображение на экране.

Это весь исходный код:

FileInputStream in = new FileInputStream("/sdcard/sample.ts");

String mimeType = "video/avc";
MediaCodec decoder = MediaCodec.createDecoderByType(mimeType);
MediaFormat format = MediaFormat.createVideoFormat(mimeType, 1920, 1080);

byte[] header_sps = { 0, 0, 0, 1, 103, 100, 0, 40, -84, 52, -59, 1, -32, 17, 31, 120, 11, 80, 16, 16, 31, 0, 0, 3, 3, -23, 0, 0, -22, 96, -108 };
byte[] header_pps = { 0, 0, 0, 1, 104, -18, 60, -128 };
format.setByteBuffer("csd-0", ByteBuffer.wrap(header_sps));
format.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1920 * 1080);
format.setInteger("durationUs", 63446722);

decoder.configure(format, surface, null, 0);
decoder.start();

ByteBuffer[] inputBuffers = decoder.getInputBuffers();
ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
BufferInfo info = new BufferInfo();
boolean isEOS = false;
long startMs = System.currentTimeMillis();

while (!Thread.interrupted()) {
    if (!isEOS) {
        int inIndex = decoder.dequeueInputBuffer(1000);
        if (inIndex >= 0) {
            byte buffer2[] = new byte[18800 * 8 * 8 * 8];
            ByteBuffer buffer = inputBuffers[inIndex];
            int sampleSize;

            sampleSize = in.read(buffer2, 0, 18800 * 4);

            buffer.clear();
            buffer.put(buffer2, 0, sampleSize);
            buffer.clear();

            if (sampleSize < 0) {
                decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                isEOS = true;
            } else {
                decoder.queueInputBuffer(inIndex, 0, sampleSize, 0, 0);
            }
        }
    }

    int outIndex = decoder.dequeueOutputBuffer(info, 10000);
    switch (outIndex) {
    case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
        Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED");
        outputBuffers = decoder.getOutputBuffers();
        break;
    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
        Log.d("DecodeActivity", "New format " + decoder.getOutputFormat());
        break;
    case MediaCodec.INFO_TRY_AGAIN_LATER:
        Log.d("DecodeActivity", "dequeueOutputBuffer timed out! " + info);
        break;
    default:
        ByteBuffer buffer = outputBuffers[outIndex];
        Log.v("DecodeActivity", "We can't use this buffer but render it due to the API limit, " + buffer);

        while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
            try {
                sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
        }
        decoder.releaseOutputBuffer(outIndex, true);
        break;
    }

    if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
        Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
        break;
    }
}

decoder.stop();
decoder.release();

Если я использую MediaExtractor, все работает нормально. Я получил значения SPS/PPS, посмотрев MediaFormat при использовании MediaExtractor. Если я удалю раздел ниже, на экране ничего не отображается.

byte[] header_sps = { 0, 0, 0, 1, 103, 100, 0, 40, -84, 52, -59, 1, -32, 17, 31, 120, 11, 80, 16, 16, 31, 0, 0, 3, 3, -23, 0, 0, -22, 96, -108 };
byte[] header_pps = { 0, 0, 0, 1, 104, -18, 60, -128 };
format.setByteBuffer("csd-0", ByteBuffer.wrap(header_sps));
format.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1920 * 1080);
format.setInteger("durationUs", 63446722);

Что мне не хватает? Как я могу получить значения SPS/PPS программно без MediaExtractor?

Ответ 1

Я предполагаю, что вы читаете исходный поток исходных файлов H.264, а не файл MP4.

Похоже, вы загружаете блоки данных фиксированного размера в декодер. Это не работает. Вам нужно поместить единый блок доступа в каждый буфер.

Ответ 2

К вашему последнему вопросу, то есть , как вы можете получить значения SPS и PPS, вам необходимо иметь механизм для чтения того же файла.

Если у вас есть файл elementary stream, вам нужно будет проанализировать файл, определить заголовки NALU и извлечь содержимое.

Если у вас есть формат container, вам понадобится механизм для чтения file format типа container и извлеките информацию.

Если у вас есть вход streaming, вы можете получать контент из входящей информации SDP.

Что касается вашего текущего кода, я бы рекомендовал объединить оба SPS и PPS в один буфер и предоставить то же самое ниже codec, как показано ниже

byte[] csd_info = { 0, 0, 0, 1, 103, 100, 0, 40, -84, 52, -59, 1, -32, 17, 31, 120, 11, 80, 16, 16, 31, 0, 0, 3, 3, -23, 0, 0, -22, 96, -108, 0, 0, 0, 1, 104, -18, 60, -128 };
format.setByteBuffer("csd-0", ByteBuffer.wrap(csd_info));
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1920 * 1080);
format.setInteger("durationUs", 63446722);