openh264 编码器源码分析:WelsCodeOneSlice 函数

介绍

  1. 功能:openh264 编码每个 slice 的核心函数
  2. 原型
int32_t WelsCodeOneSlice (sWelsEncCtx* pEncCtx, SSlice* pCurSlice, const int32_t kiNalType)
  1. 参数
    • pEncCtx:指向编码上下文结构体sWelsEncCtx的指针,包含编码过程中所需的所有上下文信息。
    • pCurSlice:指向当前要编码的切片结构体SSlice的指针。
    • kiNalType:NAL单元类型,用于指定编码的NAL单元类型。

函数调用关系图

在这里插入图片描述

函数原理

WelsCodeOneSlice函数

  1. 过程
  • 获取上下文信息;
  • 计算动态切片标志kiDynamicSliceFlag;
  • 如果是I_SLICE,
    • 设置标志bIdrFlag为 1,sScaleShift偏移为 0;
  • 否则,
    • 计算时域分层 ID值kuiTemporalId,
    • sScaleShift偏移计算根据kuiTemporalId得到;
  • 调用WelsSliceHeaderExtInit来初始化切片头扩展;
  • 如果启用了基于宏块的速率控制(pWelsSvcRc->bGomRC),则调用GomRCInitForOneSlice进行初始化;
  • 根据是否使用扩展的切片头标志,调用g_pWelsWriteSliceHeader数组中的相应函数来写入切片头;
  • 计算并设置当前片最后一个宏块的量化参数(QP)uiLastMbQp;
  • 使用g_pWelsSliceCoding数组中的函数指针来调用相应的编码函数。这个数组根据切片是否为IDR切片和是否为动态切片来选择编码函数;
  • 调用WelsWriteSliceEndSyn来写入切片结束符号,这个操作取决于熵编码模式的标志;
  1. 源码
int32_t WelsCodeOneSlice (sWelsEncCtx* pEncCtx, SSlice* pCurSlice, const int32_t kiNalType) {
  SDqLayer* pCurLayer              = pEncCtx->pCurDqLayer;
  SWelsSvcRc* pWelsSvcRc           = &pEncCtx->pWelsSvcRc[pEncCtx->uiDependencyId];
  SNalUnitHeaderExt* pNalHeadExt   = &pCurLayer->sLayerInfo.sNalHeaderExt;
  SBitStringAux* pBs               = pCurSlice->pSliceBsa;
  const int32_t kiDynamicSliceFlag =
    (pEncCtx->pSvcParam->sSpatialLayers[pEncCtx->uiDependencyId].sSliceArgument.uiSliceMode
     == SM_SIZELIMITED_SLICE);
  if (I_SLICE == pEncCtx->eSliceType) {
    pNalHeadExt->bIdrFlag = 1;
    pCurSlice->sScaleShift = 0;
  } else {
    const uint32_t kuiTemporalId = pNalHeadExt->uiTemporalId;
    pCurSlice->sScaleShift = kuiTemporalId ? (kuiTemporalId - pEncCtx->pRefPic->uiTemporalId) : 0;
  }

  WelsSliceHeaderExtInit (pEncCtx, pCurLayer, pCurSlice);

  //RomRC init slice by slice
  if (pWelsSvcRc->bGomRC) {
    GomRCInitForOneSlice (pCurSlice, pWelsSvcRc->iBitsPerMb);
  }

  g_pWelsWriteSliceHeader[pCurSlice->bSliceHeaderExtFlag] (pEncCtx, pBs, pCurLayer, pCurSlice,
      pEncCtx->pFuncList->pParametersetStrategy);

  pCurSlice->uiLastMbQp = pCurLayer->sLayerInfo.pPpsP->iPicInitQp + pCurSlice->sSliceHeaderExt.sSliceHeader.iSliceQpDelta;

  int32_t iEncReturn = g_pWelsSliceCoding[pNalHeadExt->bIdrFlag][kiDynamicSliceFlag] (pEncCtx, pCurSlice);
  if (ENC_RETURN_SUCCESS != iEncReturn)
    return iEncReturn;

  WelsWriteSliceEndSyn (pCurSlice, pEncCtx->pSvcParam->iEntropyCodingModeFlag != 0);

  return ENC_RETURN_SUCCESS;
}

