c099db2084bdb4eb48bc852fc6656757902f4526
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-good / gst / rtp / gstrtphdrext-colorspace.c
1 /* GStreamer
2  * Copyright (C) 2020-2021 Collabora Ltd.
3  *   @author: Jakub Adam <jakub.adam@collabora.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:rtphdrextcolorspace
23  * @title: GstRtphdrext-Colorspace
24  * @short_description: Helper methods for dealing with Color Space RTP header
25  * extension as defined in  http://www.webrtc.org/experiments/rtp-hdrext/color-space
26  * @see_also: #GstRTPHeaderExtension, #GstRTPBasePayload, #GstRTPBaseDepayload
27  *
28  * Since: 1.20
29  */
30
31 #include "gstrtphdrext-colorspace.h"
32
33 #include "gstrtpelements.h"
34
35 #include <gst/base/gstbytereader.h>
36 #include <gst/video/video-color.h>
37 #include <gst/video/video-hdr.h>
38
39 GST_DEBUG_CATEGORY_STATIC (rtphdrext_colorspace_debug);
40 #define GST_CAT_DEFAULT (rtphdrext_colorspace_debug)
41
42 /**
43  * GstRTPHeaderExtensionColorspace:
44  * @parent: the parent #GstRTPHeaderExtension
45  *
46  * Instance struct for Color Space RTP header extension.
47  *
48  * http://www.webrtc.org/experiments/rtp-hdrext/color-space
49  */
50 struct _GstRTPHeaderExtensionColorspace
51 {
52   GstRTPHeaderExtension parent;
53
54   GstVideoColorimetry colorimetry;
55   GstVideoChromaSite chroma_site;
56   GstVideoMasteringDisplayInfo mdi;
57   GstVideoContentLightLevel cll;
58   gboolean has_hdr_meta;
59 };
60
61 G_DEFINE_TYPE_WITH_CODE (GstRTPHeaderExtensionColorspace,
62     gst_rtp_header_extension_colorspace, GST_TYPE_RTP_HEADER_EXTENSION,
63     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "rtphdrextcolorspace", 0,
64         "RTP Color Space Header Extension");
65     );
66 GST_ELEMENT_REGISTER_DEFINE (rtphdrextcolorspace, "rtphdrextcolorspace",
67     GST_RANK_MARGINAL, GST_TYPE_RTP_HEADER_EXTENSION_COLORSPACE);
68
69 static void
70 gst_rtp_header_extension_colorspace_init (GstRTPHeaderExtensionColorspace *
71     self)
72 {
73 }
74
75 static GstRTPHeaderExtensionFlags
76 gst_rtp_header_extension_colorspace_get_supported_flags (GstRTPHeaderExtension *
77     ext)
78 {
79   GstRTPHeaderExtensionColorspace *self =
80       GST_RTP_HEADER_EXTENSION_COLORSPACE (ext);
81
82   return self->has_hdr_meta ?
83       GST_RTP_HEADER_EXTENSION_TWO_BYTE : GST_RTP_HEADER_EXTENSION_ONE_BYTE;
84 }
85
86 static gsize
87 gst_rtp_header_extension_colorspace_get_max_size (GstRTPHeaderExtension * ext,
88     const GstBuffer * buffer)
89 {
90   GstRTPHeaderExtensionColorspace *self =
91       GST_RTP_HEADER_EXTENSION_COLORSPACE (ext);
92
93   return self->has_hdr_meta ?
94       GST_RTP_HDREXT_COLORSPACE_WITH_HDR_META_SIZE :
95       GST_RTP_HDREXT_COLORSPACE_SIZE;
96 }
97
98 static gssize
99 gst_rtp_header_extension_colorspace_write (GstRTPHeaderExtension * ext,
100     const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags,
101     GstBuffer * output, guint8 * data, gsize size)
102 {
103   GstRTPHeaderExtensionColorspace *self =
104       GST_RTP_HEADER_EXTENSION_COLORSPACE (ext);
105   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
106   gboolean is_frame_last_buffer;
107   guint8 *ptr = data;
108   guint8 horizontal_site;
109   guint8 vertical_site;
110
111   g_return_val_if_fail (size >=
112       gst_rtp_header_extension_colorspace_get_max_size (ext, NULL), -1);
113   g_return_val_if_fail (write_flags &
114       gst_rtp_header_extension_colorspace_get_supported_flags (ext), -1);
115
116   if (self->colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN &&
117       self->colorimetry.primaries == GST_VIDEO_COLOR_PRIMARIES_UNKNOWN &&
118       self->colorimetry.range == GST_VIDEO_COLOR_RANGE_UNKNOWN &&
119       self->colorimetry.transfer == GST_VIDEO_TRANSFER_UNKNOWN) {
120     /* Nothing to write. */
121     return 0;
122   }
123
124   gst_rtp_buffer_map (output, GST_MAP_READ, &rtp);
125   is_frame_last_buffer = gst_rtp_buffer_get_marker (&rtp);
126   gst_rtp_buffer_unmap (&rtp);
127
128   if (!is_frame_last_buffer) {
129     /* Only a video frame's final packet should carry color space info. */
130     return 0;
131   }
132
133   *ptr++ = gst_video_color_primaries_to_iso (self->colorimetry.primaries);
134   *ptr++ = gst_video_transfer_function_to_iso (self->colorimetry.transfer);
135   *ptr++ = gst_video_color_matrix_to_iso (self->colorimetry.matrix);
136
137   if (self->chroma_site & GST_VIDEO_CHROMA_SITE_H_COSITED) {
138     horizontal_site = 1;
139   } else if (self->chroma_site & GST_VIDEO_CHROMA_SITE_NONE) {
140     horizontal_site = 2;
141   } else {
142     horizontal_site = 0;
143   }
144
145   if (self->chroma_site & GST_VIDEO_CHROMA_SITE_V_COSITED) {
146     vertical_site = 1;
147   } else if (self->chroma_site & GST_VIDEO_CHROMA_SITE_NONE) {
148     vertical_site = 2;
149   } else {
150     vertical_site = 0;
151   }
152
153   *ptr++ =
154       (self->colorimetry.range << 4) + (horizontal_site << 2) + vertical_site;
155
156   if (self->has_hdr_meta) {
157     guint i;
158
159     GST_WRITE_UINT16_BE (ptr,
160         self->mdi.max_display_mastering_luminance / 10000);
161     ptr += 2;
162     GST_WRITE_UINT16_BE (ptr, self->mdi.min_display_mastering_luminance);
163     ptr += 2;
164
165     for (i = 0; i < 3; ++i) {
166       GST_WRITE_UINT16_BE (ptr, self->mdi.display_primaries[i].x);
167       ptr += 2;
168       GST_WRITE_UINT16_BE (ptr, self->mdi.display_primaries[i].y);
169       ptr += 2;
170     }
171
172     GST_WRITE_UINT16_BE (ptr, self->mdi.white_point.x);
173     ptr += 2;
174     GST_WRITE_UINT16_BE (ptr, self->mdi.white_point.y);
175     ptr += 2;
176
177     GST_WRITE_UINT16_BE (ptr, self->cll.max_content_light_level);
178     ptr += 2;
179     GST_WRITE_UINT16_BE (ptr, self->cll.max_frame_average_light_level);
180     ptr += 2;
181   }
182
183   return ptr - data;
184 }
185
186 static gboolean
187 parse_colorspace (GstByteReader * reader, GstVideoColorimetry * colorimetry,
188     GstVideoChromaSite * chroma_site)
189 {
190   guint8 val;
191
192   g_return_val_if_fail (reader != NULL, FALSE);
193   g_return_val_if_fail (colorimetry != NULL, FALSE);
194   g_return_val_if_fail (chroma_site != NULL, FALSE);
195
196   if (gst_byte_reader_get_remaining (reader) < GST_RTP_HDREXT_COLORSPACE_SIZE) {
197     return FALSE;
198   }
199
200   if (!gst_byte_reader_get_uint8 (reader, &val)) {
201     return FALSE;
202   }
203   colorimetry->primaries = gst_video_color_primaries_from_iso (val);
204
205   if (!gst_byte_reader_get_uint8 (reader, &val)) {
206     return FALSE;
207   }
208   colorimetry->transfer = gst_video_transfer_function_from_iso (val);
209
210   if (!gst_byte_reader_get_uint8 (reader, &val)) {
211     return FALSE;
212   }
213   colorimetry->matrix = gst_video_color_matrix_from_iso (val);
214
215   *chroma_site = GST_VIDEO_CHROMA_SITE_UNKNOWN;
216
217   if (!gst_byte_reader_get_uint8 (reader, &val)) {
218     return FALSE;
219   }
220   switch ((val >> 2) & 0x03) {
221     case 1:
222       *chroma_site |= GST_VIDEO_CHROMA_SITE_H_COSITED;
223       break;
224     case 2:
225       *chroma_site |= GST_VIDEO_CHROMA_SITE_NONE;
226       break;
227   }
228
229   switch (val & 0x03) {
230     case 1:
231       *chroma_site |= GST_VIDEO_CHROMA_SITE_V_COSITED;
232       break;
233     case 2:
234       *chroma_site |= GST_VIDEO_CHROMA_SITE_NONE;
235       break;
236   }
237
238   colorimetry->range = val >> 4;
239
240   return TRUE;
241 }
242
243 static gboolean
244 parse_colorspace_with_hdr_meta (GstByteReader * reader,
245     GstVideoColorimetry * colorimetry,
246     GstVideoChromaSite * chroma_site,
247     GstVideoMasteringDisplayInfo * mastering_display_info,
248     GstVideoContentLightLevel * content_light_level)
249 {
250   guint i;
251   guint16 val16;
252
253   g_return_val_if_fail (reader != NULL, FALSE);
254   g_return_val_if_fail (mastering_display_info != NULL, FALSE);
255   g_return_val_if_fail (content_light_level != NULL, FALSE);
256
257   if (gst_byte_reader_get_remaining (reader) <
258       GST_RTP_HDREXT_COLORSPACE_WITH_HDR_META_SIZE) {
259     return FALSE;
260   }
261
262   if (!parse_colorspace (reader, colorimetry, chroma_site)) {
263     return FALSE;
264   }
265
266   if (!gst_byte_reader_get_uint16_be (reader, &val16)) {
267     return FALSE;
268   }
269   mastering_display_info->max_display_mastering_luminance = val16 * 10000;
270
271   if (!gst_byte_reader_get_uint16_be (reader, &val16)) {
272     return FALSE;
273   }
274   mastering_display_info->min_display_mastering_luminance = val16;
275
276   for (i = 0; i < 3; ++i) {
277     if (!gst_byte_reader_get_uint16_be (reader,
278             &mastering_display_info->display_primaries[i].x)) {
279       return FALSE;
280     }
281
282     if (!gst_byte_reader_get_uint16_be (reader,
283             &mastering_display_info->display_primaries[i].y)) {
284       return FALSE;
285     }
286   }
287
288   if (!gst_byte_reader_get_uint16_be (reader,
289           &mastering_display_info->white_point.x)) {
290     return FALSE;
291   }
292   if (!gst_byte_reader_get_uint16_be (reader,
293           &mastering_display_info->white_point.y)) {
294     return FALSE;
295   }
296
297   if (!gst_byte_reader_get_uint16_be (reader,
298           &content_light_level->max_content_light_level)) {
299     return FALSE;
300   }
301   if (!gst_byte_reader_get_uint16_be (reader,
302           &content_light_level->max_frame_average_light_level)) {
303     return FALSE;
304   }
305
306   return TRUE;
307 }
308
309 static gboolean
310 gst_rtp_header_extension_colorspace_read (GstRTPHeaderExtension * ext,
311     GstRTPHeaderExtensionFlags read_flags, const guint8 * data, gsize size,
312     GstBuffer * buffer)
313 {
314   GstRTPHeaderExtensionColorspace *self =
315       GST_RTP_HEADER_EXTENSION_COLORSPACE (ext);
316   gboolean has_hdr_meta;
317   GstByteReader *reader;
318   GstVideoColorimetry colorimetry;
319   GstVideoChromaSite chroma_site;
320   GstVideoMasteringDisplayInfo mdi;
321   GstVideoContentLightLevel cll;
322   gboolean caps_update_needed;
323   gboolean result;
324
325   if (size != GST_RTP_HDREXT_COLORSPACE_SIZE &&
326       size != GST_RTP_HDREXT_COLORSPACE_WITH_HDR_META_SIZE) {
327     GST_WARNING_OBJECT (ext, "Invalid Color Space header extension size %"
328         G_GSIZE_FORMAT, size);
329     return FALSE;
330   }
331
332   has_hdr_meta = size == GST_RTP_HDREXT_COLORSPACE_WITH_HDR_META_SIZE;
333
334   reader = gst_byte_reader_new (data, size);
335
336   if (has_hdr_meta) {
337     result = parse_colorspace_with_hdr_meta (reader, &colorimetry, &chroma_site,
338         &mdi, &cll);
339   } else {
340     result = parse_colorspace (reader, &colorimetry, &chroma_site);
341   }
342
343   g_clear_pointer (&reader, gst_byte_reader_free);
344
345   if (!gst_video_colorimetry_is_equal (&self->colorimetry, &colorimetry)) {
346     caps_update_needed = TRUE;
347     self->colorimetry = colorimetry;
348   }
349
350   if (self->chroma_site != chroma_site) {
351     caps_update_needed = TRUE;
352     self->chroma_site = chroma_site;
353   }
354
355   if (self->has_hdr_meta != has_hdr_meta) {
356     caps_update_needed = TRUE;
357     self->has_hdr_meta = has_hdr_meta;
358   }
359
360   if (has_hdr_meta) {
361     if (!gst_video_mastering_display_info_is_equal (&self->mdi, &mdi)) {
362       caps_update_needed = TRUE;
363       self->mdi = mdi;
364     }
365     if (!gst_video_content_light_level_is_equal (&self->cll, &cll)) {
366       caps_update_needed = TRUE;
367       self->cll = cll;
368     }
369   }
370
371   if (caps_update_needed) {
372     gst_rtp_header_extension_set_wants_update_non_rtp_src_caps (ext, TRUE);
373   }
374
375   return result;
376 }
377
378 static gboolean
379     gst_rtp_header_extension_colorspace_set_non_rtp_sink_caps
380     (GstRTPHeaderExtension * ext, const GstCaps * caps)
381 {
382   GstRTPHeaderExtensionColorspace *self =
383       GST_RTP_HEADER_EXTENSION_COLORSPACE (ext);
384   GstStructure *s;
385   const gchar *colorimetry;
386   const gchar *chroma_site;
387
388   s = gst_caps_get_structure (caps, 0);
389
390   colorimetry = gst_structure_get_string (s, "colorimetry");
391   if (colorimetry) {
392     gst_video_colorimetry_from_string (&self->colorimetry, colorimetry);
393
394     self->has_hdr_meta =
395         gst_video_mastering_display_info_from_caps (&self->mdi, caps);
396
397     gst_video_content_light_level_from_caps (&self->cll, caps);
398   }
399
400   chroma_site = gst_structure_get_string (s, "chroma-site");
401   if (chroma_site) {
402     self->chroma_site = gst_video_chroma_from_string (chroma_site);
403   }
404
405   return TRUE;
406 }
407
408 static gboolean
409     gst_rtp_header_extension_colorspace_update_non_rtp_src_caps
410     (GstRTPHeaderExtension * ext, GstCaps * caps)
411 {
412   GstRTPHeaderExtensionColorspace *self =
413       GST_RTP_HEADER_EXTENSION_COLORSPACE (ext);
414
415   gchar *color_str;
416
417   gst_structure_remove_fields (gst_caps_get_structure (caps, 0),
418       "mastering-display-info", "content-light-level", NULL);
419
420   if ((color_str = gst_video_colorimetry_to_string (&self->colorimetry))) {
421     gst_caps_set_simple (caps, "colorimetry", G_TYPE_STRING, color_str, NULL);
422     g_free (color_str);
423   }
424   if (self->chroma_site != GST_VIDEO_CHROMA_SITE_UNKNOWN) {
425     gst_caps_set_simple (caps, "chroma-site", G_TYPE_STRING,
426         gst_video_chroma_to_string (self->chroma_site), NULL);
427   }
428   if (self->has_hdr_meta) {
429     gst_video_mastering_display_info_add_to_caps (&self->mdi, caps);
430     gst_video_content_light_level_add_to_caps (&self->cll, caps);
431   }
432
433   return TRUE;
434 }
435
436 static void
437     gst_rtp_header_extension_colorspace_class_init
438     (GstRTPHeaderExtensionColorspaceClass * klass)
439 {
440   GstRTPHeaderExtensionClass *rtp_hdr_class =
441       GST_RTP_HEADER_EXTENSION_CLASS (klass);
442   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
443
444   rtp_hdr_class->get_supported_flags =
445       gst_rtp_header_extension_colorspace_get_supported_flags;
446   rtp_hdr_class->get_max_size =
447       gst_rtp_header_extension_colorspace_get_max_size;
448   rtp_hdr_class->write = gst_rtp_header_extension_colorspace_write;
449   rtp_hdr_class->read = gst_rtp_header_extension_colorspace_read;
450   rtp_hdr_class->set_non_rtp_sink_caps =
451       gst_rtp_header_extension_colorspace_set_non_rtp_sink_caps;
452   rtp_hdr_class->update_non_rtp_src_caps =
453       gst_rtp_header_extension_colorspace_update_non_rtp_src_caps;
454   rtp_hdr_class->set_caps_from_attributes =
455       gst_rtp_header_extension_set_caps_from_attributes_simple_sdp;
456
457   gst_element_class_set_static_metadata (gstelement_class,
458       "Color Space", GST_RTP_HDREXT_ELEMENT_CLASS,
459       "Extends RTP packets with color space and high dynamic range (HDR) information.",
460       "Jakub Adam <jakub.adam@collabora.com>");
461   gst_rtp_header_extension_class_set_uri (rtp_hdr_class,
462       GST_RTP_HDREXT_COLORSPACE_URI);
463 }