夺走的英文译语怎么说-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;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;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;i
ff_hevc_unref_frame(s,&s->DPB[i],~0);
av_frame_free(&s->DPB[i].frame);
}
for(i=0;i
av_buffer_unref(&s->vps_list[i]);
for(i=0;i
av_buffer_unref(&s->sps_list[i]);
for(i=0;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;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;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;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_allocated
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;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<
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_ts
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))<
y_ctb=(ctb_addr_rs/((s->sps->width+ctb_size-1)>>s->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在线翻译读音例句
发布评论