音视频面试题集锦第 8 期

我们在知识星球上创建的音视频技术社群关键帧的音视频开发圈 已经运营了一段时间了,在这里大家可以一起交流和分享音视频技术知识和实战方案。我们会不定期整理一些音视频相关的面试题,汇集一份音视频面试题集锦(可进入免费订阅)。也会循序渐进地归纳总结音视频技术知识,绘制一幅音视频知识图谱(可进入免费订阅)

下面是第 8 期音视频面试题集锦的几条精选:

  • 1、如何代码实现 PSNR 来评估编码质量?
  • 2、如何测试码率质量甜点?
  • 3、iOS 如何实现夜晚自动提示打开手电筒?
  • 4、Android Camera 如何优化视频录制的卡顿?
  • 5、Android Surface 解码如何支持带角度视频?

1、如何代码实现 PSNR 来评估编码质量?

1)PSNR 定义

PSNRPeak Signal-to-Noise Ratio,峰值信噪比)是一种衡量图像质量的指标,常用于评估压缩算法的效果。它通过比较原始图像与压缩/恢复后的图像之间的差异,来量化图像质量的损失程度。

2)PSNR 计算公式

PSNR = 10 * log10((MAX^2) / MSE)

其中,MAX 是图像像素值的最大可能取值(通常为255),MSE 是均方误差(Mean Squared Error),表示压缩/恢复图像与原始图像之间的平均像素差的平方。

3)PSNR 计算实现

arduino 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

double psnr(unsigned char* original_img, unsigned char* compressed_img, int width, int height) {
    double mse = 0.0;
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            int diff = original_img[i * width + j] - compressed_img[i * width + j];
            mse += diff * diff;
        }
    }
    mse /= (double)(width * height);
    double max_value = 255.0;
    double psnr = 10.0 * log10((max_value * max_value) / mse);
    return psnr;
}

int main() {
    // 读取原始图像和压缩/恢复后的图像
    FILE* original_file = fopen("original_image.raw", "rb");
    FILE* compressed_file = fopen("compressed_image.raw", "rb");
    if (original_file == NULL || compressed_file == NULL) {
        printf("无法打开图像文件\n");
        return 1;
    }

    int width = 512;  // 图像宽度
    int height = 512; // 图像高度

    unsigned char* original_img = (unsigned char*)malloc(width * height * sizeof(unsigned char));
    unsigned char* compressed_img = (unsigned char*)malloc(width * height * sizeof(unsigned char));
    fread(original_img, sizeof(unsigned char), width * height, original_file);
    fread(compressed_img, sizeof(unsigned char), width * height, compressed_file);

    fclose(original_file);
    fclose(compressed_file);

    // 计算PSNR
    double psnr_value = psnr(original_img, compressed_img, width, height);
    printf("PSNR: %.2f\n", psnr_value);

    // 释放内存
    free(original_img);
    free(compressed_img);

    return 0;
}

请注意,上述代码中的 original_image.rawcompressed_image.raw 是示例图像的文件名,你需要根据实际情况修改为你自己的图像文件名。此外,代码中假设图像是灰度图像,如果你的图像是彩色图像,需要进行相应的修改。

通过计算 PSNR,我们可以得到一个数值来评估压缩/恢复图像与原始图像之间的质量损失程度。PSNR 的数值越高,表示图像质量的损失越小。

2、如何测试码率质量甜点?

在视频领域,质量甜点指的是在既定的码率和屏幕大小下通过设定合理的分辨率和帧率来得到最佳视频主观质量体验。因为编码复杂度和编解码质量亦不是线性关系,两者之间也存在一个质量甜点。在音频领域也有类似的情况,针对具体的情况,我们可以测试手机的编码质量来选择指定分辨率、帧率时对应的码率甜点。

在这种测试中我们一般需要分场景进行,比如:

  • 外景拍摄,低运动
  • 外景拍摄,中等运动
  • 外景拍摄,高运动
  • 人像拍摄,低运动
  • 人像拍摄,中等运动
  • 人像拍摄,高运动

测试指标我们可以采用 PSNRSSIMVMAF 进行综合考量。

