鸿蒙(API 12 Beta3版)【时域可分层视频编码】 音视频编码

基础概念

时域可分层视频编码介绍

可分层视频编码,又叫可分级视频编码、可伸缩视频编码,是视频编码的扩展标准,目前常用的包含SVC(H.264编码标准采用的可伸缩扩展)和SHVC(H.265编码标准采用的可扩展标准)。

其特点是能一次编码出时域分层、空域分层、质量域分层的码流结构,满足因网络、终端能力和用户需求不同带来的的差异化需求。

时域可分层视频编码, 是指能编码出时域分层码流的视频编码,下图是通过参考关系构建的4层时域分层码流结构。

从高到低逐层丢弃部分层级的码流(丢弃顺序L3->L2->L1),能实现不同程度的帧率伸缩,以满足传输和解码能力的变化需求。

如下图所示,是上述4层时域分层码流结构丢弃L3后组成的新的码流结构,能在解码正常的情况下实现帧率减半的效果。其他层的丢弃同理。

时域分层码流结构介绍

基础码流是由一个或多个独立图像组(Group Of Pictures,简称GOP)组合而成的。GOP是在编码中一组从I帧开始到I帧结束的连续的可独立解码的图像组。

时域分层码流可以在GOP内继续细分为独立的一个或多个时域图像组(Temporal Group Of Pictures, 简称TGOP),每一个TGOP由一个基本层和后续的一个或多个增强层组合而成,如上述4层时域分层码流结构中的帧0到帧7是一个TGOP。

  • 基本层(Base Layer, 简称BL): 是GOP中的最底层(L0)。在时域分层中,该层用最低帧率进行编码。
  • 增强层(Enhance Layer简称EL): 是BL之上的层级,由低到高可以分为多层(L1,L2,L3)。在时域分层中,最低层的EL依据BL获得的编码信息,进一步编码帧率更高的层级,更高层的EL会依据BL或低层EL,来编码比低层更高帧率的视频。

如何实现时域分层码流结构

时域分层码流结构的实现是依靠参考关系逐帧指定实现的,参考帧按在解码图像缓存区(Decoded Picture Buffer,简称DPB)驻留的时长分为短期参考帧和长期参考帧。

  • 短期参考帧(Short-Term Reference,简称STR): 是不能长期驻留在DPB中的参考帧,更新方式是先进先出,如果DPB满,旧的短期参考帧会被移出DPB。
  • 长期参考帧(Long-Term Reference,简称LTR): 是能长期驻留在DPB中的参考帧,通过标记替换的方式更新,不主动标记替换就不会更新。

虽然STR个数大于1时,也能实现一定的跨帧参考结构,但受限于存在时效过短,时域分层结构支持的跨度有限。LTR则不存在上述问题,也能覆盖短期参考帧跨帧场景。优选使用LTR实现时域分层码流结构。

适用场景

基于上述描述的时域分层编码特点,推荐以下场景使用:

  • 场景1:播放侧无缓存或低缓存的实时编码传输场景,例如视频会议、视频直播、协同办公等。
  • 场景2:有视频预览播放或倍速播放需求的视频编码录制场景。

若开发场景不涉及动态调整时域参考结构,且分层结构简单,则推荐使用[全局时域可分层特性],否则使能[长期参考帧特性]。

约束和限制

  • 不可以混用全局时域可分层特性和长期参考帧特性。

    由于底层实现归一,全局时域可分层特性和长期参考帧特性不能同时开启。

  • 叠加强制IDR配置时,请使用随帧通路配置。

    参考帧仅在GOP内有效,刷新I帧后,DPB随之清空,参考帧也会被清空,因此参考关系的指定受I帧刷新位置影响很大。

    使能时域分层能力后,若需要通过OH_MD_KEY_REQUEST_I_FRAME临时请求I帧,应使用生效时机确定的随帧通路配置方式准确告知框架I帧刷新位置以避免参考关系错乱,参考随帧通路配置相关指导,避免使用生效时机不确定的OH_VideoEncoder_SetParameter方式。

  • 支持OH_AVBuffer回调通路,不支持OH_AVMemory回调通路。

    新特性依赖随帧特性,应避免使用OH_AVMemory回调OH_AVCodecAsyncCallback,应使用OH_AVBuffer回调OH_AVCodecCallback。

  • 支持时域P分层,不支持时域B分层。

    时域可分层编码按分层帧类型分为基于P帧的时域分层和基于B帧的时域编码,当前支持分层P编码,不支持分层B编码。

全局时域可分层特性(Feature_Temporal_Scalability)

接口介绍