WelsSliceHeaderExtInit函数

  1. 过程

初始化切片头扩展

  • pCurSliceExt:指向切片头扩展SSliceHeaderExt的指针。
  • pCurSliceHeader:指向切片头SSliceHeader的指针。

获取参数

  • pParamInternal:获取当前依赖层的内部参数。

设置切片类型

  • 将编码上下文的切片类型赋值给当前切片头的切片类型。

设置存储参考基础图像标志

  • bStoreRefBasePicFlag设置为false,表示不存储参考基础图像。

设置帧编号和IDR图像ID

  • 将内部参数中的帧编号和IDR图像ID赋值给切片头。

设置图像顺序计数的最低位

  • 将编码图像的图像顺序计数赋值给切片头。

P切片的参考图像索引设置

  • 如果切片类型为P_SLICE,设置活动参考图像索引数量为1。
  • 如果参考计数在有效范围内,覆盖活动参考图像索引数量,并设置标志。

设置量化参数偏移

  • 计算并设置切片QP偏移量,即全局QP减去图像初始化QP。

设置去块滤波器参数

  • 将解码层的去块滤波器IDC、Alpha偏移和Beta偏移赋值给切片头。

设置跨层去块滤波器标志

  • 将解码层的跨层去块滤波器IDC赋值给切片头扩展。

扩展切片头标志处理

  • 如果启用了扩展切片头标志bSliceHeaderExtFlag,则调用WelsSliceHeaderScalExtInit函数进行扩展初始化。
  • 如果没有启用,将自适应和默认的预测、模式和残差标志设置为false
  1. 源码
void WelsSliceHeaderExtInit (sWelsEncCtx* pEncCtx, SDqLayer* pCurLayer, SSlice* pSlice) {
  SSliceHeaderExt* pCurSliceExt = &pSlice->sSliceHeaderExt;
  SSliceHeader* pCurSliceHeader  = &pCurSliceExt->sSliceHeader;
  SSpatialLayerInternal* pParamInternal = &pEncCtx->pSvcParam->sDependencyLayers[pEncCtx->uiDependencyId];
  pCurSliceHeader->eSliceType = pEncCtx->eSliceType;

  pCurSliceExt->bStoreRefBasePicFlag = false;

  pCurSliceHeader->iFrameNum      = pParamInternal->iFrameNum;
  pCurSliceHeader->uiIdrPicId     = pParamInternal->uiIdrPicId;
  pCurSliceHeader->iPicOrderCntLsb = pEncCtx->pEncPic->iFramePoc;      // 0

  if (P_SLICE == pEncCtx->eSliceType) {
    pCurSliceHeader->uiNumRefIdxL0Active = 1;
    if (pCurSliceHeader->uiRefCount > 0 &&
        pCurSliceHeader->uiRefCount < pCurLayer->sLayerInfo.pSpsP->iNumRefFrames) {
      pCurSliceHeader->bNumRefIdxActiveOverrideFlag = true;
      pCurSliceHeader->uiNumRefIdxL0Active = pCurSliceHeader->uiRefCount;
    }
    //to solve mismatch between debug&release
    else {
      pCurSliceHeader->bNumRefIdxActiveOverrideFlag = false;
    }
  }

  pCurSliceHeader->iSliceQpDelta = pEncCtx->iGlobalQp - pCurLayer->sLayerInfo.pPpsP->iPicInitQp;

  //for deblocking initial
  pCurSliceHeader->uiDisableDeblockingFilterIdc = pCurLayer->iLoopFilterDisableIdc;
  pCurSliceHeader->iSliceAlphaC0Offset =
    pCurLayer->iLoopFilterAlphaC0Offset; // need update iSliceAlphaC0Offset & iSliceBetaOffset for pSlice-header if loop_filter_idc != 1
  pCurSliceHeader->iSliceBetaOffset = pCurLayer->iLoopFilterBetaOffset;
  pCurSliceExt->uiDisableInterLayerDeblockingFilterIdc = pCurLayer->uiDisableInterLayerDeblockingFilterIdc;

  if (pSlice->bSliceHeaderExtFlag) {
    WelsSliceHeaderScalExtInit (pCurLayer, pSlice);
  } else {
    //both adaptive and default flags should equal to 0.
    pCurSliceExt->bAdaptiveBaseModeFlag =
      pCurSliceExt->bAdaptiveMotionPredFlag =
        pCurSliceExt->bAdaptiveResidualPredFlag = false;

    pCurSliceExt->bDefaultBaseModeFlag =
      pCurSliceExt->bDefaultMotionPredFlag =
        pCurSliceExt->bDefaultResidualPredFlag  = false;
  }
}

