ffmpeg H264/mpegts 解析

h264解析码流的流程

理解流程的几个问题:

  1. 如何找一个完整的帧
  2. 如何处理多slice的情况
  3. 一个完整的帧如何拷贝出去的
  4. ffmpeg parse流程读数据怎么读的

如何找一个完整的帧

  • 以0x00 00 01 或者0x 00 00 00 01开头的码流,其后解析到是一个first_mb_in_slice才会一个完整的帧的开始, 因为sps pps 等也有可能是以 0x00 00 01 或者0x 00 00 00 01为起始码开头的。码流 过到 什么时候 才能说明是得到一个完整的帧?
  • 过码流, 首先获取到起始码, 说明是一帧或者sps、pps等开始,开始解析看是sps pps,aud,subsps,vd的情况,那么继续过(循环i继续加)当找到是 NALU_TYPE_SLICE 、NALU_TYPE_DPA NALU_TYPE_IDR 、NALU_TYPE_SLC_EXT 说明是一帧的开始。
  • 继续过读取到first_mb_in_slice,这个时候表明之前找到的那一帧开头是正确的,标记上已经找到帧开头了。帧开头开始继续过码流,这个时候码流里面就存放的是视频帧的数据了,当找到下一个起始头(0x00 00 01 或者0x 00 00 00 01)的时候,说明找到了完整的一个视频帧了, 停止循环返回当时读的的位置 扣掉 起始码的位置。
  • 总的来说 并不是码流解析到这一帧是视频帧数据的时候就返回,需要一直过道下一个起始码的时候才能返回这一帧的码流数据

多slice的情况

  • 对于I 或者 IDR B P 等帧。 有first_mb_in_slice并且每一帧第一片的first_mb_in_slice都是0。所以就根据当前的first_mb_in_slice 和之前的first_mb_in_slice 来判断是否是多slice的情况。
  • mb初始化为0 。 正常是单slice的情况 , 在下一次循环找到sps pps的时候起始码就返回了, 而 多slice的情况 并不会在那边返回,会重新改变state 然后跑到读mb的情况, 然后在读mb的 如果到下一个mb 就是0的时候 说明 是一个新帧的开始了,一个多slice的完整帧找到了。

完整的帧怎么拷贝的

  • ff_combine_frame:ff_h264_find_frame_end 返回的是负数,就是说明 刚刚传下来的这个buffer 是不够的,那么这边 先把这个buffer拷贝到outbuffer 中,当返回是大于0的数,这个index 表明 完整的一帧我还需要从后面传进去的这个buffer中拷走多少字节。
  • combine 就把之前那个buffer扩大到之前的buffersize+index, 然后拷贝index 个字节到这buffer中,返回到parse那边 ,添加到parse_queue。然后后面packet 读出去。

ffmpeg parse流程读数据怎么读的

  • 如果是有parse的情况, 当parse结束了,也就是next 返回的是大于0 的数的时候,会combine 这时候返回的size是大于0 ( *poutbuf = buf; *poutbuf_size = buf_size;)。否则的话 *poutbuf = NULL *poutbuf_size = 0;
  • 返回到parse_packet 这边,先把parse 出来的包add到parse queue那边。然后会继续把送入buffer 的 剩下的数据在送进去, 这个时候如果不够的,返回的size 为 0 才会退出这个循环, 返回到read_frame_internal 里面 这里面调用read_from_packet_buffer 从刚刚的parse queue 这边去把这一帧数据给读上来。 返回给应用这边。

mpegts pes解包流程

  • 编码端是怎么做的,一个es的视频数据,加上pes的header 包含时间等,变成一个pes 包,pes包 被切割然后加上ts的头形成一个个188 直接的ts,那么解码端就是编码的方向流程。

  • 解码端:ffmpeg调用io接口 读了188字节上来,一个ts的包,ts包里面有标记是不是一个pes的开始 is_start。 如果是一个pes的开始,那么进行pes包的组帧。
    初始化pes->state 为MPEGTS_HEADER,data_index 为0。循环处理这个188直接的buf

  • MPEGTS_HEADER:首先解析ts 的header,首先看一下能不能获取到pes的total_size,获取不到的话默认设置为200K的数据。根据这个total_size申请一个pes的buffer,继续解析,接下来的数据就是pes的header,解析到是pes的header的话,置pes的state pes->state = MPEGTS_PESHEADER

  • MPEGTS_PESHEADER:进入pesheader解析 置pes的state pes->state =

  • MPEGTS_PESHEADER_FILL:这边获取到pes的pts 和dts 还有一些其他信息,继续置pes的state pes->state =MPEGTS_PAYLOAD;
    MPEGTS_PAYLOAD:将剩下的数据拷贝到pes的buffer里面,此后都是pes data直到所有拷贝到的数据填满了pes的包,表明一个pes已经是完整的了。new了一个ts->pkt, 将pkt->data 指向pes->buffer。

  • 另外存在一种情况 就是 当ts 的header里面没有pes的size的时候,那么没法根据size去判断是否已经到pes 的size。这个时候 就得等下一个pes 的header 到达 即is_start 为1 的时候,也就是如下的情况,然后 继续走下去,等mpegts_push_data,继续完这一个188字节的时候 然后 上一个packet的包就生成返回回去了。

  • 总体流程:ts是从file 或者 http 那边读到数据 然后找找istart 然后把buf传到mpegts_push_data 这边, 这边先赋值pes_state 为MPEGTS_HEADER,然后去从buf里面都数据pespayload 到 pes 中, 等下一个过来了 new_pes_packet 出来 返回ts->pkt 出去

   if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) {
    
    
       new_pes_packet(pes, ts->pkt);
       ts->stop_parse = 1;
   }

总结

  • 从H264parse 到 ts的整个流程:从文件中读取188字节到ts buf中,拷贝多个ts buffer到pes buf中组成一个pes 包。 pes包经过264 parse,找264起始码。
  • 首先新建一个parse的buffer,若当前pes包没有找到起始码,说明数据不够,返回-100,将pes的数据拷贝到里面。直到在pes包中找到起始码了,返回pes包中要拷贝的数据量,将这部分数据拷贝到parse buffer中,一个完整的264 包就parse完成。

猜你喜欢

转载自blog.csdn.net/H2008066215019910120/article/details/130814718