openh264 编码器源码分析:主体框架

openh264

  • OpenH264 是一个开源的 H.264 编码解码库,专为实时应用如 WebRTC 设计。
  • OpenH264 编码器因其高效性能、广泛的操作系统和架构支持以及灵活的编码参数设置,成为许多开发者在需要 H.264 编码解码解决方案时的理想选择。
  • 关于其介绍可以参考:OpenH264 编解码器介绍。

openh264 主体框架

  1. 通过 openh264 命令行工具来分析 open264 的主体框架,具体如下图:
    在这里插入图片描述
  2. 说明:
    从上图可以看出,完成openh264 编码主要分为三个模块,创建编码器编码过程销毁编码器

核心功能介绍

创建编码器

  1. CreateSVCEncHandle函数
  • 该函数创建编码实例ISVCEncoder,函数定义如下,可以看到,内部就是调用 api
    函数WelsCreateSVCEncoder完成编码实例创建。
int32_t CreateSVCEncHandle (ISVCEncoder** ppEncoder) {
  int32_t ret = 0;
  ret = WelsCreateSVCEncoder (ppEncoder);
  return ret;
}
  1. WelsCreateSVCEncoder函数
  • 该函数作为 api 函数,在内部完成 new 编码器类CWelsH264SVCEncoder的功能,函数定义如下,因此可以看到CWelsH264SVCEncoder类是进行编码类,继承于ISVCEncoder类。
int32_t WelsCreateSVCEncoder (ISVCEncoder** ppEncoder) {
  if ((*ppEncoder = new CWelsH264SVCEncoder()) != NULL) {
    return 0;
  }

  return 1;
}

编码过程

  1. ProcessEncoding函数
  • 完成编码的主要工作,包括参数设置解析命令行解析 config 文件初始化编码器核心编码等模块;具体如函数定义。
