夺走的英文译语怎么说-common application


2023年3月31日发(作者:经典空间留言)

FFmpeg的HEVC解码器源码简单分析:解码器主⼲部分

=====================================================

HEVC源码分析⽂章列表:

【解码-libavcodecHEVC解码器】

=====================================================

本⽂分析FFmpeg的libavcodec中的HEVC解码器的主⼲部分。“主⼲部分”是相对于“CTU解码”、“环路滤波”这些细节部分⽽⾔

的。它包括了HEVC解码器直到hls_decode_entry()前⾯的函数调⽤关系(hls_decode_entry()后⾯就是HEVC解码器的细节部分,主要

包括了“CTU解码”、“环路滤波”2个部分)。

函数调⽤关系图

FFmpegHEVC解码器主⼲部分在整个HEVC解码器中的位置例如以下图所看到的。

HEVC解码器主⼲部分的源码的调⽤关系例如以下图所看到的。

从图中能够看出,HEVC解码器初始化函数是hevc_decode_init(),解码函数是hevc_decode_frame(),关闭函数是

hevc_decode_free()。当中hevc_decode_frame()调⽤了decode_nal_units()进⾏⼀帧NALU的解码,decode_nal_units()⼜调⽤了

decode_nal_unit()进⾏⼀个NALU的解码。

decode_nal_unit()⼀⽅⾯调⽤解析函数ff_hevc_decode_nal_vps(),ff_hevc_decode_nal_sps(),ff_hevc_decode_nal_pps()等对

VPS、SPS、PPS进⾏解析;还有⼀⽅⾯调⽤了hls_slice_header()和hls_slice_data()对Slice数据进⾏解码。

hls_slice_data()中调⽤了hls_decode_entry()。在当中完毕了SliceData解码的流程。该流程包括了CU、PU、TU解码,环路滤波、SAO

滤波等环节。

ff_hevc_decoder

ff_hevc_decoder是HEVC解码器相应的AVCodec结构体。该结构体的定义位于libavcodechevc.c,例如以下所看到的。

AVCodecff_hevc_decoder={

.name=\"hevc\",

.long_name=NULL_IF_CONFIG_SMALL(\"HEVC(HighEfficiencyVideoCoding)\"),

.type=AVMEDIA_TYPE_VIDEO,

.id=AV_CODEC_ID_HEVC,

.priv_data_size=sizeof(HEVCContext),

.priv_class=&hevc_decoder_class,

.init=hevc_decode_init,

.close=hevc_decode_free,

.decode=hevc_decode_frame,

.flush=hevc_decode_flush,

.update_thread_context=hevc_update_thread_context,

.init_thread_copy=hevc_init_thread_copy,

.capabilities=CODEC_CAP_DR1|CODEC_CAP_DELAY|

CODEC_CAP_SLICE_THREADS|CODEC_CAP_FRAME_THREADS,

.profiles=NULL_IF_CONFIG_SMALL(profiles),

};

从源码能够看出。HEVC解码器初始化函数是hevc_decode_init()。解码函数是hevc_decode_frame(),关闭函数是

hevc_decode_free()。

hevc_decode_init()

hevc_decode_init()⽤于初始化HEVC解码器。

该函数的定义例如以下。

//初始化HEVC解码器

staticav_coldinthevc_decode_init(AVCodecContext*avctx)

{

HEVCContext*s=avctx->priv_data;

intret;

//初始将就的近义词是什么 化CABAC

ff_init_cabac_states();

avctx->internal->allocate_progress=1;

//为HEVCContext中的变量分配内存空间

ret=hevc_init_context(avctx);

if(ret<0)

returnret;

s->enable_parallel_tiles=0;

s->picture_struct=0;

if(avctx->active_thread_type&FF_THREAD_SLICE)

s->threads_number=avctx->thread_count;

else

s->threads_number=1;

//假设AVCodecContext中包括extradata。则解码之

if(avctx->extradata_size>0&&avctx->extradata){

ret=hevc_decode_extradata(s);

if(ret<0){

hevc_decode_free(avctx);

returnret;

}

}

if((avctx->active_thread_type&FF_THREAD_FRAME)&&avctx->thread_count>1)

s->threads_type=FF_THREAD_FRAME;

else

s->threads_type=FF_THREAD_SLICE;

return0;

}

从源码中能够看出,hevc_decode_init()对HEVCContext中的变量做了⼀些初始化⼯作。当中调⽤了⼀个函数hevc_init_context()⽤于给

HEVCContext中的变量分配内存空间。

hevc_init_context()

hevc_init_context()⽤于给HEVCContext中的变量分配内存空间。该函数的定义例如以下所看到的。

//为HEVCContext中的变量分配内存空间

staticav_coldinthevc_init_context(AVCodecContext*avctx)

{

HEVCContext*s=avctx->priv_data;

inti;

s->avctx=avctx;

s->HEVClc=av_mallocz(sizeof(HEVCLocalContext));

if(!s->HEVClc)

gotofail;

s->HEVClcList[0]=s->HEVClc;

s->sList[0]=s;

s->cabac_state=av_malloc(HEVC_CONTEXTS);

if(!s->cabac_state)

gotofail;

s->tmp_frame=av_frame_alloc();

if(!s->tmp_frame)

gotofail;

s->output_frame=av_frame_alloc();

if(!s->output_frame)

gotofail;

for(i=0;iDPB);i++){

s->DPB[i].frame=av_frame_alloc();

if(!s->DPB[i].frame)

gotofail;

s->DPB[i].tf.f=s->DPB[i].frame;

}

s->max_ra=INT_MAX;

s->md5_ctx=av_md5_alloc();

if(!s->md5_ctx)

gotofail;

ff_bswa无边光景一时新的上一句 pdsp_init(&s->bdsp);

s->context_initialized=1;

s->eos=0;

return0;

fail:

hevc_decode_free(avctx);

returnAVERROR(ENOMEM);

}

