天天看點

JM代碼分析

原文位址:http://blog.csdn.net/tanxuan231/article/details/44887073

入門視訊采集與處理(學會分析YUV資料)

http://blog.csdn.net/vblittleboy/article/details/10945439

核心編碼函數研究

如果選擇的是幀内模式,則預測值由目前幀已經編碼重建的宏塊(沒有經過去塊效應濾波器)給出,最佳的模式值經過熵編碼輸出到編碼流。如果選擇的是幀間模式,則預測值由以前編碼幀的重建圖像給出,選擇的參考幀和運動向量等資訊經過熵編碼輸出到碼流。原始圖像的值和幀内或者幀間預測得到的預測值相減得到殘差資料,這些殘差資料經過變換,量化後得到的殘差系數也經過熵編碼輸出到碼流。另外,參加熵編碼的殘差系數經過反量化和反變換,和預測值相加,得到重建宏塊,存儲在目前幀的重建圖像中。目前幀的重建圖像全部完成以後,經過去塊效應濾波器的濾波,将作為參考幀存儲起來成為以後編碼圖像的幀間運動估計的參考。

編碼一個宏塊的步驟: 

幀間幀内模式選擇,7 種幀間模式包括:16x16 16x8 8x16 8x8 8x4 4x8 4x4, 分别對應模式編号1~7。在進行幀間預測的時候,隻有在選擇了把一個宏塊劃分成4 個8x8 的塊之後,才能進一步選擇8x4,4x8 和4x4的分塊。是以,在JM 模型中,把4~7 的四種幀間模式統稱為P8x8。根據配置檔案中的RDOptimization 的取值,JM 采用兩套不同的編碼步驟。

REF: http://www.cnblogs.com/xkfz007/articles/2612755.html

碼流控制RC

rate control 的總體目标是控制每一幀圖像編碼輸出的比特數,并在總比特數一定的限制條件下使得圖像失真最小,并且保證編解碼端的緩存區不發生溢出。

去塊濾波

去塊濾波是在整幅圖像編碼完後進行,以宏塊為機關,以解決DCT變換和量化引起的塊效應。

核心全局變量

static const short part_size[8][2] = 
{
  {4, 4},
  {4, 4},
  {4, 2},
  {2, 4},
  {2, 2},
  {2, 1},
  {1, 2},
  {1, 1}
};           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

SKIP模式,16×16,16×8,8×16,P8×8(8x8, 8x4, 4x8, 4x4),以一個4x4大小為基礎。

運動矢量的寫碼流

int main()
{
  // init encoder
  init_encoder(p_Enc->p_Vid, p_Enc->p_Inp);

  // encode sequence
  encode_sequence(p_Enc->p_Vid, p_Enc->p_Inp);
}

encode_sequence -> encode_one_frame
encode_sequence  -> encode_one_redundant_frame -> encode_one_frame

encode_one_frame(VideoParameters *p_Vid, InputParameters *p_Inp)
{
  if (p_Inp->PicInterlace == FIELD_CODING)
    **perform_encode_field**(p_Vid);
  else
    **perform_encode_frame**(p_Vid);
}

perform_encode_frame -> frame_picture_mp -> 
    frame_picture_mp_b_slice/frame_picture_mp_i_slice/frame_picture_mp_p_slice -> **frame_picture** 
perform_encode_frame -> **frame_picture**

perform_encode_frame -> **frame_picture** -> code_a_picture(p_Vid, frame);
perform_encode_field -> field_picture -> code_a_picture(p_Vid, top);/code_a_picture(p_Vid, bottom);

code_a_picture(VideoParameters *p_Vid, Picture *pic)
{
  if( (p_Inp->separate_colour_plane_flag != ) )
  {
    for( pl=; pl<MAX_PLANE; pl++ )
    {
      p_Vid->current_mb_nr = ;
      p_Vid->current_slice_nr = ;
      p_Vid->SumFrameQP = ;
      p_Vid->num_ref_idx_l0_active = ;
      p_Vid->num_ref_idx_l1_active = ;

      p_Vid->colour_plane_id = (char) pl;

      code_a_plane(p_Vid, p_Inp);
    }
  }
  else
  {
    code_a_plane(p_Vid, p_Inp);
  }
}