int ProcessEncoding (ISVCEncoder* pPtrEnc, int argc, char** argv, bool bConfigFile) {
  int iRet = 0;

  if (pPtrEnc == NULL)
    return 1;

  SFrameBSInfo sFbi;
  SEncParamExt sSvcParam;
  int64_t iStart = 0, iTotal = 0;

  // Preparing encoding process
  FILE* pFileYUV = NULL;
  int32_t iActualFrameEncodedCount = 0;
  int32_t iFrameIdx = 0;
  int32_t iTotalFrameMax = -1;
  uint8_t* pYUV = NULL;
  SSourcePicture* pSrcPic = NULL;
  uint32_t iSourceWidth, iSourceHeight, kiPicResSize;
  // Inactive with sink with output file handler
  FILE* pFpBs[4];
  pFpBs[0] = pFpBs[1] = pFpBs[2] = pFpBs[3] = NULL;
#if defined(COMPARE_DATA)
  //For getting the golden file handle
  FILE* fpGolden = NULL;
#endif
#if defined ( STICK_STREAM_SIZE )
  FILE* fTrackStream = fopen ("coding_size.stream", "wb");
#endif
  SFilesSet fs;
  // for configuration file
  CReadConfig cRdCfg;
  int iParsedNum = 1;

  memset (&sFbi, 0, sizeof (SFrameBSInfo));
  pPtrEnc->GetDefaultParams (&sSvcParam);
  fs.bEnableMultiBsFile = false;

  FillSpecificParameters (sSvcParam);
  pSrcPic = new SSourcePicture;
  if (pSrcPic == NULL) {
    iRet = 1;
    goto INSIDE_MEM_FREE;
  }
  //fill default pSrcPic
  pSrcPic->iColorFormat = videoFormatI420;
  pSrcPic->uiTimeStamp = 0;

  // if configure file exit, reading configure file firstly
  if (bConfigFile) {
    iParsedNum = 2;
    cRdCfg.Openf (argv[1]);
    if (!cRdCfg.ExistFile()) {
      fprintf (stderr, "Specified file: %s not exist, maybe invalid path or parameter settting.\n",
               cRdCfg.GetFileName().c_str());
      iRet = 1;
      goto INSIDE_MEM_FREE;
    }
    iRet = ParseConfig (cRdCfg, pSrcPic, sSvcParam, fs);
    if (iRet) {
      fprintf (stderr, "parse svc parameter config file failed.\n");
      iRet = 1;
      goto INSIDE_MEM_FREE;
    }
  }
  if (ParseCommandLine (argc - iParsedNum, argv + iParsedNum, pSrcPic, sSvcParam, fs) != 0) {
    printf ("parse pCommand line failed\n");
    iRet = 1;
    goto INSIDE_MEM_FREE;
  }
  pPtrEnc->SetOption (ENCODER_OPTION_TRACE_LEVEL, &g_LevelSetting);
  //finish reading the configurations
  iSourceWidth = pSrcPic->iPicWidth;
  iSourceHeight = pSrcPic->iPicHeight;
  kiPicResSize = iSourceWidth * iSourceHeight * 3 >> 1;

  pYUV = new uint8_t [kiPicResSize];
  if (pYUV == NULL) {
    iRet = 1;
    goto INSIDE_MEM_FREE;
  }

  //update pSrcPic
  pSrcPic->iStride[0] = iSourceWidth;
  pSrcPic->iStride[1] = pSrcPic->iStride[2] = pSrcPic->iStride[0] >> 1;

  pSrcPic->pData[0] = pYUV;
  pSrcPic->pData[1] = pSrcPic->pData[0] + (iSourceWidth * iSourceHeight);
  pSrcPic->pData[2] = pSrcPic->pData[1] + (iSourceWidth * iSourceHeight >> 2);

  //update sSvcParam
  sSvcParam.iPicWidth = 0;
  sSvcParam.iPicHeight = 0;
  for (int iLayer = 0; iLayer < sSvcParam.iSpatialLayerNum; iLayer++) {
    SSpatialLayerConfig* pDLayer = &sSvcParam.sSpatialLayers[iLayer];
    sSvcParam.iPicWidth = WELS_MAX (sSvcParam.iPicWidth, pDLayer->iVideoWidth);
    sSvcParam.iPicHeight = WELS_MAX (sSvcParam.iPicHeight, pDLayer->iVideoHeight);
  }
  //if target output resolution is not set, use the source size
  sSvcParam.iPicWidth = (!sSvcParam.iPicWidth) ? iSourceWidth : sSvcParam.iPicWidth;
  sSvcParam.iPicHeight = (!sSvcParam.iPicHeight) ? iSourceHeight : sSvcParam.iPicHeight;

  iTotalFrameMax = (int32_t)fs.uiFrameToBeCoded;
  //  sSvcParam.bSimulcastAVC = true;
  if (cmResultSuccess != pPtrEnc->InitializeExt (&sSvcParam)) { // SVC encoder initialization
    fprintf (stderr, "SVC encoder Initialize failed\n");
    iRet = 1;
    goto INSIDE_MEM_FREE;
  }
  for (int iLayer = 0; iLayer < MAX_DEPENDENCY_LAYER; iLayer++) {
    if (fs.sRecFileName[iLayer][0] != 0) {
      SDumpLayer sDumpLayer;
      sDumpLayer.iLayer = iLayer;
      sDumpLayer.pFileName = fs.sRecFileName[iLayer];
      if (cmResultSuccess != pPtrEnc->SetOption (ENCODER_OPTION_DUMP_FILE, &sDumpLayer)) {
        fprintf (stderr, "SetOption ENCODER_OPTION_DUMP_FILE failed!\n");
        iRet = 1;
        goto INSIDE_MEM_FREE;
      }
    }
  }
  // Inactive with sink with output file handler
  if (fs.strBsFile.length() > 0) {
    bool bFileOpenErr = false;
    if (sSvcParam.iSpatialLayerNum == 1 || fs.bEnableMultiBsFile == false) {
      pFpBs[0] = fopen (fs.strBsFile.c_str(), "wb");
      bFileOpenErr = (pFpBs[0] == NULL);
    } else { //enable multi bs file writing
      string filename_layer;
      string add_info[4] = {"_layer0", "_layer1", "_layer2", "_layer3"};
      string::size_type found = fs.strBsFile.find_last_of ('.');
      for (int i = 0; i < sSvcParam.iSpatialLayerNum; ++i) {
        filename_layer = fs.strBsFile.insert (found, add_info[i]);
        pFpBs[i] = fopen (filename_layer.c_str(), "wb");
        fs.strBsFile = filename_layer.erase (found, 7);
        bFileOpenErr |= (pFpBs[i] == NULL);
      }
    }
    if (bFileOpenErr) {
      fprintf (stderr, "Can not open file (%s) to write bitstream!\n", fs.strBsFile.c_str());
      iRet = 1;
      goto INSIDE_MEM_FREE;
    }
  } else {
    fprintf (stderr, "Don't set the proper bitstream filename!\n");
    iRet = 1;
    goto INSIDE_MEM_FREE;
  }

#if defined(COMPARE_DATA)
  //For getting the golden file handle
  if ((fpGolden = fopen (argv[3], "rb")) == NULL) {
    fprintf (stderr, "Unable to open golden sequence file, check corresponding path!\n");
    iRet = 1;
    goto INSIDE_MEM_FREE;
  }
#endif

  pFileYUV = fopen (fs.strSeqFile.c_str(), "rb");
  if (pFileYUV != NULL) {
#if defined(_WIN32) || defined(_WIN64)
#if _MSC_VER >= 1400
    if (!_fseeki64 (pFileYUV, 0, SEEK_END)) {
      int64_t i_size = _ftelli64 (pFileYUV);
      _fseeki64 (pFileYUV, 0, SEEK_SET);
      iTotalFrameMax = WELS_MAX ((int32_t) (i_size / kiPicResSize), iTotalFrameMax);
    }
#else
    if (!fseek (pFileYUV, 0, SEEK_END)) {
      int64_t i_size = ftell (pFileYUV);
      fseek (pFileYUV, 0, SEEK_SET);
      iTotalFrameMax = WELS_MAX ((int32_t) (i_size / kiPicResSize), iTotalFrameMax);
    }
#endif
#else
    if (!fseeko (pFileYUV, 0, SEEK_END)) {
      int64_t i_size = ftello (pFileYUV);
      fseeko (pFileYUV, 0, SEEK_SET);
      iTotalFrameMax = WELS_MAX ((int32_t) (i_size / kiPicResSize), iTotalFrameMax);
    }
#endif
  } else {
    fprintf (stderr, "Unable to open source sequence file (%s), check corresponding path!\n",
             fs.strSeqFile.c_str());
    iRet = 1;
    goto INSIDE_MEM_FREE;
  }

  iFrameIdx = 0;
  while (iFrameIdx < iTotalFrameMax && (((int32_t)fs.uiFrameToBeCoded <= 0)
                                        || (iFrameIdx < (int32_t)fs.uiFrameToBeCoded))) {

#ifdef ONLY_ENC_FRAMES_NUM
    // Only encoded some limited frames here
    if (iActualFrameEncodedCount >= ONLY_ENC_FRAMES_NUM) {
      break;
    }
#endif//ONLY_ENC_FRAMES_NUM
    bool bCanBeRead = false;
    bCanBeRead = (fread (pYUV, 1, kiPicResSize, pFileYUV) == kiPicResSize);

    if (!bCanBeRead)
      break;
    // To encoder this frame
    iStart = WelsTime();
    pSrcPic->uiTimeStamp = WELS_ROUND (iFrameIdx * (1000 / sSvcParam.fMaxFrameRate));
    int iEncFrames = pPtrEnc->EncodeFrame (pSrcPic, &sFbi);
    iTotal += WelsTime() - iStart;
    ++ iFrameIdx;
    if (videoFrameTypeSkip == sFbi.eFrameType) {
      continue;
    }

    if (iEncFrames == cmResultSuccess) {
      int iLayer = 0;
      int iFrameSize = 0;
      while (iLayer < sFbi.iLayerNum) {
        SLayerBSInfo* pLayerBsInfo = &sFbi.sLayerInfo[iLayer];
        if (pLayerBsInfo != NULL) {
          int iLayerSize = 0;
          int iNalIdx = pLayerBsInfo->iNalCount - 1;
          do {
            iLayerSize += pLayerBsInfo->pNalLengthInByte[iNalIdx];
            -- iNalIdx;
          } while (iNalIdx >= 0);
#if defined(COMPARE_DATA)
          //Comparing the result of encoder with golden pData
          {
            unsigned char* pUCArry = new unsigned char [iLayerSize];

            fread (pUCArry, 1, iLayerSize, fpGolden);

            for (int w = 0; w < iLayerSize; w++) {
              if (pUCArry[w] != pLayerBsInfo->pBsBuf[w]) {
                fprintf (stderr, "error @frame%d/layer%d/byte%d!!!!!!!!!!!!!!!!!!!!!!!!\n", iFrameIdx, iLayer, w);
                //fprintf(stderr, "%x - %x\n", pUCArry[w], pLayerBsInfo->pBsBuf[w]);
                break;
              }
            }
            fprintf (stderr, "frame%d/layer%d comparation completed!\n", iFrameIdx, iLayer);

            delete [] pUCArry;
          }
#endif
          if (sSvcParam.iSpatialLayerNum == 1 || fs.bEnableMultiBsFile == false)
            fwrite (pLayerBsInfo->pBsBuf, 1, iLayerSize, pFpBs[0]); // write pure bit stream into file
          else { //multi bs file write
            if (pLayerBsInfo->uiSpatialId == 0) {
              unsigned char five_bits = pLayerBsInfo->pBsBuf[4] & 0x1f;
              if ((five_bits == 0x07) || (five_bits == 0x08)) { //sps or pps
                for (int i = 0; i < sSvcParam.iSpatialLayerNum; ++i) {
                  fwrite (pLayerBsInfo->pBsBuf, 1, iLayerSize, pFpBs[i]);
                }
              } else {
                fwrite (pLayerBsInfo->pBsBuf, 1, iLayerSize, pFpBs[0]);
              }
            } else {
              fwrite (pLayerBsInfo->pBsBuf, 1, iLayerSize, pFpBs[pLayerBsInfo->uiSpatialId]);
            }
          }
          iFrameSize += iLayerSize;
        }
        ++ iLayer;
      }
#if defined (STICK_STREAM_SIZE)
      if (fTrackStream) {
        fwrite (&iFrameSize, 1, sizeof (int), fTrackStream);
      }
#endif//STICK_STREAM_SIZE
      ++ iActualFrameEncodedCount; // excluding skipped frame time
    } else {
      fprintf (stderr, "EncodeFrame(), ret: %d, frame index: %d.\n", iEncFrames, iFrameIdx);
    }

  }

  if (iActualFrameEncodedCount > 0) {
    double dElapsed = iTotal / 1e6;
    printf ("Width:\t\t%d\nHeight:\t\t%d\nFrames:\t\t%d\nencode time:\t%f sec\nFPS:\t\t%f fps\n",
            sSvcParam.iPicWidth, sSvcParam.iPicHeight,
            iActualFrameEncodedCount, dElapsed, (iActualFrameEncodedCount * 1.0) / dElapsed);
#if defined (WINDOWS_PHONE)
    g_fFPS = (iActualFrameEncodedCount * 1.0f) / (float) dElapsed;
    g_dEncoderTime = dElapsed;
    g_iEncodedFrame = iActualFrameEncodedCount;
#endif
  }
INSIDE_MEM_FREE:
  for (int i = 0; i < sSvcParam.iSpatialLayerNum; ++i) {
    if (pFpBs[i]) {
      fclose (pFpBs[i]);
      pFpBs[i] = NULL;
    }
  }
#if defined (STICK_STREAM_SIZE)
  if (fTrackStream) {
    fclose (fTrackStream);
    fTrackStream = NULL;
  }
#endif
#if defined (COMPARE_DATA)
  if (fpGolden) {
    fclose (fpGolden);
    fpGolden = NULL;
  }
#endif
  // Destruction memory introduced in this routine
  if (pFileYUV != NULL) {
    fclose (pFileYUV);
    pFileYUV = NULL;
  }
  if (pYUV) {
    delete[] pYUV;
    pYUV = NULL;
  }
  if (pSrcPic) {
    delete pSrcPic;
    pSrcPic = NULL;
  }
  return iRet;
}

  1. GetDefaultParams函数
  • api 函数,完成编码参数默认设置
