macOS VTCompressionSession:如何控制 JPEG 色度子采样模式? (YUV 4:2:0)

问题描述 投票:0回答:1

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

我们一直在使用 Apple 的 VideoToolbox.framework 进行压缩,因为它的 JPEG 编码器 (

kCMVideoCodecType_JPEG
) 非常快,并且它将成为其他格式的选择,特别是如果编解码器支持硬件加速的话( s) 有问题。 (它似乎没有硬件加速 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 的质量水平相当糟糕。另外,苹果可能会在未来的版本中改变他们的阈值,即使我们坚持使用 0.74,这也会突然破坏我们的代码。

有没有办法手动选择

VTCompressionSession
使用的色度子采样模式?

已经尝试过:我们的源帧数据以 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);
macos jpeg yuv pixelformat video-toolbox
1个回答
0
投票

我最终让自己对 VideoToolbox 更加熟悉了一点,并发现您可以使用 VTCopySupportedPropertyDictionaryForEncoder() 函数获取编码器的

所有
可配置属性。对于 JPEG 编码器,这不包括任何控制输出像素格式的内容。

“JPEG 编码器S”?是的,自从 Apple Silicon 推出以来,现在有一个通过 VideoToolbox 公开的硬件 JPEG 编码器。这个编码器的行为与软件编码器略有不同:无论质量设置如何,这里的输出似乎都是“始终”YUV420。虽然至少在某种程度上更加一致,但我们不能使用它来生成 YUV444 JPEG。 我最终的解决方案是使用 Metal Compute 着色器编写我自己的基于 GPU 的 JPEG 编码器。这样,我们就可以准确控制我们得到的内容,而且速度也一样快。

© www.soinside.com 2019 - 2024. All rights reserved.