code_a_plane
{
  while (NumberOfCodedMBs < p_Vid->PicSizeInMbs)       // loop over slices
  {
    // Encode one SLice Group
    while (!FmoSliceGroupCompletelyCoded (p_Vid, SliceGroup))
    {
      // Encode the current slice
      if (!p_Vid->mb_aff_frame_flag)
        NumberOfCodedMBs += encode_one_slice (p_Vid, SliceGroup, NumberOfCodedMBs);
      else
        NumberOfCodedMBs += encode_one_slice_MBAFF (p_Vid, SliceGroup, NumberOfCodedMBs);

      FmoSetLastMacroblockInSlice (p_Vid, p_Vid->current_mb_nr);
      // Proceed to next slice
      p_Vid->current_slice_nr++;
      p_Vid->p_Stats->bit_slice = ;
    }
    // Proceed to next SliceGroup
    SliceGroup++;
  }
}
encode_one_slice -> init_slice
encode_one_slice_MBAFF -> init_slice
init_slice
{  
  if ((*currSlice)->symbol_mode == CAVLC)
  {
    setup_cavlc(*currSlice, (*currSlice)->listXsize);
  }
  else
  {
    setup_cabac(*currSlice, (*currSlice)->listXsize);
  }
}           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85

運動矢量是以1/4像素為機關的,是以(4,8),實際代表偏移了(1,2)個像素

REF: http://www.360doc.com/content/11/0521/14/1412027_118336851.shtml

PartitionMotionSearch

REF: http://blog.csdn.net/timebomb/article/details/6121089

從碼流中提取NALU

從碼流中提取一個NALU的過程:get_ annex_ b_NALU 

對于一個原始的 H.264 NALU 單元常由 [Start Code] [NALU Header] [NALU Payload] 三部分組成, 其中 Start Code 用于标示這是一個 

NALU 單元的開始, 必須是 “00 00 00 01” 或 “00 00 01”, NALU 頭僅一個位元組, 其後都是 NALU 單元内容. 

打包時去除 “00 00 01” 或 “00 00 00 01” 的開始碼, 把其他資料封包的 RTP 包即可.

結構體

struct video_par  //視訊參數結構  Image Parameters
{
    InputParameters          *p_Inp;

    pic_parameter_set_rbsp_t *active_pps;
    seq_parameter_set_rbsp_t *active_sps;
    seq_parameter_set_rbsp_t SeqParSet[MAXSPS];
    pic_parameter_set_rbsp_t PicParSet[MAXPPS];
    pic_parameter_set_rbsp_t *pNextPPS;

    float framerate;
    int frame_no;
    int fld_type;                        //!< top or bottom field

    int  key_frame;
    int  frm_no_in_file;    

    int pix_x;                   //!< current pixel horizontal
    int pix_y;                   //!< current pixel vertical

    //currentPicture指向目前正在活動的圖像(frame_pic, top_pic或bottom_pic)
    Picture       *currentPicture; 
    struct slice  *currentSlice;     //!< pointer to current Slice data struct
    Macroblock    *mb_data;         //!< array containing all MBs of a whole frame

    int  frameNuminGOP; 

    int num_mb_per_slice;
    int number_of_slices;

    GOP_DATA *gop_structure;

    int p_dec;                      //!< decoded image file handle

    struct nalu_t *nalu;
}           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
//! Slice
typedef struct slice
{
    struct video_par    *p_Vid;   // pointer to the original video structure
    InputParameters     *p_Inp;   // pointer to the input parameters

    int                 start_mb_nr;
    int                 max_part_nr;  //!< number of different partitions
    int                 num_mb;       //!< number of MBs in the slice

    int                 frame_no;

    MotionVector *****all_mv;         //!< replaces local all_mv
    MotionVector ******bipred_mv;     //!< Biprediction MVs 
}           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

00000(IDR) 21392 28 37.638 41.571 43.231 391 0 FRM 3

