摘要
这篇主要厘清FFmpeg如何调用多种视频编解码代码进行解码的主要函数调用逻辑
背景
FFmpeg作为一个视频编解码开源框架,被企业和个人广泛使用,但是一直不清楚他是怎么调用多种编解码器的,由于现在想做一个HEVC的码流分析器,需要了解FFmpeg对265码流解析的具体过程,今天按照官方提供的解码样例代码,整理一下FFmpeg是如何从外部包装代码到指定编解码代码实现解码流程的,主要以HEVC的解码过程为例。
// 从上至下进行调用
avcodec_receive_frame
decode_receive_frame_internal
decode_simple_receive_frame
decode_simple_internal
// 从这里开始使用decode函数指针指向hevc_decode_frame
avctx->codec->decode(avctx, frame, &got_frame, pkt);//函数指针指向hevc_decode_frame(),也即调用的hevc_decode_frame;
decode_nal_units
decode_nal_unit
ctb_addr_ts = hls_slice_data(s);
// 从这里开始进入解码,前面的函数主要是解析功能
s->avctx->execute(s->avctx, hls_decode_entry, arg, ret , 1, sizeof(int));
hls_decode_entry
hls_coding_quadtree
hls_coding_unit
hls_prediction_unit
hls_transform_tree
hls_transform_unit
ff_hevc_hls_residual_coding
整理流程中,才发现PU不是获得残差信息,PU只是获取到预测角度,运动矢量等信息,而实际计算残差信号也是在TU中计算的,TU里面才真正根据PU的信息获取到残差信号。
之前一直以为先PU计算得到了残差数据,然后传递给TU,TU直接对残差数据进行DCT变换后进行cabac编码了,我就说为什么hevc解码的时候,是先解码PU,再解码TU。
按照我之前的理解先PU获取残差数据,再TU变换编码,应该先idct TU里面的数据,再解码出残差信号。
特意回去看了一下HM的编码代码,再TU的编码函数中,找到了如下代码:
Void TEncSearch::xIntraCodingTUBlock( TComYuv* pcOrgYuv,
TComYuv* pcPredYuv,
TComYuv* pcResiYuv,
Pel resiLuma[NUMBER_OF_STORED_RESIDUAL_TYPES][MAX_CU_SIZE * MAX_CU_SIZE],
const Bool checkCrossCPrediction,
Distortion& ruiDist,
const ComponentID compID,
TComTU& rTu
DEBUG_STRING_FN_DECLARE(sDebug)
,Int default0Save1Load2
)
{
if (!rTu.ProcessComponentSection(compID))
{
return;
}
const Bool bIsLuma = isLuma(compID);
const TComRectangle &rect = rTu.getRect(compID);
TComDataCU *pcCU = rTu.getCU();
const UInt uiAbsPartIdx = rTu.GetAbsPartIdxTU();
const TComSPS &sps = *(pcCU->getSlice()->getSPS());
const UInt uiTrDepth = rTu.GetTransformDepthRelAdj(compID);
const UInt uiFullDepth = rTu.GetTransformDepthTotal();
const UInt uiLog2TrSize = rTu.GetLog2LumaTrSize();
const ChromaFormat chFmt = pcOrgYuv->getChromaFormat();
const ChannelType chType = toChannelType(compID);
const Int bitDepth = sps.getBitDepth(chType);
const UInt uiWidth = rect.width;
const UInt uiHeight = rect.height;
const UInt uiStride = pcOrgYuv ->getStride (compID);
Pel *piOrg = pcOrgYuv ->getAddr( compID, uiAbsPartIdx );
Pel *piPred = pcPredYuv->getAddr( compID, uiAbsPartIdx );
Pel *piResi = pcResiYuv->getAddr( compID, uiAbsPartIdx );
Pel *piReco = pcPredYuv->getAddr( compID, uiAbsPartIdx );
const UInt uiQTLayer = sps.getQuadtreeTULog2MaxSize() - uiLog2TrSize;
Pel *piRecQt = m_pcQTTempTComYuv[ uiQTLayer ].getAddr( compID, uiAbsPartIdx );
const UInt uiRecQtStride = m_pcQTTempTComYuv[ uiQTLayer ].getStride(compID);
const UInt uiZOrder = pcCU->getZorderIdxInCtu() + uiAbsPartIdx;
Pel *piRecIPred = pcCU