Commit 46f6bdf9 authored by Christopher Cameron's avatar Christopher Cameron Committed by Commit Bot

Add media::GetFormatDescriptionColorSpace

Restrict this to macOS 10.11 and above. In theory the function should
not compile when targeting macOS 10.10. In practice it does, because
the CoreMedia constants are preprocessor defined to their CoreVideo
equivalents (which are defined).

This function is nearly identical to media::GetImageBufferColorSpace,
but instead operates on a CMFormatDescriptionRef.

Add DCHECKs that the behavior of the two functions be the same, that is,
the kCVImageBuffer-prefixed and  kCMFormatDescription-prefixed
constants be equal.

Use base::mac::CFCast instead of reinterpret_casts.

The next patch in this sequence will move these two functions over
to media/base/mac, because it will be accessed in capture.

Bug: 959962
Change-Id: Iea9d8d357dec72ddc928b7e1fd3a631c131ade74
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2449934Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Commit-Queue: ccameron <ccameron@chromium.org>
Cr-Commit-Position: refs/heads/master@{#813924}
parent 850b4866
...@@ -26,6 +26,9 @@ CreateFormatExtensions(CMVideoCodecType codec_type, ...@@ -26,6 +26,9 @@ CreateFormatExtensions(CMVideoCodecType codec_type,
MEDIA_GPU_EXPORT gfx::ColorSpace GetImageBufferColorSpace( MEDIA_GPU_EXPORT gfx::ColorSpace GetImageBufferColorSpace(
CVImageBufferRef image_buffer); CVImageBufferRef image_buffer);
MEDIA_GPU_EXPORT gfx::ColorSpace GetFormatDescriptionColorSpace(
CMFormatDescriptionRef format_description) API_AVAILABLE(macos(10.11));
} // namespace media } // namespace media
#endif // MEDIA_GPU_MAC_VT_CONFIG_UTIL_H_ #endif // MEDIA_GPU_MAC_VT_CONFIG_UTIL_H_
...@@ -253,17 +253,17 @@ void SetMasteringMetadata(const gl::HDRMetadata& hdr_metadata, ...@@ -253,17 +253,17 @@ void SetMasteringMetadata(const gl::HDRMetadata& hdr_metadata,
// Use the list of pairs in |cfstr_id_pairs| to do the conversion (by doing a // Use the list of pairs in |cfstr_id_pairs| to do the conversion (by doing a
// linear lookup). // linear lookup).
template <typename IdType, typename StringIdPair> template <typename IdType, typename StringIdPair>
bool GetImageBufferProperty(CVImageBufferRef image_buffer, bool GetImageBufferProperty(CFTypeRef value_untyped,
CFStringRef key,
const std::vector<StringIdPair>& cfstr_id_pairs, const std::vector<StringIdPair>& cfstr_id_pairs,
IdType* value_as_id) { IdType* value_as_id) {
CFStringRef value_as_string = reinterpret_cast<CFStringRef>( CFStringRef value_as_string = base::mac::CFCast<CFStringRef>(value_untyped);
CVBufferGetAttachment(image_buffer, key, nullptr));
if (!value_as_string) if (!value_as_string)
return false; return false;
for (const auto& p : cfstr_id_pairs) { for (const auto& p : cfstr_id_pairs) {
if (!CFStringCompare(value_as_string, p.cfstr, 0)) { if (p.cfstr_cm)
DCHECK(!CFStringCompare(p.cfstr_cv, p.cfstr_cm, 0));
if (!CFStringCompare(value_as_string, p.cfstr_cv, 0)) {
*value_as_id = p.id; *value_as_id = p.id;
return true; return true;
} }
...@@ -272,24 +272,32 @@ bool GetImageBufferProperty(CVImageBufferRef image_buffer, ...@@ -272,24 +272,32 @@ bool GetImageBufferProperty(CVImageBufferRef image_buffer,
return false; return false;
} }
gfx::ColorSpace::PrimaryID GetImageBufferPrimary( gfx::ColorSpace::PrimaryID GetCoreVideoPrimary(CFTypeRef primaries_untyped) {
CVImageBufferRef image_buffer) {
struct CVImagePrimary { struct CVImagePrimary {
const CFStringRef cfstr; const CFStringRef cfstr_cv;
const CFStringRef cfstr_cm;
const gfx::ColorSpace::PrimaryID id; const gfx::ColorSpace::PrimaryID id;
}; };
static const base::NoDestructor<std::vector<CVImagePrimary>> static const base::NoDestructor<std::vector<CVImagePrimary>>
kSupportedPrimaries([] { kSupportedPrimaries([] {
std::vector<CVImagePrimary> supported_primaries; std::vector<CVImagePrimary> supported_primaries;
supported_primaries.push_back({kCVImageBufferColorPrimaries_ITU_R_709_2, supported_primaries.push_back(
{kCVImageBufferColorPrimaries_ITU_R_709_2,
kCMFormatDescriptionColorPrimaries_ITU_R_709_2,
gfx::ColorSpace::PrimaryID::BT709}); gfx::ColorSpace::PrimaryID::BT709});
supported_primaries.push_back({kCVImageBufferColorPrimaries_EBU_3213, supported_primaries.push_back(
{kCVImageBufferColorPrimaries_EBU_3213,
kCMFormatDescriptionColorPrimaries_EBU_3213,
gfx::ColorSpace::PrimaryID::BT470BG}); gfx::ColorSpace::PrimaryID::BT470BG});
supported_primaries.push_back({kCVImageBufferColorPrimaries_SMPTE_C, supported_primaries.push_back(
{kCVImageBufferColorPrimaries_SMPTE_C,
kCMFormatDescriptionColorPrimaries_SMPTE_C,
gfx::ColorSpace::PrimaryID::SMPTE240M}); gfx::ColorSpace::PrimaryID::SMPTE240M});
if (@available(macos 10.11, *)) { if (@available(macos 10.11, *)) {
supported_primaries.push_back( supported_primaries.push_back(
{kCVImageBufferColorPrimaries_ITU_R_2020, {kCVImageBufferColorPrimaries_ITU_R_2020,
kCMFormatDescriptionColorPrimaries_ITU_R_2020,
gfx::ColorSpace::PrimaryID::BT2020}); gfx::ColorSpace::PrimaryID::BT2020});
} }
return supported_primaries; return supported_primaries;
...@@ -297,66 +305,85 @@ gfx::ColorSpace::PrimaryID GetImageBufferPrimary( ...@@ -297,66 +305,85 @@ gfx::ColorSpace::PrimaryID GetImageBufferPrimary(
// The named primaries. Default to BT709. // The named primaries. Default to BT709.
auto primary_id = gfx::ColorSpace::PrimaryID::BT709; auto primary_id = gfx::ColorSpace::PrimaryID::BT709;
if (!GetImageBufferProperty(image_buffer, kCVImageBufferColorPrimariesKey, if (!GetImageBufferProperty(primaries_untyped, *kSupportedPrimaries,
*kSupportedPrimaries, &primary_id)) { &primary_id)) {
DLOG(ERROR) << "Failed to find CVImageBufferRef primaries."; DLOG(ERROR) << "Failed to find CVImageBufferRef primaries.";
} }
return primary_id; return primary_id;
} }
gfx::ColorSpace::TransferID GetImageBufferTransferFn( gfx::ColorSpace::TransferID GetCoreVideoTransferFn(CFTypeRef transfer_untyped,
CVImageBufferRef image_buffer, CFTypeRef gamma_untyped,
double* gamma) { double* gamma) {
struct CVImageTransferFn { struct CVImageTransferFn {
const CFStringRef cfstr; const CFStringRef cfstr_cv;
const CFStringRef cfstr_cm;
const gfx::ColorSpace::TransferID id; const gfx::ColorSpace::TransferID id;
}; };
static const base::NoDestructor<std::vector<CVImageTransferFn>> static const base::NoDestructor<std::vector<CVImageTransferFn>>
kSupportedTransferFuncs([] { kSupportedTransferFuncs([] {
std::vector<CVImageTransferFn> supported_transfer_funcs; std::vector<CVImageTransferFn> supported_transfer_funcs;
// The constants kCMFormatDescriptionTransferFunction_ITU_R_709_2,
// SMPTE_240M_1995, and UseGamma will compile against macOS 10.10
// because they are #defined to their kCVImageBufferTransferFunction
// equivalents. They are technically not present until macOS 10.11.
supported_transfer_funcs.push_back( supported_transfer_funcs.push_back(
{kCVImageBufferTransferFunction_ITU_R_709_2, {kCVImageBufferTransferFunction_ITU_R_709_2,
kCMFormatDescriptionTransferFunction_ITU_R_709_2,
gfx::ColorSpace::TransferID::BT709_APPLE}); gfx::ColorSpace::TransferID::BT709_APPLE});
supported_transfer_funcs.push_back( supported_transfer_funcs.push_back(
{kCVImageBufferTransferFunction_SMPTE_240M_1995, {kCVImageBufferTransferFunction_SMPTE_240M_1995,
kCMFormatDescriptionTransferFunction_SMPTE_240M_1995,
gfx::ColorSpace::TransferID::SMPTE240M}); gfx::ColorSpace::TransferID::SMPTE240M});
supported_transfer_funcs.push_back( supported_transfer_funcs.push_back(
{kCVImageBufferTransferFunction_UseGamma, {kCVImageBufferTransferFunction_UseGamma,
kCMFormatDescriptionTransferFunction_UseGamma,
gfx::ColorSpace::TransferID::CUSTOM}); gfx::ColorSpace::TransferID::CUSTOM});
if (@available(macos 10.11, *)) { if (@available(macos 10.11, *)) {
supported_transfer_funcs.push_back( supported_transfer_funcs.push_back(
{kCVImageBufferTransferFunction_ITU_R_2020, {kCVImageBufferTransferFunction_ITU_R_2020,
kCMFormatDescriptionTransferFunction_ITU_R_2020,
gfx::ColorSpace::TransferID::BT2020_10}); gfx::ColorSpace::TransferID::BT2020_10});
} }
if (@available(macos 10.12, *)) { if (@available(macos 10.12, *)) {
supported_transfer_funcs.push_back( supported_transfer_funcs.push_back(
{kCVImageBufferTransferFunction_SMPTE_ST_428_1, {kCVImageBufferTransferFunction_SMPTE_ST_428_1,
kCMFormatDescriptionTransferFunction_SMPTE_ST_428_1,
gfx::ColorSpace::TransferID::SMPTEST428_1}); gfx::ColorSpace::TransferID::SMPTEST428_1});
} }
if (@available(macos 10.13, *)) { if (@available(macos 10.13, *)) {
supported_transfer_funcs.push_back( supported_transfer_funcs.push_back(
{kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ, {kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ,
kCMFormatDescriptionTransferFunction_SMPTE_ST_2084_PQ,
gfx::ColorSpace::TransferID::SMPTEST2084}); gfx::ColorSpace::TransferID::SMPTEST2084});
supported_transfer_funcs.push_back( supported_transfer_funcs.push_back(
{kCVImageBufferTransferFunction_ITU_R_2100_HLG, {kCVImageBufferTransferFunction_ITU_R_2100_HLG,
kCMFormatDescriptionTransferFunction_ITU_R_2100_HLG,
gfx::ColorSpace::TransferID::ARIB_STD_B67}); gfx::ColorSpace::TransferID::ARIB_STD_B67});
supported_transfer_funcs.push_back( supported_transfer_funcs.push_back(
{kCVImageBufferTransferFunction_sRGB, {kCVImageBufferTransferFunction_sRGB, nullptr,
gfx::ColorSpace::TransferID::IEC61966_2_1}); gfx::ColorSpace::TransferID::IEC61966_2_1});
} }
if (@available(macos 10.14, *)) { if (@available(macos 10.14, *)) {
supported_transfer_funcs.push_back( supported_transfer_funcs.push_back(
{kCVImageBufferTransferFunction_Linear, {kCVImageBufferTransferFunction_Linear,
kCMFormatDescriptionTransferFunction_Linear,
gfx::ColorSpace::TransferID::LINEAR}); gfx::ColorSpace::TransferID::LINEAR});
} }
if (@available(macos 10.15, *)) {
supported_transfer_funcs.push_back(
{kCVImageBufferTransferFunction_sRGB,
kCMFormatDescriptionTransferFunction_sRGB,
gfx::ColorSpace::TransferID::IEC61966_2_1});
}
return supported_transfer_funcs; return supported_transfer_funcs;
}()); }());
// The named transfer function. // The named transfer function.
auto transfer_id = gfx::ColorSpace::TransferID::BT709; auto transfer_id = gfx::ColorSpace::TransferID::BT709;
if (!GetImageBufferProperty(image_buffer, kCVImageBufferTransferFunctionKey, if (!GetImageBufferProperty(transfer_untyped, *kSupportedTransferFuncs,
*kSupportedTransferFuncs, &transfer_id)) { &transfer_id)) {
DLOG(ERROR) << "Failed to find CVImageBufferRef transfer."; DLOG(ERROR) << "Failed to find CVImageBufferRef transfer.";
} }
...@@ -365,11 +392,9 @@ gfx::ColorSpace::TransferID GetImageBufferTransferFn( ...@@ -365,11 +392,9 @@ gfx::ColorSpace::TransferID GetImageBufferTransferFn(
// If we fail to retrieve the gamma parameter, fall back to BT709. // If we fail to retrieve the gamma parameter, fall back to BT709.
constexpr auto kDefaultTransferFn = gfx::ColorSpace::TransferID::BT709; constexpr auto kDefaultTransferFn = gfx::ColorSpace::TransferID::BT709;
CFNumberRef gamma_number = CFNumberRef gamma_number = base::mac::CFCast<CFNumberRef>(gamma_untyped);
reinterpret_cast<CFNumberRef>(CVBufferGetAttachment(
image_buffer, kCVImageBufferGammaLevelKey, nullptr));
if (!gamma_number) { if (!gamma_number) {
DLOG(ERROR) << "Failed to get CVImageBufferRef gamma level."; DLOG(ERROR) << "Failed to get gamma level.";
return kDefaultTransferFn; return kDefaultTransferFn;
} }
...@@ -389,30 +414,39 @@ gfx::ColorSpace::TransferID GetImageBufferTransferFn( ...@@ -389,30 +414,39 @@ gfx::ColorSpace::TransferID GetImageBufferTransferFn(
return transfer_id; return transfer_id;
} }
gfx::ColorSpace::MatrixID GetImageBufferMatrix(CVImageBufferRef image_buffer) { gfx::ColorSpace::MatrixID GetCoreVideoMatrix(CFTypeRef matrix_untyped) {
struct CVImageMatrix { struct CVImageMatrix {
const CFStringRef cfstr; const CFStringRef cfstr_cv;
const CFStringRef cfstr_cm;
gfx::ColorSpace::MatrixID id; gfx::ColorSpace::MatrixID id;
}; };
static const base::NoDestructor<std::vector<CVImageMatrix>> static const base::NoDestructor<std::vector<CVImageMatrix>>
kSupportedMatrices([] { kSupportedMatrices([] {
std::vector<CVImageMatrix> supported_matrices; std::vector<CVImageMatrix> supported_matrices;
supported_matrices.push_back({kCVImageBufferYCbCrMatrix_ITU_R_709_2, supported_matrices.push_back(
{kCVImageBufferYCbCrMatrix_ITU_R_709_2,
kCMFormatDescriptionYCbCrMatrix_ITU_R_709_2,
gfx::ColorSpace::MatrixID::BT709}); gfx::ColorSpace::MatrixID::BT709});
supported_matrices.push_back({kCVImageBufferYCbCrMatrix_ITU_R_601_4, supported_matrices.push_back(
{kCVImageBufferYCbCrMatrix_ITU_R_601_4,
kCMFormatDescriptionYCbCrMatrix_ITU_R_601_4,
gfx::ColorSpace::MatrixID::SMPTE170M}); gfx::ColorSpace::MatrixID::SMPTE170M});
supported_matrices.push_back({kCVImageBufferYCbCrMatrix_SMPTE_240M_1995, supported_matrices.push_back(
{kCVImageBufferYCbCrMatrix_SMPTE_240M_1995,
kCMFormatDescriptionYCbCrMatrix_SMPTE_240M_1995,
gfx::ColorSpace::MatrixID::SMPTE240M}); gfx::ColorSpace::MatrixID::SMPTE240M});
if (@available(macos 10.11, *)) { if (@available(macos 10.11, *)) {
supported_matrices.push_back({kCVImageBufferYCbCrMatrix_ITU_R_2020, supported_matrices.push_back(
{kCVImageBufferYCbCrMatrix_ITU_R_2020,
kCMFormatDescriptionYCbCrMatrix_ITU_R_2020,
gfx::ColorSpace::MatrixID::BT2020_NCL}); gfx::ColorSpace::MatrixID::BT2020_NCL});
} }
return supported_matrices; return supported_matrices;
}()); }());
auto matrix_id = gfx::ColorSpace::MatrixID::INVALID; auto matrix_id = gfx::ColorSpace::MatrixID::INVALID;
if (!GetImageBufferProperty(image_buffer, kCVImageBufferYCbCrMatrixKey, if (!GetImageBufferProperty(matrix_untyped, *kSupportedMatrices,
*kSupportedMatrices, &matrix_id)) { &matrix_id)) {
DLOG(ERROR) << "Failed to find CVImageBufferRef YUV matrix."; DLOG(ERROR) << "Failed to find CVImageBufferRef YUV matrix.";
} }
return matrix_id; return matrix_id;
...@@ -512,11 +546,17 @@ CFMutableDictionaryRef CreateFormatExtensions( ...@@ -512,11 +546,17 @@ CFMutableDictionaryRef CreateFormatExtensions(
return base::mac::NSToCFCast(extensions); return base::mac::NSToCFCast(extensions);
} }
gfx::ColorSpace GetImageBufferColorSpace(CVImageBufferRef image_buffer) { namespace {
gfx::ColorSpace GetCoreVideoColorSpaceInternal(CFTypeRef primaries_untyped,
CFTypeRef transfer_untyped,
CFTypeRef gamma_untyped,
CFTypeRef matrix_untyped) {
double gamma; double gamma;
auto primary_id = GetImageBufferPrimary(image_buffer); auto primary_id = GetCoreVideoPrimary(primaries_untyped);
auto matrix_id = GetImageBufferMatrix(image_buffer); auto matrix_id = GetCoreVideoMatrix(matrix_untyped);
auto transfer_id = GetImageBufferTransferFn(image_buffer, &gamma); auto transfer_id =
GetCoreVideoTransferFn(transfer_untyped, gamma_untyped, &gamma);
// Use a matrix id that is coherent with a primary id. Useful when we fail to // Use a matrix id that is coherent with a primary id. Useful when we fail to
// parse the matrix. Previously it was always defaulting to MatrixID::BT709 // parse the matrix. Previously it was always defaulting to MatrixID::BT709
...@@ -548,4 +588,30 @@ gfx::ColorSpace GetImageBufferColorSpace(CVImageBufferRef image_buffer) { ...@@ -548,4 +588,30 @@ gfx::ColorSpace GetImageBufferColorSpace(CVImageBufferRef image_buffer) {
return gfx::ColorSpace(primary_id, transfer_id, matrix_id, range_id); return gfx::ColorSpace(primary_id, transfer_id, matrix_id, range_id);
} }
} // anonymous namespace
gfx::ColorSpace GetImageBufferColorSpace(CVImageBufferRef image_buffer) {
return GetCoreVideoColorSpaceInternal(
CVBufferGetAttachment(image_buffer, kCVImageBufferColorPrimariesKey,
nullptr),
CVBufferGetAttachment(image_buffer, kCVImageBufferTransferFunctionKey,
nullptr),
CVBufferGetAttachment(image_buffer, kCVImageBufferGammaLevelKey, nullptr),
CVBufferGetAttachment(image_buffer, kCVImageBufferYCbCrMatrixKey,
nullptr));
}
gfx::ColorSpace GetFormatDescriptionColorSpace(
CMFormatDescriptionRef format_description) {
return GetCoreVideoColorSpaceInternal(
CMFormatDescriptionGetExtension(
format_description, kCMFormatDescriptionExtension_ColorPrimaries),
CMFormatDescriptionGetExtension(
format_description, kCMFormatDescriptionExtension_TransferFunction),
CMFormatDescriptionGetExtension(format_description,
kCMFormatDescriptionExtension_GammaLevel),
CMFormatDescriptionGetExtension(
format_description, kCMFormatDescriptionExtension_YCbCrMatrix));
}
} // namespace media } // namespace media
...@@ -74,6 +74,34 @@ base::ScopedCFTypeRef<CVImageBufferRef> CreateCVImageBuffer( ...@@ -74,6 +74,34 @@ base::ScopedCFTypeRef<CVImageBufferRef> CreateCVImageBuffer(
return image_buffer; return image_buffer;
} }
base::ScopedCFTypeRef<CMFormatDescriptionRef> CreateFormatDescription(
CFStringRef primaries,
CFStringRef transfer,
CFStringRef matrix) {
base::ScopedCFTypeRef<CFMutableDictionaryRef> extensions(
CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
if (primaries) {
CFDictionarySetValue(
extensions, kCMFormatDescriptionExtension_ColorPrimaries, primaries);
}
if (transfer) {
CFDictionarySetValue(
extensions, kCMFormatDescriptionExtension_TransferFunction, transfer);
}
if (matrix) {
CFDictionarySetValue(extensions, kCMFormatDescriptionExtension_YCbCrMatrix,
matrix);
}
base::ScopedCFTypeRef<CMFormatDescriptionRef> result;
CMFormatDescriptionCreate(nullptr, kCMMediaType_Video,
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
extensions.get(), result.InitializeInto());
return result;
}
gfx::ColorSpace ToBT709_APPLE(gfx::ColorSpace cs) { gfx::ColorSpace ToBT709_APPLE(gfx::ColorSpace cs) {
return gfx::ColorSpace(cs.GetPrimaryID(), return gfx::ColorSpace(cs.GetPrimaryID(),
gfx::ColorSpace::TransferID::BT709_APPLE, gfx::ColorSpace::TransferID::BT709_APPLE,
...@@ -364,4 +392,26 @@ TEST(VTConfigUtil, GetImageBufferColorSpace_BT2020_HLG) { ...@@ -364,4 +392,26 @@ TEST(VTConfigUtil, GetImageBufferColorSpace_BT2020_HLG) {
} }
} }
TEST(VTConfigUtil, FormatDescriptionInvalid) {
if (__builtin_available(macos 10.11, *)) {
auto format_descriptor =
CreateFormatDescription(CFSTR("Cows"), CFSTR("Go"), CFSTR("Moo"));
ASSERT_TRUE(format_descriptor);
auto cs = GetFormatDescriptionColorSpace(format_descriptor);
EXPECT_EQ(gfx::ColorSpace::CreateREC709(), cs);
}
}
TEST(VTConfigUtil, FormatDescriptionBT709) {
if (__builtin_available(macos 10.11, *)) {
auto format_descriptor = CreateFormatDescription(
kCMFormatDescriptionColorPrimaries_ITU_R_709_2,
kCMFormatDescriptionTransferFunction_ITU_R_709_2,
kCMFormatDescriptionYCbCrMatrix_ITU_R_709_2);
ASSERT_TRUE(format_descriptor);
auto cs = GetFormatDescriptionColorSpace(format_descriptor);
EXPECT_EQ(ToBT709_APPLE(gfx::ColorSpace::CreateREC709()), cs);
}
}
} // namespace media } // namespace media
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment