音视频面试题集锦第 23 期 | 编码相关

2024-06-06 19:34:56 浏览数 (2)

下面是第 23 期面试题精选:

  • 1、VideoToolbox 遵循哪种视频码率控制策略?如何设置?
  • 2、Annex B 如何转换为 AVCC?
  • 3、iOS 中如何判断一个视频帧是不是关键帧?
  • 4、纹理有哪些环绕方式(wrapping)?

1、VideoToolbox 遵循哪种视频码率控制策略?如何设置?

码率控制策略主要分为以下几种:

  • CBR(Constant Bit Rate)恒定码率:一定时间范围内比特率基本保持的恒定。
    • 有运动发生时,由于码率恒定,只能通过增大 QP 来减少码字大小,图像质量变差;当场景静止时,图像质量又变好,因此图像质量不稳定。
    • 优点是码率处于一个稳定值,缺点是质量不稳定,在复杂运动场景下的视频会很糊。
    • 适合在流式播放中应用。
  • VBR(Variable Bit Rate)可变码率:码率分配根据图像内容的复杂度进行。
    • 简单场景分配较低码率,复杂场景分配较高码率。
    • 优点是视频质量稳定,缺点是码率不可控,编码速度较慢。
    • 适合的应用场景是本地存储(如视频录制),不适合网络传输(如直播推流)。
  • ABR(Average Bitrate)平均目标码率:控制一段时间内的编码平均码率。
    • 是在 CBR 和 VBR 两者之间的一种权衡,即设定一段时间的平均码率,在此时间内,对简单的、静态的图像分配低于平均码率的码率,对于复杂的,大量运动的图像分配高于平均码率的码流。
    • 速度快,同时兼顾了视频质量和带宽,对于转码速度有要求的情况下也可以选择该模式。
    • 适合网络传输。

目前 VideoToolbox 没有属性可以直接设置码率控制策略给调用方,只有开放了 kVTCompressionPropertyKey_DataRateLimits(为最高码率上限)和 kVTCompressionPropertyKey_AverageBitRate(编码平均码率)。可以通过 API 属性名称和注释结合编码后的视频码率猜测 VideoToolBox 目前使用的应该是 ABR 视频编码策略。

2、 Annex B 如何转换为 AVCC?

Annex B 格式通常以 0x0000010x00000001 用于标识 NAL 单元的开始。SPS 和 PPS 按流的方式写在头部。

AVCC 格式使用 NALU 长度(固定字节,通常为 4 字节)分隔 NAL;在头部包含 extradata 或 sequence header 的结构体。

以下是 AnnexB 转换为 AVCC 的思路:

  • 1、解析 Annex B 格式:读取字节流,识别每个 NAL 单元的起始码,确定每个 NAL 单元的开始和结束位置。
  • 2、去除起始码:去除每个 NAL 单元的起始码。
  • 3、计算长度:对于每个 NAL 单元,计算其长度(以字节为单位)。
  • 4、写入长度前缀:将每个 NAL 单元的长度作为字节序列写入到 AVCC 格式的流中,可能 1 个字节,2 字节或者 4 字节(较为常见),NAL 单元长度会存储在 AVCC 的 extradata 中。
  • 5、根据 Annex B 的 SPS 和 PPS 生成对应的 extradata。
  • 6、写入 NAL 单元数据:在长度字段后面写入去除起始码后的 NAL 单元数据。

3、iOS 中如何判断一个视频帧是不是关键帧?

在 VideoToolbox 中,可以通过检查给定的 CMSampleBuffer 是否是视频帧,并且是否是关键帧。通过检查 kCMSampleAttachmentKey_NotSync 键的值,如果它为 false ,则说明这是一个关键帧。以下是示例代码

代码语言:javascript复制
#import <VideoToolbox/VideoToolbox.h>

BOOL isKeyFrame(CMSampleBufferRef sampleBuffer) {
    CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer);
    if (!formatDescription) {
        return NO;
    }
    
    CMMediaType mediaType = CMFormatDescriptionGetMediaType(formatDescription);
    if (mediaType != kCMMediaType_Video) {
        return NO;
    }
    
    CFArrayRef sampleAttachmentsArray = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, true);
    CFDictionaryRef sampleAttachments = (CFDictionaryRef)CFArrayGetValueAtIndex(sampleAttachmentsArray, 0);
    BOOL isKeyFrame = !CFDictionaryContainsKey(sampleAttachments, kCMSampleAttachmentKey_NotSync);
    return isKeyFrame;
}

4、纹理有哪些环绕方式(wrapping)?

  • 重复(GL_Repeat):纹理在每个纹理坐标轴上重复出现,当纹理坐标超出 [0,1] 范围时,纹理会在该轴上重复出现。这种方式适用于创建无缝平铺效果。这是对纹理的默认行为。
  • 镜像重复(GL_MIRRORED_REPEAT):与重复(GL_Repeat)方式相似,但当纹理坐标超出 [0,1] 范围时,会将其镜像翻转后再重复出现。这可以有效减少纹理重复造成的视觉疲劳。
  • 夹取到边缘(GL_CLAMP_TO_EDGE):与夹取方式类似,但在超出范围时,会使用边缘纹素的颜色,产生一种边缘被拉伸的效果。
  • 夹取到边框(GL_CLAMP_TO_BORDER):超出范围时,使用指定的边框颜色。这种方式通常用于在超出纹理范围时填充边框颜色,避免黑边。

纹理环绕方式

0 人点赞