/* Interfaces override from ISVCEncoder */

int CWelsH264SVCEncoder::GetDefaultParams (SEncParamExt* argv) {
  SWelsSvcCodingParam::FillDefault (*argv);
  return cmResultSuccess;
}

  1. FillSpecificParameters函数
  • 填写特定参数
int FillSpecificParameters (SEncParamExt& sParam) {
  /* Test for temporal, spatial, SNR scalability */
  sParam.iUsageType = CAMERA_VIDEO_REAL_TIME;
  sParam.fMaxFrameRate  = 60.0f;                // input frame rate
  sParam.iPicWidth      = 1280;                 // width of picture in samples
  sParam.iPicHeight     = 720;                  // height of picture in samples
  sParam.iTargetBitrate = 2500000;              // target bitrate desired
  sParam.iMaxBitrate    = UNSPECIFIED_BIT_RATE;
  sParam.iRCMode        = RC_QUALITY_MODE;      //  rc mode control
  sParam.iTemporalLayerNum = 3;    // layer number at temporal level
  sParam.iSpatialLayerNum  = 4;    // layer number at spatial level
  sParam.bEnableDenoise    = 0;    // denoise control
  sParam.bEnableBackgroundDetection = 1; // background detection control
  sParam.bEnableAdaptiveQuant       = 1; // adaptive quantization control
  sParam.bEnableFrameSkip           = 1; // frame skipping
  sParam.bEnableLongTermReference   = 0; // long term reference control
  sParam.iLtrMarkPeriod = 30;
  sParam.uiIntraPeriod  = 320;           // period of Intra frame
  sParam.eSpsPpsIdStrategy = INCREASING_ID;
  sParam.bPrefixNalAddingCtrl = 0;
  sParam.iComplexityMode = LOW_COMPLEXITY;
  sParam.bSimulcastAVC         = false;
  int iIndexLayer = 0;
  sParam.sSpatialLayers[iIndexLayer].uiProfileIdc       = PRO_BASELINE;
  sParam.sSpatialLayers[iIndexLayer].iVideoWidth        = 160;
  sParam.sSpatialLayers[iIndexLayer].iVideoHeight       = 90;
  sParam.sSpatialLayers[iIndexLayer].fFrameRate         = 7.5f;
  sParam.sSpatialLayers[iIndexLayer].iSpatialBitrate    = 64000;
  sParam.sSpatialLayers[iIndexLayer].iMaxSpatialBitrate    = UNSPECIFIED_BIT_RATE;
  sParam.sSpatialLayers[iIndexLayer].sSliceArgument.uiSliceMode = SM_SINGLE_SLICE;

  ++ iIndexLayer;
  sParam.sSpatialLayers[iIndexLayer].uiProfileIdc       = PRO_SCALABLE_BASELINE;
  sParam.sSpatialLayers[iIndexLayer].iVideoWidth        = 320;
  sParam.sSpatialLayers[iIndexLayer].iVideoHeight       = 180;
  sParam.sSpatialLayers[iIndexLayer].fFrameRate         = 15.0f;
  sParam.sSpatialLayers[iIndexLayer].iSpatialBitrate    = 160000;
  sParam.sSpatialLayers[iIndexLayer].iMaxSpatialBitrate    = UNSPECIFIED_BIT_RATE;
  sParam.sSpatialLayers[iIndexLayer].sSliceArgument.uiSliceMode = SM_SINGLE_SLICE;

  ++ iIndexLayer;
  sParam.sSpatialLayers[iIndexLayer].uiProfileIdc       = PRO_SCALABLE_BASELINE;
  sParam.sSpatialLayers[iIndexLayer].iVideoWidth        = 640;
  sParam.sSpatialLayers[iIndexLayer].iVideoHeight       = 360;
  sParam.sSpatialLayers[iIndexLayer].fFrameRate         = 30.0f;
  sParam.sSpatialLayers[iIndexLayer].iSpatialBitrate    = 512000;
  sParam.sSpatialLayers[iIndexLayer].iMaxSpatialBitrate    = UNSPECIFIED_BIT_RATE;
  sParam.sSpatialLayers[iIndexLayer].sSliceArgument.uiSliceMode = SM_SINGLE_SLICE;
  sParam.sSpatialLayers[iIndexLayer].sSliceArgument.uiSliceNum = 1;

  ++ iIndexLayer;
  sParam.sSpatialLayers[iIndexLayer].uiProfileIdc       = PRO_SCALABLE_BASELINE;
  sParam.sSpatialLayers[iIndexLayer].iVideoWidth        = 1280;
  sParam.sSpatialLayers[iIndexLayer].iVideoHeight       = 720;
  sParam.sSpatialLayers[iIndexLayer].fFrameRate         = 30.0f;
  sParam.sSpatialLayers[iIndexLayer].iSpatialBitrate    = 1500000;
  sParam.sSpatialLayers[iIndexLayer].iMaxSpatialBitrate    = UNSPECIFIED_BIT_RATE;
  sParam.sSpatialLayers[iIndexLayer].sSliceArgument.uiSliceMode = SM_SINGLE_SLICE;
  sParam.sSpatialLayers[iIndexLayer].sSliceArgument.uiSliceNum = 1;

  float fMaxFr = sParam.sSpatialLayers[sParam.iSpatialLayerNum - 1].fFrameRate;
  for (int32_t i = sParam.iSpatialLayerNum - 2; i >= 0; -- i) {
    if (sParam.sSpatialLayers[i].fFrameRate > fMaxFr + EPSN)
      fMaxFr = sParam.sSpatialLayers[i].fFrameRate;
  }
  sParam.fMaxFrameRate = fMaxFr;

  return 0;
}

  1. ParseConfig函数
  • 解析 config 文件的函数
