提取现成的ts流中的i帧文件

来源:互联网 发布:西安市莲湖区行知小学 编辑:程序博客网 时间:2024/06/05 07:14

    进入公司的新人培训第二个作业,看了几天的文档都没太搞懂es包的结构,网上的资源也比较少,多数是介绍ts流和pes包的。所以花费了好几天才完成。

    这个作业是对第一个作业的继续,在上一次作业中成功提取出来了pes包的视频,这次作业在第一次的基础上将pes包的包头去掉提取出来es视频,然后在es视频中提取出i帧来。

  

上图是pes包的结构。

     pes包6个字节的包头,其中Optional PES Header的长度是不固定的,一个字节的PES Header Data Length的值表示该字段后的字段的长度。后边的pacrt data bytes就是es包了。

      所以提取es包就是要确定es包前面的长度。

adaption_control == 0x10时表明ts包中没有调整字段,ts包头后直接是有效负载,若payload_unit_start == 1说明ts包头后面是pes包那么es包前的长度就是length = 4 + 6 + 3 +PES_header_data_length;   若payload_unit_start == 0说明ts包后面的有效负载直接是es包,那么es包前面的长度就是length = 4 ;即ts包header的长度。

adaption_control == 0x30时表明ts包头后面是调整字段,调整字段后面才是有效负载,此时计算es包前面的长度则根据上面的情况再加上一段调整字段的长度。若payload_unit_start == 1则length = 4 + 1 + adaption_length + 6 + 3 + PES_header_data_length ;若payload_unit_start == 0;length = 4 + 1 + adaption_length ;这个地方的1是调整字段的第一个字节,这个字节的值表明该字节后调整字段的长度。

知道es包前面的长度,将这些去掉就可以将es文件保存下来了。保存的结构应该是一段可以播放的视频。

下面我们来提取i帧:

提取i帧首先要了解es包的结构,但是es结构并没有想上面pes结构一样那么详细的介绍,

由上图我们可以看到es结构是分层结构,下面分别是起始码,sequence_start_code是视频数据包的的起始码,group_start_code是图像组的起始码,picture_start_code是图片的起始码,也就是每个帧的起始码。其中要注意的是,每个起始码是由一个起始码前缀和跟在后面的一个起始码值组成的,起始码前缀由23个0和一个1组成,这样起始码前缀就是 00 00 01.所以开头都是4个字节32位,24位前缀加8位起始码组成。

然后我们要判断哪个帧为i帧。每个帧的标识是由picture的第6个字节的第3 4 5 位来表示的 001表示i帧 010表示p帧 011表示B帧(如果视频是h264另当别论),找到i帧后就要确定i帧的起始位置和结束位置,然后将i帧保存下来就可以了!!!

首先判断i帧的起始位置,用picture的起始码00 00 01 00 找到一个picture若这个picture的picture_coding_type==001即第六个字节的第345位是001那么这就是i帧,将开始位置保留。遇到下一个picture的开始或者是group的开始或者是下一个视频包的开始都可以表示i帧结束,即 00 00 01 00 或者 00 00 01 B3 或者00 00 01 B8都是判断i帧结束的标识。将碰到的每一个i帧保存到文件里即可。

如果想要能保存的文件可以播放必须还要在开始保存一个视频包的序列头,这个序列头里包含了图像大小帧速率量化矩阵层号分级法码率等信息(具体播放时如何解码我也不是很清楚,但是没有保存序列头保存出来的i帧是无法播放的),注:保存序列头并不是保存00 00 01 B3这个起始码,而是从起始码开始到GOP开始这一段都要保存下来,长度不固定所以还是要靠起始码判断数据头的开始和结尾。

代码写的略有点乱,总结一下问题:

一。申请一兆的缓冲区:unsigned char buffer[2014*2014]而不是unsigned char buffer[2014]。

二。还有就是一些符号的优先级问题加不加括号?

if((es_buffer[j + 5] >> 3)&0x07== 0x01)错!位运算比==优先级低应该是if(((es_buffer[j + 5] >> 3)&0x07) == 0x01),不其实论优先级高低要习惯性加括号哟~

if(((es_buffer[j] == 0x00 )&&( es_buffer[j + 1] == 0x00 )&&( es_buffer[j + 2] == 0x01) &&( es_buffer[j + 3] == 0x00))||
     ((es_buffer[j] == 0x00 )&&( es_buffer[j + 1] == 0x00 )&&( es_buffer[j + 2] == 0x01) &&( es_buffer[j + 3] == 0xB3))||
     ((es_buffer[j] == 0x00 )&&( es_buffer[j + 1] == 0x00 )&&( es_buffer[j + 2] == 0x01) &&( es_buffer[j + 3] == 0xB8)))&&的优先级大于||但是每个||的条件都要用括号括起来。

三。fwrite(temp, size, 1 , fp);fwrite是代表把temp里的内容从temp[0]开始的size个字节保存到fp指向的文件里,所以如果从第j个开始保存是fwrite(temp+j, size, 1 , fp);或者fwrite(&temp[j], size, 1 , fp);

四。计算一个文件的长度 ,fp是指向文件的指针

    fseek(fp , 0 , SEEK_END);
    size = (unsigned int)ftell(fes);
    fseek(fp , 0 , SEEK_SET);

五。for(i = 0 ; i < es_size/(1024*1024) ; i ++)注意加括号哦   for(i = 0 ; i < es_size/1024*1024 ; i ++)错!!!

六。while for 循环 看要不要加break!

 

 

0 0
原创粉丝点击