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

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

本程序的功能:提取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包的语法结构

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流程


为了图的简洁并未画出完整的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

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值