int ParseConfig (CReadConfig& cRdCfg, SSourcePicture* pSrcPic, SEncParamExt& pSvcParam, SFilesSet& sFileSet) {
 //代码有删减
 return iRet;
}
  1. ParseCommandLine函数

解析命令行函数

int ParseCommandLine (int argc, char** argv, SSourcePicture* pSrcPic, SEncParamExt& pSvcParam, SFilesSet& sFileSet) {
	//代码有删减
	return 0}
  1. SetOption函数
  • api 函数,设置编码选项
/************************************************************************
* InDataFormat, IDRInterval, SVC Encode Param, Frame Rate, Bitrate,..
************************************************************************/
int CWelsH264SVCEncoder::SetOption (ENCODER_OPTION eOptionId, void* pOption) {
	//代码有删减
	return 0;
}
  1. InitializeExt函数
  • api 函数,初始化编码器
int CWelsH264SVCEncoder::InitializeExt (const SEncParamExt* argv) {
  if (m_pWelsTrace == NULL) {
    return cmMallocMemeError;
  }

  WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "CWelsH264SVCEncoder::InitEncoder(), openh264 codec version = %s",
           VERSION_NUMBER);

  if (NULL == argv) {
    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "CWelsH264SVCEncoder::InitializeExt(), invalid argv= 0x%p",
             argv);
    return cmInitParaError;
  }

  SWelsSvcCodingParam sConfig;
  // Convert SEncParamExt into WelsSVCParamConfig here..
  if (sConfig.ParamTranscode (*argv)) {
    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR,
             "CWelsH264SVCEncoder::InitializeExt(), parameter_translation failed.");
    TraceParamInfo (&sConfig);
    Uninitialize();
    return cmInitParaError;
  }

  return InitializeInternal (&sConfig);
}
  1. EncodeFrame函数
  • api 函数,完成具体的编码功能,是 openh264 编码模块的核心模块。