-------------------------------------------------------------------------------
Frame     Bit/pic    QP   SnrY    SnrU    SnrV    Time(ms) MET(ms) Frm/Fld Ref  
-------------------------------------------------------------------------------
00000(NVB)     320 
00000(IDR)   21392   28  37.638  41.571  43.231       391       0    FRM    3
00002( B )    5280   31  35.548  40.735  42.217      1368     689    FRM    1
00001( B )    2096   32  35.308  41.128  42.611      1281     778    FRM    0
-------------------------------------------------------------------------------           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

image.c 

encode_ one_frame -> ReportNALNonVLCBits ReportFirstframe

buf2img_basic 将檔案中的一幀轉換為PIC

void buf2img_basic (imgpel** imgX,            //!< Pointer to image plane
                    unsigned char* buf,       //!< Buffer for file output
                    int size_x,               //!< horizontal size of picture
                    int size_y,               //!< vertical size of picture
                    int o_size_x,             //!< horizontal size of picture output
                    int o_size_y,             //!< vertical size of picture output
                    int symbol_size_in_bytes, //!< number of bytes in file used for one pixel  一個像素所需的位元組數
                    int dummy                 //!< dummy variable used for allowing function pointer use
                    )
{
  int i,j;
  unsigned char* temp_buf = buf;

  if (symbol_size_in_bytes> sizeof(imgpel))  //sizeof(imgpel) = 2 byte =16bit
  {
    error ("Source picture has higher bit depth than imgpel data type. \nPlease recompile with larger data type for imgpel.", );
  }

  if (( sizeof (imgpel) == symbol_size_in_bytes))
  {    
    // imgpel == pixel_in_file -> simple copy
    if (size_x == o_size_x && size_y == o_size_y)
      memcpy(&imgX[][], temp_buf, size_x * size_y * sizeof(imgpel));
    else
    {
      int iminwidth   = imin(size_x, o_size_x);
      int iminheight  = imin(size_y, o_size_y);
      int dst_offset_x  = , dst_offset_y = ;
      int offset_x = , offset_y = ; // currently not used

      // determine whether we need to center the copied frame or crop it
      if ( o_size_x >= size_x ) 
        dst_offset_x = ( o_size_x  - size_x  ) >> ;

      if (o_size_y >= size_y) 
        dst_offset_y = ( o_size_y - size_y ) >> ;

      // check copied area to avoid copying memory garbage
      // source
      iminwidth  =  ( (offset_x + iminwidth ) > size_x ) ? (size_x  - offset_x) : iminwidth;
      iminheight =  ( (offset_y + iminheight) > size_y ) ? (size_y - offset_y) : iminheight;
      // destination
      iminwidth  =  ( (dst_offset_x + iminwidth ) > o_size_x  ) ? (o_size_x  - dst_offset_x) : iminwidth;
      iminheight =  ( (dst_offset_y + iminheight) > o_size_y )  ? (o_size_y - dst_offset_y) : iminheight;

      for (i=; i<iminheight;i++) {
        memcpy(&imgX[i + dst_offset_y][dst_offset_x], &(temp_buf[(i + offset_y) * size_x + offset_x]), iminwidth * sizeof(imgpel));
      }
    }
  }
  else
  {
    int j_pos;
    uint16 ui16;
    if (size_x == o_size_x && size_y == o_size_y)
    {
      for (j=; j < o_size_y; j++) //按行
      {
        j_pos = j * size_x;  //0*144;1*144;2*144....每行行首
        for (i=; i < o_size_x; i++)
        {
          ui16=;             
          //行首+一行的偏移(i + j_pos) * symbol_size_in_bytes表示位元組數
          memcpy(&(ui16), buf + ((i + j_pos) * symbol_size_in_bytes), symbol_size_in_bytes);
          imgX[j][i]= (imgpel) ui16;
        }
      } //@1 為解釋此for循環
    }
    else
    {
      int iminwidth   = imin(size_x, o_size_x);
      int iminheight  = imin(size_y, o_size_y);
      int dst_offset_x  = , dst_offset_y = ;
      int offset_x = , offset_y = ; // currently not used

      // determine whether we need to center the copied frame or crop it
      if ( o_size_x >= size_x ) 
        dst_offset_x = ( o_size_x  - size_x  ) >> ;

      if (o_size_y >= size_y) 
        dst_offset_y = ( o_size_y - size_y ) >> ;

      // check copied area to avoid copying memory garbage
      // source
      iminwidth  =  ( (offset_x + iminwidth ) > size_x ) ? (size_x  - offset_x) : iminwidth;
      iminheight =  ( (offset_y + iminheight) > size_y ) ? (size_y - offset_y) : iminheight;
      // destination
      iminwidth  =  ( (dst_offset_x + iminwidth ) > o_size_x  ) ? (o_size_x  - dst_offset_x) : iminwidth;
      iminheight =  ( (dst_offset_y + iminheight) > o_size_y )  ? (o_size_y - dst_offset_y) : iminheight;

      for (j = ; j < iminheight; j++) 
      {
        memcpy(&imgX[j + dst_offset_y][dst_offset_x], &(temp_buf[(j + offset_y) * size_x + offset_x]), iminwidth * symbol_size_in_bytes);
      }
      for (j=; j < iminheight; j++)
      {        
        j_pos = (j + offset_y) * size_x + offset_x;
        for (i=; i < iminwidth; i++)
        {
          ui16 = ;
          memcpy(&(ui16), buf + ((i + j_pos) * symbol_size_in_bytes), symbol_size_in_bytes);
          imgX[j + dst_offset_y][i + dst_offset_x]= (imgpel) ui16;
        }
      }    
    }
  }
}           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107

