首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >视频呈现中断MediaCodec H.264流

视频呈现中断MediaCodec H.264流
EN

Stack Overflow用户
提问于 2015-09-22 17:39:52
回答 1查看 4.5K关注 0票数 5

我正在使用MediaCodec Java实现一个解码器,用于解码实时H.264远程流。我正在接收H.264编码的数据从本机层使用回调(void OnRecvEncodedData(byte[] encodedData)),解码和渲染的Surface of TextureView。我的实现已经完成(使用回调、解码和呈现等检索编码流)。这是我的解码课:

代码语言:javascript
运行
复制
public class MediaCodecDecoder extends Thread implements MyFrameAvailableListener {

    private static final boolean VERBOSE = true;
    private static final String LOG_TAG = MediaCodecDecoder.class.getSimpleName();
    private static final String VIDEO_FORMAT = "video/avc"; // h.264
    private static final long mTimeoutUs = 10000l;

    private MediaCodec mMediaCodec;
    Surface mSurface;
    volatile boolean m_bConfigured;
    volatile boolean m_bRunning;
    long startMs;

    public MediaCodecDecoder() {
        JniWrapper.SetFrameAvailableListener(this);
    }

    // this is my callback where I am receiving encoded streams from native layer 
    @Override
    public void OnRecvEncodedData(byte[] encodedData) {
        if(!m_bConfigured && bKeyFrame(encodedData)) {
            Configure(mSurface, 240, 320, encodedData);
        }
        if(m_bConfigured) {
            decodeData(encodedData);
        }
    }

    public void SetSurface(Surface surface) {
        if (mSurface == null) {
            mSurface = surface;
        }
    }

    public void Start() {
        if(m_bRunning)
            return;
        m_bRunning = true;
        start();
    }

    public void Stop() {
        if(!m_bRunning)
            return;
        m_bRunning = false;
        mMediaCodec.stop();
        mMediaCodec.release();
    }

    private void Configure(Surface surface, int width, int height, byte[] csd0) {
        if (m_bConfigured) {
            Log.e(LOG_TAG, "Decoder is already configured");
            return;
        }
        if (mSurface == null) {
            Log.d(LOG_TAG, "Surface is not available/set yet.");
            return;
        }
        MediaFormat format = MediaFormat.createVideoFormat(VIDEO_FORMAT, width, height);
        format.setByteBuffer("csd-0", ByteBuffer.wrap(csd0));
        try {
            mMediaCodec = MediaCodec.createDecoderByType(VIDEO_FORMAT);
        } catch (IOException e) {
            Log.d(LOG_TAG, "Failed to create codec: " + e.getMessage());
        }

        startMs = System.currentTimeMillis();
        mMediaCodec.configure(format, surface, null, 0);
        if (VERBOSE) Log.d(LOG_TAG, "Decoder configured.");

        mMediaCodec.start();
        Log.d(LOG_TAG, "Decoder initialized.");

        m_bConfigured = true;
    }

    @SuppressWarnings("deprecation")
    private void decodeData(byte[] data) {
        if (!m_bConfigured) {
            Log.e(LOG_TAG, "Decoder is not configured yet.");
            return;
        }
        int inIndex = mMediaCodec.dequeueInputBuffer(mTimeoutUs);
        if (inIndex >= 0) {
            ByteBuffer buffer;
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                buffer = mMediaCodec.getInputBuffers()[inIndex];
                buffer.clear();
            } else {
                buffer = mMediaCodec.getInputBuffer(inIndex);
            }
            if (buffer != null) {
                buffer.put(data);
                long presentationTimeUs = System.currentTimeMillis() - startMs;
                mMediaCodec.queueInputBuffer(inIndex, 0, data.length, presentationTimeUs, 0);
            }
        }
    }

    private static boolean bKeyFrame(byte[] frameData) {
        return ( ( (frameData[4] & 0xFF) & 0x0F) == 0x07);
    }

    @Override
    public void run() {
        try {
            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
            while(m_bRunning) {
                if(m_bConfigured) {
                    int outIndex = mMediaCodec.dequeueOutputBuffer(info, mTimeoutUs);
                    if(outIndex >= 0) {
                        mMediaCodec.releaseOutputBuffer(outIndex, true);
                    }
                } else {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException ignore) {
                    }
                }
            }
        } finally {
            Stop();
        }
    }
}

现在的问题是-流被解码和渲染在表面,但视频是不清楚的。似乎框架被打破了,场景被扭曲/肮脏。运动是打破和方形碎片到处(我真的很抱歉,因为我现在没有截图)。

关于我的流-它的H.264编码,只包括I帧和P帧(没有B帧)。每个I帧都有SPS + PPS + payload结构。在编码期间使用的颜色格式(在本机层中使用FFMPEG )是YUV420计划器。从本机层发送的数据长度是可以的(宽度*高度* (3 / 2))。

configure()期间,我只是用SPS框架设置了csd-0值。用于配置的框架是I帧(SPS + PPS +有效负载)-前缀是SPS帧,因此我认为配置是成功的。注意,我没有用PPS框架设置csd-1值(这是一个问题吗?)

对于p帧和i帧,每个帧都有前面的开始代码(0x00 0x00 0x00 0x01) (对于i帧,开始代码同时存在于SPS和PPS帧的前面)。

此外,我将每个帧的表示时间戳设置为System.currrentTimeMillis() - startTime,这将增加每个新帧的订单。我认为这不会引起任何问题(如果我错了,请纠正我)。

