安卓媒体编解码器视频解码

9

这是我的第一个问题,如果我漏掉了什么,请告诉我!

使用Android API 16的新Media Codec实现来尝试解码视频,以便我可以发送帧以作为纹理应用(纹理部分已完成)。所以我根据一些stack的帮助编写了以下代码,但是在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 ) );
    }

}

}

1个回答

9

詹姆斯,欢迎来到Stack Overflow(作为一个提问者)!

我尝试使用MediaCodec类,发现它非常有限并且文档不完善。但是,请查看Cedric Fung的这篇优秀文章(以及他链接的Github)。他的Github项目应该可以直接在API-17(JellyBean 4.2+)设备上运行。

我相信您可以从那里确定需要更改什么,不过正如我之前暗示的那样,由于当前API水平的局限性,您在这里的灵活性很有限。

关于您的具体问题,我认为您正在使用媒体解码器调用锁定UI,这是不推荐的。您应该采用线程方法,并且将超时设置为例如10000而不是-1,并允许其被多次调用直到其处于活动状态。

希望这可以帮到您(虽然您已经提出了问题数月!)


嗨Korc,谢谢你的回答。是的,我现在已经解决了它,并且它运行得非常好,但是这个类有更多的问题,特别是像你说的那样,它的灵活性。幸运的是,我们的应用程序主要用于S3,但是如果您要在多个设备(和API)上使用此类,则由于您必须允许的编解码器数量,它几乎是无用的,尤其是当您尝试对数据进行一些后期处理时,您不知道会得到什么缓冲区(YUV420等),直到您在设备上尝试它!疯狂! - James
是的!针对后处理(这也是我探索MediaCodec类的原因之一),我找到的解决方案是将缓冲区直接写入一个表面(类似于Cedric的代码),但使用TextureView而不是SurfaceView(我可以为您提供代码)。一旦您拥有了它,您就可以使用TextureView的巧妙的“getBitmap”操作填充一个Android Bitmap对象--该对象具有已知的格式,您可以设置它,通常是ARGB_8888--您可以对其进行操作。这并不像高效或优雅,但可以完成工作。 - kOrc
2
MediaCodec在4.3(API 18)中有了一些改进。这里链接了一些示例代码:http://bigflake.com/mediacodec/。 - fadden
kOrc,您能否提供一个使用TextureView的示例链接? - Nativ
1
一些额外的示例,在 API 18 SDK 应用程序中(包括使用 TextureView 的视频播放器):https://github.com/google/grafika - fadden

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接