• encodeprocess编码过程理解


    1. jm8.6中所涉及的几项关于比特分布的地方:

    序列参数集SPS:

    parset.c文件中的GernateSPS...

    具体宏块编码中的比特分布:

    #if TRACE

    snprintf(currSE->tracestring, TRACESTRING_SIZE, "Intra mode = %3d %d",currSE->value1,currSE->context);

    #endif

    还有很相关的一个是和比特计数相关的:

    bitCount[BITS_COEFF_Y_MB]+=currSE->len;

    rate += currSE->len;

     

    int bits;

    printf ("%04d(IDR)%8d %1d %2d %7.3f %7.3f %7.3f %7d %5d %3s %3d\n",

    frame_no, stat->bit_ctr - stat->bit_ctr_n,0,

    img->qp, snr->snr_y, snr->snr_u, snr->snr_v, tmp_time, me_time,

    img->fld_flag ? "FLD" : "FRM", intras);

    编码中涉及到的片类型(程序中共设定了5个)

    typedef enum {

    P_SLICE = 0,

    B_SLICE,

    I_SLICE,

    SP_SLICE,

    SI_SLICE

    } SliceType;

     

    这是在程序中自动进行比特使用统计的.

    // Update the statistics

    stat->bit_use_mb_type [img->type] += bitCount[BITS_MB_MODE];

    stat->bit_use_coeffY [img->type] += bitCount[BITS_COEFF_Y_MB] ;

    stat->tmp_bit_use_cbp [img->type] += bitCount[BITS_CBP_MB];

    stat->bit_use_coeffC [img->type] += bitCount[BITS_COEFF_UV_MB];

    stat->bit_use_delta_quant[img->type] += bitCount[BITS_DELTA_QUANT_MB];

     

    ++stat->mode_use[img->type][currMB->mb_type];

    stat->bit_use_mode[img->type][currMB->mb_type]+= bitCount[BITS_INTER_MB];

     

    JM中将编码模式转为一个值, 这个值再去利用熵编码进行编码.

    Int MBType2Value (Macroblock* currMB)

    {

    static const int dir1offset[3] = { 1, 2, 3};

    static const int dir2offset[3][3] = {{ 0, 4, 8}, // 1. block forward

    { 6, 2, 10}, // 1. block backward

    {12, 14, 16}}; // 1. block bi-directional

     

    int mbtype, pdir0, pdir1;

     

    if (img->type!=B_SLICE)

    {

    if (currMB->mb_type==I4MB) return (img->type==I_SLICE ? 0 : 6);

    else if (currMB->mb_type==I16MB) return (img->type==I_SLICE ? 0 : 6) + img->i16offset;

    else if (currMB->mb_type==P8x8)

    {

    if (input->symbol_mode==UVLC && ZeroRef (currMB)) return 5;

    else return 4;

    }

    else return currMB->mb_type;

    }

    函数中的ZeroRef用来判断当前的宏块中的每一个4x4块是否存在参考,如果参考帧都是0的话, 说明他们没有参考帧.(代码中是对宏块的每一个4x4块都存储一个运动矢量的.)

    在函数writeMotionInfo2NAL中是将当前宏块的运动矢量进行熵编码, 这个函数调用了函数writeReferenceFrame来写参考帧号, 调用 函数int writeMotionVector8x8来写运动矢量(一个宏块分为4个8x8块, 调用4次函数writeMotionVector8x8), 在函数writeMotionVector8x8中要对4个4x4块计算mvd然后进行相应的熵编码.

     

    编码图像的编号

    for (img->number=0; img->number < input->no_frames; img->number++)

    这个编号指的是I/P帧的编号.

    定义在global.h中的全局变量frame_no是编码图像的原始编号(属于播放顺序不是编码顺序)

    在配置文件中给出的量:

    FramesToBeEncoded = 10

    这指的是要进行编码的I/P帧的数量, 不包括B帧

     

    编码的过程:

    for循环, 利用img->number来控制循环次数:

    (0)编码I帧, 判断是否要编码B帧, 如果配置文件允许编码B帧, 此时也不能编码B帧, 因为现在只编码了一帧, B帧是双向参考的, 所以需要已经被编码的帧数(其实就是img->number代表的含义)必须大于1才行.

    (1)编码P帧, 此时判断是否编码B帧, 发现符合条件(配置文件中允许编码B帧,并且被编码帧数大于1), 进行编码B帧. 在配置文件中的NumberBFrames=2指明的是编码B帧的数量, 所以在这儿要循环NumberBFrames次. 相当于在这儿编码NumberBFrames个B帧.

    (2)编码P帧, 同样编码NumberBFrames个B帧

    (3)编码P帧, 同样编码NumberBFrames个B帧

    (4)编码P帧, 同样编码NumberBFrames个B帧

    ......

    (FramesToBeEncoded-1)编码P帧, 同样编码NumberBFrames个B帧

    循环结束的条件是I/P帧数之和等于FramesToBeEncoded

    这种情况下的编码序列是(FramesToBeEncoded=10, NumberBFrames=2,FrameSkip=2,IntraPeriod=0,IDRIntraEnable=0):

     

    img->number 

    0 

    1 

      

    2 

      

    3 

      

    4 

      

    ... 

    9 

      

    编码顺序

    0 

    1 

    2 

    3 

    4 

    5 

    6 

    7 

    8 

    9 

    10 

    11 

    15 

    ..... 

    25 

    26 

    27 

    帧类型

    I 

    P 

    B 

    B 

    P 

    B 

    B 

    P 

    B 

    B 

    P 

    B 

    B 

    ....... 

    P 

    B 

    B 

    播放顺序(原始顺序)frame_no

    0 

    3 

    1 

    2 

    6 

    4 

    5 

    9 

    7 

    8 

    12 

    10 

    11 

     

    27 

    25 

    26 

    img->frame_num 

    0 

    1 

    2 

    2 

    2 

    3 

    3 

    3

    4 

    4 

    4 

    5 

    5 

    ....... 

    9 

    10 

    10 

    img->number 

    0 

    1 

    1 

    1 

    2 

    2 

    2 

    3 

    3 

    3 

    4 

    4 

    4 

    ...... 

    9 

    9 

    9 

     

    在配置文件中的FrameSkip是来制定两个P帧之间的间隔, 不包括之间的B帧数(B帧数量是由NumberBFrames来决定的), 在代码中frame_no = start_tr_in_this_IGOP + IMG_NUMBER * (input->jumpd + 1);

    两个P(I)帧之间的间隔(跨越的帧数)是要大于等于B帧的数量的, 即frameSkip>=NumberBFrames的.

    这种情况下的编码序列是(FramesToBeEncoded=10, NumberBFrames=2,FrameSkip=3,IntraPeriod=0,IDRIntraEnable=0):

    img->number 

    0 

    1 

      

    2 

      

    3 

      

    4 

      

    ... 

    9 

      

    编码顺序

    0 

    1 

    2 

    3 

    4 

    5 

    6 

    7 

    8 

    9 

    10 

    11 

    15 

    ..... 

    25 

    26 

    27 

    帧类型

    I 

    P 

    B 

    B 

    P 

    B 

    B 

    P 

    B 

    B 

    P 

    B 

    B 

    ....... 

    P 

    B 

    B 

    播放顺序(原始顺序)

    0 

    4 

    1 

    2 

    8 

    5 

    6 

    12 

    9 

    10 

    16 

    13 

    14 

    ... 

    36 

    33 

    34 

    img->frame_num 

    0 

    1 

    2 

    2 

    2 

    3 

    3 

    3 

    4 

    4 

    4 

    5 

    5 

    ....... 

    9 

    10 

    10 

    img->number 

    0 

    1 

    1 

    1 

    2 

    2 

    2 

    3 

    3 

    3 

    4 

    4 

    4 

    ...... 

    9 

    9 

    9 

     

    对于配置文件中的IntraPeriod理解也很简单的, 因为在编码的过程中img->framenum控制编码的I/P帧的顺序,并且IntraPeriod也是针对P帧来说的(是和FramesToBeEncoded相关的). 在编码的过程中, 利用SetImgType函数来确定当前要进行编码的帧的类型(不包括B帧,只是判断当前帧是设定为I帧还是P帧): 首先第一帧肯定是I帧, 然后在编码下一帧(不考虑B帧)的时候要根据IntraPeriod这个量来决定当前帧是否要编为I帧,如果IntraPeriod不进行设定的话, 只有第一帧是I帧,然后后面的(FramesToBeEncoded-1)帧都是P帧, 如果对IntraPeriod进行了设定的话,需要利用img->framenum和IntraPeriod进行计算判断是否设置当前帧为I帧还是P帧

     

    配置文件中的IDRIntraEnable是用来设定在进行I帧编码的时候是否使用IDR刷新的方式, 这个设置是要和IntraPeriod一同起作用的, 即如果IntraPeriod没有设定(值为0)的话,即使设定了IDRIntraEnable(值为1)的话, 也不会进行IDR刷新的. 在代码中可以看到:

    input->intra_period && input->idr_enable

    在函数static int CalculateFrameNumber()中计算frame_no, 因为在进行编码的过程中, 是依据frame_no来获取要进行编码的帧的原始数据的. img->frame_num

    img->number是I/P编码循环的控制变量

    从上面的表中, 可以看到img->frame_num和img->number在I/P帧对应的值是一样的, 只是在B 帧的地方不一样.

     

    total_frame_buffer可以计算出该编码过程一共编码了多少帧, 这个变量在函数encode_one_frame 函数中完成了递增的.

    编码的过程:

    for(img->number=0; img->number < input->no_frames(FramesToBeEncoded); img->number++)

    {

    计算img->frame_num

    SetImgType:根据intra_period设定当前要编码的帧是设定为I帧还是P帧

    encode_one_frame():编码一帧(I/P)

     

    encode_one_frame

    {

    在函数CalculateFrameNumber中计算当前要编码帧在原始视频序列中的序号(frame_no)

    获取要编码帧数据

    frame_picture{

    code_a_picture{

    while()

    {encode_one_slice()};

    }

     

    }

     

    }

    判断接下来是否要编码B帧

    if ((input->successive_Bframe != 0) && (IMG_NUMBER > 0))

    {

    设定编码的类型:img->type = B_SLICE;

    计算frame_num(其实让frame_num++)

     

    编码一定数量的B帧

    for(img->b_frame_to_code=1; img->b_frame_to_code<=input->successive_Bframe(NumberBFrames); img

    ->b_frame_to_code++)

    {

    encode_one_frame():编码B帧

    }

    }

    }


  • 相关阅读:
    监考
    初步确定五一粤东出行计划
    煤矿粉尘监控系统中心站软件3层设计
    c# 程序调用代码生成数据库
    Socket 一个服务器监听多个客户端 功能实现
    软件开发进度表
    sql server日期时间格式转换字符串简介
    Sql建表和sql语句的注意事项
    分布式设计与开发(一)宏观概述
    分布式设计与开发(四)数据拆分
  • 原文地址:https://www.cnblogs.com/xkfz007/p/2613152.html
Copyright © 2020-2023  润新知