@1: 

JM代碼分析

是以imgX這個二位數組裡存放了Y DATA的資料 

====================== Y Data ====================== 

+——————————–+——————————–+ 

| 49, 50, 49, 49, 49, 48, 48, 48,| 48, 49, 51, 57, 62, 62, 61, 62,| 

| 47, 47, 48, 48, 48, 48, 48, 47,| 47, 48, 48, 47, 48, 50, 50, 50,| 

| 45, 45, 45, 44, 45, 45, 46, 45,| 45, 45, 45, 44, 43, 43, 44, 44,| 

| 41, 42, 43, 43, 43, 41, 41, 43,| 43, 41, 42, 43, 43, 42, 43, 43,| 

| 38, 39, 38, 38, 39, 40, 39, 38,| 39, 39, 39, 40, 39, 38, 39, 38,| 

| 39, 39, 39, 38, 39, 41, 40, 40,| 40, 41, 41, 40, 40, 41, 42, 40,| 

| 51, 51, 52, 52, 51, 50, 51, 51,| 51, 50, 50, 50, 50, 50, 51, 50,| 

| 64, 64, 65, 64, 63, 62, 62, 62,| 62, 62, 62, 61, 61, 61, 60, 60,| 

+——————————–+——————————–+ 

| 73, 74, 74, 74, 73, 72, 70, 70,| 71, 71, 71, 70, 70, 70, 70, 70,| 

| 77, 79, 78, 78, 78, 77, 75, 75,| 76, 75, 75, 75, 75, 75, 75, 74,| 

| 79, 79, 79, 79, 78, 77, 76, 76,| 76, 75, 75, 75, 74, 74, 74, 74,| 

| 82, 81, 81, 80, 78, 76, 76, 76,| 76, 76, 75, 75, 75, 75, 75, 74,| 

| 93, 91, 87, 83, 80, 77, 76, 76,| 76, 76, 75, 74, 75, 76, 76, 76,| 

|105,102, 94, 86, 82, 78, 77, 76,| 76, 78, 76, 75, 76, 77, 77, 77,| 

|111,108, 99, 89, 82, 80, 77, 77,| 77, 78, 76, 75, 76, 76, 78, 77,| 

|117,111,102, 91, 82, 80, 78, 79,| 78, 77, 76, 76, 76, 77, 77, 76,| 

+——————————–+——————————–+: 

2、int read_one_frame (VideoParameters *p_Vid, VideoDataFile *input_file, int FrameNoInFile, int HeaderSize, FrameFormat *source, FrameFormat *output, imgpel **pImage[3]) 

pImage[3]存放了Y/U/V的資料 

JM代碼分析

H.264級别Level、DPB 與MaxDpb Mbs 詳解

http://blog.csdn.net/vblittleboy/article/details/8033133

