openh264 SVC 时域分层原理介绍

作者 : admin 本文共3921个字,预计阅读时间需要10分钟 发布时间: 2024-06-17 共1人阅读

openh264

OpenH264是一个开源的H.264编码器,由Cisco公司开发并贡献给开源社区。它支持包括SVC(Scalable Video Coding)在内的多种编码特性,适用于实时应用场景,比如WebRTC。OpenH264项目在GitHub上是公开的,任何人都可以访问和使用它的源代码。

SVC编码

SVC(Scalable Video Coding,可伸缩视频编码)是一种先进的视频编码技术,它允许视频流被分割成多个层级,每个层级可以独立解码,并且可以根据网络带宽和解码能力提供不同质量的视频。这种技术特别适用于视频会议、视频监控和流媒体服务等场景,其中用户的网络条件和设备能力可能有很大差异。
SVC技术通过分层编码,实现了视频质量分辨率帧率的可扩展性。

  • 其中帧率可扩展即时域分层编码,允许根据网络带宽能力实现不同的帧率变化;在网络带宽较低时,接收端可以只解码基本层帧率的视频,而在带宽较高时,可以解码更多的帧以获得更流畅的视频效。

SVC技术的关键优势在于其灵活性和适应性。通过一次编码过程,可以生成多个可解码的子码流,适应不同的网络条件和终端设备。例如,在视频会议中,可以根据每个参与者的网络带宽动态调整发送的视频质量,确保所有人都能获得尽可能好的观看体验。

SVC技术的应用可以有效解决传统视频编码中的一些问题,如多次编码的需求和转码过程中的计算复杂度及性能损失。它通过提供一种编码后的视频码流,使得可以根据不同的网络环境和终端需求灵活地传输和解码视频,从而优化了视频的传输效率和用户体验。

openh264 SVC时域分层编码原理

代码流程

openh264 SVC 时域分层原理介绍插图

原理

  1. 定义二维数组g_kuiTemporalIdListTable,是 openh264 实现时域分层的精髓部分,如下定义,分别对应 uiGopSize=1、2、3、4,表示时域分层 1、2、3、4 层;
    • 当uiGopSize = 1 时,表示时域分层为 1 层,即所有帧都是 0 0 0 0 层;
    • 当uiGopSize = 2 时,表示时域分层为 2 层,即帧序层是 0 1 0 1 层;
    • 当uiGopSize = 3 时,表示时域分层为 3 层,即帧序层是 0 2 1 2 0 2 1 2 层;
    • 当uiGopSize = 4 时,表示时域分层为 4 层,即帧序层是 0 3 2 3 1 3 2 3 层;
const uint8_t   g_kuiTemporalIdListTable[MAX_TEMPORAL_LEVEL][MAX_GOP_SIZE + 1] = {
  {
    0, 0, 0, 0, 0, 0, 0, 0,
    0
  },  // uiGopSize = 1
  {
    0, 1, 0, 0, 0, 0, 0, 0,
    0
  },  // uiGopSize = 2
  {
    0, 2, 1, 2, 0, 0, 0, 0,
    0
  },  // uiGopSize = 4
  {
    0, 3, 2, 3, 1, 3, 2, 3,
    0
  }  //uiGopSize = 8
};
  1. ParamTranscode函数中通过输入参数iTemporalLayerNum计算得到uiGopSize;
    • 其中iTemporalLayerNum的取值范围 1、2、3、4;
    • 因此通过计算uiGopSize的取值为 、2、4、8;
//代码有删减
iTemporalLayerNum = (int8_t)WELS_CLIP3 (pCodingParam.iTemporalLayerNum, 1,
                        MAX_TEMPORAL_LEVEL); // number of temporal layer specified
uiGopSize           = 1 << (iTemporalLayerNum - 1); // Override GOP size based temporal layer
  1. 定义WELS_LOG2函数通过输入uiGopSize 计算iDecStages;