*
 *  SVC core encoding
 */
int CWelsH264SVCEncoder::EncodeFrame (const SSourcePicture* kpSrcPic, SFrameBSInfo* pBsInfo) {
  if (! (kpSrcPic && m_bInitialFlag && pBsInfo)) {
    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "CWelsH264SVCEncoder::EncodeFrame(), cmInitParaError.");
    return cmInitParaError;
  }
  if (kpSrcPic->iColorFormat != videoFormatI420) {
    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "CWelsH264SVCEncoder::EncodeFrame(), wrong iColorFormat %d",
             kpSrcPic->iColorFormat);
    return cmInitParaError;
  }

  const int32_t kiEncoderReturn = EncodeFrameInternal (kpSrcPic, pBsInfo);

  if (kiEncoderReturn != cmResultSuccess) {
    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "CWelsH264SVCEncoder::EncodeFrame(), kiEncoderReturn %d",
             kiEncoderReturn);
    return kiEncoderReturn;
  }

#ifdef REC_FRAME_COUNT
  ++ m_uiCountFrameNum;
  WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO,
           "CWelsH264SVCEncoder::EncodeFrame(), m_uiCountFrameNum= %d,", m_uiCountFrameNum);
#endif//REC_FRAME_COUNT

  return kiEncoderReturn;
}