宏塊模式

static const char mb_mode_table[]  = 
{
    , //16X16 Direct模式,在B幀中有效
    , //Inter16X16,在幀間有效
    , //Inter16X8,在幀間有效
    , //Inter8X16,在幀間有效
    P8x8,  //幀間有效
    I16MB, //Intra16X16幀内有效
    I4MB,  //Intra有效
    I8MB,  //Intra有效
    IPCM,  //Intra有效,不要預測,直接對RAW資料編碼
    SI4MB
}; // DO NOT CHANGE ORDER !!!           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

H264碼流結構分析

REF: http://blog.csdn.net/chenchong_219/article/details/37990541

編碼端寫碼流

REF: http://bbs.chinavideo.org/viewthread.php?tid=8325 

編碼器: 

一直以為寫碼流在write_one_macroblock()函數中進行,其實不是,這個函數的名字對初學者來說絕對有誤導作用。(該函數隻是将文法元素寫入緩沖區,并未寫入檔案) 

真正的寫碼流過程如下: 

—-lencod.c中調用start_sequence()和terminate_sequence()分别打開和關閉檔案流(input->outfile). 

filehandle.c中的start_sequence()和terminate_sequence()中有幾個與寫碼流相關的重要的操作: 

    OpenAnexxbFile(input->outfile); 

WriteNALU=WriteAnexxbNALU; 

于是WriteNALU在編碼器中擔任了寫碼流的主要及核心工作。 

—-在OpenAnexxbFile()中(Anexxb.c中)将檔案打開,句柄賦與FILE *f全局變量,f=fopen(Filename,”wb”) 從這句以後檔案流一直處于打開狀态,便于寫碼流。 

—-打開後緊接着調用WriteNALU寫頭部。 

注意:WriteNALU的參數及其中各函數的NALU_t類型變量。 

—-搜尋WriteNALU: 

在WriteUnit中有調用,而WriteUnit在WriteOut_Picture中有調用,WriteOut_Picture()在encode_one_frame()中有調用。于是從encode_one_frame開始 

,整個寫碼流流程是: 

encode_one_frame—->frame_picture 

—->WriteOut_Picture—->WriteUnit—–>WriteNALU(即WriteAnexxbNALU) 

整個檔案編碼完并寫碼流結束後調用terminate_sequence()關閉檔案流。 

**  注意各函數中的NALU_t類型變量,以及WriteNALU中的FILE *f 

最終落實寫碼流的語句是:fwrite(n->buf,1,n->len,f);

解碼器: 

配置檔案中共包含三個檔案: 

test.264 test_dec_yuv test_rec_yuv分别對應inp->infile(bits),inp->outfile(p_out),inp->reffile(p_ref),括号内為檔案所對應的變量 初始化在 

init_conf()中進行。 

舉例:OpenBitstreamFile()中有bits=fopen(fn,”rb”)于是bits便與fn建立起了對應關系,其它兩個類同。 

read_new_slice()中img->currSlice->currStream儲存了從碼流中讀進來的文法元素(memcpy(currStream->streamBuffer,&nalu->buf[1],nalu->len-1);) 

注意這裡的nalu->len,這個分量控制眷讀碼流的位數,很重要,它在GetAnexxbNALU中求得。 

是以解碼器中整個碼流流向為: 

inp->infile->bits->nalu->[CurrStream->streamBuffer]

解碼端讀取運動矢量資訊

readMBMotionVectors

#0  readMBMotionVectors (currSE=0xbfffdde8, dP=0x81cdff0, currMB=0x8148d68, list=0, step_h0=4, step_v0=4, 
    offset=) at src/macroblock.c:
#1  0x08093fc1 in read_motion_info_from_NAL_p_slice (currMB=0x8148d68) at src/macroblock.c:1264
#2  0x0809c585 in read_inter_macroblock (currMB=0x8148d68) at src/mb_read.c:1059
#3  0x0809daea in read_one_macroblock_p_slice_cabac (currMB=0x8148d68) at src/mb_read.c:1728
#4  0x0806554f in decode_one_slice (currSlice=0x81c9000) at src/image.c:2534
#5  0x08060e94 in decode_slice (currSlice=0x81c9000, current_header=2) at src/image.c:748
#6  0x0806169d in decode_one_frame (pDecoder=0x810d170) at src/image.c:943
#7  0x08087376 in DecodeOneFrame (ppDecPicList=0xbfffdfe4) at src/ldecod.c:1254
#8  0x08054fd3 in main (argc=1, argv=0xbffff174) at src/decoder_test.c:282           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