static inline int32_t WELS_LOG2 (uint32_t v) {
  int32_t r = 0;
  while (v >>= 1) {
    ++r;
  }
  return r;

}
  1. DetermineTemporalSettings函数中实现将g_kuiTemporalIdListTable转换到uiCodingIdx2TemporalId一维数组中;
    • 使用WELS_LOG2函数来计算GOP大小的对数,根据uiGopSize确定时域分层数;
    • 定义pTemporalIdList指针,指向表g_kuiTemporalIdListTable;
    • 遍历uiGopSize中,确定pTemporalIdList中帧的时域层级kiTemporalId;
    • 根据kiTemporalId计算出每个空域层中的uiCodingIdx2TemporalId中的时域层级别标志,即对应g_kuiTemporalIdListTable中数字号;
  /*!
* \brief  determined key coding tables for temporal scalability, uiProfileIdc etc for each spatial layer settings
* \param  SWelsSvcCodingParam, and carried with known GOP size, max, input and output frame rate of each spatial
* \return NONE (should ensure valid parameter before this procedure)
*/
int32_t DetermineTemporalSettings() {
const int32_t iDecStages = WELS_LOG2 (uiGopSize); // (int8_t)GetLogFactor(1.0f, 1.0f * pcfg->uiGopSize);  //log2(uiGopSize)
const uint8_t* pTemporalIdList = &g_kuiTemporalIdListTable[iDecStages][0];
SSpatialLayerInternal* pDlp    = &sDependencyLayers[0];
SSpatialLayerConfig* pSpatialLayer = &sSpatialLayers[0];
int8_t i = 0;
while (i < iSpatialLayerNum) {
const uint32_t kuiLogFactorInOutRate = GetLogFactor (pDlp->fOutputFrameRate, pDlp->fInputFrameRate);
const uint32_t kuiLogFactorMaxInRate = GetLogFactor (pDlp->fInputFrameRate, fMaxFrameRate);
if (UINT_MAX == kuiLogFactorInOutRate || UINT_MAX == kuiLogFactorMaxInRate) {
return ENC_RETURN_INVALIDINPUT;
}
int32_t iNotCodedMask = 0;
int8_t iMaxTemporalId = 0;
memset (pDlp->uiCodingIdx2TemporalId, INVALID_TEMPORAL_ID, sizeof (pDlp->uiCodingIdx2TemporalId));
iNotCodedMask = (1 << (kuiLogFactorInOutRate + kuiLogFactorMaxInRate)) - 1;
for (uint32_t uiFrameIdx = 0; uiFrameIdx <= uiGopSize; ++ uiFrameIdx) {
if (0 == (uiFrameIdx & iNotCodedMask)) {
const int8_t kiTemporalId = pTemporalIdList[uiFrameIdx];
pDlp->uiCodingIdx2TemporalId[uiFrameIdx] = kiTemporalId;
if (kiTemporalId > iMaxTemporalId) {
iMaxTemporalId = kiTemporalId;
}
}
}
pDlp->iHighestTemporalId   = iMaxTemporalId;
pDlp->iTemporalResolution  = kuiLogFactorMaxInRate + kuiLogFactorInOutRate;
pDlp->iDecompositionStages = iDecStages - kuiLogFactorMaxInRate - kuiLogFactorInOutRate;
if (pDlp->iDecompositionStages < 0) {
return ENC_RETURN_INVALIDINPUT;
}
++ pDlp;
++ pSpatialLayer;
++ i;
}
iDecompStages = (int8_t)iDecStages;
return ENC_RETURN_SUCCESS;
}
  1. 之后根据时域分层 ID,进行参考帧管理和帧重要性管理操作。
    //代码有删减
if (iCurTid == 0 || pCtx->eSliceType == I_SLICE)
eNalRefIdc = NRI_PRI_HIGHEST;
else if (iCurTid == iDecompositionStages)
eNalRefIdc = NRI_PRI_LOWEST;
else if (1 + iCurTid == iDecompositionStages)
eNalRefIdc = NRI_PRI_LOW;
else // more details for other temporal layers?
eNalRefIdc = NRI_PRI_HIGHEST;
pCtx->eNalType = eNalType;
pCtx->eNalPriority = eNalRefIdc;
本站无任何商业行为
个人在线分享 » openh264 SVC 时域分层原理介绍
E-->