全局时域可层特性,适用于编码稳定和简单的时域分层结构,初始配置,全局生效,不支持动态修改。开发配置参数如下:

配置参数 语义
OH_MD_KEY_VIDEO_ENCODER_ENABLE_TEMPORAL_SCALABILITY 全局时域分层编码使能参数
OH_MD_KEY_VIDEO_ENCODER_TEMPORAL_GOP_SIZE 全局时域分层编码TGOP大小参数
OH_MD_KEY_VIDEO_ENCODER_TEMPORAL_GOP_REFERENCE_MODE 全局时域分层编码TGOP参考模式
  • 全局时域分层编码使能参数: 在配置阶段配置,仅特性支持才会真正使能成功。
  • 全局时域分层编码TGOP大小参数: 可选配置,影响时域关键帧之间的间隔,用户需要基于自身业务场景下抽帧需求自定义关键帧密度,可在[2, GopSize)范围内配置,若不配置则使用默认值
  • 全局时域分层编码TGOP参考模式参数: 可选配置,影响非关键帧参考模式。包括相邻参考ADJACENT_REFERENCE和跨帧参考JUMP_REFERENCE。相邻参考相对跨帧参考拥有更好的压缩性能,跨帧参考相对相邻参考拥有更好的丢帧自由度,如不配置则使用默认值。

使用举例1:TGOP=4,相邻参考模式

使用举例2:TGOP=4,跨帧参考模式

开发指导

基础编码流程请参考[视频编码开发指导],下面仅针与基础视频编码过程中存在的区别做具体说明。

  1. 在初始阶段创建编码实例时,校验当前视频编码器是否支持全局时域可分层特性。

    // 1.1 获取对应视频编码器能力句柄,此处以H.264为例
    OH_AVCapability *cap = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true);
    // 1.2 通过特性能力查询接口校验是否支持全局时域可分层特性
    bool isSupported = OH_AVCapability_isFeatureSupported(cap, VIDEO_ENCODER_TEMPORAL_SCALABILITY);

若支持,则可以使能全局时域可分层特性。

  1. 在配置阶段,配置全局时域分层编码特性参数。

    constexpr int32_t TGOP_SIZE = 3;
    // 2.1 创建配置用临时AVFormat
    OH_AVFormat *format = OH_AVFormat_Create();
    // 2.2 填充使能参数键值对
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_ENABLE_TEMPORAL_SCALABILITY, 1);
    // 2.3 (可选)填充TGOP大小和TGOP内参考模式键值对
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_TEMPORAL_GOP_SIZE, TGOP_SIZE);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_TEMPORAL_GOP_REFERENCE_MODE, ADJACENT_REFERENCE);
    // 2.4 参数配置
    int32_t ret = OH_VideoEncoder_Configure(videoEnc, format);
    if (ret != AV_ERR_OK) {
    // 异常处理
    }
    // 2.5 配置完成后销毁临时AVFormat
    OH_AVFormat_Destroy(format);

  2. (可选)在运行阶段输出轮转中,获取码流对应时域层级信息。

    开发者可基于已配置的TGOP参数,按编码出帧数目周期性获取。

    通过配置周期获取示例代码如下:

    uint32_t outPoc = 0;
    // 通过输出回调中有效帧数,获取TGOP内相对位置,对照配置确认层级
    static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
    {
    // 注:若涉及复杂处理流程,建议相关
    struct OH_AVCodecBufferAttr attr;
    (void)buffer->GetBufferAttr(attr);
    // 刷新I帧后poc归零
    if (attr.flags & AVCODEC_BUFFER_FLAG_KEY_FRAME) {
    outPoc = 0;
    }
    // 没有帧码流只有XPS的输出需要跳过
    if (attr.flags != AVCODEC_BUFFER_FLAG_CODEC_DATA) {
    int32_t tGopInner = outPoc % TGOP_SIZE;
    if (tGopInner == 0) {
    // 时域关键帧,后续传输、解码流程不可丢弃
    } else {
    // 时域非关键帧,后续传输、解码流程可以丢弃
    }
    outPoc++;
    }
    }

  3. (可选)在运行阶段输出轮转中,使用步骤3获取的时域层级信息,自适应传输或自适应解码。

    基于获取的时域可分层码流和对应的层级信息,开发者可选择需要的层级进行传输,或携带至对端自适应选帧解码。

长期参考帧特性(Feature_Long-Term_Reference)

接口介绍

长期参考帧特性提供帧级灵活的参考关系配置。适用于灵活和复杂的时域分层结构。

