video - RTP解包

标签 video protocols h.264 rtp rtsp-client

我正在尝试解析 RTP AVC 视频流以为 H264 解码器做好准备。

这是我的Wireshark捕获的数据包流 首先,我试图找到 IDR 切片、SPS 和 PPS 参数,所以就是这样 https://dl.dropboxusercontent.com/u/76817805/frame.pcapng

接下来我将执行以下操作:

1) 查找 PPS 和 SPS 数据,并将它们复制到 NAL 单元,并以 [0x00 0x00 0x01] 开始序列进入解包缓冲区。

[00 00 01 | SPS][00 00 01 | PPS]

2) 对于以 [0x7C 0x85] 开头的数据包(起始位 = 1),我正在重建第一个 NAL heder(对于我的情况为 0x65),并将 0x7C 0x85 后面的数据复制到解包缓冲区中启动序列。

[00 00 01 65 |视频数据……]

3) 对于以 [0x7C 0x05] 开头的数据包,我正在将除前 2 个字节之外的数据复制到解包缓冲区中。

[.....视频数据.....]

4) 对于以 [0x7C 0x45] 开头的数据包(停止位 = 1),我将除前 2 个字节之外的数据复制到解包缓冲区中。 [.....视频数据(最后一个字节)]

5) 对于未分段的数据包,我只是将数据复制到带有起始序列的解包缓冲区中。

[00 00 01 | NALu]

因此,在解析示例视频流的最后,我得到了这个二进制文件 https://dl.dropboxusercontent.com/u/76817805/raw.264 ,但无法正确解码。 ![在此处输入图像描述][1]

有人可以帮我找出我的算法中的错误吗?我究竟做错了什么? 非常感谢大家。

UInt32 parseRTP( Uint8 * buf, int inputDataLen, Uint32 curAdr)
{
int result_len = 0;

// filter zero bytes at the end of packet

for (i = inputDataLen-1; i>0; i--)
{
    if (buf[i] == 0x00) inputDataLen--;
    else break;
}

// get NAL type
nal = buf[0];
type = (nal & 0x1f);

if ((buf[0] == 0x7C) && (buf[1] == 0x85)) IFrameisOK = 1; // Start of I frame

if (type == 6)
    return 0;

if (type == 7) // new SPS
{
    memcpy((void*)sps, start_sequence, sizeof(start_sequence));
    memcpy((void*)(sps + sizeof(start_sequence)), buf, inputDataLen);
    sps_len = inputDataLen + sizeof(start_sequence);
    SPSisOK = 1;
    return 0;
}

if (type == 8) // new PPS
{
    memcpy((void*)pps, start_sequence, sizeof(start_sequence));
    memcpy((void*)(pps + sizeof(start_sequence)), buf, inputDataLen);
    pps_len = inputDataLen + sizeof(start_sequence);
    PPSisOK = 1;
    return 0;
}



if (SPSisOK == 1 && PPSisOK == 1)
{
    if (IFrameisOK == 0) return 0; // wait I-frame

    /*  Simplify the case.
        These are all the nal types used internally by the h264 codec
     */
    if (type >= 1 && type <= 23) type = 1;
    switch (type)
        {
            case 0: // undefined;
                break;

            case 1:
                // copy start sequence

                memcpy((void*)curAdr, start_sequence, sizeof(start_sequence));
                curAdr += sizeof(start_sequence);

                // copy data
                memcpy((void*)curAdr, buf, inputDataLen);
                curAdr += inputDataLen;

                result_len = sizeof(start_sequence) +  inputDataLen;
                break;

            case 24: // STAP-A (one packet, multiple nals)  not used in this project
                break;
            case 25: // STAP-B
            case 26: // MTAP-16
            case 27: // MTAP-24
            case 29: // FU-B
                //not used in this project
                break;
            case 28: // FU-A (fragmented nal)

                inputDataLen -= 2; // delete 2 first bytes for fragmented units
                //skip the fu_indicator
                buf++;

                Uint8 fu_indicator = nal;
                Uint8 fu_header = *buf; // read the fu_header.
                Uint8 start_bit = fu_header >> 7;
                Uint8 reconstructed_nal;
                Uint8 nal_type = (fu_header & 0x1f);


                /* reconstruct this packet's true nal; only the
                data follows..*/
                reconstructed_nal = fu_indicator & (0xe0);

                /*the original nal forbidden bit and NRI are stored in this
                packet's nal*/
                reconstructed_nal |= nal_type;

                // skip the fu_header...
                buf++;

                if(start_bit)
                {
                    if (NEED_CONFIGS)
                    {
                        // copy SPS and PPS first
                        memcpy((void*)curAdr, sps, sps_len);
                        curAdr += sps_len;

                        memcpy((void*)curAdr, pps, pps_len);
                        curAdr += pps_len;

                    }

                    // copy in the start sequence
                    memcpy((void*)curAdr, start_sequence, sizeof(start_sequence));
                    curAdr += sizeof(start_sequence);


                    // copy reconstructed nal
                    memcpy((void*)curAdr,&reconstructed_nal, sizeof(reconstructed_nal));
                    curAdr += sizeof(reconstructed_nal);


                    // copy payload
                    memcpy((void*)curAdr,buf, inputDataLen);
                    curAdr += inputDataLen;

                    if (NEED_CONFIGS)
                    {
                        result_len = (sps_len + pps_len + sizeof(start_sequence) + sizeof(reconstructed_nal) + inputDataLen);
                        NEED_CONFIGS = 0;
                    }
                    else
                    {
                        result_len += (sizeof(start_sequence) + sizeof(reconstructed_nal) + inputDataLen);
                    }

                }
                else
                {

                    memcpy((void*)curAdr,buf, inputDataLen);
                    curAdr += inputDataLen;

                    result_len = inputDataLen;
                }


                break;
            default:
                break;
        }
    return result_len;
}
else
{
    return 0;
}

}

最佳答案

解包规则在RFC 6184 - RTP Payload Format for H.264 Video中描述。你应该遵循它们,而不是尝试发明自己的。

您认为在片段前面添加 00 00 01 65 会重建 NAL 单元的假设是不正确的。

这个想法是,这个 NAL 单元太大,无法容纳单个数据包,然后将其分成多个部分。您将接收多个 RTP 片段,然后将它们组合成单个 NAL 单元,并以其原始状态完整重建它。参见部分 5.8.有关详细信息,请参阅碎片单元 (FU)。

您无需遵循上述内容,只需将 00 00 01 65 添加到 NAL 单元的每个部分 - 预计这不会产生可解码的输出。

另请参阅:

关于video - RTP解包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25362239/

相关文章:

objective-c - AVFoundation H.265 硬件编码

用于场交错 H264 的 ios vtb 解码器

css - webkit 中非 flash 视频的 z-index

android捕获视频帧

java - 通过以太网转换器连接RS485设备

ios - 如何用 where 子句重构这个 Swift 协议(protocol)?

ffmpeg - 转码后的视频流无法在 QuickTime 播放器中播放

javascript - 通过 Websockets 进行 WebRTC 视频聊天

javascript - 一个小时无法播放一组视频

ios - Swift 协议(protocol)不起作用