比如,我们可以测试 iOS 硬编,使用 540P,15 帧推流时,设置不同的码率(800kbps-1300kbps)分别测试各场景下的各指标值,找出 R-D(码率-失真)曲线拐点出现的区间,这就是我们要找的码率甜点。

3、iOS 如何实现夜晚自动提示打开手电筒?

当夜晚使用共享单车扫码时,应该都见过提示"打开手电筒",在 iOS 中我们如何实现呢?主要基于图像环境光参数,参考如下代码。

iOS 视频采集设置 AVCaptureVideoDataOutput AVCaptureVideoDataOutputSampleBufferDelegate,通过获取 CMSampleBufferRef 每一帧视频数据环境参数进行判断即可。

objectivec 复制代码
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
 if(!sampleBuffer){
        return;
    }
    
    CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,sampleBuffer,  kCMAttachmentMode_ShouldPropagate);

 NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge  NSDictionary*)metadataDict];

 CFRelease(metadataDict);

 NSDictionary *exifMetadata = [[metadata objectForKey:(NSString  *)kCGImagePropertyExifDictionary] mutableCopy];

 float brightnessValue = [[exifMetadata objectForKey:(NSString  *)kCGImagePropertyExifBrightnessValue] floatValue];
 
 // 根据brightnessValue的值来打开和关闭手电筒
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    if ((brightnessValue < 0)) {
    // 打开手电筒
 else if(brightnessValue > 0) {
 // 关闭手电筒
 }
}

4、Android Camera 如何优化视频录制的卡顿?

1)视频录制流程

  • 打开 Camera
  • 创建 SurfaceTextue ,将 Camera 输出的数据渲染到 SurfaceTextue
  • SurfaceTexture 拿到的结果进行特效处理。
  • 特效处理的结果异步分发给 RenderView 预览与 MediaCodec 编码。
  • 编码后的结果进行 Muxer 合成 Mp4 视频。

2)视频录制流程优化

  • 相机、编码根据不同机型控制不同帧率、分辨率。
  • 实现丢帧模块,将采集后的帧进入丢帧模块进行控制帧率,降低渲染以及编码性能。
  • 采集、渲染、编码按照多个线程处理,每个模块发挥最优性能。
  • 预览视图优先采用 SurfaceView,少量使用 TextureView,因为 TextureView 占用主线程渲染。
  • 编码优先使用 Surface 异步编码。

5、Android Surface 解码如何支持带角度视频?

1)直接解码到 Surface

需要通过 MediaFormat 设置解码参,通过 MediaFormat.KEY_ROTATION 配置旋转角度,则可以正确显示。

2)解码到 SurfaceTexture

解码到 SurfaceTexture,通过 MediaFormat.KEY_ROTATION 配置旋转角度,但输出纹理提供接口获取旋转矩阵,mSurfaceTexture.getSurfaceTexture().getTransformMatrix,拿到旋转矩阵后通过 FBO 渲染调整为正确尺寸,这种模式好处可以将解码后数据经过自定义处理传递给编码层与渲染上屏。


更多的音视频知识、面试题、技术方案干货可以进群来看:

相关推荐
玫瑰花开一片一片9 小时前
Flutter IOS 真机 Widget 错误。Widget 安装后系统中没有
flutter·ios·widget·ios widget
烎就是我11 小时前
100行代码swift从零实现一个iOS日历
ios·swift
音视频牛哥14 小时前
如何实现Android屏幕和音频采集并启动RTSP服务?
音视频开发·视频编码·直播
音视频牛哥1 天前
Linux平台实现低延迟的RTSP、RTMP播放
音视频开发·视频编码·直播
鸿蒙布道师1 天前
鸿蒙NEXT开发通知工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
鸿蒙布道师1 天前
鸿蒙NEXT开发网络相关工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
心走1 天前
鸿蒙WebRTC编译指南&踩坑(Native 编译指导)
harmonyos·音视频开发
余生大大1 天前
关于Safari浏览器在ios<16.3版本不支持正则表达式零宽断言的解决办法
ios·正则表达式·safari
爱分享的程序员1 天前
前端跨端框架的开发以及IOS和安卓的开发流程和打包上架的详细流程
android·前端·ios