解碼端讀取殘差資訊

  • Get coefficients (run/level) of one 8x8 block
  • from the NAL (CABAC Mode) 

    readCompCoeff8x8_CABAC

編碼端寫殘差資訊

#0  write_significance_map (currMB=0x8233210, eep_dp=0x87c9d04, type=6, coeff=0x87c61a8, coeff_ctr=4, 
    tex_ctx=0x87c6920) at src/cabac.c:1739
#1  0x08058476 in writeRunLevel_CABAC (currMB=0x8233210, se=0xbfffea20, dp=0x87c9cf0) at src/cabac.c:1861
#2  0x080a8559 in write_chroma_coeff (currMB=0x8233210) at src/macroblock.c:3532
#3  0x080a66f8 in write_i_slice_MB_layer (currMB=0x8233210, rdopt=1, coeff_rate=0xbfffeae4, Extraction=0)
    at src/macroblock.c:2800
#4  0x0812bfd7 in RDCost_for_macroblocks (currMB=0x8233210, lambda=570, mode=10) at src/rdopt.c:1964
#5  0x080f5524 in compute_mode_RD_cost (currMB=0x8233210, enc_mb=0xbfffece8, mode=10, inter_skip=0xbfffec1e)
    at src/mode_decision.c:460
#6  0x080be518 in encode_one_macroblock_high (currMB=0x8233210) at src/md_high.c:289
#7  0x08149f80 in encode_one_slice (p_Vid=0x81d0020, SliceGroupId=0, TotalCodedMBs=0) at src/slice.c:513
#8  0x0806cad2 in code_a_plane (p_Vid=0x81d0020, p_Inp=0x81e4c88) at src/image.c:223
#9  0x0806ce05 in code_a_picture (p_Vid=0x81d0020, pic=0x8365ab8) at src/image.c:308
#10 0x080709db in frame_picture (p_Vid=0x81d0020, frame=0x8365ab8, imgData=0x81d00ec, rd_pass=0)
    at src/image.c:1665
#11 0x0806e578 in perform_encode_frame (p_Vid=0x81d0020) at src/image.c:820
#12 0x0806fbe2 in encode_one_frame (p_Vid=0x81d0020, p_Inp=0x81e4c88) at src/image.c:1302
#13 0x0808dfea in encode_sequence (p_Vid=0x81d0020, p_Inp=0x81e4c88) at src/lencod.c:1106
#14 0x0808c0b3 in main (argc=1, argv=0xbffff174) at src/lencod.c:392
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

JM18中編碼一個I宏塊的過程

以I16MB為例:

RDCost_for_macroblocks -> 
currSlice->mode_decision_for_I16x16_MB (mode_decision_for_I16x16_MB_RDO) ->
currMB->residual_transform_quant_luma_16x16(residual_transform_quant_luma_16x16)           
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
#0  residual_transform_quant_luma_16x16 (currMB=0x8233210, pl=PLANE_Y) at src/block.c:212
#1  0x08124bd3 in mode_decision_for_I16x16_MB_RDO (currMB=0x8233210, lambda=570) at src/rd_intra_jm.c:490
#2  0x0812bbc1 in RDCost_for_macroblocks (currMB=0x8233210, lambda=570, mode=10) at src/rdopt.c:1875
#3  0x080f544c in compute_mode_RD_cost (currMB=0x8233210, enc_mb=0xbfffece8, mode=10, inter_skip=0xbfffec1e)
    at src/mode_decision.c:
#4  0x080be440 in encode_one_macroblock_high (currMB=0x8233210) at src/md_high.c:289
#5  0x08149ea8 in encode_one_slice (p_Vid=0x81d0020, SliceGroupId=0, TotalCodedMBs=0) at src/slice.c:513
#6  0x0806cad2 in code_a_plane (p_Vid=0x81d0020, p_Inp=0x81e4c88) at src/image.c:223
#7  0x0806ce05 in code_a_picture (p_Vid=0x81d0020, pic=0x8365f58) at src/image.c:308
#8  0x08070903 in frame_picture (p_Vid=0x81d0020, frame=0x8365f58, imgData=0x81d00ec, rd_pass=0)
    at src/image.c:
#9  0x0806e578 in perform_encode_frame (p_Vid=0x81d0020) at src/image.c:820
#10 0x0806fbe2 in encode_one_frame (p_Vid=0x81d0020, p_Inp=0x81e4c88) at src/image.c:1302
#11 0x0808df12 in encode_sequence (p_Vid=0x81d0020, p_Inp=0x81e4c88) at src/lencod.c:1105
#12 0x0808bfdb in main (argc=1, argv=0xbffff174) at src/lencod.c:392           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

write_i_slice_MB_layer 

writeIPCMData 寫IPCM宏塊到碼流

CBP

CBP全稱為Coded Block Pattern,指亮度和色度分量的各小塊的殘差的編碼方案。H.264解碼器中cbp變量(一個uint8_t類型變量)高4位存儲了色度CBP,低4位存儲了亮度CBP。色度CBP和亮度CBP的含義是不一樣的: 

亮度CBP資料從最低位開始,每1位對應1個子宏塊,該位等于1時表明對應子宏塊殘差系數被傳送。(是以亮度CBP資料通常需要當成二進制資料來看) 

色度CBP包含3種取值: 

0:代表所有殘差都不被傳送 

1:隻傳送DC系數 

2:傳送DC系數以及AC系數 

(是以色度CBP資料通常可以當成十進制資料來看)

JM使用

SliceMode = 0 # Slice mode (0=off 1=fixed mb in slice, 2=fixed bytes in slice, 3=use callback) 

正如注釋所說: 

值為 0:表示不采用分片。也就是一個片組為一個片,如果不采用片組那麼就是一幅圖像為一個片。 

值為 1:表示将每 SliceArgument 個宏塊分為一個片; 

值為 2:表示将每 SliceArgument 個位元組分為一個片 

值為 3:我也不知道是什麼意思,猜測可能是根據解碼器或者其他什麼的回報資訊來确定。這樣根據回報資訊可以更好适應不同網絡環境下抗錯能力。 

SliceArgument = 50 # Slice argument (Arguments to modes 1 and 2 above) 

REF:http://blog.sina.com.cn/s/blog_784448d60100x33x.html

encoder_ baseline.cfg, encoder_ extended.cfg, encoder_ main.cfg, encoder_ tonemapping.cfg, encoder_yuv422.cfg。它們裡面的參數名都一樣的,隻是值不同。把其他一個檔案拷貝成檔案名 encoder.cfg。

H.264步步為營 

http://wmnmtm.blog.163.com/blog/static/382457142011812111015157/?suggestedreading&wumii

下載下傳Elecard StreamEye,用于播放test.264檔案。

縮寫

ME: Motion Estimation 運動估計 

qp/QP: quantization parameter 量化參數 

RDOQ: rd optimization 率失真率 

PSNR: Peak Signal to Noise Ratio 峰值信噪比 評價圖像的客觀标準 

blk: block 塊 

idr/IDR: IDR幀 

CBP: CodedBlockPattern 目前塊的編碼模式 

POC: picture order count 

CPB: coded picture buffer 儲存編碼圖像的隊列緩存區 

用來反應該宏塊編碼中殘差情況的文法元素。CBP每位都為0,表示沒有殘差要編碼,不為0的位數越多表示要編碼的殘差越多。 用于表示目前宏塊是否存在非零值 

http://imeradio.blog.163.com/blog/static/153419404201011224714936/ 

DPB: decoded picture buffer,解碼圖檔緩存區 

在做視訊解碼時,需要将最近的若幹幅參考幀緩存起來,這個緩沖區就叫做DPB。是以最大存儲幀數也是最大參考幀數(ref)。DPB一般以宏塊數為機關(DpbMbs),計算公式為—— 

