如果对音视频、AIGC、区块链技术感兴趣,可以关注一下保持联系:
我们在知识星球上创建的音视频技术社群关键帧的音视频开发圈已经运营了一段时间了,在这里大家可以一起交流和分享音视频技术知识和实战方案。我们会不定期整理一些音视频相关的面试题,汇集一份音视频面试题集锦(可进入免费订阅)。也会循序渐进地归纳总结音视频技术知识,绘制一幅音视频知识图谱(可进入免费订阅)。
下面是第 20 期面试题精选:
1、为什么在 YUV 转 RGB 转换中 UV 分量要减去 0.5? 2、在编辑 SDK 中的播放器对 OpenGL 的使用有哪些进阶的用法,和播放 SDK 中的视频播放存在哪些区别呢? 3、如何获取视频流中的 QP 值? 4、视频编码对 QP 值的控制有哪些?
1、为什么在 YUV 转 RGB 转换中 UV 分量要减去 0.5?
在 YUV 到 RGB 的转换公式中,U 和 V 分量减去 0.5 的原因与 YUV 颜色空间的编码方式有关。YUV 格式通常用于视频压缩,其中 Y 代表亮度(luminance),而 U 和 V 代表色度(chrominance),也就是颜色信息。在某些 YUV 格式中,U 和 V 的取值范围是标准化的,例如在 8 位颜色深度中,U 和 V 的取值范围是从 -128 到 127。这种表示方法将色度的中心点设在了 0,使得色度信号可以表示正负偏差。
在进行 YUV 到 RGB 的转换时,为了将 U 和 V 的取值范围从对称的 -128 到 127 归一化为非对称的 0 到 255,并且将中心点从 128 移动到 0,需要对 U 和 V 进行偏移量的减法操作。具体来说,通过减去 0.5(或 128 对应的小数形式),可以将 U 和 V 的取值范围转换为 0 到 255,从而与 RGB 的取值范围相匹配。
例如,如果使用如下的转换公式:
[ R = Y + 1.4075 * (V - 128) ]
[ G = Y - 0.34414 * (U - 128) - 0.71414 * (V - 128) ]
[ B = Y + 1.772 * (U - 128) ]
在这个公式中,U 和 V 减去 128 实际上就是将色度信号的中心从 128 移动到 0,然后再进行缩放操作以匹配 RGB 的取值范围。如果不进行这个减法操作,色度信号将不会正确地转换为 RGB 颜色空间,导致颜色失真。
总结来说,U 和 V 分量后面减去 0.5 是为了将色度信号的表示方式从 YUV 颜色空间转换为 RGB 颜色空间,确保颜色信息的准确传递。
2、在编辑 SDK 中的播放器和播放 SDK 中的视频播放存在哪些区别呢?编辑场景的播放器对 OpenGL 的使用有哪些进阶的用法?
剪辑方向的视频播放与播放器的视频播放相比最大的区别就是:需要处理更复杂渲染场景。
编辑场景的播放器可以注意下面这些点:
处理复杂的输入和渲染。特效的输入可能是多个视频多个参数,因此需要对顶点着色器、片段着色器、渲染管道有更深的理解。例如,转场特效有两个视频输入、旋转特效的顶点函数设置需要考虑透视矩阵等。 处理多线程渲染。因为多个特效的加入需要对渲染的流程做优化可能会引入多线程渲染。多线程渲染需要考虑的问题如下: 每个线程通常需要自己的 OpenGL 上下文(除非是在共享列表中共享)。创建和销毁 OpenGL 上下文需要谨慎处理,以避免资源泄露和上下文不一致的问题。 资源共享。在多线程渲染中,需要处理好 OpenGL 资源(如纹理、缓冲区对象等)的共享问题。要注意 FBO 和 VAO 是不能共享。 同步问题。确保线程间的渲染命令顺序正确。OpenGL 的同步机制(如 glFinish)太慢可考虑 glFence 等。 避免状态冲突。在一个线程中修改 OpenGL 状态,在另一个线程中可能会导致不可预测的结果。需要同步的状态应考虑同步机制,不需要同步的状态应该在切换之前将状态恢复。 多线程环境中,资源的创建和销毁需要特别注意。确保在所有线程中正确地清理和释放资源,避免内存泄漏和其他资源管理问题。 避免频繁的上下文切换。频繁切换 OpenGL 上下文是一个非常耗时的操作,尤其是当涉及到多个线程时。应该尽可能地减少上下文切换,或者设计合理的上下文使用策略,以提高性能。 渲染流程结构可以做优化设计。 有可异步处理的效果可以异步线程处理。 如果有缩小特效可以将缩小特效放在前面,这样后面的特效处理所需的数据大小将会降低。 何时将解码数据转换为纹理,避免 GPU 和 CPU 之间数据拷贝。 调试和报错。glGet 拿到关键状态,在关键节点 glGetError 以及处理这些报错。
3、如何获取视频流中的 QP 值?
在 H.264 中,量化参数(QP)的获取涉及到几个层级:
图像参数集(PPS):包含初始量化参数 pic_init_qp_minus26
,其取值范围是 -26 到 +25。片头(Slice Header):每个片(Slice)都有自己的片头,其中包含 slice_qp_delta
,表示当前片所有宏块的量化参数初始值 QPy Slice,计算公式为:QPy Slice = 26 + pic_init_qp_minus26 + slice_qp_delta
,其取值范围是 0 到 51。宏块(Macroblock, MB):宏块是编码的基本单元,宏块量化参数偏移值 mb_qp_delta
表示前后两个宏块之间的偏移,取值范围是 -26 到 +25。第一个宏块的 QP 值等于片头的 QP 值,后续宏块的 QP 值计算方式为QP = (QPprev + mb_qp_delta + 52) % 52
。
要从 H.264 码流中提取 QP 值,需要执行以下步骤:
1、解析 NALU:首先需要定位并提取 NALU,因为 QP 值信息分布在 PPS 和 Slice Header 中。 2、解析 PPS:找到 PPS NALU 并解析出 pic_init_qp_minus26
。3、解析 Slice Header:对于每个 Slice,解析 Slice Header 以获取 slice_qp_delta
。4、计算 QP 值:根据上述解析出的参数和宏块信息,计算每个宏块的 QP 值。
使用工具:可以使用如 ffmpeg 等工具来辅助解析码流和提取 QP 值。例如,ffmpeg 提供了 -showqp
选项来显示量化参数。
编程实现:也可以通过编程方式,如使用 Python 结合相关库来解析 H.264 码流并提取 QP 值。
在实际应用中,可能需要结合具体的编码场景和需求来选择合适的工具和方法,以实现对 H.264 码流中 QP 值的提取。
4、视频编码对 QP 值的控制有哪些?
在视频编码中,QP(Quantization Parameter)值是一个重要的概念,它对编码后视频的质量和码率有着直接的影响。视频编码中的量化步骤是将像素值映射到一个较小的数值范围内,这一步骤会损失一些图像细节,但可以显著减少编码后视频的数据量。
QP 值决定了量化过程中的量化步长,从而影响量化的精度。较小的 QP 值意味着量化步长较小,量化过程更精细,编码后的视频质量更高,但同时也会导致码率增加; 较大的 QP 值意味着量化步长较大,量化过程更粗糙,编码后的视频质量较低,但码率会减少。
对于部分软编库来说是可以设置平均/最大/最小 QP 值的。下面我们重点说下客户端如何设置 QP。
在 iOS 中使用 VideoToolbox 编码视频时通过属性值设置最大 QP 和最小 QP 值。属性 key 为 kVTCompressionPropertyKey_MinAllowedFrameQP
、kVTCompressionPropertyKey_MaxAllowedFrameQP
。 这样编码器就会保证是编码出来的文件 QP 值在这个范围。其次如果你设置了码率,他也会在这个范围内尽量使用你设置的码率。但是如果你设置的码率过高或者过低,QP 值无法满足时,编码器则根据优先以 QP 值自动调整码率。Android 部分机型的编码器可以开启质量编码模式,即 KEY_BITRATE_MODE
为BITRATE_MODE_CQ
。质量可以通过设置参数KEY_VIDEO_QP_MAX
、KEY_VIDEO_QP_MIN
、KEY_VIDEO_QP_P_MAX
、KEY_VIDEO_QP_P_MIN
、KEY_VIDEO_QP_B_MAX
、KEY_VIDEO_QP_B_MIN
等一系列参数来控制帧的 QP 值。
更多的音视频知识、面试题、技术方案干货可以进群来看: