Mediacodec解码h264 OutputBuffer索引返回-1

时间:2016-01-03 14:07:49

标签: android decode h.264 mediacodec surface

我正在使用Mediacodec从Android上的无人机解码h264视频流。无人机每次发送包含NAL单元的字节数组。无人机文档表明IDR帧不包含在返回的字节数组中。因此,我设法从他们的网站下载I-Frame文件,并在任何其他访问单元之前将其提供给解码器。但是,从mCodec.dequeueOutputBuffer返回的索引始终为-1。

更新04/01/2016: 现在我在任何其他NAL单元之前正确地向解码器提供SPS / PPS数据和IDR帧。我观察到dequeueOutputBuffer返回-3然后是一些正数。之后它一直返回-1。所以我的猜测是我处理视频流中的访问单元有什么问题?

inputBuffer.put(SPS_PPS,0,SPS_PPS.length);
mCodec.queueInputBuffer(inIndex, 0, SPS_PPS.length, presentationTime, BUFFER_FLAG_CODEC_CONFIG);

我做了一些假设:

1。我为Mediacodec的每个输入缓冲区提供一个完整的访问单元,从00 00 00 01 09开始

2。我使用GLSurface.getHolder()。getSurface()从xml布局中的预定义GLSurface解码到曲面。我不确定这是否是正确的做法。

我的问题:

1。返回的outputbuffer索引始终为-1,屏幕上没有视频

2。无人机返回的字节数组的后半部分始终为0x00。我不知道这些0是否也应该全部包含在NAL单位中

第3。字节数组包含NAL单元,我必须自己拆分它们。但是这些nal单元只有0x25,0x27,0x28,0x06和0x09类型。我只知道0x09表示AUD

我真的希望任何人都可以提供最微小的建议,因为这件事让我疯狂了。

我的代码片段:

首先,我的主要活动类实现了surfaceHolder回调。

设置表面支架:

private DjiGLSurfaceView mDjiGLSurfaceView;
mDjiGLSurfaceView = (DjiGLSurfaceView)findViewById(R.id.DjiSurfaceView_);
mDjiGLSurfaceView.getHolder().addCallback(this);

从.264文件中读取I帧以存储在字节数组中 - > iframe中:

BufferedInputStream buf = new BufferedInputStream(is);
buf.read(iframe, 0, iframe.length);

我的异步处理部分:

当收到包含视频数据的字节数组时,将调用DJIReceivedVideoDataCallBack。

public void surfaceCreated(SurfaceHolder holder){
    Log.e(TAG, "Surface Created!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    format.setString("KEY_MIME", videoFormat);
    try
    {
        mCodec = MediaCodec.createDecoderByType(videoFormat);
        mCodec.configure(format, mDjiGLSurfaceView.getHolder().getSurface(), null, 0 );
        mCodec.start();

        mReceivedVideoDataCallBack = new DJIReceivedVideoDataCallBack(){
            private int packetLength = 0;
            private ByteBuffer accessUnitBuffer = ByteBuffer.allocate(50000);
            int inIndex;
            long presentationTime = 0;

            @Override
            public void onResult(byte[] videoBuffer, int size){
                ArrayList<byte []> NAL_Units = splitNALunits(videoBuffer,size);
                //Send the I-Frame first
                if (!seq_start && iframe_ready){
                    inIndex = mCodec.dequeueInputBuffer(0);
                    if (inIndex >= 0) {
                        ByteBuffer inputBuffer = mCodec.getInputBuffer(inIndex);
                        inputBuffer.put(iframe,0,iframe.length);
                        mCodec.queueInputBuffer(inIndex, 0, iframe.length, presentationTime, 0);
                        presentationTime += 100;
                        seq_start = true;
                        handler.sendMessage(handler.obtainMessage(SHOWTOAST, "I-Frame queued!!!"));
                    }
                }
                for( int i=0; i< NAL_Units.size(); i++ ) {
                    if( NAL_Units.get(i)[4] == 0x09 ) {
                        // Send off the current buffer of data (Access Unit)
                        inIndex = mCodec.dequeueInputBuffer(0);
                        if (inIndex >= 0) {
                            ByteBuffer inputBuffer = mCodec.getInputBuffer(inIndex);
                            if (packetLength > 0)
                                inputBuffer.put(accessUnitBuffer.array(), 0, packetLength);
                            mCodec.queueInputBuffer(inIndex, 0, packetLength, presentationTime, 0);
                            presentationTime += 100;
                            packetLength = 0;
                            accessUnitBuffer.clear();
                            accessUnitBuffer.rewind();
                        }
                    }
                    accessUnitBuffer.put(NAL_Units.get(i));
                    packetLength += NAL_Units.get(i).length;
                }
                MediaCodec.BufferInfo bufferinfo = new MediaCodec.BufferInfo();
                int outputBufferIndex = mCodec.dequeueOutputBuffer(bufferinfo, 0);
                handler.sendMessage(handler.obtainMessage(SHOWTOAST, "outputBufferIndex = "+ outputBufferIndex));
                while (outputBufferIndex >= 0){
                    mCodec.releaseOutputBuffer(outputBufferIndex, true);
                    outputBufferIndex = mCodec.dequeueOutputBuffer(bufferinfo, 0);
                }
            }
        };
        DJIDrone.getDjiCamera().setReceivedVideoDataCallBack(mReceivedVideoDataCallBack);
    }
    catch(IOException e)
    {
        e.printStackTrace();
    }
}

2 个答案:

答案 0 :(得分:0)

在媒体codec.configure()中,如果你没有设置surface(设置为null),它将获得解码的结果,你需要自己绘制图像。

您可以搜索关键字:“media codec opengl”。你会找到样品。

PS。输出格式不确定。 (也许是RGBA)

答案 1 :(得分:0)

尝试使用MediaExtractor。如果所有受支持的数据源(请参阅setDataSource方法)都不符合您的需求,请考虑通过实施MediaDataSource来创建自定义数据源

另外,请注意,某些编解码器可能需要您在生成单个输出缓冲区之前输入多个输入缓冲区。