博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从JM8.6代码看Bitstream、DataPartition、Slice、Picture的关系及码流结构本质
阅读量:4141 次
发布时间:2019-05-25

本文共 4174 字,大约阅读时间需要 13 分钟。

        在global.h中有:

typedef struct{  int             byte_pos;           //!< current position in bitstream;  int             bits_to_go;         //!< current bitcounter  byte            byte_buf;           //!< current buffer for last written byte  int             stored_byte_pos;    //!< storage for position in bitstream;  int             stored_bits_to_go;  //!< storage for bitcounter  byte            stored_byte_buf;    //!< storage for buffer of last written byte  byte            byte_buf_skip;      //!< current buffer for last written byte  int             byte_pos_skip;      //!< storage for position in bitstream;  int             bits_to_go_skip;    //!< storage for bitcounter  byte            *streamBuffer;      //!< actual buffer for written bytes  int             write_flag;         //!< Bitstream contains data and needs to be written} Bitstream;

      在Bitstream中最重要的便是streamBuffer,这个里面装码流

   

typedef struct datapartition{  Bitstream           *bitstream;  EncodingEnvironment ee_cabac;  int                 (*writeSyntaxElement)(SyntaxElement *, struct datapartition *);                      /*!< virtual function;                           actual method depends on chosen data partition and                           entropy coding method  */} DataPartition;

     可见,一个数据分块对应一个Bitstream

typedef struct{  int                 picture_id;  int                 qp;  int                 picture_type; //!< picture type  int                 start_mb_nr;  int                 max_part_nr;  //!< number of different partitions  int                 num_mb;       //!< number of MBs in the slice  DataPartition       *partArr;     //!< array of partitions  MotionInfoContexts  *mot_ctx;     //!< pointer to struct of context models for use in CABAC  TextureInfoContexts *tex_ctx;     //!< pointer to struct of context models for use in CABAC  // !KS: RMPNI buffer should be retired. just do some sore simple stuff  RMPNIbuffer_t        *rmpni_buffer; //!< stores the slice temporary buffer remapping commands  int                 ref_pic_list_reordering_flag_l0;  int                 *remapping_of_pic_nums_idc_l0;  int                 *abs_diff_pic_num_minus1_l0;  int                 *long_term_pic_idx_l0;  int                 ref_pic_list_reordering_flag_l1;  int                 *remapping_of_pic_nums_idc_l1;  int                 *abs_diff_pic_num_minus1_l1;  int                 *long_term_pic_idx_l1;  Boolean             (*slice_too_big)(int bits_slice); //!< for use of callback functions  int                 field_ctx[3][2]; //GB} Slice;

       可见,x(x >= 1,且x通常为1,为了简便起见,此处只讨论x为1)个数据分块对应一个片(也叫条带)

 

typedef struct {  int   no_slices;  int   idr_flag;  Slice *slices[MAXSLICEPERPICTURE];  int bits_per_picture;  float distortion_y;  float distortion_u;  float distortion_v;} Picture;

       可见,x(x >= 1,且x通常为1)片构成一个图像(可以是帧,可以是顶场和底场)

 

      从宏观上来看,码流的结构大致是这样的: (其中      表示分隔符,只考虑一个数据分块对应一个片)

                SPS             PPS          IDR Slice           Slice            Slice             PPS              Slice  ............

            NALU1       NALU2        NALU3        NALU4        NALU5       NALU6       NALU7

    (注意:PPS理论上可以很多,实际上可以只有第一个,  具体情况由编码器决定.)

 

      我们知道,写码流操作是这样的:每写一次码流,实际上要写“分隔符” + “NALU”. 程序在调用start_sequence函数的时候,写入的是“分隔符” + “SPS” 和 “分隔符” + “PPS”. 跟踪程序发现,除了这两次之外,每编码一个片都要进行一次码流的写入,一下程序就证明了这一点:

 

static int writeout_picture(Picture *pic){  Bitstream *currStream;  int partition, slice;  Slice *currSlice;  img->currentPicture=pic;  // 每个片,都会调用一次writeUnit,每个writeUnit都会调用一次WriteAnnexNALU for (slice=0; slice
no_slices; slice++) { currSlice = pic->slices[slice]; for (partition=0; partition
max_part_nr; partition++) // 调试发现,这层循环只有一次,故为了简便起见,仅仅讨论一个数据分片对应一个片的情形 { currStream = (currSlice->partArr[partition]).bitstream; assert (currStream->bits_to_go == 8); //! should always be the case, the //! byte alignment is done in terminate_slice writeUnit (currSlice->partArr[partition].bitstream,partition); } // partition loop } // slice loop return 0; }

 

      综上所述:(为了简便起见,仅考虑一个数据分块对应一个片

     

      1.  一个Bitstream对应一个DataPartition对应一个Slice, 而x(x >= 1)个Slice对应一个Picture.

           (一个Slice最终可以组装成一个NALU)

 

      2. 组成H.264码流的

          从理论上来讲可以是:一个SPS 和m个PPS和n个Slice,并在其前插入(1 + m + n)个分隔符.

          但从实际编码器来讲:视频序列的PPS可以共用,故只需插入 (1 + 1 + n)个分隔符,JM8.6中就是这种形式.

转载地址:http://sjzti.baihongyu.com/

你可能感兴趣的文章
NGINX
查看>>
Qt文件夹选择对话框
查看>>
1062 Talent and Virtue (25 分)
查看>>
1061 Dating (20 分)
查看>>
1060 Are They Equal (25 分)
查看>>
83. Remove Duplicates from Sorted List(easy)
查看>>
88. Merge Sorted Array(easy)
查看>>
leetcode刷题191 位1的个数 Number of 1 Bits(简单) Python Java
查看>>
leetcode刷题198 打家劫舍 House Robber(简单) Python Java
查看>>
NG深度学习第一门课作业2 通过一个隐藏层的神经网络来做平面数据的分类
查看>>
leetcode刷题234 回文链表 Palindrome Linked List(简单) Python Java
查看>>
NG深度学习第二门课作业1-1 深度学习的实践
查看>>
Ubuntu下安装Qt
查看>>
Qt札记
查看>>
我的vimrc和gvimrc配置
查看>>
hdu 4280
查看>>
禁止使用类的copy构造函数和赋值操作符
查看>>
C++学习路线
查看>>
私有构造函数
查看>>
组队总结
查看>>