配置参数 语义
OH_MD_KEY_VIDEO_ENCODER_LTR_FRAME_COUNT 长期参考帧个数参数
OH_MD_KEY_VIDEO_ENCODER_PER_FRAME_MARK_LTR 当前帧标记为LTR帧
OH_MD_KEY_VIDEO_ENCODER_PER_FRAME_USE_LTR 当前帧参考的LTR帧号
  • 长期参考帧个数参数: 在配置阶段配置,应小于等于查询到的最大支持数目,查询方式详见开发指导。
  • 当前帧标记为LTR帧: BL层标记为LTR,被跳跃参考的EL层也标记为LTR。
  • 当前帧参考的LTR帧号: 如当前帧需要跳跃参考前面已被标记为LTR的帧号。

使用举例,实现[时域可分层视频编码介绍]中的4层时域分层结构的配置如下:

  1. 在配置阶段,将OH_MD_KEY_VIDEO_ENCODER_LTR_FRAME_COUNT 配置为5。

  2. 在运行阶段输入轮转中,按如下表所示随帧配置LTR相关参数,下表中\表示不做配置。

    配置\POC 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
    MARK_LTR 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1
    USE_LTR \ \ 0 \ 0 \ 4 \ 0 \ 8 \ 8 \ 12 0 8

开发指导

基础编码流程请参考[视频编码开发指导],下面仅针与基础视频编码过程中存在的区别做具体说明。

  1. 在初始阶段创建编码实例时,校验当前视频编码器是否支持LTR特性。

    constexpr int32_t NEEDED_LTR_COUNT = 5;
    bool isSupported = false;
    int32_t supportedLTRCount = 0;
    // 1.1 获取对应编码器能力句柄,此处以H.264为例
    OH_AVCapability *cap = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true);
    // 1.2 通过特性能力查询接口校验是否支持LTR特性
    isSupported = OH_AVCapability_isFeatureSupported(cap, VIDEO_ENCODER_LONG_TERM_REFERENCE);
    // 1.3 确定支持的LTR数目
    if (isSupported) {
    OH_AVFormat *properties = OH_AVCapability_GetFeatureProperties(cap, VIDEO_ENCODER_LONG_TERM_REFERENCE);
    OH_AVFormat_GetIntValue(properties, OH_FEATURE_PROPERTY_KEY_VIDEO_ENCODER_MAX_LTR_FRAME_COUNT, &supportedLTRCount);
    OH_AVFormat_Destroy(properties);
    // 1.4 判断LTR是否满足结构需求
    isSupported = supportedLTRCount >= NEEDED_LTR_COUNT;
    }

若支持,且支持的LTR数目满足自身码流结构需求,则可以使能LTR特性。

  1. 在配置之前注册回调时,注册随帧通路回调。

Buffer输入模式示例:

复制代码
// 2.1 编码输入回调OH_AVCodecOnNeedInputBuffer实现
static void OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
{
    // 输入帧buffer对应的index,送入InIndexQueue队列
    // 输入帧的数据buffer送入InBufferQueue队列
    // 数据处理,请参考:
    // - 写入编码码流
    // - 通知编码器码流结束
    // - 随帧参数写入
    OH_AVFormat *format = OH_AVBuffer_GetParameter(buffer);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PER_FRAME_MARK_LTR, 1);
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_PER_FRAME_USE_LTR, 4);
    OH_AVBuffer_SetParameter(buffer, format);
    // 通知编码器buffer输入完成
    OH_VideoEncoder_PushInputBuffer(codec, index);
}

// 2.2 编码输出回调OH_AVCodecOnNewOutputBuffer实现
static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
{
    // 完成帧buffer对应的index,送入outIndexQueue队列
    // 完成帧的数据buffer送入outBufferQueue队列
    // 数据处理,请参考:
    // - 释放编码帧
    // - 记录POC和LTR生效情况
}

// 2.3 注册数据回调
OH_AVCodecCallback cb;
cb.onNeedInputBuffer = OnNeedInputBuffer;
cb.onNewOutputBuffer = OnNewOutputBuffer;
OH_VideoEncoder_RegisterCallback(codec, cb, nullptr);

Surface输入模式示例:

复制代码
// 2.1 编码输入参数回调OH_VideoEncoder_OnNeedInputParameter实现
static void OnNeedInputParameter(OH_AVCodec *codec, uint32_t index, OH_AVFormat *parameter, void *userData)
{
    // 输入帧buffer对应的index,送入InIndexQueue队列
    // 输入帧的数据avformat送入InFormatQueue队列
    // 数据处理,请参考:
    // - 写入编码码流
    // - 通知编码器码流结束
    // - 随帧参数写入
    OH_AVFormat_SetIntValue(parameter, OH_MD_KEY_VIDEO_ENCODER_PER_FRAME_MARK_LTR, 1);
    OH_AVFormat_SetIntValue(parameter, OH_MD_KEY_VIDEO_ENCODER_PER_FRAME_USE_LTR, 4);
    // 通知编码器随帧参数配置输入完成
    OH_VideoEncoder_PushInputParameter(codec, index);
}