销毁编码器

  1. DestroySVCEncHandle函数
  • 该函数销毁编码实例ISVCEncoder,函数定义如下,可以看到,内部就是调用 api
    函数WelsCreateSVCEncoder完成编码实例销毁。
void DestroySVCEncHandle (ISVCEncoder* pEncoder) {
  if (pEncoder) {
    WelsDestroySVCEncoder (pEncoder);

  }
}
  1. WelsCreateSVCEncoder函数
  • 该函数作为 api 函数,在内部完成 delete编码器类CWelsH264SVCEncoder的功能,函数定义如下,因此可以看到CWelsH264SVCEncoder类是进行编码类,继承于ISVCEncoder类。
void WelsDestroySVCEncoder (ISVCEncoder* pEncoder) {
  CWelsH264SVCEncoder* pSVCEncoder = (CWelsH264SVCEncoder*)pEncoder;

  if (pSVCEncoder) {
    delete pSVCEncoder;
    pSVCEncoder = NULL;
  }
}

后续

  • 后续针对编码核心模块EncodeFrame函数进行分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/701024.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

express+宝塔实现文件上传服务

文章目录 服务器部分开启存放文件的端口配置nginx该端口入口手动在/www/wwwroot/file目录下存放一张图片进行访问 express接口部分代码测试 服务器部分 开启存放文件的端口 我这里以83为例 先到对应的服务商开启端口&#xff0c;比如我这里是阿里云 测试&#xff0c;比如这里…

