DSC参考代码分析

分类: computerVision |
encoder中的代码:
输入命令行options:
static
cmdarg_t cmd_args[] = {
// The array arguments have to
be first:
{ IVARG, NULL,
"RC_OFFSET",
"-rcofs",
0, 15}, // RC
offset values
{ IVARG, NULL,
"RC_MINQP",
"-rcmqp",
0, 15}, //
Min QP values
{ IVARG, NULL,
"RC_MAXQP",
"-rcmxqp",
0, 15}, // Max QP
values
{ IVARG,
NULL, "RC_BUF_THRESH",
"-rcbt",
0, 14}, // RC buffer
threshold
{ PARG,
&rcModelSize,
"RC_MODEL_SIZE",
"-rms",
0, 0}, // RC
model size
{ FARG,
&bitsPerPixel,
"BITS_PER_PIXEL", "-bpp",
0, 0}, //
bits per pixel
{ PARG,
&bitsPerComponent,
"BITS_PER_COMPONENT", "-bpc",
0, 0}, // bits per
component
{ PARG,
&enable422,
"ENABLE_422",
"-e422", 0,
0}, //
enable_422
{ PARG,
&bpEnable,
"BLOCK_PRED_ENABLE",
"-bpe", 0,
0}, // Block prediction
range
{ PARG,
&lineBufferBpc,
"LINE_BUFFER_BPC",
"-lbpc",
0, 0}, //
Line buffer storage bits/component
{ PARG,
&sliceWidth,
"SLICE_WIDTH",
"-nsh", 0,
0}, // Slice width (0=pic
width)
{ PARG,
&sliceHeight,
"SLICE_HEIGHT",
"-nsv",
0, 0}, //
slice height (0=pic height)
{ PARG,
&firstLineBpgOfs,
"FIRST_LINE_BPG_OFFSET", "-flbo", 0,
0}, // Additional bpp budget
for 1st line
{ PARG,
&initialFullnessOfs,
"INITIAL_FULLNESS_OFFSET", "-ifo", 0, 0}, //
Initial fullness offset
{ PARG,
&initialDelay,
"INITIAL_DELAY",
"-id", 0,
0}, // Initial delay (in pixel
time units) from encode start to xmit start
{ PARG,
&useYuvInput,
"USE_YUV_INPUT",
"-uyi",
0, 0}, //
Use YUV input (convert if necessary)
{ PARG,
&rbSwap,
"SWAP_R_AND_B",
"-rbswp", 0, 0},
// Swap red & blue
components
{ PARG,
&rbSwapOut,
"SWAP_R_AND_B_OUT",
"-rbswpo", 0, 0},
// Swap red & blue
components
{ PARG,
&function,
"FUNCTION",
"-do",
0, 0}, //
0=encode/decode, 1=encode, 2=decode
{ IARG,
&dpxBugsOverride,
"DPX_BUGS_OVERRIDE",
"-dpxbugs", 0, 0}, // Sets the
DPX bugs mode (else autodetect)
{ PARG,
&dpxPadLineEnds,
"DPX_PAD_LINE_ENDS",
"-dpxpad", 0, 0},
// Pad line ends for DPX
output
{ PARG,
&dpxWriteBSwap,
"DPX_WRITE_BSWAP",
"-dpxwbs",
0, 0}, // Pad line ends for DPX
output
{ PARG,
&enableVbr,
"VBR_ENABLE",
"-vbr", 0,
0}, //
1=disable stuffing bits (on/off VBR)
{ PARG,
&muxWordSize,
"MUX_WORD_SIZE",
"-mws",
0, 0},
// mux word size if SSM
enabled
{ PARG,
&tgtOffsetHi,
"RC_TGT_OFFSET_HI",
"-thi", 0,
0}, // Target
hi
{ PARG,
&tgtOffsetLo,
"RC_TGT_OFFSET_LO",
"-tlo", 0,
0}, // Target
lo
{ PARG,
&rcEdgeFactor,
"RC_EDGE_FACTOR",
"-ef",
0, 0},
// Edge factor
{ PARG,
&quantIncrLimit0,
"RC_QUANT_INCR_LIMIT0", "-qli0",
0, 0}, //
Quant limit incr 0
{ PARG,
&quantIncrLimit1,
"RC_QUANT_INCR_LIMIT1", "-qli1",
0, 0}, //
Quant limit incr 1
{ PARG,
&flatnessMinQp,
"FLATNESS_MIN_QP",
"-fmin",
0, 0}, // Flatness min
QP
{ PARG,
&flatnessMaxQp,
"FLATNESS_MAX_QP",
"-fmax",
0, 0}, // Flatness max
QP
{ PARG,
&flatnessDetThresh,
"FLATNESS_DET_THRESH", "-fdt",
0, 0}, //
Flatness detect threshold
{ PARG,
&muxingMode,
"MUXING_MODE",
"-mm",
0, 0}, //
Multiplexing mode
};
根据不同的文件类型来调用open函数读取一帧的pixels:
pic_t *ip;
dpx_read(infname, &ip,
dpxBugsOverride)
ppm_read(infname,
&ip)
读取的picture数据放到了一个数据结构中:
typedef enum format_e {FRAME, TOP, BOTTOM,
UNDEFINED_FORMAT} format_t;
typedef enum
color_e {RGB, YUV_SD, YUV_HD,
UNDEFINED_COLOR} color_t;
typedef enum
chroma_e {YUV_420, YUV_422, YUV_444, YUV_4444,
UNDEFINED_CHROMA} chroma_t;
typedef
struct yuv_s {
}
yuv_t;
typedef
struct rgb_s {
}
rgb_t;
typedef struct pic_s
{
} pic_t;
这里有几个命令行的options会影响到输入图像的读取:
{ PARG,
&rbSwap,
"SWAP_R_AND_B",
"-rbswp",
0, 0}, // Swap red & blue
components 这个值表示RGB输入中的R和B需要进行交换一下。
{ PARG,
&bitsPerComponent,
"BITS_PER_COMPONENT",
"-bpc", 0,
0}, // bits per component
这个是表示DSC
encoder能支持的bpc,如果输入的图像的bpc和这个不相等,那么需要做一下处理.比如设置的bpc=9,但是输入的RGB是8bit,那么输入的RGB需要左移位1bit,变成9bits。反过来,如果要求的bpc小于输入图像的bpc,同样也要进行相应的处理。
{ PARG,
&enable422,
"ENABLE_422",
"-e422", 0,
0}, // enable_422
是否支持YUV422,如果不支持,那么YUV422需要转换成YUV444再进行处理。yuv_422_444(ip,
ip2);反过来,如果要求是YUV422作为输入,那么YUV444需要先转换成YUV422。yuv_444_422(ip, ip2);
{ PARG,
&useYuvInput,
"USE_YUV_INPUT",
"-uyi", 0,
0}, // Use YUV input (convert
if necessary)
是否只能使用YUV作为输入,如果是,那么RGB需要先转换成YUV再进行处理。rgb2yuv(ip,
ip2);相反的,如果只能使用RGB作为输入,那么如果输入是YUV,需要先转换成RGB。yuv2rgb(ip, ip2);
有下面的几个checking:
{ PARG,
&sliceWidth,
"SLICE_WIDTH",
"-nsh", 0,
0}, // Slice width (0=pic
width)
对于YUV422的输入来说,picture width和slice
width都必须是2的倍数,因为chroma只有一半。需要能够整除。如果不是的话,
如果picture width不满足,那么做crop:
如果是slice
width不满足,调整:
总结: 在DSC
encoder的一开始,根据配置信息或者输入参数来对输入的图像RGB444/YUV422/YUV444进行处理成要求的输入格式:
那么如果输入不是这种要求,就转换成这个要求的格式。
2.
输入cfg文件
encoder端的输入cfg文件:
/// Configuration for a single RC model range
typedef struct dsc_range_cfg_s {
int range_min_qp; ///< Min QP allowed for
this range
int range_max_qp; ///< Max QP allowed for
this range
int range_bpg_offset; ///< Bits/group
offset to apply to target for this group
} dsc_range_cfg_t;
这个配置结构中的一些值需要外部的命令行的输入来确定。但是无论如何,这个struct的信息就是engine需要的输入信息。在硬件中,它们大部分会被转换成register配置信息。
在encoder中也是利用这个struct来生成PPS的:
下面是一个8bpp/8bpc的cfg的例子:
3. RC buffer的分配和处理
在代码中根据rc_model_size的设置来分配实际的RC
buffer的大小。
详细的代码如下:
代码解释:
总结:实际的rate buffer
size是基于delay和bpp在rc_model_size上进行的细微的调整。计算出来的最小的RC
buffer的需求可能比rc_model_size更大,也可以更小。
3.
PPS中的一些RC 参数的计算
init_scale_value
: rcXformScale 在一个slice开始时的初始值
scale_decrement_interval 计算方法:
numExtraMuxBits
: 包括3个component的所有bits。
在slice结束的时候由于SSM的原因(因为SSM是以mux word为单位的)导致遗留下来的bits数目。也就是说不够mux
word的部分都会遗留下来(balance FIFO).
final_offset:
scale_increment_interval:
this value specifies the number of group times between
incrementing the rcXformScale
factor at the end of a slice .
first_line_bpg_offset
nfl_bpg_offset
slice_bpg_offset:
#define OFFSET_FRACTIONAL_BITS
11
first_line_bpg_ofs是slice的第一行多分配的bits/group,因此其他的行就必须要少分配这么多bits,平均到每一个其他的行的group中。
slice_bpg_offset:This value
specifies the number of bits (inc luding fractional bits) that are
de-allocated for each group in order to enforce
the slice constraint
RC的buffer在满足fullness的要求的前提下的空余空间/整个slice的groups。
http://s14/mw690/002mfkyxgy6ER8xyhYN3d&690
从大的流程上来说,算法模型是slice by slice的进行编码的。slice之间是按照raster scan的顺序进行处理的。
从大的流程上来说,算法模型是slice by slice的进行编码的。slice之间是按照raster scan的顺序进行处理的。
4.1 slice
编码
如果当前输入是RGB,首先把当前slice转换成YCoCg进行处理:
while ( !done ) {
}
数据结构说明:基于component和group的结构,有下面的数据结构
#define MAX_UNITS_PER_GROUP
3 //component的数目
#define SAMPLES_PER_UNIT 3
//一个group中的pixel数目
int quantizedResidual[MAX_UNITS_PER_GROUP][SAMPLES_PER_UNIT];
///< Quantized residuals for current
group
比如:dsc_state->quantizedResidual[CType][sampModCnt] =
QuantizeResidual(err_raw, qlevel);
代码的每行分析:
1. slice的RGB2YCoCg的转换
2. 初始化和数据准备
int *currLine[NUM_COMPONENTS]; ///<
Current line reconstructed samples
int *origLine[NUM_COMPONENTS]; ///<
Current line original samples (for encoder)
问题:为什么slice line buffer的大小需要做padding 5?需要知道slice
width已经考虑了partial group。
3. 读取origin line
从输入picture中读取当前要处理的origin slice
line的数据存储在origLine[component]中。
4. 更新ICH buffer
http://s4/mw690/002mfkyxgy6ERbGwebh73&690
在正常跑起来后,前一个group如果是ICH mode或者是P-MODE,都可能需要更新ICH buffer,如果前一个是ICH,那么就需要更新MRU;如果是P-MODE,就需要替换LRU。
在正常跑起来后,前一个group如果是ICH mode或者是P-MODE,都可能需要更新ICH buffer,如果前一个是ICH,那么就需要更新MRU;如果是P-MODE,就需要替换LRU。
这里的函数只是处理如果前一个group是ICH的情况,也就是update
MRU。把当前选择的index送到MRU的位置(buffer index 0)
dsc_state->history.pixels[CType][0] = recon[CType];
dsc_state->history.valid[0] = 1;
下面是component循环中的代码:
for ( CType = 0; CType
5. P-MODE prediction计算
对于MMAP/BP来说,有些计算是提前一行做的,包括BPVector的计算和MMAP/BP mode
decision等等。
PRED_TYPE *prevLinePred; ///< BP/MMAP
selection decsion buffer (since model calculates BP offset one line
ahead of time)这个数据结构就是存储预先计算好的mode
decision的结果。
typedef enum { PT_MAP=0, PT_LEFT, PT_BLOCK } PRED_TYPE;
//PT_LEFT是JPEG的MAP,DSC不使用。
根据前一行的计算得到第一步mode decision的BP/MMAP模式下的prediction。
开始做第二步的mode
decision,进行size的check,判断是否使用MPP:
利用上面得到的pred_x和orig计算出residual,并且进行量化,得到量化后的err.
MPP的residual和相应的调整:
计算MPP
mode下的err,前面也说过,如果MPP得到的量化后的residual的编码bit>max_bpc-qpLevel,那么这个量化后的residual需要进行调整,是的编码bits不回超过这个bit要求,因此直接利用pred和orig计算出来的MPP的residual不是最终的residual,而是还可能需要调整。
这里有2个函数就是利用residual来得到所需要的size的:
从前面的描述可以知道,group编码采用的是VLC编码,prefix+suffix,其中suffix就是对residual的直接编码。因此实际需要的bits可以通过residual的值的范围就可以知道需要多少bits。
//直接编码的size(sign)
经过上面的过程得到了MMAP/BP/MPP下的量化后的residual:
这两个数据结构的定义:
int quantizedResidual[MAX_UNITS_PER_GROUP][SAMPLES_PER_UNIT];
///< Quantized residuals for current
group
int
quantizedResidualMid[MAX_UNITS_PER_GROUP][SAMPLES_PER_UNIT];
///< Quantized residuals assuming midpoint
prediction for current group
IQ-RECON后得到reconstruct的pixels(MMAP/BP/MPP),计算BP/MMAP的重构的值recon_x和
最大的MPP的maxErr/SAD:
int maxError[MAX_UNITS_PER_GROUP]; ///<
Max error for each component using DPCM(BP/MMAP)
int maxMidError[MAX_UNITS_PER_GROUP];
///< Max error for each component using
MPP
int maxIchError[MAX_UNITS_PER_GROUP];
///< Max error for each component using
ICH
存储recons数据到对应的buffer中:因为在buffer定义中可以知道,left和right都提供了5个pixels的padding,因此实际的第一个pixel是从5开始存储的:
int *prevLine[NUM_COMPONENTS]; ///<
Previous line reconstructed samples
int *currLine[NUM_COMPONENTS];
///< Current line reconstructed samples
int *origLine[NUM_COMPONENTS];
///< Current line original samples (for encoder)
// line buffers have padding to left and right
lbufWidth =
dsc_cfg->slice_width + PADDING_LEFT + PADDING_RIGHT; // pixels
to left and right
上面3个line buffer都是按照lbufWidth来开的。
int hSkew =
PADDING_LEFT;
// there are hSkew fake pixels to the left of first real
pixel
http://s11/mw690/002mfkyxgy6EStrQFrs3a&690
得到的MAPP/BP的reconst数据存储量到对应的currLine buffer中,并且存储到输出buffer中。
得到的MAPP/BP的reconst数据存储量到对应的currLine buffer中,并且存储到输出buffer中。
可见,recon_x是模式MMAP/BP时的重构数据,而不是MPP/ICH的重构数据。
问题: 为什么直接就存储了,mode decision什么时候做?
总结:到目前为止都是在for ( CType = 0; CType(当然大循环是while ( !done )
这是一个pixel的循环),在这个循环中对每一个component计算MMAP/BP
和MPP模式下的maxErr/SAD,并且计算MMAP/BP模式下的recon 值存储到对应的line
buffer和输出buffer中。
Flatness checking:
flatness是以supergroup为单位的,也就是说当收集到一个完整的supergroup后可以开始flatness checking。
那么怎么算是一个supergroup?
从代码上可以看到,当收集满一个group的pixel或者当前pixel是slice
line的最后一个pixel(partial group)都会开始检测是否是supergroup(4
group)。这就意味着如果是partial group也是作为一个group来计算的。
从spec上来看,认为slice的第一个group不作为supergroup:
但是根据代码上的groupCount的描述
下面是几个计数器的使用:
关于groupCount:当前处理的group的数目
initial: int group_count = 0;
Flatness checking();
VLCGroup( dsc_cfg, dsc_state, &cmpr_buf)
group_count++;
dsc_state->groupCount = group_count;
可见 group_count ++是在flatness
checking后进行的。
关于hIndex变量:当前处理的pixel的位置。
slice开始的时候初始为0
flatness checking
...
hIndex ++;
可见,也是和groupCount以后在flatness后面才加1的。
sampModCnt:
group内的当前处理的pixel计数,记到3后回到0.
因为DSC
spec规定,只有当masterQP是在flatness要求的范围内才去检测flatness,因此首先要check这个masterQP是否符合要求。这个masterQP是当前group的QP。
这里有几个和flatness有关的变量:
int firstFlat; ///< If -1, none of the 4 group set are
flat, otherwise indicates which group is the one where flatness
starts
int prevFirstFlat; ///< If -1, none of the 4 group set are
flat, otherwise indicates which group is the one where flatness
starts
int prevIsFlat; ///< Flag indicating the previous group is
flat
int flatnessType; ///< 0 = somewhat flat; 1 = very
flat
int prevFlatnessType; ///< The
flatnessType coded with the previous group
下面开始在supergroup中对4个group进行循环检测,得到每一个group的flatness
type:
for (i=0; i
}
注意输入是flatness处理的当前Group和pixel的当前group(groupCount)是不同的:
从下面的代码可以看到:
其实slice的第一个group也算在了supergroup中,
masterQP的选择:
当前supergroup的左边第2个group的QP,见下面的图。The
masterQp value that is used is the one that is
used for the 2nd group to the left of the
supergroup that is being
tested。是符合spec的描述的。
从代码中分析,
因为flatness是基于original的pixel来做的,而不是reconst的值,因此只需要取origin line
buffer中的数据就可以了。其它的都是根据DSC
spec中的算法类似,做两次flatness checking,只有当flatness check1失败后才做flatness
check2.得到very flat/somewhat flat/not flat 的三种判断。
这里有2个需要注意的情况:
1.
supergroup可以跨行,如果当前要处理的group是在下一行,那么这个group是肯定是非flat的,不需要做判断。
// If group starts past the end of the slice, it can't be
flat
if (hIndex+1 >= dsc_cfg->slice_width)
return (0);
2.
从这里可以看到,如果当前supergroup跨了line,那么需要做padding。
p = dsc_state->origLine[CType][PADDING_LEFT + MIN(dsc_cfg->slice_width-1, hIndex+i)]
从上面可以看到,最大的padding长度是3个pixels(flatness
check2需要当前group的后面一个group)。
下面是一个图描述了完整的flatness的处理过程:
其余的请看上面一篇blog。
下面是详细的代码:DSC_Algorithm()
从代码上看,flatness是基于当前group的QP来调整给下一个group使用的QP。
下面是代码执行的顺序:
http://s10/mw690/002mfkyxgy6F5xzh86Z19&690
从上面的顺序可以知道,RC和flatness做的QP都是给下一个group使用的。这是合理的,本来就是根据当前group的编码情况来确定下一个group的QP。
从上面的顺序可以知道,RC和flatness做的QP都是给下一个group使用的。这是合理的,本来就是根据当前group的编码情况来确定下一个group的QP。
int masterQp; ///< QP used for the current group
这才是当前group正在使用的QP值。
int prevMasterQp; ///< QP used for the previous group
flatness调整后的QP会在下一个group开始编码之前设置为下一个group的masterQP:
从SPEC中可以知道,MMAP/BP的mode decision和BPVector的获取都是基于上一行的recon
数据,和当前行的数据无关,因此可以提前做这一步,在代码中也是这么做的,在做行切换的时候做这一行的所有的MMAP/BP的decision并且存储到下面的数据结构中:
typedef enum { PT_MAP=0(MMAP),
PT_LEFT(不需要top neibor的MMAP简化版),
PT_BLOCK(BP) } PRED_TYPE;
slice的第一行不能使用BP mode,而MMAP需要使用简化版的PT_LEFT来代替:
代码如下:
if ( hIndex >= dsc_cfg->slice_width ) 表示这是一个行切换的时机。
因为MMAP/BP的mode
decision的结果是对group的(luma和chroma相同的mode),因此在做mode
decision的时候应该同时考虑luma和chroma,上面的代码中有一个componennt的循环来决定mode
decision:
核心代码是BlockPredSearch():
#define BP_EDGE_COUNT
9
#define BP_EDGE_STRENGTH
32
hoffsete=0表示这是slice
line的第一个group。在BP的判断中需要判断edge,如果edgeCount>9个,那么不应该使用BP的。
这里有一个变量来存储BP/MMAP
mode和BPVector:
typedef enum { PT_MAP=0, PT_LEFT, PT_BLOCK } PRED_TYPE;
typedef enum { PT_MAP=0, PT_LEFT, PT_BLOCK } PRED_TYPE;
在DSC中,如果一个值>enum,那么表示的就是BP MODE and BP Vector。
从BP/MMAP 以及BPVector的search:
pred_x
= SamplePredict( dsc_state, currLine[CType], currLine[CType],
hOffset, (PRED_TYPE)(i+PT_BLOCK), 0,
CType );
(PRED_TYPE)(i+PT_BLOCK)
表示BPVector+1
如下面的代码就是BPmode:
#define BP_RANGE
10
#define BP_SIZE
3
#define PRED_BLK_SIZE
3
上面predErr[ctype][bprange]存储的是:
http://s11/mw690/002mfkyxgy6F3Y6etaWca&690
也就是存储当前group(Hpos)和BPV开始(从右往左) 的3个pixels 的recon之间的SAD累加).
也就是存储当前group(Hpos)和BPV开始(从右往左) 的3个pixels 的recon之间的SAD累加).
是SAD3X1,每一个component存储自己的。
每一个SAD3X1需要做clamp。根据spec规定,每一个diff需要clamp到6bits。
Clamp操作:
上面得到每一个pixel的error值都需要先进行clamp到了6bits:
然后再去进行SAD3X1的累加。
candidateVector’s (-1, -3, -4, -5,
-6, -7, -8, -9,
and -10) ,BPVector中没有-2。
这里的代码计算了10个BPV的prediction值(包括-2,后面再抛弃不用),实际上我们只有9个.
SAD利用的是上一行的recon pixels计算得到的9pixels的SAD9x1来做类似于MV search的BPV
search:
上面图中当前的9个pixels就是-8~0,
假设BPV=-10(最大),那么另外9个pixels就是-18~-10。
如果上面参与运算的pixels有超过了slice的边界就用padding来补,包括left
padding和right padding。
从下面的代码可以得到这一点,做了MAX,因此left是需要padding的,padding的数目可能很大>10:
p = currLine[MAX(h_offset_array_idx - 1
- offset,0)];
int* currLine,
// reconstructed samples for current
line
The BP predictor is used to predict all
three components from the pixel referred to by the block
prediction vector:
但是这里有个问题没有解决,最好的BPVector是怎么得到的?这就是9个BPV的9
pixels搜索:
下面是具体的代码:
#define BP_SIZE
3
int
lastErr[NUM_COMPONENTS][BP_SIZE][BP_RANGE];
///< 3-pixel SAD's for each of the past 3
3-pixel-wide prediction blocks for each BP offset
因为BPV是对luma和chroma的,因此cost需要同时考虑luma和chroma的cost。这就是ctype<2的作用。
上面是计算3 pixel
SAD。
if ( pixel_mod_cnt == PRED_BLK_SIZE - 1 )
:表示当group的最后一个pixels的时候才开始计算SAD9X1。
计算9
pixel SAD9x1的步骤:
a.
首先把3个pixels的3个component的SAD3X1累加。
3个pixels的3个component的SAD累加,并且clamp到511.
因为最大的diff被clamp到了6bits,1个pixel的3个components的累加为8bits。3个pixels的3个component累加为10bits。clamp到9bits。
b.
累加当前group的left的2个groups的SAD3X1。cursamp 包含了3个groups(left -2, left
-1, current group)
最终得到了SAD9X1.可见这个SAD9X1包括了9个pixels的每一个pixel的3个components的SAD3X1的和并进行clamp到8bits。
这和SPEC上规定的是一致的。
int bp_sads[BP_RANGE];
bpSad[candidateVector] = MIN(511,
sad3x1_0[candidateVector] + sad3x1_1[candidateVector]
+
sad3x1_2[candidateVector]);
MMAP和BP的mode decision:根据spec的规定
bpcount是从hpos>9才开始计算的。可见前面几个group是不可能选择BP的。
LastEdgeCount:
另外还有一个lastEdgeCount是利用top
neibor中的recon来检测连续的edge的数目。表示在检测到edge之前有多少个pixel是非edge的。
1. forceMPP的确定
在SPEC中是这样定义的:
The reason forceMpp is
needed is to ensure that the encoder rate buffer
has enough bits to allow the chunk to end on a
byte boundary.
forceMPP的两种情况:
a.
主要是检查bufferFullness,如果fullness太小了,需要增加bit的输出,使用forceMPP。
b.
另外一个就是如果当前group中的maxSize>maxBPC-qplevel,那么也需要MPP。这是mode
decision中使用的,但是不叫forceMPP。而只是mode decision选择MPP。
代码如下:
1. substream multiplexing
以mux word为单位从balance FIFO中取到RC buffer中,request的顺序由virtual
decoder来确定。
VLCGroup():
从上面的代码可以看到,当VLC后,生成的bits被送入了encoder balance
FIFO中。在VLC_Unit()中直接调用addbits()来加入(prefix+suffix=total_size):
{
fifo_put_bits(&(dsc_state->encShifter[CType]), d,
nbits);
dsc_state->numBits += nbits;
}
对于substream multiplexing:
if (dsc_state->groupCount >
dsc_cfg->mux_word_size + MAX_SE_SIZE - 1)
ProcessGroupEnc(dsc_cfg, dsc_state, *byte_out_p);
当编码的group大于dsc_cfg->mux_word_size
+ MAX_SE_SIZE -
1后开始做substream,也就是说第一次的substream要求是至少处理了这么多的group后才开始。为什么???
实际的处理过程如下:
在这里使用了virtual decoder来确定request是否发出,发出的顺序是什么.
在DSC spec中规定,如果request发出后,必须保证mux word数据已经准备好了,在C代码中去checking
balance FIFO中的fullness,是否包含了mux word的数据。并且取出mux word的数据。
上面的代码中还分析了如果mux word没有准备好,那么需要填0,但是在正常的情况下这不是DSC
SPEC允许的。
只有在最后的request中是可以允许的,比如最后的data确实不够一个mux word,因此需要进行填充输出。
函数AddBits:
// Write bits to output bitstream
if (prefix_value == max_pfx_size) //
Trailing "1" can be omitted
AddBits(dsc_cfg, dsc_state, CType, 0, max_pfx_size);
else
AddBits(dsc_cfg, dsc_state, CType, 1, prefix_value+1);
AddBits(dsc_cfg, dsc_state, CType, 0,
max_pfx_size);
表示输出max_pfx_size个0到balance
FIFO中。
AddBits(dsc_cfg, dsc_state, CType, 1,
prefix_value+1)
表示输出0x0001 (0的个数为prefix_value)到balance
FIFO。
在slice编码结束后有一个check:最后的RC bufferness不能大于这个threshold:
slice multiplexing:
这是以chunk 为单位输出到decoder(encoder端用写文件的方式来模拟)。
int codec_main(int argc, char *argv[])
从上面的循环可以知道,是按照slice by slice的进行encoder的。但是从下面的RC
buffer开辟来说,
从上面可以知道,在encoder后,这个mux word数据写入了RC
buffer中,最后以chunk的方式输出到decoder。
http://s8/mw690/002mfkyxgy6F3Hns2Gzc7&690
encoder端用写文件的方式来输出了chunk。因为display端是按照行进行扫描的,因此数据必须是按照行来发送的,在DSC后的chunk也是必须按照行来进行交织(尽管是多slice/line),因此最终也是按照picture line进行传输chunk的。
encoder端用写文件的方式来输出了chunk。因为display端是按照行进行扫描的,因此数据必须是按照行来发送的,在DSC后的chunk也是必须按照行来进行交织(尽管是多slice/line),因此最终也是按照picture line进行传输chunk的。
根据DSC的spec规定,RC
buffer输出bits到decoder需要等待Init_xmit_delay时间后才能开始。因此上面的代码就实现了这一点。当pixel
time大于了这个delay,开始输出RC 中的chunk数据。
b. linear transform
根据上一个group的VLC送过来的2个size:
rcGroupSize,codedGroupSize来更新RC中的fullness。并且使用linear
transform将fullness转换成rcModelBufferFullness
http://s9/mw690/002mfkyxgy6EKRViHNK98&690<wbr>display <wbr>stream <wbr>compression <wbr>standard(DSC)详解" TITLE="VESA <wbr>display <wbr>stream <wbr>compression <wbr>standard(DSC)详解" ACTION-DATA="http://s9/mw690/002mfkyxgy6EKRViHNK98&690" ACTION-TYPE="show-slide" STYLE="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-style: initial; border-color: initial; list-style-type: none; list-style-position: initial; list-style-image: initial; border-style: initial; border-color: initial;" />
关于rcXformOffset有下面的spec定义:
也就是从外部接收一个rcXform的初始值,在做linear
transform之前需要减去rc_model_size,得到一个值,这个值应该是一个负值。原因是因为DSC规定的rcModelBufferFullness是一个负值(0~
-rc_model_size)
1. RC buffer
前一篇:useful forum
后一篇:DSC问题收集和回答