从PS视频流中提取H264数据

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

从PS视频流中提取H264数据_第1张图片

    结果发现,出现问题的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数据_第2张图片

从PS视频流中提取H264数据_第3张图片

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

你可能感兴趣的:(从PS视频流中提取H264数据)