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

从mpeg ts文件中提取I帧(1):将ts包拼装为section或pes包

程序员文章站 2022-07-14 18:18:52
...

写在前面的话:
        不觉中已经在数码工作了十年有余,十年的青春、十年的汗水,如今即将离开,个中滋味难以言表!
以后可能不会在广电行业工作了,就用这个小程序,为自己十年的广电工作画上一个句号吧。
依旧是:版权没有 盗版不纠 欢迎转载。

本程序的功能:提取ts文件的视频I帧并显示输出,同时可以将I帧保存为yuv bmp格式的文件。
基本思路:
1、探测文件中ts包的长度 188?192?204?
2、将ts包拼装为section,解析pat section。
3、依据pat,解析所有的pmt section。
4、依据pmt,分析video pid是否合法。
5、将ts包拼装为pes,依据video pid获取video pes。
6、解析video pes,获取I帧。
7、使用ffmpeg解码I帧为 yuv bmp格式。
8、使用opencv显示yuv。

开发环境:
ubuntu-14.04-64位
gcc-4.8.4 
ffmpeg-3.0.0 
opencv-3.4.0
SlickEdit Pro 2016 感兴趣的可以参考:https://blog.csdn.net/maxzero/article/details/78728098

本章主要讲解如何将ts包拼装为section或pes包。依据的标准为ITU-T H.222.0建议书。

一、mpeg ts包的语法结构

从mpeg ts文件中提取I帧(1):将ts包拼装为section或pes包

从mpeg ts文件中提取I帧(1):将ts包拼装为section或pes包

1、ts包的长度有3种
     * 欧洲标准是 188B
     * 日本标准是 192B
     * 第三种是     204B 是在188B的基础上,加上16B的FEC(前向纠错).

2、相关字段解析
     * sync_byte  ts包的起始值,固定为0x47。
     * payload_unit_start_indicator 
       当传输流包有效载荷包含 PES 数据时: 
      1-指示此传输流包承载PES包的首字节。0-指示此传输流包不承载PES包的首字节。 
      当传输流包有效载荷包含 PSI 数据时: 
      1-指示此传输流包承载PSI分段的首字节,并存在pointer_field字段。
      0-指示此传输流包不承载PSI分段的首字节,不存在pointer_field字段。
    *data_byte 有效负载
    *pid 指示包有效载荷中存储的数据类型。
    *continuity_counter 连续性计数器。相同pid的ts包continuity_counter在0-15之间循环。可以用来判断是否丢包。

二、ts包拼装为section pes流程

从mpeg ts文件中提取I帧(1):将ts包拼装为section或pes包
为了图的简洁并未画出完整的ts header。已pat(pid=0)为例:
1、首先找到pid=0,payload_unit_start_indicator=1的ts包,该包为pat section的起始包,并记录continuity_counter的值n。
2、下一个包就应该是 pid=0,payload_unit_start_indicator=0 continuity_counter=n+1。
3、以此类推,当找到pid=0,payload_unit_start_indicator=1时,搜索结束。
4、去掉ts header将这些有效负载依次拼装起来就是完整的pat section。

说明:
1、对于section数据,我们拿到第一个ts包后,可以通过分析section头部的section_length字段获取section的总长度,
      并依据这个长度来接收剩余的section数据。
2、对于pes数据,其长度是可变的,只能依据上图描述的方法进行接收。

三、ts包的解析
重点注意:有效负载为0的情况和pointer_field字段即可。

int mpeg_tsp_packet_parse(transport_packet_t *ptspkt, uint8_t *data, uint32_t size, uint32_t type)
{
    uint8_t *ptr = data;

    if (NULL==ptr || NULL==ptspkt || size<MPEGTS_PACKET_SIZE) {
        print_err("ptspkt=%p data=%p size=%d\n", ptspkt, data, size);
        return -1;
    }

    ptspkt->sync_byte                    = ((ptr[0]));
    ptspkt->transport_error_indicator    = ((ptr[1]>>7));
    ptspkt->payload_unit_start_indicator = ((ptr[1]<<1) >> 7);
    ptspkt->transport_priority           = ((ptr[1]<<2) >> 7);
    ptspkt->pid                          = ((ptr[1]&0x1f)<<8) | ptr[2];
    ptspkt->transport_scrambling_control = ((ptr[3]&0xc0) >> 6);
    ptspkt->adaption_field_control       = ((ptr[3]&0x30) >> 4);
    ptspkt->continuity_counter           = ((ptr[3]&0x0f));

    if (0x47 != ptspkt->sync_byte) {
        print_err("sync_byte=0x%x\n", ptspkt->sync_byte);
        return -1;
    }
    if (1 == ptspkt->transport_error_indicator) {
        print_err("pid=0x%04x transport_error_indicator=0x%x\n", ptspkt->pid, ptspkt->transport_error_indicator);
        return -1;
    }

    ptr += 4;
    ptspkt->playload_offset += 4;
    
    /*处理adaption_field字段*/
    if (ptspkt->adaption_field_control==0x02 || ptspkt->adaption_field_control==0x00) {
        ptspkt->adaptation_field_length = ptr[0];
        ptspkt->playload_offset = 0;
        /*
        print_dbg("pid=0x%04x adaptation_field. no playload.\n", ptspkt->pid);*/
        return 0;
    }
    
    if (ptspkt->adaption_field_control == 0x03) {
        ptspkt->adaptation_field_length = ptr[0];
        ptr += (ptspkt->adaptation_field_length + 1);
        ptspkt->playload_offset += (ptspkt->adaptation_field_length + 1);
    }
    
    /*承载PSI/SI数据时才有pointer_field*/
    if (ptspkt->payload_unit_start_indicator==1 && MPEGTS_PACKET_TYPE_PSI==type) { 
        ptspkt->pointer_field = ptr[0];
        ptr += 1;
        ptspkt->playload_offset += 1;
    }

    return 0;
}

mpeg2标准:https://download.csdn.net/download/maxzero/10402761
完整的代码:https://download.csdn.net/download/maxzero/10572383

相关标签: mpeg ts pes