GomRCInitForOneSlice函数

  1. 作用
    主要作用是为当前切片设置基于宏块的速率控制参数,包括起始和结束宏块索引以及目标比特数。这些参数对于后续的编码过程中控制每个切片的比特分配非常重要,有助于实现更高效的编码和传输,同时保持视频质量。
  2. 源码
void GomRCInitForOneSlice (SSlice* pSlice, const int32_t kiBitsPerMb) {
  SRCSlicing* pSOverRc        = &pSlice->sSlicingOverRc;
  pSOverRc->iStartMbSlice     = pSlice->sSliceHeaderExt.sSliceHeader.iFirstMbInSlice;
  pSOverRc->iEndMbSlice       = pSOverRc->iStartMbSlice + pSlice->iCountMbNumInSlice - 1;
  pSOverRc->iTargetBitsSlice  = WELS_DIV_ROUND (static_cast<int64_t> (kiBitsPerMb) * pSlice->iCountMbNumInSlice,
                                INT_MULTIPLY);
}

g_pWelsWriteSliceHeader[2]数组

  1. 作用:g_pWelsWriteSliceHeader 数组是一个设计用来根据不同需求选择不同切片头写入函数的工具,它通过索引来决定调用基础还是扩展的切片头写入函数,从而适应不同的编码场景。
  2. 源码
static const PWelsSliceHeaderWriteFunc g_pWelsWriteSliceHeader[2] = {  // 0: for base; 1: for ext;
  WelsSliceHeaderWrite,
  WelsSliceHeaderExtWrite
};

g_pWelsSliceCoding[2][2]数组

  1. 过程
  • 是一个函数指针数组,用于根据切片的类型和动态切片标志来选择相应的编码函数;
  • 当第一索引为0时,表示P切片。如果第二索引为0,即非动态P切片,使用的编码函数是WelsCodePSlice。如果第二索引为1,即动态P切片,使用的编码函数是WelsCodePOverDynamicSlice
  • 当第一索引为1时,表示I切片。如果第二索引为0,即非动态I切片,使用的编码函数是WelsISliceMdEnc。如果第二索引为1,即动态I切片,使用的编码函数是WelsISliceMdEncDynamic
  1. 源码
// 1st index: 0: for P pSlice; 1: for I pSlice;
// 2nd index: 0: for non-dynamic pSlice; 1: for dynamic I pSlice;
static const PWelsCodingSliceFunc g_pWelsSliceCoding[2][2] = {
  { WelsCodePSlice, WelsCodePOverDynamicSlice }, // P SSlice
  { WelsISliceMdEnc, WelsISliceMdEncDynamic }    // I SSlice
};

WelsWriteSliceEndSyn函数

  1. 作用:在编码单个视频切片的末尾进行必要的编码状态处理和数据输出。它根据编码配置选择不同的编码模式,并确保编码后的数据正确地写入比特流。这对于视频编码过程中正确编码和解码切片至关重要。
  2. 源码
void WelsWriteSliceEndSyn (SSlice* pSlice, bool bEntropyCodingModeFlag) {
  SBitStringAux* pBs = pSlice->pSliceBsa;
  if (bEntropyCodingModeFlag) {
    WelsCabacEncodeFlush (&pSlice->sCabacCtx);
    pBs->pCurBuf = WelsCabacEncodeGetPtr (&pSlice->sCabacCtx);

  } else {
    BsRbspTrailingBits (pBs);
    BsFlush (pBs);
  }
}

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

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

相关文章

gpt、llama大模型模型结构细节探索

参考&#xff1a; https://github.com/naklecha/llama3-from-scratch&#xff08;一定要看看&#xff09; https://github.com/karpathy/build-nanogpt/blob/master/play.ipynb 视频&#xff1a; https://www.youtube.com/watch?vl8pRSuU81PU https://tiktokenizer.vercel…

