下面是 2023.10 月音视频面试题集锦的几条干货精选:
1)PSNR 定义
PSNR
(Peak Signal-to-Noise Ratio
,峰值信噪比)是一种衡量图像质量的指标,常用于评估压缩算法的效果。它通过比较原始图像与压缩/恢复后的图像之间的差异,来量化图像质量的损失程度。
2)PSNR 计算公式
PSNR = 10 * log10((MAX^2) / MSE)
其中,MAX 是图像像素值的最大可能取值(通常为255),MSE
是均方误差(Mean Squared Error
),表示压缩/恢复图像与原始图像之间的平均像素差的平方。
3)PSNR 计算实现
#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.raw
和 compressed_image.raw
是示例图像的文件名,你需要根据实际情况修改为你自己的图像文件名。此外,代码中假设图像是灰度图像,如果你的图像是彩色图像,需要进行相应的修改。
通过计算 PSNR
,我们可以得到一个数值来评估压缩/恢复图像与原始图像之间的质量损失程度。PSNR
的数值越高,表示图像质量的损失越小。
在视频领域,质量甜点指的是在既定的码率和屏幕大小下通过设定合理的分辨率和帧率来得到最佳视频主观质量体验。因为编码复杂度和编解码质量亦不是线性关系,两者之间也存在一个质量甜点。在音频领域也有类似的情况,针对具体的情况,我们可以测试手机的编码质量来选择指定分辨率、帧率时对应的码率甜点。
在这种测试中我们一般需要分场景进行,比如:
测试指标我们可以采用 PSNR
、SSIM
、VMAF
进行综合考量。
比如,我们可以测试 iOS
硬编,使用 540P
,15 帧推流时,设置不同的码率(800kbps-1300kbps)分别测试各场景下的各指标值,找出 R-D(码率-失真)曲线拐点出现的区间,这就是我们要找的码率甜点。
当夜晚使用共享单车扫码时,应该都见过提示“打开手电筒”,在 iOS
中我们如何实现呢?主要基于图像环境光参数,参考如下代码。
iOS
视频采集设置 AVCaptureVideoDataOutput AVCaptureVideoDataOutputSampleBufferDelegate
,通过获取 CMSampleBufferRef
每一帧视频数据环境参数进行判断即可。
- (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) {
// 关闭手电筒
}
}
1)视频录制流程
Camera
。SurfaceTextue
,将 Camera
输出的数据渲染到 SurfaceTextue
。SurfaceTexture
拿到的结果进行特效处理。RenderView
预览与 MediaCodec
编码。Muxer
合成 Mp4
视频。2)视频录制流程优化
SurfaceView
,少量使用 TextureView
,因为 TextureView
占用主线程渲染。Surface
异步编码。1)直接解码到 Surface
需要通过 MediaFormat
设置解码参,通过 MediaFormat.KEY_ROTATION
配置旋转角度,则可以正确显示。
2)解码到 SurfaceTexture
解码到 SurfaceTexture
,通过 MediaFormat.KEY_ROTATION
配置旋转角度,但输出纹理提供接口获取旋转矩阵,mSurfaceTexture.getSurfaceTexture().getTransformMatrix
,拿到旋转矩阵后通过 FBO
渲染调整为正确尺寸,这种模式好处可以将解码后数据经过自定义处理传递给编码层与渲染上屏。