欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

从PS视频流中提取H264数据

程序员文章站 2022-07-06 12:34:11
...

       最近一线同事反映,视频流解码后出现花屏现象。于是我让现场人员用wireshark抓一下包,发现服务器拉流走的是UDP协议的流,怪不得会花屏,网络差的时候,丢包是肯定的了。将花屏的视频文件下载下来后,发现是PS封装的H264。重点是有的PS文件能用ffplay播放,有的不能。我就纳闷了,本着上次被海康平台坑过一回的阴影,还是自己写一个从PS文件里面提取H264裸码流数据的小工具为好。

从PS视频流中提取H264数据

    结果发现,出现问题的PS文件的第一个PS-packet的pes_packet_length比实际的大,而剩下的其他PS包都是正常的,说明第一个PS包出现了问题,从而导致ffmpeg打开失败,ffmpeg解析PS的代码文件为 ffmpeg-3.2.4/libavformat/mpeg.c


static int mpegps_read_pes_header(AVFormatContext *s,
                                  int64_t *ppos, int *pstart_code,
                                  int64_t *ppts, int64_t *pdts)
{
    MpegDemuxContext *m = s->priv_data;
    int len, size, startcode, c, flags, header_len;
    int pes_ext, ext2_len, id_ext, skip;
    int64_t pts, dts;
    int64_t last_sync = avio_tell(s->pb);
... ...

    /* find matching stream */
    if (!((startcode >= 0x1c0 && startcode <= 0x1df) ||
          (startcode >= 0x1e0 && startcode <= 0x1ef) ||
          (startcode == 0x1bd) ||
          (startcode == PRIVATE_STREAM_2) ||
          (startcode == 0x1fd)))
        goto redo;
    if (ppos) {
        *ppos = avio_tell(s->pb) - 4;
    }
    len = avio_rb16(s->pb); //此处即为 pes_packet_length (16bits)
    pts =
    dts = AV_NOPTS_VALUE;
    if (startcode != PRIVATE_STREAM_2)
    {
    /* stuffing */
    for (;;) {
        if (len < 1)
            goto error_redo;
        c = avio_r8(s->pb);
        len--;
        /* XXX: for MPEG-1, should test only bit 7 */
        if (c != 0xff)
            break;
    }
... ...
}

从PS视频流中提取H264数据

ffmpeg中直接读取的pes_packet_length值作为PES包的大小,而实际上,此PES包所在的PS包总大小比pes_packet_length小,从而导致ffmpeg将下一个PS Header误认为是H264数据,后续的所有PS包都会出现定位不准。这种情况,有两种解决办法,

1. 不相信pes_packet_length的值,先找到两个相邻的PS包头的位置,即找到相邻的两个 00 00 01 BA 包起始码位置,比如pos1和pos2,那么只需要在 [pos1, pos2] 范围内查找所有的PES包起始码 00 00 01 E0 ,就可以找到每个PS包中含有的所有PES包,对于每一个PES包,跳过PES header,剩下的就是H264裸码流数据了,把这些H264数据保存到同一个文件中就可以了

2. 既然是 pes_packet_length 的值不正确,那么将原始的pes_packet_length值对应修改正确就可以了

最后说一句,下载的PS文件中的program_stream_map_length跟ISO/IEC文档定义的不一样,HIK又一个坑

工程地址:https://github.com/jfu222/ps_h264_payload_split