ソースを参照

mac-videotoolbox: Add HDR metadata

jpark37 3 年 前
コミット
0fc3cb1962
1 ファイル変更87 行追加3 行削除
  1. 87 3
      plugins/mac-videotoolbox/encoder.c

+ 87 - 3
plugins/mac-videotoolbox/encoder.c

@@ -184,6 +184,58 @@ static CFStringRef obs_to_vt_transfer(enum video_colorspace cs)
 	}
 }
 
+/* Adapted from Chromium GenerateMasteringDisplayColorVolume */
+static CFDataRef obs_to_vt_masteringdisplay(uint32_t hdr_nominal_peak_level)
+{
+	struct mastering_display_colour_volume {
+		uint16_t display_primaries[3][2];
+		uint16_t white_point[2];
+		uint32_t max_display_mastering_luminance;
+		uint32_t min_display_mastering_luminance;
+	};
+	static_assert(sizeof(struct mastering_display_colour_volume) == 24,
+		      "May need to adjust struct packing");
+
+	struct mastering_display_colour_volume mdcv;
+	mdcv.display_primaries[0][0] = __builtin_bswap16(13250);
+	mdcv.display_primaries[0][1] = __builtin_bswap16(34500);
+	mdcv.display_primaries[1][0] = __builtin_bswap16(7500);
+	mdcv.display_primaries[1][1] = __builtin_bswap16(3000);
+	mdcv.display_primaries[2][0] = __builtin_bswap16(34000);
+	mdcv.display_primaries[2][1] = __builtin_bswap16(16000);
+	mdcv.white_point[0] = __builtin_bswap16(15635);
+	mdcv.white_point[1] = __builtin_bswap16(16450);
+	mdcv.max_display_mastering_luminance =
+		__builtin_bswap32(hdr_nominal_peak_level * 10000);
+	mdcv.min_display_mastering_luminance = 0;
+
+	UInt8 bytes[sizeof(struct mastering_display_colour_volume)];
+	memcpy(bytes, &mdcv, sizeof(bytes));
+	return CFDataCreate(NULL, bytes, sizeof(bytes));
+}
+
+/* Adapted from Chromium GenerateContentLightLevelInfo */
+static CFDataRef
+obs_to_vt_contentlightlevelinfo(uint16_t hdr_nominal_peak_level)
+{
+	struct content_light_level_info {
+		uint16_t max_content_light_level;
+		uint16_t max_pic_average_light_level;
+	};
+	static_assert(sizeof(struct content_light_level_info) == 4,
+		      "May need to adjust struct packing");
+
+	struct content_light_level_info clli;
+	clli.max_content_light_level =
+		__builtin_bswap16(hdr_nominal_peak_level);
+	clli.max_pic_average_light_level =
+		__builtin_bswap16(hdr_nominal_peak_level);
+
+	UInt8 bytes[sizeof(struct content_light_level_info)];
+	memcpy(bytes, &clli, sizeof(bytes));
+	return CFDataCreate(NULL, bytes, sizeof(bytes));
+}
+
 #define STATUS_CHECK(c)                                 \
 	code = c;                                       \
 	if (code) {                                     \
@@ -350,6 +402,22 @@ static OSStatus session_set_colorspace(VTCompressionSessionRef session,
 	SESSION_CHECK(session_set_prop(session,
 				       kVTCompressionPropertyKey_YCbCrMatrix,
 				       obs_to_vt_colorspace(cs)));
+	const bool pq = cs == VIDEO_CS_2100_PQ;
+	const bool hlg = cs == VIDEO_CS_2100_HLG;
+	if (pq || hlg) {
+		const uint16_t hdr_nominal_peak_level =
+			pq ? (uint16_t)obs_get_video_hdr_nominal_peak_level()
+			   : (hlg ? 1000 : 0);
+		SESSION_CHECK(session_set_prop(
+			session,
+			kVTCompressionPropertyKey_MasteringDisplayColorVolume,
+			obs_to_vt_masteringdisplay(hdr_nominal_peak_level)));
+		SESSION_CHECK(session_set_prop(
+			session,
+			kVTCompressionPropertyKey_ContentLightLevelInfo,
+			obs_to_vt_contentlightlevelinfo(
+				hdr_nominal_peak_level)));
+	}
 
 	return noErr;
 }
@@ -1013,15 +1081,31 @@ bool get_cached_pixel_buffer(struct vt_encoder *enc, CVPixelBufferRef *buf)
 	// I would have expected pixel buffers from the session's
 	// pool to have the correct color space stuff set
 
+	const enum video_colorspace cs = enc->colorspace;
 	CVBufferSetAttachment(pixbuf, kCVImageBufferYCbCrMatrixKey,
-			      obs_to_vt_colorspace(enc->colorspace),
+			      obs_to_vt_colorspace(cs),
 			      kCVAttachmentMode_ShouldPropagate);
 	CVBufferSetAttachment(pixbuf, kCVImageBufferColorPrimariesKey,
-			      obs_to_vt_primaries(enc->colorspace),
+			      obs_to_vt_primaries(cs),
 			      kCVAttachmentMode_ShouldPropagate);
 	CVBufferSetAttachment(pixbuf, kCVImageBufferTransferFunctionKey,
-			      obs_to_vt_transfer(enc->colorspace),
+			      obs_to_vt_transfer(cs),
 			      kCVAttachmentMode_ShouldPropagate);
+	const bool pq = cs == VIDEO_CS_2100_PQ;
+	const bool hlg = cs == VIDEO_CS_2100_HLG;
+	if (pq || hlg) {
+		const uint16_t hdr_nominal_peak_level =
+			pq ? (uint16_t)obs_get_video_hdr_nominal_peak_level()
+			   : (hlg ? 1000 : 0);
+		CVBufferSetAttachment(
+			pixbuf, kCVImageBufferMasteringDisplayColorVolumeKey,
+			obs_to_vt_masteringdisplay(hdr_nominal_peak_level),
+			kCVAttachmentMode_ShouldPropagate);
+		CVBufferSetAttachment(
+			pixbuf, kCVImageBufferContentLightLevelInfoKey,
+			obs_to_vt_contentlightlevelinfo(hdr_nominal_peak_level),
+			kCVAttachmentMode_ShouldPropagate);
+	}
 
 	*buf = pixbuf;
 	return true;