Декодирование видео Android Media Codec

Это мой первый вопрос, поэтому, пожалуйста, дайте мне знать, если я что-то пропустил!

Использование новой реализации медиакодека Android API 16 для попытки декодирования видео, чтобы я мог отправлять кадры для применения в качестве текстуры (текстурная часть уже выполнена). Итак, я придумал следующий код с некоторой помощью вне стека, но в runOutputBuffer() мой outputBufIndex возвращается -1 (или в бесконечном цикле, поскольку я предоставил -1 в качестве тайм-аута), кто-нибудь может помочь с этим и / или дать какой-либо совет на куда идти оттуда?

Спасибо за вашу помощь, и вот мой код:

public MediaDecoder( BPRenderView bpview )
{

    surface = bpview;
    extractor = new MediaExtractor( );
    extractor.setDataSource( filePath );
    format = extractor.getTrackFormat( 0 );
    mime = format.getString( MediaFormat.KEY_MIME );
    createDecoder( );
    runInputBuffer( );

}

public void createDecoder( )
{

    codec = MediaCodec.createDecoderByType( "video/avc" );
    // format =extractor.getTrackFormat( 0 );
    Log.d( LOG_TAG, "Track Format: " + mime );
    // format.setInteger( MediaFormat.KEY_BIT_RATE, 125000 );
    // format.setInteger( MediaFormat.KEY_FRAME_RATE, 15 );
    // format.setInteger( MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar );
    // format.setInteger( MediaFormat.KEY_I_FRAME_INTERVAL, 5 );
    codec.configure( format, null, null, 0 );
    codec.start( );

    codecInputBuffers = codec.getInputBuffers( );
    codecOutputBuffers = codec.getOutputBuffers( );
    extractor.selectTrack( 0 );
}

public void runInputBuffer( )
{
    // This should take in the entire video and put it in the input buffer
    int inputBufIndex = codec.dequeueInputBuffer( -1 );
    if( inputBufIndex >= 0 )
    {
        ByteBuffer dstBuf = codecInputBuffers[ inputBufIndex ];

        int sampleSize = extractor.readSampleData( dstBuf, 0 );
        Log.d( "Sample Size", String.valueOf( sampleSize ) );
        long presentationTimeUs = 0;
        if( sampleSize < 0 )
        {
            sawInputEOS = true;
            sampleSize = 0;
        }
        else
        {
            presentationTimeUs = extractor.getSampleTime( );
        }
        Log.d( LOG_TAG, "Input Buffer" );
        Log.d( "InputBufIndex:", String.valueOf( inputBufIndex ) );
        Log.d( "PresentationTimeUS", String.valueOf( presentationTimeUs ) );
        codec.queueInputBuffer( inputBufIndex, 0, // offset
                sampleSize, presentationTimeUs, sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0 );
        if( !sawInputEOS )
        {
            Log.d( "Extractor", " Advancing" );
            extractor.advance( );

        }
    }
    runOutputBuffer( );
}

public void runOutputBuffer( )
{
    BufferInfo info = new BufferInfo( );

    final int res = codec.dequeueOutputBuffer( info, -1 );

    Log.d( "RES: ", String.valueOf( res ) );
    if( res >= 0 )
    {
        int outputBufIndex = res;
        ByteBuffer buf = codecOutputBuffers[ outputBufIndex ];
        final byte[ ] chunk = new byte[ info.size ];
        buf.get( chunk ); // Read the buffer all at once
        buf.clear( ); // ** MUST DO!!! OTHERWISE THE NEXT TIME YOU GET THIS SAME BUFFER BAD THINGS WILL HAPPEN

        if( chunk.length > 0 )
        {
            Log.d( "Chunk: ", String.valueOf( chunk.length ) );

            surface.setTexture( chunk, 320, 240 );

            // mAudioTrack.write( chunk, 0, chunk.length );
            // do the things
        }
        codec.releaseOutputBuffer( outputBufIndex, false /* render */);

        if( ( info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM ) != 0 )
        {
            sawOutputEOS = true;
        }
    }
    else if( res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED )
    {
        codecOutputBuffers = codec.getOutputBuffers( );
    }
    else if( res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED )
    {
        final MediaFormat oformat = codec.getOutputFormat( );
        Log.d( LOG_TAG, "Output format has changed to " + oformat );
        // mAudioTrack.setPlaybackRate( oformat.getInteger( MediaFormat.KEY_SAMPLE_RATE ) );
    }

}

}


person James    schedule 26.11.2012    source источник


Ответы (1)


Джеймс, добро пожаловать в Stack Overflow (как вопрошающий)!

Я пытался играть с классом MediaCodec, он ужасно ограничен и плохо документирован. Тем не менее, посмотрите этот красивый солидный пост (и связанный с ним гитхаб) Седрика Фунга. Его проект github должен работать из коробки на устройстве API-17 (JellyBean 4.2)+.

Я уверен, что вы можете определить, что вам нужно изменить, хотя, как я упоминал ранее, у вас есть ограниченная гибкость здесь с текущим уровнем API.

Что касается вашей конкретной проблемы, я думаю, что вы блокируете пользовательский интерфейс вызовами медиа-декодера, что не рекомендуется. Вы должны использовать многопоточный подход и вместо того, чтобы устанавливать -1 в качестве тайм-аута, установите тайм-аут, скажем, 10000 и разрешите это вызываться несколько раз, пока не станет активным.

Надеюсь, это поможет (хотя с тех пор, как вы задали вопрос, прошли месяцы)!

person kOrc    schedule 26.03.2013
comment
Привет Korc Спасибо за ваш ответ. Да, я исправил это сейчас, и оно работает очень хорошо, но с классом есть много проблем, особенно, как вы сказали, с его гибкостью. К счастью, наше приложение в первую очередь предназначено для использования на S3, но если вы будете использовать этот класс на нескольких устройствах (и API), это более или менее бесполезно, учитывая количество кодеков, которые вы должны разрешить, особенно когда вы пытаетесь выполнить некоторую постобработку. на данных, и вы не знаете, какой буфер (YUV420 и т. д.) вы получите обратно, пока не попробуете его на устройстве! Безумие! - person James; 27.03.2013
comment
Ага! Для постобработки (одна из причин, по которой я изучал класс MediaCodec) решение, которое я нашел для работы, состояло в том, чтобы записать буфер прямо на поверхность (аналогично коду Седрика), за исключением использования TextureView вместо SurfaceView (я может направить вас к коду, который позволяет вам это сделать). Получив это, вы можете использовать изящную операцию getBitmap TextureView для заполнения объекта Android Bitmap, который имеет известный формат, который вы можете установить, обычно ARGB_8888, с которым вы можете работать. Это не так эффективно или элегантно, но может выполнить свою работу. - person kOrc; 03.04.2013
comment
MediaCodec был немного улучшен в версии 4.3 (API 18). Пример кода приведен здесь: bigflake.com/mediacodec - person fadden; 24.07.2013
comment
kOrc, не могли бы вы предоставить ссылку на пример, использующий TextureView? - person Nativ; 03.11.2013
comment
Некоторые дополнительные примеры в приложении API 18 SDK (включая видеопроигрыватель, использующий TextureView): github.com/google/ графика - person fadden; 22.12.2013