从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
上一篇: 用python生成(动态彩色)二维码(使用myqr库实现)
下一篇: 二维码简单Demo