hevc_decode_free()

hevc_decode_free()⽤于关闭HEVC解码器。该函数的定义例如以下所看到的。

//关闭HEVC解码器

staticav_coldinthevc_decode_free(AVCodecContext*avctx)

{

HEVCContext*s=avctx->priv_data;

inti;

pic_arrays_free(s);

av_freep(&s->md5_ctx);

for(i=0;inals_allocated;i++){

av_freep(&s->skipped_bytes_pos_nal[i]);

}

av_freep(&s->skipped_bytes_pos_size_nal);

av_freep(&s->skipped_bytes_nal);

av_freep(&s->skipped_bytes_pos_nal);

av_freep(&s->cabac_state);

av_frame_free(&s->tmp_frame);

av_frame_free(&s->output_frame);

for(i=0;iDPB);i++){

ff_hevc_unref_frame(s,&s->DPB[i],~0);

av_frame_free(&s->DPB[i].frame);

}

for(i=0;ivps_list);i++)

av_buffer_unref(&s->vps_list[i]);

for(i=0;isps_list);i++)

av_buffer_unref(&s->sps_list[i]);

for(i=0;ipps_list);i++)

av_buffer_unref(&s->pps_list[i]);

s->sps=NULL;

s->pps=NULL;

s->vps=NULL;

av_buffer_unref(&s->current_sps);

av_freep(&s->_point_offset);

av_freep(&s->);

av_freep(&s->);

for(i=1;ithreads_number;i++){

HEVCLocalContext*lc=s-情景的近义词 >HEVClcList[i];

if(lc){

av_freep(&s->HEVClcList[i]);

av_freep(&s->sList[i]);

}

}

if(s->HEVClc==s->HEVClcList[0])

s->HEVClc=NULL;

av_freep(&s->HEVClcList[0]);

for(i=0;inals_allocated;i++)

av_freep(&s->nals[i].rbsp_buffer);

av_freep(&s->nals);

s->nals_allocated=0;

return0;

}

从源码能够看出,hevc_decode_free()释放了HEVCContext中的内存。

hevc_decode_frame()

hevc_decode_frame()是HEVC解码器中最关键的函数。⽤于解码⼀帧数据。

该函数的定义例如以下所看到的。

/*

*解码⼀帧数据

*

*凝视:雷霄骅

*leixiaohua1020@

*/leixiaohua1020

*

*/

staticinthevc_decode_frame(AVCodecContext*avctx,void*data,int*got_output,

AVPacket*avpkt)

