1

对于我们当前的项目,我们已经生成了一系列图像帧,经过一些处理,我们需要使用一些编解码器实时压缩并通过网络发送。第一个实现需要使用 JPEG,尽管稍后将添加其他更专注于视频的编码。

我们一直在使用 Apple 的 VideoToolbox.framework 进行压缩,因为它的 JPEG 编码器kCMVideoCodecType_JPEG( . (它似乎没有硬件加速 JPEG,FWIW。)

这一切都很好,除了输出帧上的一些典型的 JPEG 振铃伪影。理论上这没有问题,有一个kVTCompressionPropertyKey_Quality属性。不幸的是,调整这个值似乎隐含地改变了色度子采样模式—— 0.75向上似乎将编码器从 YUV 4:2:0 子采样切换到 4:2:2,并且在途中的某个地方1.0再次翻转到 4:4: 4. 由于我们无法控制的原因,我们需要将帧编码为 4:2:0 JPEG,而 0.74 的质量水平非常糟糕。另外,Apple 可能会在未来的版本中更改他们的阈值,即使我们坚持使用 0.74,这也会突然破坏我们的代码。

有没有办法手动选择 aVTCompressionSession使用的色度二次采样模式?

已经尝试过:我们的源帧数据以 BRGA 形式出现,这就是我们一直用于源CVPixelBuffer对象的像素格式。一种想法是自己进行色彩空间转换,并提供kCVPixelFormatType_420YpCbCr8BiPlanarFullRange像素格式的像素缓冲区。压缩会话肯定不会将其上采样到 422 或 444?事实证明确实如此。没有帮助。

还有其他建议吗?目前还不太清楚可以在压缩会话、每一帧、像素缓冲区等上设置哪些属性。 - 我已经挖掘了框架头文件,并没有发现任何明显的东西,但我错过了什么吗?或者是切换到不同 JPEG 编码器的唯一解决方案?

这是我们的压缩会话初始化代码,包括质量设置:

const void* keys[] = {
    kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder,
};
const void* values[] = {
    kCFBooleanTrue,
};
CFDictionaryRef encoder_spec = CFDictionaryCreate(
    kCFAllocatorDefault, keys, values, sizeof(keys) / sizeof(keys[0]), &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

VTCompressionSessionRef session = NULL;
OSStatus error = VTCompressionSessionCreate(
    kCFAllocatorDefault, image_width, image_height, kCMVideoCodecType_JPEG, encoder_spec, NULL /*source buffer spec */, NULL /*allocator*/, output_callback, vscs /* session refcon*/, &session);
CFRelease(encoder_spec);

if (error != 0)
{
    // … error handling
}

int field_count = 1; // progressive
CFNumberRef field_count_val = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &field_count);
VTSessionSetProperty(session, kVTCompressionPropertyKey_FieldCount, field_count_val);
CFRelease(field_count_val);

VTSessionSetProperty(session, kVTCompressionPropertyKey_AllowFrameReordering, kCFBooleanFalse);

int max_frame_delay_count = 0; // encode frames in order
CFNumberRef max_frame_delay_count_val = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &max_frame_delay_count);
VTSessionSetProperty(session, kVTCompressionPropertyKey_MaxFrameDelayCount, max_frame_delay_count_val);
CFRelease(max_frame_delay_count_val);

float quality = 0.74f; // highest quality that defaults to YUV420
CFNumberRef quality_val = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &quality);
VTSessionSetProperty(session, kVTCompressionPropertyKey_Quality, quality_val);
CFRelease(quality_val);

像素缓冲区的创建方式如下:

CVPixelBufferCreate(kCFAllocatorDefault, image_width, image_height, k32BGRAPixelFormat, NULL, &px_buf);

或者在使用 YUV420 像素缓冲区时:

CVPixelBufferCreate(kCFAllocatorDefault, image_width, image_height, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, NULL, &yuv_px_buf);

每个帧编码都是通过这个调用开始的:

OSStatus error = VTCompressionSessionEncodeFrame(
    session, img_buffer, timestamp, kCMTimeInvalid, NULL /* frame_properties */, NULL /* frame_refcon */, &flags);
4

0 回答 0