/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/video/video-hdr.c
Line | Count | Source |
1 | | /* GStreamer |
2 | | * Copyright (C) <2018-2019> Seungha Yang <seungha.yang@navercorp.com> |
3 | | * |
4 | | * This library is free software; you can redistribute it and/or |
5 | | * modify it under the terms of the GNU Library General Public |
6 | | * License as published by the Free Software Foundation; either |
7 | | * version 2 of the License, or (at your option) any later version. |
8 | | * |
9 | | * This library is distributed in the hope that it will be useful, |
10 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | | * Library General Public License for more details. |
13 | | * |
14 | | * You should have received a copy of the GNU Library General Public |
15 | | * License along with this library; if not, write to the |
16 | | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
17 | | * Boston, MA 02110-1301, USA. |
18 | | */ |
19 | | |
20 | | #ifdef HAVE_CONFIG_H |
21 | | # include "config.h" |
22 | | #endif |
23 | | |
24 | | #include <string.h> |
25 | | #include <gst/base/gstbitreader.h> |
26 | | |
27 | | #include <gst/video/video.h> |
28 | | |
29 | | #include "video-hdr.h" |
30 | | |
31 | 0 | #define HDR10_PLUS_MAX_BEZIER_CURVE_ANCHORS 9 |
32 | 0 | #define HDR10_PLUS_NUM_DISTRIBUTIONS 9 |
33 | | static const guint8 HDR10_PLUS_DISTRIBUTION_INDEX[] = { |
34 | | 1, 5, 10, 25, 50, 75, 90, 95, 99 |
35 | | }; |
36 | | |
37 | | |
38 | 0 | #define N_ELEMENT_MASTERING_DISPLAY_INFO 10 |
39 | | #define MASTERING_FORMAT \ |
40 | 0 | "%d:%d:" \ |
41 | 0 | "%d:%d:" \ |
42 | 0 | "%d:%d:" \ |
43 | 0 | "%d:%d:" \ |
44 | 0 | "%d:%d" |
45 | | |
46 | | #define MASTERING_PRINTF_ARGS(m) \ |
47 | 0 | (m)->display_primaries[0].x, (m)->display_primaries[0].y, \ |
48 | 0 | (m)->display_primaries[1].x, (m)->display_primaries[1].y, \ |
49 | 0 | (m)->display_primaries[2].x, (m)->display_primaries[2].y, \ |
50 | 0 | (m)->white_point.x, (m)->white_point.y, \ |
51 | 0 | (m)->max_display_mastering_luminance, \ |
52 | 0 | (m)->min_display_mastering_luminance |
53 | | |
54 | | /** |
55 | | * gst_video_hdr_format_to_string: |
56 | | * @format: a #GstVideoHDRFormat |
57 | | * |
58 | | * Returns: (nullable): a string containing a descriptive name for |
59 | | * the #GstVideoHDRFormat if there is one, or %NULL otherwise. |
60 | | * |
61 | | * Since: 1.30 |
62 | | */ |
63 | | const gchar * |
64 | | gst_video_hdr_format_to_string (GstVideoHDRFormat format) |
65 | 0 | { |
66 | 0 | switch (format) { |
67 | 0 | case GST_VIDEO_HDR_FORMAT_HDR10: |
68 | 0 | return "hdr10"; |
69 | 0 | case GST_VIDEO_HDR_FORMAT_HDR10_PLUS: |
70 | 0 | return "hdr10+"; |
71 | 0 | default: |
72 | 0 | return NULL; |
73 | 0 | } |
74 | 0 | } |
75 | | |
76 | | /** |
77 | | * gst_video_hdr_format_from_string: |
78 | | * @format: (nullable): a #GstVideoHDRFormat |
79 | | * |
80 | | * Returns: the #GstVideoHDRFormat for @format or GST_VIDEO_HDR_FORMAT_NONE when the |
81 | | * string is not a known format. |
82 | | * |
83 | | * Since: 1.30 |
84 | | */ |
85 | | GstVideoHDRFormat |
86 | | gst_video_hdr_format_from_string (const gchar * format) |
87 | 0 | { |
88 | 0 | if (!g_strcmp0 (format, "hdr10")) |
89 | 0 | return GST_VIDEO_HDR_FORMAT_HDR10; |
90 | 0 | else if (!g_strcmp0 (format, "hdr10+")) |
91 | 0 | return GST_VIDEO_HDR_FORMAT_HDR10_PLUS; |
92 | | |
93 | 0 | return GST_VIDEO_HDR_FORMAT_NONE; |
94 | 0 | } |
95 | | |
96 | | /** |
97 | | * gst_video_mastering_display_info_init: |
98 | | * @minfo: a #GstVideoMasteringDisplayInfo |
99 | | * |
100 | | * Initialize @minfo |
101 | | * |
102 | | * Since: 1.18 |
103 | | */ |
104 | | void |
105 | | gst_video_mastering_display_info_init (GstVideoMasteringDisplayInfo * minfo) |
106 | 0 | { |
107 | 0 | g_return_if_fail (minfo != NULL); |
108 | | |
109 | 0 | memset (minfo, 0, sizeof (GstVideoMasteringDisplayInfo)); |
110 | 0 | } |
111 | | |
112 | | /** |
113 | | * gst_video_mastering_display_info_from_string: |
114 | | * @minfo: (out): a #GstVideoMasteringDisplayInfo |
115 | | * @mastering: a #GstStructure representing #GstVideoMasteringDisplayInfo |
116 | | * |
117 | | * Extract #GstVideoMasteringDisplayInfo from @mastering |
118 | | * |
119 | | * Returns: %TRUE if @minfo was filled with @mastering |
120 | | * |
121 | | * Since: 1.18 |
122 | | */ |
123 | | gboolean |
124 | | gst_video_mastering_display_info_from_string (GstVideoMasteringDisplayInfo * |
125 | | minfo, const gchar * mastering) |
126 | 0 | { |
127 | 0 | gboolean ret = FALSE; |
128 | 0 | gchar **split; |
129 | 0 | gint i; |
130 | 0 | gint idx = 0; |
131 | 0 | guint64 val; |
132 | |
|
133 | 0 | g_return_val_if_fail (minfo != NULL, FALSE); |
134 | 0 | g_return_val_if_fail (mastering != NULL, FALSE); |
135 | | |
136 | 0 | split = g_strsplit (mastering, ":", -1); |
137 | |
|
138 | 0 | if (g_strv_length (split) != N_ELEMENT_MASTERING_DISPLAY_INFO) |
139 | 0 | goto out; |
140 | | |
141 | 0 | for (i = 0; i < G_N_ELEMENTS (minfo->display_primaries); i++) { |
142 | 0 | if (!g_ascii_string_to_unsigned (split[idx++], |
143 | 0 | 10, 0, G_MAXUINT16, &val, NULL)) |
144 | 0 | goto out; |
145 | | |
146 | 0 | minfo->display_primaries[i].x = (guint16) val; |
147 | |
|
148 | 0 | if (!g_ascii_string_to_unsigned (split[idx++], |
149 | 0 | 10, 0, G_MAXUINT16, &val, NULL)) |
150 | 0 | goto out; |
151 | | |
152 | 0 | minfo->display_primaries[i].y = (guint16) val; |
153 | 0 | } |
154 | | |
155 | 0 | if (!g_ascii_string_to_unsigned (split[idx++], |
156 | 0 | 10, 0, G_MAXUINT16, &val, NULL)) |
157 | 0 | goto out; |
158 | | |
159 | 0 | minfo->white_point.x = (guint16) val; |
160 | |
|
161 | 0 | if (!g_ascii_string_to_unsigned (split[idx++], |
162 | 0 | 10, 0, G_MAXUINT16, &val, NULL)) |
163 | 0 | goto out; |
164 | | |
165 | 0 | minfo->white_point.y = (guint16) val; |
166 | |
|
167 | 0 | if (!g_ascii_string_to_unsigned (split[idx++], |
168 | 0 | 10, 0, G_MAXUINT32, &val, NULL)) |
169 | 0 | goto out; |
170 | | |
171 | 0 | minfo->max_display_mastering_luminance = (guint32) val; |
172 | |
|
173 | 0 | if (!g_ascii_string_to_unsigned (split[idx++], |
174 | 0 | 10, 0, G_MAXUINT32, &val, NULL)) |
175 | 0 | goto out; |
176 | | |
177 | 0 | minfo->min_display_mastering_luminance = (guint32) val; |
178 | 0 | ret = TRUE; |
179 | |
|
180 | 0 | out: |
181 | 0 | g_strfreev (split); |
182 | 0 | if (!ret) |
183 | 0 | gst_video_mastering_display_info_init (minfo); |
184 | |
|
185 | 0 | return ret; |
186 | 0 | } |
187 | | |
188 | | /** |
189 | | * gst_video_mastering_display_info_to_string: |
190 | | * @minfo: a #GstVideoMasteringDisplayInfo |
191 | | * |
192 | | * Convert @minfo to its string representation |
193 | | * |
194 | | * Returns: (transfer full): a string representation of @minfo |
195 | | * |
196 | | * Since: 1.18 |
197 | | */ |
198 | | gchar * |
199 | | gst_video_mastering_display_info_to_string (const GstVideoMasteringDisplayInfo * |
200 | | minfo) |
201 | 0 | { |
202 | 0 | g_return_val_if_fail (minfo != NULL, NULL); |
203 | | |
204 | 0 | return g_strdup_printf (MASTERING_FORMAT, MASTERING_PRINTF_ARGS (minfo)); |
205 | 0 | } |
206 | | |
207 | | /** |
208 | | * gst_video_mastering_display_info_is_equal: |
209 | | * @minfo: a #GstVideoMasteringDisplayInfo |
210 | | * @other: a #GstVideoMasteringDisplayInfo |
211 | | * |
212 | | * Checks equality between @minfo and @other. |
213 | | * |
214 | | * Returns: %TRUE if @minfo and @other are equal. |
215 | | * |
216 | | * Since: 1.18 |
217 | | */ |
218 | | gboolean |
219 | | gst_video_mastering_display_info_is_equal (const GstVideoMasteringDisplayInfo * |
220 | | minfo, const GstVideoMasteringDisplayInfo * other) |
221 | 0 | { |
222 | 0 | gint i; |
223 | |
|
224 | 0 | g_return_val_if_fail (minfo != NULL, FALSE); |
225 | 0 | g_return_val_if_fail (other != NULL, FALSE); |
226 | | |
227 | 0 | for (i = 0; i < G_N_ELEMENTS (minfo->display_primaries); i++) { |
228 | 0 | if (minfo->display_primaries[i].x != other->display_primaries[i].x || |
229 | 0 | minfo->display_primaries[i].y != other->display_primaries[i].y) |
230 | 0 | return FALSE; |
231 | 0 | } |
232 | | |
233 | 0 | if (minfo->white_point.x != other->white_point.x || |
234 | 0 | minfo->white_point.y != other->white_point.y || |
235 | 0 | minfo->max_display_mastering_luminance != |
236 | 0 | other->max_display_mastering_luminance |
237 | 0 | || minfo->min_display_mastering_luminance != |
238 | 0 | other->min_display_mastering_luminance) |
239 | 0 | return FALSE; |
240 | | |
241 | 0 | return TRUE; |
242 | 0 | } |
243 | | |
244 | | /** |
245 | | * gst_video_mastering_display_info_from_caps: |
246 | | * @minfo: a #GstVideoMasteringDisplayInfo |
247 | | * @caps: a #GstCaps |
248 | | * |
249 | | * Parse @caps and update @minfo |
250 | | * |
251 | | * Returns: %TRUE if @caps has #GstVideoMasteringDisplayInfo and could be parsed |
252 | | * |
253 | | * Since: 1.18 |
254 | | */ |
255 | | gboolean |
256 | | gst_video_mastering_display_info_from_caps (GstVideoMasteringDisplayInfo * |
257 | | minfo, const GstCaps * caps) |
258 | 0 | { |
259 | 0 | GstStructure *structure; |
260 | 0 | const gchar *s; |
261 | |
|
262 | 0 | g_return_val_if_fail (minfo != NULL, FALSE); |
263 | 0 | g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); |
264 | | |
265 | 0 | structure = gst_caps_get_structure (caps, 0); |
266 | |
|
267 | 0 | if ((s = gst_structure_get_string (structure, |
268 | 0 | "mastering-display-info")) == NULL) |
269 | 0 | return FALSE; |
270 | | |
271 | 0 | return gst_video_mastering_display_info_from_string (minfo, s); |
272 | 0 | } |
273 | | |
274 | | /** |
275 | | * gst_video_mastering_display_info_add_to_caps: |
276 | | * @minfo: a #GstVideoMasteringDisplayInfo |
277 | | * @caps: a #GstCaps |
278 | | * |
279 | | * Set string representation of @minfo to @caps |
280 | | * |
281 | | * Returns: %TRUE if @minfo was successfully set to @caps |
282 | | * |
283 | | * Since: 1.18 |
284 | | */ |
285 | | gboolean |
286 | | gst_video_mastering_display_info_add_to_caps (const GstVideoMasteringDisplayInfo |
287 | | * minfo, GstCaps * caps) |
288 | 0 | { |
289 | 0 | gchar *s; |
290 | |
|
291 | 0 | g_return_val_if_fail (minfo != NULL, FALSE); |
292 | 0 | g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); |
293 | 0 | g_return_val_if_fail (gst_caps_is_writable (caps), FALSE); |
294 | | |
295 | 0 | s = gst_video_mastering_display_info_to_string (minfo); |
296 | 0 | if (!s) |
297 | 0 | return FALSE; |
298 | | |
299 | 0 | gst_caps_set_simple (caps, "mastering-display-info", G_TYPE_STRING, s, NULL); |
300 | 0 | g_free (s); |
301 | |
|
302 | 0 | return TRUE; |
303 | 0 | } |
304 | | |
305 | | /** |
306 | | * gst_video_content_light_level_init: |
307 | | * @linfo: a #GstVideoContentLightLevel |
308 | | * |
309 | | * Initialize @linfo |
310 | | * |
311 | | * Since: 1.18 |
312 | | */ |
313 | | void |
314 | | gst_video_content_light_level_init (GstVideoContentLightLevel * linfo) |
315 | 0 | { |
316 | 0 | g_return_if_fail (linfo != NULL); |
317 | | |
318 | 0 | memset (linfo, 0, sizeof (GstVideoContentLightLevel)); |
319 | 0 | } |
320 | | |
321 | | /** |
322 | | * gst_video_content_light_level_from_string: |
323 | | * @linfo: a #GstVideoContentLightLevel |
324 | | * @level: a content-light-level string from caps |
325 | | * |
326 | | * Parse the value of content-light-level caps field and update @minfo |
327 | | * with the parsed values. |
328 | | * |
329 | | * Returns: %TRUE if @linfo points to valid #GstVideoContentLightLevel. |
330 | | * |
331 | | * Since: 1.18 |
332 | | */ |
333 | | gboolean |
334 | | gst_video_content_light_level_from_string (GstVideoContentLightLevel * linfo, |
335 | | const gchar * level) |
336 | 0 | { |
337 | 0 | gboolean ret = FALSE; |
338 | 0 | gchar **split; |
339 | 0 | guint64 val; |
340 | |
|
341 | 0 | g_return_val_if_fail (linfo != NULL, FALSE); |
342 | 0 | g_return_val_if_fail (level != NULL, FALSE); |
343 | | |
344 | 0 | split = g_strsplit (level, ":", -1); |
345 | |
|
346 | 0 | if (g_strv_length (split) != 2) |
347 | 0 | goto out; |
348 | | |
349 | 0 | if (!g_ascii_string_to_unsigned (split[0], 10, 0, G_MAXUINT16, &val, NULL)) |
350 | 0 | goto out; |
351 | | |
352 | 0 | linfo->max_content_light_level = (guint16) val; |
353 | |
|
354 | 0 | if (!g_ascii_string_to_unsigned (split[1], 10, 0, G_MAXUINT16, &val, NULL)) |
355 | 0 | goto out; |
356 | | |
357 | 0 | linfo->max_frame_average_light_level = (guint16) val; |
358 | |
|
359 | 0 | ret = TRUE; |
360 | |
|
361 | 0 | out: |
362 | 0 | g_strfreev (split); |
363 | 0 | if (!ret) |
364 | 0 | gst_video_content_light_level_init (linfo); |
365 | |
|
366 | 0 | return ret; |
367 | 0 | } |
368 | | |
369 | | /** |
370 | | * gst_video_content_light_level_to_string: |
371 | | * @linfo: a #GstVideoContentLightLevel |
372 | | * |
373 | | * Convert @linfo to its string representation. |
374 | | * |
375 | | * Returns: (transfer full): a string representation of @linfo. |
376 | | * |
377 | | * Since: 1.18 |
378 | | */ |
379 | | gchar * |
380 | | gst_video_content_light_level_to_string (const GstVideoContentLightLevel * |
381 | | linfo) |
382 | 0 | { |
383 | 0 | g_return_val_if_fail (linfo != NULL, NULL); |
384 | | |
385 | 0 | return g_strdup_printf ("%d:%d", |
386 | 0 | linfo->max_content_light_level, linfo->max_frame_average_light_level); |
387 | 0 | } |
388 | | |
389 | | /** |
390 | | * gst_video_content_light_level_is_equal: |
391 | | * @linfo: a #GstVideoContentLightLevel |
392 | | * @other: a #GstVideoContentLightLevel |
393 | | * |
394 | | * Checks equality between @linfo and @other. |
395 | | * |
396 | | * Returns: %TRUE if @linfo and @other are equal. |
397 | | * |
398 | | * Since: 1.20 |
399 | | */ |
400 | | gboolean |
401 | | gst_video_content_light_level_is_equal (const GstVideoContentLightLevel * linfo, |
402 | | const GstVideoContentLightLevel * other) |
403 | 0 | { |
404 | 0 | g_return_val_if_fail (linfo != NULL, FALSE); |
405 | 0 | g_return_val_if_fail (other != NULL, FALSE); |
406 | | |
407 | 0 | return (linfo->max_content_light_level == other->max_content_light_level && |
408 | 0 | linfo->max_frame_average_light_level == |
409 | 0 | other->max_frame_average_light_level); |
410 | 0 | } |
411 | | |
412 | | /** |
413 | | * gst_video_content_light_level_from_caps: |
414 | | * @linfo: a #GstVideoContentLightLevel |
415 | | * @caps: a #GstCaps |
416 | | * |
417 | | * Parse @caps and update @linfo |
418 | | * |
419 | | * Returns: if @caps has #GstVideoContentLightLevel and could be parsed |
420 | | * |
421 | | * Since: 1.18 |
422 | | */ |
423 | | gboolean |
424 | | gst_video_content_light_level_from_caps (GstVideoContentLightLevel * linfo, |
425 | | const GstCaps * caps) |
426 | 0 | { |
427 | 0 | GstStructure *structure; |
428 | 0 | const gchar *s; |
429 | |
|
430 | 0 | g_return_val_if_fail (linfo != NULL, FALSE); |
431 | 0 | g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); |
432 | | |
433 | 0 | structure = gst_caps_get_structure (caps, 0); |
434 | |
|
435 | 0 | if ((s = gst_structure_get_string (structure, "content-light-level")) == NULL) |
436 | 0 | return FALSE; |
437 | | |
438 | 0 | return gst_video_content_light_level_from_string (linfo, s); |
439 | 0 | } |
440 | | |
441 | | /** |
442 | | * gst_video_content_light_level_add_to_caps: |
443 | | * @linfo: a #GstVideoContentLightLevel |
444 | | * @caps: a #GstCaps |
445 | | * |
446 | | * Parse @caps and update @linfo |
447 | | * |
448 | | * Returns: %TRUE if @linfo was successfully set to @caps |
449 | | * |
450 | | * Since: 1.18 |
451 | | */ |
452 | | gboolean |
453 | | gst_video_content_light_level_add_to_caps (const GstVideoContentLightLevel * |
454 | | linfo, GstCaps * caps) |
455 | 0 | { |
456 | 0 | gchar *s; |
457 | |
|
458 | 0 | g_return_val_if_fail (linfo != NULL, FALSE); |
459 | 0 | g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); |
460 | 0 | g_return_val_if_fail (gst_caps_is_writable (caps), FALSE); |
461 | | |
462 | 0 | s = gst_video_content_light_level_to_string (linfo); |
463 | 0 | gst_caps_set_simple (caps, "content-light-level", G_TYPE_STRING, s, NULL); |
464 | 0 | g_free (s); |
465 | |
|
466 | 0 | return TRUE; |
467 | 0 | } |
468 | | |
469 | | /* Dynamic HDR Meta implementation */ |
470 | | |
471 | | GType |
472 | | gst_video_hdr_meta_api_get_type (void) |
473 | 0 | { |
474 | 0 | static GType type = 0; |
475 | |
|
476 | 0 | if (g_once_init_enter (&type)) { |
477 | 0 | static const gchar *tags[] = { |
478 | 0 | GST_META_TAG_VIDEO_STR, |
479 | 0 | NULL |
480 | 0 | }; |
481 | 0 | GType _type = gst_meta_api_type_register ("GstVideoHDRMetaAPI", tags); |
482 | 0 | g_once_init_leave (&type, _type); |
483 | 0 | } |
484 | 0 | return type; |
485 | 0 | } |
486 | | |
487 | | static gboolean |
488 | | gst_video_hdr_meta_transform (GstBuffer * dest, GstMeta * meta, |
489 | | GstBuffer * buffer, GQuark type, gpointer data) |
490 | 0 | { |
491 | 0 | GstVideoHDRMeta *dmeta, *smeta; |
492 | | |
493 | | /* We always copy over the HDR meta */ |
494 | 0 | smeta = (GstVideoHDRMeta *) meta; |
495 | |
|
496 | 0 | GST_DEBUG ("copy HDR metadata"); |
497 | 0 | dmeta = |
498 | 0 | gst_buffer_add_video_hdr_meta (dest, smeta->format, smeta->data, |
499 | 0 | smeta->size); |
500 | 0 | if (!dmeta) |
501 | 0 | return FALSE; |
502 | | |
503 | 0 | return TRUE; |
504 | 0 | } |
505 | | |
506 | | static gboolean |
507 | | gst_video_hdr_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer) |
508 | 0 | { |
509 | 0 | GstVideoHDRMeta *emeta = (GstVideoHDRMeta *) meta; |
510 | |
|
511 | 0 | emeta->data = NULL; |
512 | |
|
513 | 0 | return TRUE; |
514 | 0 | } |
515 | | |
516 | | static void |
517 | | gst_video_hdr_meta_free (GstMeta * meta, GstBuffer * buffer) |
518 | 0 | { |
519 | 0 | GstVideoHDRMeta *emeta = (GstVideoHDRMeta *) meta; |
520 | |
|
521 | 0 | g_free (emeta->data); |
522 | 0 | } |
523 | | |
524 | | const GstMetaInfo * |
525 | | gst_video_hdr_meta_get_info (void) |
526 | 0 | { |
527 | 0 | static const GstMetaInfo *meta_info = NULL; |
528 | |
|
529 | 0 | if (g_once_init_enter ((GstMetaInfo **) & meta_info)) { |
530 | 0 | const GstMetaInfo *mi = gst_meta_register (GST_VIDEO_HDR_META_API_TYPE, |
531 | 0 | "GstVideoHDRMeta", |
532 | 0 | sizeof (GstVideoHDRMeta), |
533 | 0 | gst_video_hdr_meta_init, |
534 | 0 | gst_video_hdr_meta_free, |
535 | 0 | gst_video_hdr_meta_transform); |
536 | 0 | g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) mi); |
537 | 0 | } |
538 | 0 | return meta_info; |
539 | 0 | } |
540 | | |
541 | | /** |
542 | | * gst_buffer_add_video_hdr_meta: |
543 | | * @buffer: a #GstBuffer |
544 | | * @format: The type of dynamic HDR contained in the meta. |
545 | | * @data: contains the dynamic HDR data |
546 | | * @size: The size in bytes of @data |
547 | | * |
548 | | * Attaches #GstVideoHDRMeta metadata to @buffer with the given |
549 | | * parameters. |
550 | | * |
551 | | * Returns: (transfer none): the #GstVideoHDRMeta on @buffer. |
552 | | * |
553 | | * Since: 1.30 |
554 | | */ |
555 | | GstVideoHDRMeta * |
556 | | gst_buffer_add_video_hdr_meta (GstBuffer * buffer, |
557 | | GstVideoHDRFormat format, const guint8 * data, gsize size) |
558 | 0 | { |
559 | 0 | GstVideoHDRMeta *meta; |
560 | |
|
561 | 0 | g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); |
562 | 0 | g_return_val_if_fail (data != NULL, NULL); |
563 | | |
564 | 0 | meta = (GstVideoHDRMeta *) gst_buffer_add_meta (buffer, |
565 | 0 | GST_VIDEO_HDR_META_INFO, NULL); |
566 | 0 | g_assert (meta != NULL); |
567 | | |
568 | 0 | meta->format = format; |
569 | 0 | meta->data = g_memdup2 (data, size); |
570 | 0 | meta->size = size; |
571 | |
|
572 | 0 | return meta; |
573 | 0 | } |
574 | | |
575 | 0 | #define CHECK_HDR10PLUS_REMAINING(br, needed) \ |
576 | 0 | if (gst_bit_reader_get_remaining (&br) < needed) { \ |
577 | 0 | GST_DEBUG ("Not enough bits remaining %d, needed %d", gst_bit_reader_get_remaining (&br), needed); \ |
578 | 0 | return FALSE; \ |
579 | 0 | } |
580 | | |
581 | | /** |
582 | | * gst_video_hdr_parse_hdr10_plus: |
583 | | * @data: HDR10+ data |
584 | | * @size: size of data |
585 | | * @hdr10_plus: (out): #GstVideoHDR10Plus structure to fill in. |
586 | | * |
587 | | * Parse HDR10+ (SMPTE2094-40) user data and store in @hdr10_plus |
588 | | * For more details, see: |
589 | | * https://www.atsc.org/wp-content/uploads/2018/02/S34-301r2-A341-Amendment-2094-40-1.pdf |
590 | | * and SMPTE ST2094-40 |
591 | | * |
592 | | * Returns: %TRUE if @data was successfully parsed to @hdr10_plus |
593 | | * |
594 | | * Since: 1.30 |
595 | | */ |
596 | | gboolean |
597 | | gst_video_hdr_parse_hdr10_plus (const guint8 * data, gsize size, |
598 | | GstVideoHDR10Plus * hdr10_plus) |
599 | 0 | { |
600 | 0 | guint16 provider_oriented_code; |
601 | 0 | int w, i, j; |
602 | 0 | GstBitReader br; |
603 | | |
604 | | /* there must be at least one byte, and not more than GST_VIDEO_HDR10_PLUS_MAX_BYTES bytes */ |
605 | 0 | g_return_val_if_fail (data != NULL, FALSE); |
606 | | |
607 | 0 | memset (hdr10_plus, 0, sizeof (GstVideoHDR10Plus)); |
608 | 0 | gst_bit_reader_init (&br, data, size); |
609 | 0 | GST_MEMDUMP ("HDR10+", data, size); |
610 | 0 | CHECK_HDR10PLUS_REMAINING (br, 16 + 8 + 8 + 2); |
611 | 0 | provider_oriented_code = gst_bit_reader_get_bits_uint16_unchecked (&br, 16); |
612 | 0 | if (provider_oriented_code != 0x0001) |
613 | 0 | return FALSE; |
614 | | |
615 | | |
616 | 0 | hdr10_plus->application_identifier = |
617 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 8); |
618 | 0 | hdr10_plus->application_version = |
619 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 8); |
620 | 0 | hdr10_plus->num_windows = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); |
621 | 0 | if (hdr10_plus->application_version == 1 && |
622 | 0 | hdr10_plus->num_windows != GST_VIDEO_HDR10_PLUS_NUM_WINDOWS) |
623 | 0 | return FALSE; |
624 | 0 | if (hdr10_plus->application_version == 0) { |
625 | 0 | for (w = 0; w < hdr10_plus->num_windows; w++) { |
626 | 0 | CHECK_HDR10PLUS_REMAINING (br, |
627 | 0 | 16 + 16 + 16 + 16 + 16 + 16 + 8 + 16 + 16 + 16 + 1); |
628 | 0 | hdr10_plus->processing_window[w].window_upper_left_corner_x = |
629 | 0 | gst_bit_reader_get_bits_uint16_unchecked (&br, 16); |
630 | 0 | hdr10_plus->processing_window[w].window_upper_left_corner_y = |
631 | 0 | gst_bit_reader_get_bits_uint16_unchecked (&br, 16); |
632 | 0 | hdr10_plus->processing_window[w].window_lower_right_corner_x = |
633 | 0 | gst_bit_reader_get_bits_uint16_unchecked (&br, 16); |
634 | 0 | hdr10_plus->processing_window[w].window_lower_right_corner_y = |
635 | 0 | gst_bit_reader_get_bits_uint16_unchecked (&br, 16); |
636 | 0 | hdr10_plus->processing_window[w].center_of_ellipse_x = |
637 | 0 | gst_bit_reader_get_bits_uint16_unchecked (&br, 16); |
638 | 0 | hdr10_plus->processing_window[w].center_of_ellipse_y = |
639 | 0 | gst_bit_reader_get_bits_uint16_unchecked (&br, 16); |
640 | 0 | hdr10_plus->processing_window[w].rotation_angle = |
641 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 8); |
642 | 0 | hdr10_plus->processing_window[w].semimajor_axis_internal_ellipse = |
643 | 0 | gst_bit_reader_get_bits_uint16_unchecked (&br, 16); |
644 | 0 | hdr10_plus->processing_window[w].semimajor_axis_external_ellipse = |
645 | 0 | gst_bit_reader_get_bits_uint16_unchecked (&br, 16); |
646 | 0 | hdr10_plus->processing_window[w].semiminor_axis_external_ellipse = |
647 | 0 | gst_bit_reader_get_bits_uint16_unchecked (&br, 16); |
648 | 0 | hdr10_plus->processing_window[w].overlap_process_option = |
649 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 1); |
650 | 0 | } |
651 | 0 | } |
652 | 0 | CHECK_HDR10PLUS_REMAINING (br, 27 + 1); |
653 | 0 | hdr10_plus->targeted_system_display_maximum_luminance = |
654 | 0 | gst_bit_reader_get_bits_uint32_unchecked (&br, 27); |
655 | 0 | hdr10_plus->targeted_system_display_actual_peak_luminance_flag = |
656 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 1); |
657 | 0 | if (hdr10_plus->targeted_system_display_actual_peak_luminance_flag) { |
658 | 0 | if (hdr10_plus->application_version == 1) |
659 | 0 | return FALSE; |
660 | 0 | CHECK_HDR10PLUS_REMAINING (br, 5 + 5); |
661 | 0 | hdr10_plus->num_rows_targeted_system_display_actual_peak_luminance = |
662 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 5); |
663 | 0 | hdr10_plus->num_cols_targeted_system_display_actual_peak_luminance = |
664 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 5); |
665 | 0 | if (hdr10_plus->num_rows_targeted_system_display_actual_peak_luminance > |
666 | 0 | GST_VIDEO_HDR10_PLUS_MAX_ROWS_TSD_APL) |
667 | 0 | return FALSE; |
668 | 0 | if (hdr10_plus->num_cols_targeted_system_display_actual_peak_luminance > |
669 | 0 | GST_VIDEO_HDR10_PLUS_MAX_COLS_MD_APL) |
670 | 0 | return FALSE; |
671 | 0 | CHECK_HDR10PLUS_REMAINING (br, |
672 | 0 | hdr10_plus->num_rows_targeted_system_display_actual_peak_luminance * |
673 | 0 | hdr10_plus->num_cols_targeted_system_display_actual_peak_luminance * 4); |
674 | 0 | for (i = 0; |
675 | 0 | i < hdr10_plus->num_rows_targeted_system_display_actual_peak_luminance; |
676 | 0 | i++) { |
677 | 0 | for (j = 0; |
678 | 0 | j < |
679 | 0 | hdr10_plus->num_cols_targeted_system_display_actual_peak_luminance; |
680 | 0 | j++) |
681 | 0 | hdr10_plus->targeted_system_display_actual_peak_luminance[i][j] = |
682 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 4); |
683 | 0 | } |
684 | 0 | } |
685 | 0 | for (w = 0; w < hdr10_plus->num_windows; w++) { |
686 | 0 | CHECK_HDR10PLUS_REMAINING (br, (17 * 3)); |
687 | 0 | for (i = 0; i < 3; i++) |
688 | 0 | hdr10_plus->processing_window[w].maxscl[i] = |
689 | 0 | gst_bit_reader_get_bits_uint32_unchecked (&br, 17); |
690 | 0 | CHECK_HDR10PLUS_REMAINING (br, 17 + 4); |
691 | 0 | hdr10_plus->processing_window[w].average_maxrgb = |
692 | 0 | gst_bit_reader_get_bits_uint32_unchecked (&br, 17); |
693 | 0 | hdr10_plus->processing_window[w].num_distributions = |
694 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 4); |
695 | 0 | if (hdr10_plus->processing_window[w].num_distributions != |
696 | 0 | HDR10_PLUS_NUM_DISTRIBUTIONS) |
697 | 0 | return FALSE; |
698 | 0 | CHECK_HDR10PLUS_REMAINING (br, |
699 | 0 | hdr10_plus->processing_window[w].num_distributions * (17 + 7)); |
700 | 0 | for (i = 0; i < hdr10_plus->processing_window[w].num_distributions; i++) { |
701 | 0 | hdr10_plus->processing_window[w].distribution_index[i] = |
702 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 7); |
703 | 0 | if (hdr10_plus->processing_window[w].distribution_index[i] != |
704 | 0 | HDR10_PLUS_DISTRIBUTION_INDEX[i]) |
705 | 0 | return FALSE; |
706 | 0 | hdr10_plus->processing_window[w].distribution_values[i] = |
707 | 0 | gst_bit_reader_get_bits_uint32_unchecked (&br, 17); |
708 | 0 | } |
709 | 0 | CHECK_HDR10PLUS_REMAINING (br, 10); |
710 | 0 | hdr10_plus->processing_window[w].fraction_bright_pixels = |
711 | 0 | gst_bit_reader_get_bits_uint16_unchecked (&br, 10); |
712 | 0 | if (hdr10_plus->application_version == 1 && |
713 | 0 | hdr10_plus->processing_window[w].fraction_bright_pixels != 0) |
714 | 0 | return FALSE; |
715 | 0 | } |
716 | 0 | CHECK_HDR10PLUS_REMAINING (br, 1); |
717 | 0 | hdr10_plus->mastering_display_actual_peak_luminance_flag = |
718 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 1); |
719 | 0 | if (hdr10_plus->mastering_display_actual_peak_luminance_flag) { |
720 | 0 | if (hdr10_plus->application_version == 1) |
721 | 0 | return FALSE; |
722 | 0 | CHECK_HDR10PLUS_REMAINING (br, 5 + 5); |
723 | 0 | hdr10_plus->num_rows_mastering_display_actual_peak_luminance = |
724 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 5); |
725 | 0 | hdr10_plus->num_cols_mastering_display_actual_peak_luminance = |
726 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 5); |
727 | 0 | if (hdr10_plus->num_rows_mastering_display_actual_peak_luminance > |
728 | 0 | GST_VIDEO_HDR10_PLUS_MAX_ROWS_TSD_APL) |
729 | 0 | return FALSE; |
730 | 0 | if (hdr10_plus->num_cols_mastering_display_actual_peak_luminance > |
731 | 0 | GST_VIDEO_HDR10_PLUS_MAX_COLS_MD_APL) |
732 | 0 | return FALSE; |
733 | 0 | CHECK_HDR10PLUS_REMAINING (br, |
734 | 0 | hdr10_plus->num_rows_mastering_display_actual_peak_luminance * |
735 | 0 | hdr10_plus->num_cols_mastering_display_actual_peak_luminance * 4); |
736 | 0 | for (i = 0; |
737 | 0 | i < hdr10_plus->num_rows_mastering_display_actual_peak_luminance; i++) { |
738 | 0 | for (j = 0; |
739 | 0 | j < hdr10_plus->num_cols_mastering_display_actual_peak_luminance; j++) |
740 | 0 | hdr10_plus->mastering_display_actual_peak_luminance[i][j] = |
741 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 4); |
742 | 0 | } |
743 | 0 | } |
744 | 0 | for (w = 0; w < hdr10_plus->num_windows; w++) { |
745 | 0 | CHECK_HDR10PLUS_REMAINING (br, 1); |
746 | 0 | hdr10_plus->processing_window[w].tone_mapping_flag = |
747 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 1); |
748 | 0 | if (hdr10_plus->processing_window[w].tone_mapping_flag) { |
749 | 0 | CHECK_HDR10PLUS_REMAINING (br, 12 + 12 + 4); |
750 | 0 | hdr10_plus->processing_window[w].knee_point_x = |
751 | 0 | gst_bit_reader_get_bits_uint16_unchecked (&br, 12); |
752 | 0 | hdr10_plus->processing_window[w].knee_point_y = |
753 | 0 | gst_bit_reader_get_bits_uint16_unchecked (&br, 12); |
754 | 0 | hdr10_plus->processing_window[w].num_bezier_curve_anchors = |
755 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 4); |
756 | 0 | if (hdr10_plus->processing_window[w].num_bezier_curve_anchors > |
757 | 0 | HDR10_PLUS_MAX_BEZIER_CURVE_ANCHORS) |
758 | 0 | return FALSE; |
759 | 0 | CHECK_HDR10PLUS_REMAINING (br, |
760 | 0 | 10 * hdr10_plus->processing_window[w].num_bezier_curve_anchors); |
761 | 0 | for (i = 0; |
762 | 0 | i < hdr10_plus->processing_window[w].num_bezier_curve_anchors; i++) |
763 | 0 | hdr10_plus->processing_window[w].bezier_curve_anchors[i] = |
764 | 0 | gst_bit_reader_get_bits_uint16_unchecked (&br, 10); |
765 | 0 | } |
766 | 0 | CHECK_HDR10PLUS_REMAINING (br, 1); |
767 | 0 | hdr10_plus->processing_window[w].color_saturation_mapping_flag = |
768 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 1); |
769 | 0 | if (hdr10_plus->processing_window[w].color_saturation_mapping_flag) { |
770 | 0 | if (hdr10_plus->application_version == 1) |
771 | 0 | return FALSE; |
772 | 0 | CHECK_HDR10PLUS_REMAINING (br, 6); |
773 | 0 | hdr10_plus->processing_window[w].color_saturation_weight = |
774 | 0 | gst_bit_reader_get_bits_uint8_unchecked (&br, 6); |
775 | 0 | } |
776 | 0 | } |
777 | 0 | return TRUE; |
778 | 0 | } |