大模型基础——从零实现一个Transformer(3)

大模型基础——从零实现一个Transformer(1)-CSDN博客 一、前言 之前两篇文章已经讲了Transformer的Embedding,Tokenizer,Attention,Position Encoding, 本文我们继续了解Transformer中剩下的其他组件. 二、归一化 2.1 Layer Normalization layerNorm是针对序列数据提出的一种…

如何掌握多门编程语言?

我的答案是&#xff1a;掌握学习新编程语言的能力。 授之以鱼不如授之以渔 对于一个编程老手来说&#xff0c;学习新的编程语言应该很容易。他们只需要一个周末甚至是几个小时就可以开始用新学的编程语言写代码了。优秀的程序员可以为了完成某个任务使用任何一门编程语言。毕…

UE5 Sequencer 使用指导 - 学习笔记

https://www.bilibili.com/video/BV1jG411L7r7/?spm_id_from333.337.search-card.all.click&vd_source707ec8983cc32e6e065d5496a7f79ee6 Sequencer 01 1.1 调整视口 调整窗口数量 调整视口类型为Cinematic视口 视口显示网格&#xff0c;或者条件参考线 1.2 关卡动画与…

探索工厂智能制造解决方案的革新与应用

随着工业4.0时代的到来&#xff0c;工厂智能制造解决方案正在以前所未有的速度和规模改变着传统制造业的面貌。从自动化生产到智能化管理&#xff0c;工厂智能制造解决方案正在为制造企业带来前所未有的效率提升和竞争优势。本文将深入探讨工厂智能制造解决方案的革新与应用&am…

ANSYS电池包行业结构仿真解决方案

目前动力电池开发中面临的问题&#xff1a; ▪ 性能(能量密度及功率密度) ▪ 耐用性和使用寿命(考虑在不同环境和使用周期) ▪ 安全性(考虑恶劣环境) ▪ 费用成本 ▪ 复杂的多尺度、多物理场系统 ▪ 快速发展的材料和设计理念 ▪ 现有软件工具局限性 目录 1. 动力电池开…

Python 制作词云图

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

VM ubuntu终端使用Host代理的方法

1、设置网络地址转换NAT 2、在终端敲击如下命令 先敲击 ip route show 找到网关。再敲击如下命令&#xff1a; export http_proxyhttp://10.0.2.2:33210 export https_proxyhttp://10.0.2.2:33210 export HTTP_PROXYhttp://10.0.2.2:33210/ export HTTPS_PROXYhttp://10.0.2.…

“探索AIGC市场:腾讯元宝APP加入竞争,大模型产品的未来走向与个人选择“

文章目录 每日一句正能量前言使用体验分享独特优势和倾向选择字节豆包百度文心一言阿里通义千问腾讯元宝个人倾向选择结论 未来发展方向技术创新可持续可拓展性用户体验应用场景政府赋能数据安全与隐私保护伦理与社会责任国际合作与竞争结论 后记 每日一句正能量 不管现在有多么…

