对于我们当前的项目,我们已经生成了一系列图像帧,经过一些处理后,我们需要使用一些编解码器实时压缩并通过网络发送。第一个实现需要使用JPEG,虽然显然其他更多的视频聚焦编码将在以后添加。
我们一直在使用Apple的VideoToolbox.framework进行压缩,因为它的JPEG编码器(kCMVideoCodecType_JPEG
)非常快,而且它是其他方法格式,特别是如果所讨论的编解码器支持硬件加速。 (它似乎不会硬件加速JPEG,FWIW。)
除了输出帧上的一些典型的JPEG振铃伪像外,这一切都很好用。理论上这不是问题,有kVTCompressionPropertyKey_Quality
属性。不幸的是,似乎调整此值隐式地改变了色度子采样模式 - 0.75
并且似乎将编码器从YUV 4:2:0子采样切换到4:2:2,并且在去往{{1的路上它再次翻到4:4:4。由于我们无法控制的原因,我们需要将帧编码为4:2:0 JPEG,质量等级为0.74非常糟糕。此外,Apple可能会在未来的版本中更改其阈值,即使我们坚持使用0.74也会突然破坏我们的代码。
有没有办法手动选择1.0
使用的色度子采样模式?
已经尝试过:我们的源帧数据以BRGA格式显示,因此我们用于源VTCompressionSession
对象的像素格式。一种想法是自己进行色彩空间转换,并提供像素缓冲区CVPixelBuffer
像素格式。当然压缩会话不会将其上采样到422或444? 事实证明确实如此。没用。
还有其他建议吗?它还不太清楚压缩会话,每个帧,像素缓冲区等可以设置哪些属性 - 我已经挖掘了框架头文件,并且没有发现任何明显的东西,但我错过了什么吗?或者是切换到不同JPEG编码器的唯一解决方案?
这是我们的压缩会话初始化代码,包括质量设置:
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
像素缓冲区的创建如下:
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);
或者使用YUV420像素缓冲区时:
CVPixelBufferCreate(kCFAllocatorDefault, image_width, image_height, k32BGRAPixelFormat, NULL, &px_buf);
通过此调用启动每个帧编码:
CVPixelBufferCreate(kCFAllocatorDefault, image_width, image_height, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, NULL, &yuv_px_buf);