DpbMbs = ref(參考幀數) * PicWidthInMbs(水準宏塊數) * FrameHeightInMbs(垂直宏塊數) 

deblock: 去塊濾波 

FMO: 指靈活塊映射,也即多片組模式。 REF: http://blog.csdn.net/newthinker_wei/article/details/8784754 

RDO: Rate–distortion optimization 率失真優化 

SSE: MMX/SSE/SSE2指令集對H.264解碼器的關鍵算法進行優化 

coef: 變換系數,殘內插補點進行DCT變換之後得到。 

SAE: 定義了每種預測的預測誤差 

PCM/DPCM: 

預測編碼[1] 中典型的壓縮方法有脈沖編碼調制(PCM,Pulse Code Modulation)、差分脈沖編碼調制(DPCM,Differential Pulse Code Modulation)、自适應差分脈沖編碼調制(ADPCM,Adaptive Differential Pulse Code Modulation)等 

BS: Boundary Strength 邊界強度,去塊濾波過程中的。 

UVLC: 通用可變長編碼 熵編碼 

CABAC: 基于文本的自适應二進制算術編碼 熵編碼

問題

1、宏塊編碼長度 

2、宏塊編碼資料

TIFF檔案格式

标簽圖像檔案格式(Tagged Image File Format,簡寫為TIFF) 是一種主要用來存儲包括照片和藝術圖在内的圖像的檔案格式。TIFF 是一個靈活适應性強的檔案格式,通過在檔案頭中包含“标簽”它能夠在一個檔案中處理多幅圖像和資料。标簽能夠标明圖像的如圖像大小這樣的基本幾何尺寸或者定義圖像資料是如何排列的并且是否使用了各種各樣的圖像壓縮選項。

像素由多少位構成

Q:一幅圖像存儲容量為64KB,分辨率為256×128 

整幅圖是用64×1024×8=524288位二進制表示 

每一個像素是用524288÷(256×128)=16位表示

C庫函數

int access(const char *filenpath, int mode) 

FILE * fopen(const char * path,const char * mode); 

lseek()便是用來控制該檔案的讀寫位置 

int read(int handle, void *buf, int nbyte); nbyte:要讀多少個位元組,不能大于buffer指向的緩沖區 

void *memcpy(void *dest, const void *src, size_t n);

gdb調試指令

bt / info stack 檢視棧資訊 

list 檢視源程式 

list指令後面還可以更一些參數,來顯示更多功能: 

行号。 

<+> [offset] 目前行号的正偏移量。 

<-> [offset] 目前行号的負偏移量。 

檔案的中的行行。 

函數的代碼 

檔案中的函數。 

<*address> 程式運作時的語句在記憶體中的位址。 

print p 檢視運作時資料

檢視二位數組: 

一個16*16的二維數組的第一行 

p **currSlice->[email protected]

格式:print [</format>] <expr>
    例如:(gdb) p /x 3+2*5
                $19 = 0xd
    format的取值範圍有如下幾種:
    x 按十六進制格式顯示變量。
    d 按十進制格式顯示變量。
    u 按十六進制格式顯示無符号整型。
    o 按八進制格式顯示變量。
    t 按二進制格式顯示變量。
    a 按十六進制格式顯示變量。
    c 按字元格式顯示變量。
    f 按浮點數格式顯示變量。
           

檢視函數傳回值 

* finish指令運作至函數結束,此時會列印函數傳回值 

* 傳回值會存儲在eax寄存器中,p $eax

跳出循環: 

until NUM 執行到一個比目前行号大的行,或者也可以指定在目前frame(我了解成函數)中的某一行 

跳出函數: 

finish 執行,直到標明的frame執行結束,然後列印傳回值,将其放入曆史值中,停止

REF: http://www.cnblogs.com/TianFang/archive/2013/01/21/2869474.html

檢視目前程式運作到的檔案 

info source

important REF: http://www.cnblogs.com/kzloser/archive/2012/09/21/2697185.html

收集的方法

1、if ((source->pic_unit_size_on_disk & 0x07) == 0) 

source->pic_unit_size_on_disk這個數要能被8整除

繼續閱讀