小程序视图渲染数据和部分事件的绑定

今天依旧使用这个目录进行教学 数据的渲染 在 index.js的 page中定义一个data对象结构是这样的 Page({data:{name:张三} }) 在index.wxml 中 利用模板语法进行渲染 <view >{{name}}</view> 注意这个模板里边不能使用js的方法 要循环渲染数组&#xff0c;如 在…

AIGC简介

目录 1.概述 2.诞生背景 3.作用 4.优缺点 4.1.优点 4.2.缺点 5.应用场景 5.1.十个应用场景 5.2.社交媒体内容 6.如何使用 7.未来展望 8.总结 1.概述 AIGC 是“人工智能生成内容”&#xff08;Artificial Intelligence Generated Content&#xff09;的缩写&#x…

vue element-ui 下拉框 以及 input 限制输入,小数点后保留两位 界面设计案例 和 例子:支持mp4和m3u8视频播放

vue input 限制输入&#xff0c;小数点后保留两位 以及 图片垂直居中显示 和 分享 git 小技巧-CSDN博客文章浏览阅读430次&#xff0c;点赞5次&#xff0c;收藏4次。error:Your local changes to the following files would be overwritten by merge:_error: your local change…

VS Code扩展开发介绍和快速示例

VS Code 介绍 VS Code&#xff08;Visual Studio Code&#xff09;是一款由微软开发的轻量级的免费开源的源代码编辑器&#xff0c;它支持多种操作系统&#xff0c;包括Windows、macOS和Linux。以下是对VS Code的详细介绍&#xff1a; 一、跨平台支持 VS Code是一个真正的跨…

阅读源码解析dynamic-datasource-spring-boot-starter中是如何动态切换数据源的

dynamic-datasource-spring-boot-starter是苞米豆提供的一个动态切换数据源的工具&#xff0c;可以帮助企业或者个人实现多数据源的切换&#xff0c;这里通过阅读源码的方式解析是如何动态的切换数据源的&#xff0c;采用的版本是3.5.1 源码解析 通过官方文档可以看到&#x…

HTML制作一个太阳、地球、月球之间的绕转动画

大家好&#xff0c;今天制作一个太阳、地球、月球之间的绕转动画&#xff01; 先看具体效果&#xff1a; 要制作一个太阳、地球、月球之间的绕转动画&#xff0c;我们需要结合HTML、CSS和JavaScript。由于CSS动画和JavaScript动画各有优缺点&#xff0c;这里我将给出一个使用…

LCD电子广告牌课程设计

概述 1.1课程设计简介 亮丽实用的广告牌可以给我们的生活添加光彩、可以给店铺招揽生意。传统的广告牌都是固定的汉字&#xff0c;并且时间长了会掉色&#xff0c;使汉字模糊难认&#xff0c;这就给我的生活带来很多的不便。尤其到了晚上传统广告牌就会失去其该有的作用。所以在…

空山新雨后-故事和场景搭建(一)

1、关于故事 1.故事的灵感 2.故事的表线 3.故事的里线 4.故事的参考图 5.故事的资源 6.关于音乐,听了100多首音乐选出来的 2、场景搭建 1.放入HDRI,放入PostProcessVolumn,将PostProcessVolumn设置为无限范围,将曝光补偿、Min EUV、Max EUV都设置为0,

SpringBoot Vue Bootstrap 旅游管理系统

SpringBoot Vue 旅游管理系统源码&#xff0c;附带环境安装&#xff0c;运行说明 源码地址 开发环境 jdk1.8,mysql8,nodejs16,navicat,idea 使用技术springboot mybatis vue bootstrap 部分功能截图预览

投稿要求的Cover Letter该怎么写?附全文模版+例句

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 投稿时提交的Cover letter&#xff0c;是作者写给期刊编辑的信&#xff0c;编辑在看完你的cover letter之后&#xff0c;再决定是否继续看全文。Cover Letter不会随正文一起发…

零基础直接上手java跨平台桌面程序,使用javafx(五)TableView显示excel表

我们在窗口的中间加上TableVie&#xff1a; 在hello-view.fxml的文本中&#xff0c;要增加一些代码。在TableView定义中加上fx:id"TableView1"&#xff0c;这样java代码才方便访问&#xff0c;在java代码中要加上FXML private TableView TableView1;表示定义TableVie…