Vitis HLS 学习笔记--移除内存分配malloc

目录 1. 简介 2. 示例解析 2.1 源码解释 2.2 malloc 分析 2.3 替代方案分析 3. 总结 1. 简介 Vitis HLS 也不支持动态创建或删除 C/C 对象&#xff08;用于综合&#xff09;。 本文探究如何在C/C代码中避免使用显式的malloc函数来分配内存。在硬件设计和FPGA开发中&…

b站视频下载到电脑本地mp4格式,b站视频下载到手机相册

有时候我们想把网站上的视频下载到电脑或手机&#xff0c;其实有多种方法可供选择。本文将详细介绍几种常见的下载方式&#xff0c;并探讨它们的优缺点&#xff0c;帮助读者根据自己的需求选择最适合的下载方法。 方法一&#xff1a; 1、使用 "小白兔视频格式在线转换网站…

LogicFlow 学习笔记——3. LogicFlow 基础 节点 Node

节点 Node LogicFlow 内置了一些基础节点&#xff0c;开发者在实际应用场景中&#xff0c;可以基于这些基础节点&#xff0c;定义符合自己业务逻辑的节点。 认识基础节点 LogicFlow是基于svg做的流程图编辑框架&#xff0c;所以我们的节点和连线都是svg基本形状&#xff0c;…

中电联系列四:rocket手把手教你理解中电联协议!

分享《慧哥的充电桩开源SAAS系统&#xff0c;支持汽车充电桩、二轮自行车充电桩。》 电动汽车充换电服务信息交换 第4部分&#xff1a;数据传输与安全 Interactive of charging and battery swap service information for electric vehicle Part 4:Data transmission and secu…

nosql数据库的特点

NoSQL简介 NoSQL是一种不同于关系数据库的数据库管理系统设计方式,是对非关系型数据库的统称,它所采用的数据模型并非传统关系数据库的关系模型,而是类似键/值、列族、文档等非关系模型。NoSQL数据库没有固定的表结构,通常也不存在连接操作,也没有严格遵守ACID约束。因此…

Redis高性能原理:Redis为什么这么快?

目录 前言&#xff1a; 一、Redis知识系统观 二、Redis为什么这么快&#xff1f; 三、Redis 唯快不破的原理总结 四、Redis6.x的多线程 前言&#xff1a; Redis 为了高性能&#xff0c;从各方各面都进行了优化。学习一门技术&#xff0c;通常只接触了零散的技术点&#xff…

用ChatGPT 4o画漂亮的燃尽图代码

把代码给ChatGPT&#xff0c;然后他就会帮我生成出来了。 而且图是动态的&#xff0c;可以调整颜色文字之类的内容 # Given data for Sprint 5 Progress data_sprint_5 {User Story: [BEAN-40, BEAN-42, BEAN-41, BEAN-22, BEAN-33, BEAN-44, BEAN-10, BEAN-26, BEAN-37, BEA…

【学术小白成长之路】03三方演化博弈(基于复制动态方程)均衡点与稳定性分析

从本专栏开始&#xff0c;笔者正式研究演化博弈分析&#xff0c;其中涉及到双方演化博弈分析&#xff0c;三方演化博弈分析&#xff0c;复杂网络博弈分析等等。 先阅读了大量相关的博弈分析的文献&#xff0c;总结了现有的研究常用的研究流程&#xff0c;针对每个流程进行拆解。…

Sqlite3入门和c/c++下使用