我的设备是谷歌的Nexus 5,安卓版本4.4.4,芯片组是高通( Qualcomm ) MSM8974 Snap巨龙800。我正在使用Surface解码,所以我认为不应该有任何特定的设备颜色格式不匹配问题。

如果需要,我还可以提供我的TextureView代码。

我不正确的解码/渲染可能是什么原因?提前感谢!

编辑1

我尝试在配置过程中手动传递特定于编解码器的数据(SPS和PPS字节)。但这并没有改变:

代码语言:javascript
运行
复制
byte[] sps  = {0x00, 0x00, 0x00, 0x01, 0x67, 0x4d, 0x40, 0x0c, (byte) 0xda, 0x0f, 0x0a, 0x68, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x07, (byte) 0xa3, (byte) 0xc5, 0x0a, (byte) 0xa8};
format.setByteBuffer("csd-0", ByteBuffer.wrap(sps));

byte[] pps = {0x00, 0x00, 0x00, 0x01, 0x68, (byte) 0xef, 0x04, (byte) 0xf2, 0x00, 0x00};
format.setByteBuffer("csd-1", ByteBuffer.wrap(pps));

我也尝试了削减开始代码(0x00, 0x00, 0x00, 0x01),但没有进展!

编辑2

我尝试了硬件加速{textureView},就像在正式文件中提到的那样(尽管我在MediaCodec的示例项目中没有找到任何H/W加速代码)。但还是没有进展。现在我注释了H/W加速代码片段。

编辑3

这些截图现在是可以看的了:

编辑4

为了进一步澄清,这是我的H.264编码的i帧十六进制流格式:

00 00 01 67 4d 40 C da 0f 0a 68 40 00 00 03 00 40 0007 a3 c5 0a a8 00 01 68 ef 04 f2 00 01 06 ff ff 69 dc 45 bd e6 d9 48 b7 96 2c d8 20 b7 23 ee ef 78 36 34 20 2d 20 63 6f 72 65 31 34 20 20 2d 20 48 2e 32 34 34 2f 4d 45 47 2d 34 41 56 43 63 664 63 20 664 63 20 20 23 f 70 79 6c 65 79 6c 66 32 30 30 33 2d 32 3230 31 35 20 2d 20 68 74 74 70 3a 2f 2f 77 77 2e 76 69 64 65 6f 6c 61 6e 6e 6f 72 67 2f 78 36 34 2e 68 74 6d 6d 20 2d 20 6f 70 74 69 6f 6e 733 a 20 61 61 3d 31 31 65 3d 31 31 20 20 61 6c 6f 63 6c 6f 63 6b 3d 31 3a 30 3a 30 3a 30 20 61 e 61 6c 73 3d 30 31 3a 30 78 20 31 d 65 3d 68 78 20 6d 65 3d 68 78 20 73 62 6d 65 30 70 73 79 3d 31 3120 70 73 79 5f 72 64 3d 31 2e 30 30 3a 30 30 20 669 78 65 64 5f 72 66 3d 30 20 60 d 65 65 61 6e 67 36 20 63 72 6f 6d 61 6d 6f 6d 65 31 31 f 6d 65 20 72 65 6c 69 73 30 38 38 38 64 74

这是一个P帧:

000001 41 9a 26 df b2 ef 57 ac 5b b6 3b 68 b9 87 a5 9b 61 3c 93公元前79 bc 79 c5 ab 0f 87 f6 40 6a cd 80 03 b1 a2 c2 4e 13 cd 4e 3c 62 3e 44 0a 97 80 ec 81 3c f1 29 f1 43 a0 c0 a9 0a 74 62 c7 62 74 da c3 94 c7 19 23 ff 4b 9c 69 55 52f 62 en20#ec 19 654 2f 62 ec 20 5e 18 358 7f 18 358 73 e 93 6e 06 fd 80 cf 86 71 24 7f7 56 2c c1 57 cf ba 05 17 17 18 f1 8b 3c 33 40 18 30 1 f b0 19 23 44 ec 91 c4 bd 80 65 4a 46 b3 1e 53 5d a3 f0 b5 50 3a 93 ba 81 71 f3 09 41 43 ba 5f a1 0d 41 a3 7b c3 fd 15 89 766 a9 ee 3a 9c 1b c1 aa f8 58 10 88 0c 79 77 ff 15 28 eb 12 a7 1b 36 aa 384 a9 3e 63 cf e1 cf 4a 21 3c 933#3c 9c 56 en22 4c 6c 4b 12c5 ec 5a 98 8c 12 75 eb fd 98 a4 fb 7f 80 5d 28 f9 f9 43 a4 0a约25 75 19 6b f7 14 7b 76 af e9 8f 7 79 fa 9d 9a 63 de 1 f是fa 6c 65 ba 5f b0 b0 f4 71 cb e2 d6 d6 dc c6 55 98 1b cd 55 d9 eb 9c 75 fc 9d ec

我非常肯定我的流的正确性,因为我使用ffmpeg解码成功地呈现了流,用OpenGLES 2.0成功地呈现了GLSurfaceview

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-10-23 04:14:39

我从本机层和Java层进行了H.264转储,发现本地层的转储效果很好,但是Java层的转储就像解码流一样被破坏。问题是--在将编码流从本机层传递到Java时,编码流没有正确传递(损坏),这是因为我的错误实现(很抱歉,由于这种不便而跟踪这个线程的人)。

此外,我只将I帧的有效负载传递给解码器,从而导致渲染中断。现在我正在传递完整的NAL单元(SPS + PPS +有效载荷),现在一切都好了:)

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32723393

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档