// 2.2 编码输出回调OH_AVCodecOnNewOutputBuffer实现
static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData)
{
    // 完成帧buffer对应的index,送入outIndexQueue队列
    // 完成帧的数据buffer送入outBufferQueue队列
    // 数据处理,请参考:
    // - 释放编码帧
    // - 记录POC和LTR生效情况
}

// 2.3 注册数据回调
OH_AVCodecCallback cb;
cb.onNewOutputBuffer = OnNewOutputBuffer;
OH_VideoEncoder_RegisterCallback(codec, cb, nullptr);
// 2.4 注册随帧参数回调
OH_VideoEncoder_OnNeedInputParameter inParaCb = OnNeedInputParameter;
OH_VideoEncoder_RegisterParameterCallback(codec, inParaCb, nullptr);
  1. 在配置阶段,配置同时存在LTR最大数目。

    constexpr int32_t TGOP_SIZE = 3;
    // 3.1 创建配置用临时AVFormat
    OH_AVFormat *format = OH_AVFormat_Create();
    // 3.2 填充使能LTR个数键值对
    OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_LTR_FRAME_COUNT, NEEDED_LTR_COUNT);
    // 3.3 参数配置
    int32_t ret = OH_VideoEncoder_Configure(videoEnc, format);
    if (ret != AV_ERR_OK) {
    // 异常处理
    }
    // 3.4 配置完成后销毁临时AVFormat
    OH_AVFormat_Destroy(format);

  2. (可选)在运行阶段输出轮转中,获取码流对应时域层级信息。

    同全局时域分层特性。

    因在输入轮转有配置LTR参数,也可在输入轮转中中记录,输出轮转中找到对应的输入参数。

  3. (可选)在运行阶段输出轮转中,使用步骤4获取的时域层级信息,自适应传输或自适应解码。

    同全局时域分层特性。

最后呢

很多开发朋友不知道需要学习那些鸿蒙技术?鸿蒙开发岗位需要掌握那些核心技术点?为此鸿蒙的开发学习必须要系统性的进行。

而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造的《鸿蒙NEXT星河版OpenHarmony开发文档 》里面内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点

如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。下面是鸿蒙开发的学习路线图。

针对鸿蒙成长路线打造的鸿蒙学习文档。话不多说,我们直接看详细鸿蒙(OpenHarmony )手册(共计1236页)与鸿蒙(OpenHarmony )开发入门视频,帮助大家在技术的道路上更进一步。

  • 《鸿蒙 (OpenHarmony)开发学习视频》
  • 《鸿蒙生态应用开发V2.0白皮书》
  • 《鸿蒙 (OpenHarmony)开发基础到实战手册》
  • OpenHarmony北向、南向开发环境搭建
  • 《鸿蒙开发基础》
  • 《鸿蒙开发进阶》
  • 《鸿蒙开发实战》

总结

鸿蒙---作为国家主力推送的国产操作系统。部分的高校已经取消了安卓课程,从而开设鸿蒙课程;企业纷纷跟进启动了鸿蒙研发。

并且鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,未来将会支持 50 万款的应用。那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行! 自↓↓↓拿

相关推荐
yunteng52128 分钟前
音视频(二)ffmpeg编译及推流
ffmpeg·音视频·h264·媒体推流
用户5457483417733 分钟前
Harmonyos5应用开发实战——地图组件集成与定位功能实现(part1)
harmonyos
用户5457483417733 分钟前
Harmonyos5应用开发实战——订单页面开发(part2)
harmonyos
用户5457483417736 分钟前
Harmonyos5应用开发实战——地图组件集成与定位功能实现(part2)
harmonyos
用户545748341771 小时前
HarmonyOS Next应用开发实战——登录页面实现(part1)
harmonyos
用户545748341771 小时前
HarmonyOS Next应用开发实战——底部弹框组件的实现(part1)
harmonyos
用户545748341771 小时前
HarmonyOS Next应用开发实战——底部弹框组件的实现(part2)
harmonyos
用户545748341771 小时前
HarmonyOS Next应用开发实战——多功能页面组件构建(part1)
harmonyos
用户545748341771 小时前
HarmonyOS Next应用开发实战——多功能页面组件构建(part2)
harmonyos
星释1 小时前
鸿蒙Flutter实战:18-组合而非替换,现有插件快速鸿蒙化
flutter·华为·harmonyos