{

intret;

HEVCContext*s=avctx->priv_data;

//没有输⼊码流的时候。输出解码器中剩余数据

//相应“FlushDecoder”功能

if(!avpkt->size){

//第3个參数flush取值为1

ret=ff_hevc_output_frame(s,data,1);

if(ret<0)

returnret;

*got_output=ret;

return0;

}

s->ref=NULL;

//解码⼀帧数据

ret=decode_nal_units(s,avpkt->data,avpkt->size);

if(ret<0)

returnret;

/*verifytheSEIchecksum*/

if(avctx->err_recognition&AV_EF_CRCCHECK&&s->is_decoded&&

s->is_md5){

ret=verify_md5(s,s->ref->frame);

if(ret<0&&avctx->err_recognition&AV_EF_EXPLODE){

ff_hevc_unref_frame(s,s->ref,~0);

returnret;

}

}

s->is_md5=0;

if(s->is_decoded){

av_log(avctx,AV_LOG_DEBUG,\"DecodedframewithPOC%d.n\",s->poc);

s->is_decoded=0;

}

if(s->output_frame->buf[0]){

//输出解码后数据

av_frame_move_ref(data,s->output_frame);

*got_output=1;

}

returnavpkt->size;

}

从源码能够看出。hevc_decode_frame()依据输⼊的AVPacket的data是否为NULL分成两个情况:

(1)AVPacket的data为NULL的时候。代表没有输⼊码流。这时候直接调⽤ff_hevc_output_frame()输出解码器中缓存的帧。

(2)AVPacket的data不清平乐六盘山的诗意 为NULL的时候。调⽤decode_nal_units()解码输⼊的⼀帧数据的NALU。

以下看⼀下⼀帧NALU的解码函数decode_nal_units()。

decode_nal_units()

decode_nal_units()⽤于解码⼀帧NALU。该函数的定义例如以下所看到的。

//解码⼀帧数据

staticintdecode_nal_units(HEVCContext*s,constuint8_t*buf,intlength)

{

inti,狗尾续貂的主人公是谁 consumed,ret=0;

s->ref=NULL;

s->last_eos=s->eos;

s->eos=0;

/*splittheinputpacketintoNALunits,soweknowtheupperboundonthe

*numberofslicesintheframe*/

s->nb_nals=0;

while(length>=4){

HEVCNAL*nal;

intextract_length=0;

if(s->is_nalff){

inti;

for(i=0;inal_length_size;i++)

extract_length=(extract_length<<8)|buf[i];

buf+=s->nal_length_size;

length-=s->nal_length_size;

if(extract_length>length){

av_log(s->avctx,AV_LOG_ERROR,\"InvalidNALunitsize.n\");

ret=AVERROR_INVALIDDATA;

gotofail;

}

}else{

/*searchstartcode*/

//查找起始码0x000001

while(buf[0]!=0||buf[1]!=0||buf[2]!=1){

++buf;

--length;

if(length<4){

av_log(s->avctx,AV_LOG_ERROR,\"Nostartcodeisfound.n\");

ret=AVERROR_INVALIDDATA;

gotofail;

}

}

//找到后,跳过起始码(3Byte)

buf+=3;

length-=3;

}

if(!s->is_nalff)

extract_length=length;

if(s->nals_allocatednb_nals+1){

intnew_size=s->nals_allocated+1;

intnew_size=s->nals_allocated+1;

HEVCNAL*tmp=av_realloc_array(s->nals,new_size,sizeof(*tmp));

if(!tmp){

ret=AVERROR(ENOMEM);

gotofail;

}

s->nals=tmp;

memset(s->nals+s->nals_allocated,0,

(new_size-s->nals_allocated)*sizeof(*tmp));

av_reallocp_array(&s->skipped_bytes_nal,new_size,sizeof(*s->skipped_bytes_nal));

av_reallocp_array(&s->skipped_bytes_pos_size_nal,new_size,sizeof(*s->skipped_bytes_pos_size_nal));

av_reallocp_array(&s->skipped_bytes_pos_nal,new_size,sizeof(*s->skipped_bytes_pos_nal));

s->skipped_bytes_pos_size_nal[s->nals_al何的拼音 located]=1024;//initialbuffersize

s->skipped_bytes_pos_nal[s->nals_allocated]=av_malloc_array(s->skipped_bytes_pos_size_nal[s->nals_allocated],sizeof(*s->skipped_bytes_pos));

s->nals_allocated=new_size;

}

s->skipped_bytes_pos_size=s->skipped_bytes_pos_size_nal[s->nb_nals];

s->skipped_bytes_pos=s->skipped_bytes_pos_nal[s->nb_nals];

nal=&s->nals[s->nb_nals];

consumed=ff_hevc_extract_rbsp(s,buf,extract_length,nal);

s->skipped_bytes_nal[s->nb_nals]=s->skipped_bytes;

s->skipped_bytes_pos_size_nal[s->nb_nals]=s->skipped_bytes_pos_size;

s->skipped_bytes_pos_nal[s->nb_nals++]=s->skipped_bytes_pos;

if(consumed<0){

ret=consumed;

gotofail;

}

ret=init_get_bits8(&s->HEVClc->gb,nal->data,nal->size);

if(ret<0)

gotofail;

hls_nal_unit(s);

if(s->nal_unit_type==NAL_EOB_NUT||

s->nal_unit_type==NAL_EOS_NUT)

s->eos=1;

buf+=consumed;

length-=consumed;

}

/*parsetheNALunits*/

for(i=0;inb_nals;i++){

intret;

s->skipped_bytes=s->skipped_bytes_nal[i];

s->skipped_bytes_pos=s->skipped_bytes_pos_nal[i];

//解码NALU

ret=decode_nal_unit(s,s->nals[i].data,s->nals[i].size);

if(ret<0){

av_log(s->avct少年易学老难成 x,AV_LOG_WARNING,

\"ErrorparsingNALunit#%d.n\",i);

gotofail;

}

}

fail:

if(s->ref&&s->threads_type==FF_THREAD_FRAME)

ff_thread_report_progress(&s->ref->tf,INT_MAX,0);

returnret;

}

从源码能够看出。decode_nal_units()中⼜调⽤了还有⼀个函数decode_nal_unit(),两者的名字仅仅相差⼀个“s”。

由此能够看出decode_nal_unit()作⽤是解码⼀个NALU。

decode_nal_unit()

decode_nal_unit()⽤于解码⼀个NALU。该函数的定义例如以下所看到的。

//解码⼀个NALU

staticintdecode_nal_unit(HEVCContext*s,constuint8_t*nal,intlength)

{

HEVCLocalContext*lc=s->HEVClc;

GetBitContext*gb=&lc->gb;

intctb_addr_ts,ret;

ret=init_get_bits8(gb,nal,length);

if(ret<0)

returnret;

ret=hls_nal_unit(s);

if(ret<0){

av_log(s->avctx,AV_LOG_ERROR,\"InvalidNALunit%d,skipping.n\",

s->nal_unit_type);

gotofail;

}elseif(!ret)

return0;

switch(s->nal_unit_type){

caseNAL_VPS:

//解析VPS

ret=ff_hevc_decode_nal_vps(s);

if(ret<0)

gotofail;

break;

caseNAL_SPS:

//解析SPS

ret=ff_hevc_decode_nal_sps(s);

if(ret<0)

gotofail;

break;

caseNAL_PPS:

//解析PPS

ret=ff_hevc_decode_nal_pps(s);

if(ret<0)

gotofail;

break;

caseNAL_SEI_PREFIX:

caseNAL_SEI_SUFFIX:

//解析SEI

ret=ff_hevc_decode_nal_sei(s);

if(ret<0)

gotofail;

break;

caseNAL_TRAIL_R:

caseNAL_TRAIL_N:

caseNAL_TSA_N:

caseNAL_TSA_R:

caseNAL_STSA_N:

caseNAL_STSA_R:

caseNAL_BLA_W_LP:

caseNAL_BLA_W_RADL:

caseNAL_BLA_N_LP:

caseNAL_IDR_W_RADL:

caseNAL_IDR_W_RADL:

caseNAL_IDR_N_LP:

caseNAL_CRA_NUT:

caseNAL_RADL_N:

caseNAL_RADL_R:

caseNAL_RASL_N:

caseNAL_RASL_R:

//解析Slice

//解析SliceHeader

ret=hls_slice_header(s);

if(ret<0)

returnret;

if(s->max_ra==INT_MAX){

if(s->nal_unit_type==NAL_CRA_NUT||IS_BLA(s)){

s->max_ra=s->poc;

}else{

if(IS_IDR(s))

s->max_ra=INT_MIN;

}

}

if((s->nal_unit_type==NAL_RASL_R||s->nal_unit_type==NAL_RASL_N)&&

s->poc<=s->max_ra){

s->is_decoded=0;

break;

}else{

if(s->nal_unit_type==NAL_RASL_R&&s->poc>s->max_ra)

s->max_ra=INT_MIN;

}

if(s->_slice_in_pic_flag){

ret=hevc_frame_start(s);

if(ret<0)

returnret;

}elseif(!s->ref){

av_log(s->avctx,AV_LOG_ERROR,\"Firstsliceinaframemissing.n\");

gotofail;

}

if(s->na大江歌罢掉头东全诗意思 l_unit_type!=s->first_nal_type){

av_log(s->avctx,AV_LOG_ERROR,

\"Non-matchingNALtypesoftheVCLNALUs:%d%dn\",

s->first_nal_type,s->nal_unit_type);

returnAVERROR_INVALIDDATA;

}

if(!s->ent_slice_segment_flag&&

s->_type!=I_SLICE){

ret=ff_hevc_slice_rpl(s);

if(ret<0){

av_log(s->avctx,AV_LOG_WARNING,

\"Errorconstructingthereferencelistsforthecurrentslice.n\");

gotofail;

}

}

//解码SliceData

if(s->threads_number>1&&s->_entry_point_offsets>0)

ctb_addr_ts=hls_slice_data_wpp(s,nal,length);

else

ctb_addr_ts=hls_slice_data(s);

if(ctb_addr_ts>=(s->sps->ctb_width*s->sps->ctb_height)){

s->is_decoded=1;

}

if(ctb_addr_ts<0){

if(ctb_addr_ts<0){

ret=ctb_addr_ts;

gotofail;

}

break;

caseNAL_EOS_NUT:

caseNAL_EOB_NUT:

s->seq_喧闹的反义词 decode=(s->seq_decode+1)&0xff;

s->max_ra=INT_MAX;

break;

caseNAL_AUD:

caseNAL_FD_NUT:

break;

default:

av_log(s->avctx,AV_LOG_INFO,

\"SkippingNALunit%dn\",s->nal_unit_type);

}

return0;

fail:

if(s->avctx->err_recognition&AV_EF_EXPLODE)

returnret;

return0;

}

从源码能够看出。decode_nal_unit()依据不同的NALU类型调⽤了不同的处理函数。这些处理函数能够分为两类——解析函数和解码函

数,例如以下所看到的。

(1)解析函数(获取信息):

ff_hevc_decode_nal_vps():解析VPS。

ff_hevc_decode_nal_sps():解析SPS。

ff_hevc_decode_nal_pps():解析PPS。

ff_hevc_decode_nal_sei():解析SEI。

hls_slice_header():解析SliceHeader。

(2)解码函数(解码得到图像):

hls_slice_data():解码SliceData。

当中解析函数在⽂章《FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分》已经有过介绍,就不再反复叙述了。解码函数

hls_slice_data()完毕了解码Slice的⼯作,以下看⼀下该函数的定义。

hls_slice_data()

hls_slice_data()⽤于解码SliceData。该函数的定义例如以下所看到的。

//解码SliceData

staticinthls_slice_data(HEVCContext*s)

{

intarg[2];

intret[2];

arg[0]=0;

arg[1]=1;

//解码⼊⼝函数

s->avctx->execute(s->avctx,hls_decode_entry,arg,ret,1,sizeof(int));

returnret[0];

}

能够看出该函数的源码⾮常easy,调⽤了还有⼀个函数hls_decode_entry()。

hls_decode_entry()

hls_decode_entry()是SliceData解码的⼊⼝函数。该函数的定义例如以下所看到的。

/*

*解码⼊⼝函数

*

*凝视:雷霄骅

*leixiaohua1020@

*/leixiaohua1020

*

*/

staticinthls_decode_entry(AVCodecContext*avctxt,void*isFilterThread)

{

HEVCContext*s=avctxt->priv_data;

//CTB尺⼨

intctb_size=1<sps->log2_ctb_size;

intmore_data=1;

intx_ctb=0;

inty_ctb=0;

intctb_addr_ts=s->pps->ctb_addr_rs_to_ts[s->_ctb_addr_rs];

if(!ctb_addr_ts&&s->ent_slice_segment_flag){

av_log(s->avctx,AV_LOG_ERROR,\"Impossibleinitialtile.n\");

returnAVERROR_INVALIDDATA;

}

if(s->ent_slice_segment_flag){

intprev_rs=s->pps->ctb_addr_ts_to_rs[ctb_addr_ts-1];

if(s->tab_slice_address[prev_rs]!=s->_addr){

av_log(s->avctx,AV_LOG_ERROR,\"Previousslicesegmentmissingn\");

returnAVERROR_INVALIDDATA;

}

}

while(more_data&&ctb_addr_tssps->ctb_size孤城遥望玉门关全诗 ){

intctb_addr_rs=s->pps->ctb_addr_ts_to_rs[ctb_addr_ts];

//CTB的位置x和y

x_ctb=(ctb_addr_rs%((s->sps->width+ctb_size-1)>>s->sps->log2_ctb_size))<sps->log2_ctb_size;

y_ctb=(ctb_addr_rs/((s->sps->width+ctb_size-1)>>s->sps->log2_ctb_size))<sps->log2_ctb_size;

//初始化周围的參数

hls_decode_neighbour(s,x_ctb,y_ctb,ctb_addr_ts);

//初始化CABAC

ff_hevc_cabac_init(s,ctb_addr_ts);

//样点⾃适应补偿參数

hls_sao_param(s,x_ctb>>s->sps->log2_ctb_size,y_ctb>>s->sps->log2_ctb_size);

hls_sao_param(s,x_ctb>>s->sps->log2_ctb_size,y_ctb>>s->sps->log2_ctb_size);

s->deblock[ctb_addr_rs].beta_offset=s->_offset;

s->deblock[ctb_addr_rs].tc_offset=s->_offset;

s->filter_slice_edges[ctb_addr_rs]=s->_loop_filter_across_slices_enabled_flag;

/*

*CU⽰意图

*

*64x64块

*

*深度d=0

*split_flag=1时候划分为4个32x32

*

*+--------+--------+--------+--------+--------+--------+--------+--------+

*||

*|||

*||

*+|+

*||

*|||

*||

*+|+

*||

*|||

*||

*+|+

*||

*|||

*||

*+------------------+------------------+

*|||

*||

*|||

*++

*|||

*||

*|||

*++

*|||

*||

*|||

*++

*|||

*||

*|||

*+--------+--------+--------+--------+--------+--------+--------+--------+

*

*

*32x32块

*深度d=1

*split_flag=1时候划分为4个16x16

*

*+--------+--------+--------+--------+

*||

*|||

*||

*+|+

*||

*|||

*||

*+--------+--------+

*||

*|||

*||

*+|+

*||

*||

*|||

*||

*+--------+--------+--------+--------+

*

*

*16x16块

*深度d=2

*split_flag=1时候划分为4个8x8

*

*+--------+--------+

*||

*|||

*||

*+----+----+

*||

*|||

*||

*+--------+--------+

*

*

*8x8块

*深度d=3

*split_flag=1时候划分为4个4x4

*

*+----+----+

*|||

*+--+--+

*|||

*+----+----+

*

*/

/*

*解析四叉树结构。⽽且解码

*

*hls_coding_quadtree(HEVCContext*s,intx0,inty0,intlog2_cb_size,intcb_depth)中:

*s:HEVCContext上下⽂结构体

*x_ctb:CB位置的x坐标

*y_ctb:CB位置的y坐标

*log2_cb_size:CB⼤⼩取log2之后的值

*cb_depth:深度

*

*/

more_data=hls_coding_quadtree(s,x_ctb,y_ctb,s->sps->log2_ctb_size,0);

if(more_data<0){

s->tab_slice_address[ctb_addr_rs]=-1;

returnmore_data;

}

ctb_addr_ts++;

//保存解码信息以供下次使⽤

ff_hevc_save_states(s,ctb_addr_ts);

//去块效应滤波

ff_hevc_hls_filters(s,x_ctb,y_ctb,ctb_size);

}

if(x_ctb+ctb_size>=s->sps->width&&

y_ctb+ctb_size>=s->sps->height)

ff_hevc_hls_filter(s,x_ctb,y_ctb,ctb_size);

returnctb_addr_ts;

}

从源码能够看出。hls_decode_entry()以CTB为单位处理输⼊的视频流。每⼀个CTB的压缩数据经过以下两个基本步骤进⾏处理:

(1)调⽤hls_coding_quadtree()对CTB解码。当中包括了CU、PU、TU的解码。

(2)调⽤ff_hevc_hls_filters()进⾏滤波。当中包括去块效应滤波和SAO滤波。

hls_decode_entry()的函数调⽤关系例如以下图所看到的。

兴许的⼏篇⽂章将会对其调⽤的函数进⾏分析。

⾄此。FFmpegHEVC解码器的主⼲部分的源码就分析完毕了。

雷霄骅

leixiaohua1020@

/leixiaohua1020

更多推荐

unref是什么意思ef在线翻译读音例句