1. SQLite3基本介绍 1.1 数据库的数据类型 1.2 基本语法 1. 创建数据表格 create table 表名(字段名 数据类型&#xff0c; 字段名 数据类型)&#xff1b; create table student(id int, name varchar(256), address text, QQ char(32)); 2. 插入数据 insert into 表名 valu…

手机流畅运行470亿参数大模型,上交大发布PowerInfer-2推理框架,性能提升29倍

苹果一出手&#xff0c;在手机等移动设备上部署大模型迅速成为行业焦点。 目前&#xff0c;移动设备上运行的模型相对较小&#xff08;苹果的是3B&#xff0c;谷歌的是2B&#xff09;&#xff0c;并且消耗大量内存&#xff0c;这在很大程度上限制了其应用场景。 即使是苹果&…

运营商二要素核验-手机号机主姓名核验接口-运营商二要素核验接口

通过电信运营商验证手机号码与姓名是否一致。广泛用于实名注册、风控审核等场景&#xff0c;如电商、游戏、直播、金融等需要用户实名认证的场景。支持携号转网核验。 更新周期&#xff1a;联通T1 电信T3 移动T3~5 均为工作日 接口地址&#xff1a; https://www.wapi.cn/api_de…

RabbitMQ系列-rabbitmq无法重新加入集群,启动失败的问题

当前存在3个节点&#xff1a;rabbitmq5672、rabbitmq5673、rabbitmq5674 当rabbitmq5673节点掉线之后&#xff0c;重启失败 重启的时候5672节点报错如下&#xff1a; 解决方案 在集群中取消失败节点 rabbitmqctl forget_cluster_node rabbitrabbitmq5673删除失败节点5673的…

木头姐预测:2029年特斯拉股价将达2600美元,市值8.2万亿美元

ARK预计特斯拉将在未来两年内推出robotaxi服务&#xff0c;并估计到2029年特斯拉近90%的市值和盈利将归功于robotaxi业务。此外研究表明&#xff0c;FSD模式下的特斯拉比人类驾驶的特斯拉安全约5倍&#xff0c;比道路上的普通汽车安全约16倍。 北京时间12日晚&#xff0c;木头姐…

【课程总结】Day9(上):深度学习基本流程

前言 在上一篇课程《【课程总结】Day7&#xff1a;深度学习概述》中&#xff0c;我们了解到&#xff1a; 模型训练过程→本质上是固定w和b参数的过程&#xff1b;让模型更好→本质上就是让模型的损失值loss变小&#xff1b;让loss变小→本质上就是求loss函数的最小值&#xf…

Java中ArrayList(顺序表)的自我实现(如果想知道Java中怎么自我实现ArrayList,那么只看这一篇就足够了!)

前言&#xff1a;在 Java 编程中&#xff0c;ArrayList 是一种非常常用的数据结构&#xff0c;它提供了动态数组的实现方式&#xff0c;可以方便地存储和操作数据。相比于传统的数组&#xff0c;ArrayList 具有更多的灵活性和便利性&#xff0c;可以根据需要动态地调整大小&…

构建 deno/fresh 的 docker 镜像

众所周知, 最近 docker 镜像的使用又出现了新的困难. 但是不怕, 窝们可以使用曲线救国的方法: 自己制作容器镜像 ! 下面以 deno/fresh 举栗, 部署一个简单的应用. 目录 1 创建 deno/fresh 项目2 构建 docker 镜像3 部署和测试4 总结与展望 1 创建 deno/fresh 项目 执行命令…

嵌套查询(一)-谓词IN、量词ANY、量词ALL

一、在多个表之间进行数据查询&#xff0c;除了可以使用连接查询之外&#xff0c;也可以使用嵌套查询&#xff0c;那么什么是嵌套查询呢&#xff1f;如何使用嵌套查询呢&#xff1f; 1、将一个SELECT-FROM查询&#xff0c;嵌套在另一个SELECT查询语句中&#xff0c;那么这个SE…

响应式企业网站建站系统源码 模版丰富+一站式建站 全开源可二次开发 带源码包+搭建部署教程

系统概述 在数字化转型的浪潮中&#xff0c;企业官网作为品牌展示、产品推广及客户服务的重要窗口&#xff0c;其建设质量直接影响着企业的线上形象与市场竞争力。响应式企业网站建站系统源码的出现&#xff0c;为企业提供了一种高效、灵活且成本可控的建站解决方案。 代码示…

【安装笔记-20240612-Linux-内网穿透服务之cpolar极点云】

安装笔记-系列文章目录 安装笔记-20240612-Linux-内网穿透服务之 cpolar 极点云 文章目录 安装笔记-系列文章目录安装笔记-20240612-Linux-内网穿透服务之 cpolar 极点云 前言一、软件介绍名称&#xff1a;cpolar极点云主页官方介绍 二、安装步骤测试版本&#xff1a;openwrt-…