mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-09 05:38:58 +00:00
6dd15acf2d
The color space RTP header extension encodes color range as specified in https://www.webmproject.org/docs/container/#Range. In other words: 0: Unspecified, 1: Broadcast Range, 2: Full range, 3: Defined by matrix coefficients and transfer characteristic. This does not match the values of GstVideoColorRange, so it is not correct to just write the colorimetry.range value to the header extension. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1482>
483 lines
14 KiB
C
483 lines
14 KiB
C
/* GStreamer
|
|
* Copyright (C) 2020-2021 Collabora Ltd.
|
|
* @author: Jakub Adam <jakub.adam@collabora.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:rtphdrextcolorspace
|
|
* @title: GstRtphdrext-Colorspace
|
|
* @short_description: Helper methods for dealing with Color Space RTP header
|
|
* extension as defined in http://www.webrtc.org/experiments/rtp-hdrext/color-space
|
|
* @see_also: #GstRTPHeaderExtension, #GstRTPBasePayload, #GstRTPBaseDepayload
|
|
*
|
|
* Since: 1.20
|
|
*/
|
|
|
|
#include "gstrtphdrext-colorspace.h"
|
|
|
|
#include "gstrtpelements.h"
|
|
|
|
#include <gst/base/gstbytereader.h>
|
|
#include <gst/video/video-color.h>
|
|
#include <gst/video/video-hdr.h>
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (rtphdrext_colorspace_debug);
|
|
#define GST_CAT_DEFAULT (rtphdrext_colorspace_debug)
|
|
|
|
/**
|
|
* GstRTPHeaderExtensionColorspace:
|
|
* @parent: the parent #GstRTPHeaderExtension
|
|
*
|
|
* Instance struct for Color Space RTP header extension.
|
|
*
|
|
* http://www.webrtc.org/experiments/rtp-hdrext/color-space
|
|
*/
|
|
struct _GstRTPHeaderExtensionColorspace
|
|
{
|
|
GstRTPHeaderExtension parent;
|
|
|
|
GstVideoColorimetry colorimetry;
|
|
GstVideoChromaSite chroma_site;
|
|
GstVideoMasteringDisplayInfo mdi;
|
|
GstVideoContentLightLevel cll;
|
|
gboolean has_hdr_meta;
|
|
};
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GstRTPHeaderExtensionColorspace,
|
|
gst_rtp_header_extension_colorspace, GST_TYPE_RTP_HEADER_EXTENSION,
|
|
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "rtphdrextcolorspace", 0,
|
|
"RTP Color Space Header Extension");
|
|
);
|
|
GST_ELEMENT_REGISTER_DEFINE (rtphdrextcolorspace, "rtphdrextcolorspace",
|
|
GST_RANK_MARGINAL, GST_TYPE_RTP_HEADER_EXTENSION_COLORSPACE);
|
|
|
|
static void
|
|
gst_rtp_header_extension_colorspace_init (GstRTPHeaderExtensionColorspace *
|
|
self)
|
|
{
|
|
}
|
|
|
|
static GstRTPHeaderExtensionFlags
|
|
gst_rtp_header_extension_colorspace_get_supported_flags (GstRTPHeaderExtension *
|
|
ext)
|
|
{
|
|
GstRTPHeaderExtensionColorspace *self =
|
|
GST_RTP_HEADER_EXTENSION_COLORSPACE (ext);
|
|
|
|
return self->has_hdr_meta ?
|
|
GST_RTP_HEADER_EXTENSION_TWO_BYTE : GST_RTP_HEADER_EXTENSION_ONE_BYTE;
|
|
}
|
|
|
|
static gsize
|
|
gst_rtp_header_extension_colorspace_get_max_size (GstRTPHeaderExtension * ext,
|
|
const GstBuffer * buffer)
|
|
{
|
|
GstRTPHeaderExtensionColorspace *self =
|
|
GST_RTP_HEADER_EXTENSION_COLORSPACE (ext);
|
|
|
|
return self->has_hdr_meta ?
|
|
GST_RTP_HDREXT_COLORSPACE_WITH_HDR_META_SIZE :
|
|
GST_RTP_HDREXT_COLORSPACE_SIZE;
|
|
}
|
|
|
|
static gssize
|
|
gst_rtp_header_extension_colorspace_write (GstRTPHeaderExtension * ext,
|
|
const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags,
|
|
GstBuffer * output, guint8 * data, gsize size)
|
|
{
|
|
GstRTPHeaderExtensionColorspace *self =
|
|
GST_RTP_HEADER_EXTENSION_COLORSPACE (ext);
|
|
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
|
|
gboolean is_frame_last_buffer;
|
|
guint8 *ptr = data;
|
|
guint8 range;
|
|
guint8 horizontal_site;
|
|
guint8 vertical_site;
|
|
|
|
g_return_val_if_fail (size >=
|
|
gst_rtp_header_extension_colorspace_get_max_size (ext, NULL), -1);
|
|
g_return_val_if_fail (write_flags &
|
|
gst_rtp_header_extension_colorspace_get_supported_flags (ext), -1);
|
|
|
|
if (self->colorimetry.matrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN &&
|
|
self->colorimetry.primaries == GST_VIDEO_COLOR_PRIMARIES_UNKNOWN &&
|
|
self->colorimetry.range == GST_VIDEO_COLOR_RANGE_UNKNOWN &&
|
|
self->colorimetry.transfer == GST_VIDEO_TRANSFER_UNKNOWN) {
|
|
/* Nothing to write. */
|
|
return 0;
|
|
}
|
|
|
|
gst_rtp_buffer_map (output, GST_MAP_READ, &rtp);
|
|
is_frame_last_buffer = gst_rtp_buffer_get_marker (&rtp);
|
|
gst_rtp_buffer_unmap (&rtp);
|
|
|
|
if (!is_frame_last_buffer) {
|
|
/* Only a video frame's final packet should carry color space info. */
|
|
return 0;
|
|
}
|
|
|
|
*ptr++ = gst_video_color_primaries_to_iso (self->colorimetry.primaries);
|
|
*ptr++ = gst_video_transfer_function_to_iso (self->colorimetry.transfer);
|
|
*ptr++ = gst_video_color_matrix_to_iso (self->colorimetry.matrix);
|
|
|
|
switch (self->colorimetry.range) {
|
|
case GST_VIDEO_COLOR_RANGE_0_255:
|
|
range = 2;
|
|
break;
|
|
case GST_VIDEO_COLOR_RANGE_16_235:
|
|
range = 1;
|
|
break;
|
|
default:
|
|
range = 0;
|
|
break;
|
|
}
|
|
|
|
if (self->chroma_site & GST_VIDEO_CHROMA_SITE_H_COSITED) {
|
|
horizontal_site = 1;
|
|
} else if (self->chroma_site & GST_VIDEO_CHROMA_SITE_NONE) {
|
|
horizontal_site = 2;
|
|
} else {
|
|
horizontal_site = 0;
|
|
}
|
|
|
|
if (self->chroma_site & GST_VIDEO_CHROMA_SITE_V_COSITED) {
|
|
vertical_site = 1;
|
|
} else if (self->chroma_site & GST_VIDEO_CHROMA_SITE_NONE) {
|
|
vertical_site = 2;
|
|
} else {
|
|
vertical_site = 0;
|
|
}
|
|
|
|
*ptr++ = (range << 4) + (horizontal_site << 2) + vertical_site;
|
|
|
|
if (self->has_hdr_meta) {
|
|
guint i;
|
|
|
|
GST_WRITE_UINT16_BE (ptr,
|
|
self->mdi.max_display_mastering_luminance / 10000);
|
|
ptr += 2;
|
|
GST_WRITE_UINT16_BE (ptr, self->mdi.min_display_mastering_luminance);
|
|
ptr += 2;
|
|
|
|
for (i = 0; i < 3; ++i) {
|
|
GST_WRITE_UINT16_BE (ptr, self->mdi.display_primaries[i].x);
|
|
ptr += 2;
|
|
GST_WRITE_UINT16_BE (ptr, self->mdi.display_primaries[i].y);
|
|
ptr += 2;
|
|
}
|
|
|
|
GST_WRITE_UINT16_BE (ptr, self->mdi.white_point.x);
|
|
ptr += 2;
|
|
GST_WRITE_UINT16_BE (ptr, self->mdi.white_point.y);
|
|
ptr += 2;
|
|
|
|
GST_WRITE_UINT16_BE (ptr, self->cll.max_content_light_level);
|
|
ptr += 2;
|
|
GST_WRITE_UINT16_BE (ptr, self->cll.max_frame_average_light_level);
|
|
ptr += 2;
|
|
}
|
|
|
|
return ptr - data;
|
|
}
|
|
|
|
static gboolean
|
|
parse_colorspace (GstByteReader * reader, GstVideoColorimetry * colorimetry,
|
|
GstVideoChromaSite * chroma_site)
|
|
{
|
|
guint8 val;
|
|
|
|
g_return_val_if_fail (reader != NULL, FALSE);
|
|
g_return_val_if_fail (colorimetry != NULL, FALSE);
|
|
g_return_val_if_fail (chroma_site != NULL, FALSE);
|
|
|
|
if (gst_byte_reader_get_remaining (reader) < GST_RTP_HDREXT_COLORSPACE_SIZE) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_byte_reader_get_uint8 (reader, &val)) {
|
|
return FALSE;
|
|
}
|
|
colorimetry->primaries = gst_video_color_primaries_from_iso (val);
|
|
|
|
if (!gst_byte_reader_get_uint8 (reader, &val)) {
|
|
return FALSE;
|
|
}
|
|
colorimetry->transfer = gst_video_transfer_function_from_iso (val);
|
|
|
|
if (!gst_byte_reader_get_uint8 (reader, &val)) {
|
|
return FALSE;
|
|
}
|
|
colorimetry->matrix = gst_video_color_matrix_from_iso (val);
|
|
|
|
*chroma_site = GST_VIDEO_CHROMA_SITE_UNKNOWN;
|
|
|
|
if (!gst_byte_reader_get_uint8 (reader, &val)) {
|
|
return FALSE;
|
|
}
|
|
switch ((val >> 2) & 0x03) {
|
|
case 1:
|
|
*chroma_site |= GST_VIDEO_CHROMA_SITE_H_COSITED;
|
|
break;
|
|
case 2:
|
|
*chroma_site |= GST_VIDEO_CHROMA_SITE_NONE;
|
|
break;
|
|
}
|
|
|
|
switch (val & 0x03) {
|
|
case 1:
|
|
*chroma_site |= GST_VIDEO_CHROMA_SITE_V_COSITED;
|
|
break;
|
|
case 2:
|
|
*chroma_site |= GST_VIDEO_CHROMA_SITE_NONE;
|
|
break;
|
|
}
|
|
|
|
switch (val >> 4) {
|
|
case 1:
|
|
colorimetry->range = GST_VIDEO_COLOR_RANGE_16_235;
|
|
break;
|
|
case 2:
|
|
colorimetry->range = GST_VIDEO_COLOR_RANGE_0_255;
|
|
break;
|
|
default:
|
|
colorimetry->range = GST_VIDEO_COLOR_RANGE_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
parse_colorspace_with_hdr_meta (GstByteReader * reader,
|
|
GstVideoColorimetry * colorimetry,
|
|
GstVideoChromaSite * chroma_site,
|
|
GstVideoMasteringDisplayInfo * mastering_display_info,
|
|
GstVideoContentLightLevel * content_light_level)
|
|
{
|
|
guint i;
|
|
guint16 val16;
|
|
|
|
g_return_val_if_fail (reader != NULL, FALSE);
|
|
g_return_val_if_fail (mastering_display_info != NULL, FALSE);
|
|
g_return_val_if_fail (content_light_level != NULL, FALSE);
|
|
|
|
if (gst_byte_reader_get_remaining (reader) <
|
|
GST_RTP_HDREXT_COLORSPACE_WITH_HDR_META_SIZE) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!parse_colorspace (reader, colorimetry, chroma_site)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_byte_reader_get_uint16_be (reader, &val16)) {
|
|
return FALSE;
|
|
}
|
|
mastering_display_info->max_display_mastering_luminance = val16 * 10000;
|
|
|
|
if (!gst_byte_reader_get_uint16_be (reader, &val16)) {
|
|
return FALSE;
|
|
}
|
|
mastering_display_info->min_display_mastering_luminance = val16;
|
|
|
|
for (i = 0; i < 3; ++i) {
|
|
if (!gst_byte_reader_get_uint16_be (reader,
|
|
&mastering_display_info->display_primaries[i].x)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_byte_reader_get_uint16_be (reader,
|
|
&mastering_display_info->display_primaries[i].y)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!gst_byte_reader_get_uint16_be (reader,
|
|
&mastering_display_info->white_point.x)) {
|
|
return FALSE;
|
|
}
|
|
if (!gst_byte_reader_get_uint16_be (reader,
|
|
&mastering_display_info->white_point.y)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gst_byte_reader_get_uint16_be (reader,
|
|
&content_light_level->max_content_light_level)) {
|
|
return FALSE;
|
|
}
|
|
if (!gst_byte_reader_get_uint16_be (reader,
|
|
&content_light_level->max_frame_average_light_level)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_rtp_header_extension_colorspace_read (GstRTPHeaderExtension * ext,
|
|
GstRTPHeaderExtensionFlags read_flags, const guint8 * data, gsize size,
|
|
GstBuffer * buffer)
|
|
{
|
|
GstRTPHeaderExtensionColorspace *self =
|
|
GST_RTP_HEADER_EXTENSION_COLORSPACE (ext);
|
|
gboolean has_hdr_meta;
|
|
GstByteReader *reader;
|
|
GstVideoColorimetry colorimetry;
|
|
GstVideoChromaSite chroma_site;
|
|
GstVideoMasteringDisplayInfo mdi;
|
|
GstVideoContentLightLevel cll;
|
|
gboolean caps_update_needed;
|
|
gboolean result;
|
|
|
|
if (size != GST_RTP_HDREXT_COLORSPACE_SIZE &&
|
|
size != GST_RTP_HDREXT_COLORSPACE_WITH_HDR_META_SIZE) {
|
|
GST_WARNING_OBJECT (ext, "Invalid Color Space header extension size %"
|
|
G_GSIZE_FORMAT, size);
|
|
return FALSE;
|
|
}
|
|
|
|
has_hdr_meta = size == GST_RTP_HDREXT_COLORSPACE_WITH_HDR_META_SIZE;
|
|
|
|
reader = gst_byte_reader_new (data, size);
|
|
|
|
if (has_hdr_meta) {
|
|
result = parse_colorspace_with_hdr_meta (reader, &colorimetry, &chroma_site,
|
|
&mdi, &cll);
|
|
} else {
|
|
result = parse_colorspace (reader, &colorimetry, &chroma_site);
|
|
}
|
|
|
|
g_clear_pointer (&reader, gst_byte_reader_free);
|
|
|
|
if (!gst_video_colorimetry_is_equal (&self->colorimetry, &colorimetry)) {
|
|
caps_update_needed = TRUE;
|
|
self->colorimetry = colorimetry;
|
|
}
|
|
|
|
if (self->chroma_site != chroma_site) {
|
|
caps_update_needed = TRUE;
|
|
self->chroma_site = chroma_site;
|
|
}
|
|
|
|
if (self->has_hdr_meta != has_hdr_meta) {
|
|
caps_update_needed = TRUE;
|
|
self->has_hdr_meta = has_hdr_meta;
|
|
}
|
|
|
|
if (has_hdr_meta) {
|
|
if (!gst_video_mastering_display_info_is_equal (&self->mdi, &mdi)) {
|
|
caps_update_needed = TRUE;
|
|
self->mdi = mdi;
|
|
}
|
|
if (!gst_video_content_light_level_is_equal (&self->cll, &cll)) {
|
|
caps_update_needed = TRUE;
|
|
self->cll = cll;
|
|
}
|
|
}
|
|
|
|
if (caps_update_needed) {
|
|
gst_rtp_header_extension_set_wants_update_non_rtp_src_caps (ext, TRUE);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static gboolean
|
|
gst_rtp_header_extension_colorspace_set_non_rtp_sink_caps
|
|
(GstRTPHeaderExtension * ext, const GstCaps * caps)
|
|
{
|
|
GstRTPHeaderExtensionColorspace *self =
|
|
GST_RTP_HEADER_EXTENSION_COLORSPACE (ext);
|
|
GstStructure *s;
|
|
const gchar *colorimetry;
|
|
const gchar *chroma_site;
|
|
|
|
s = gst_caps_get_structure (caps, 0);
|
|
|
|
colorimetry = gst_structure_get_string (s, "colorimetry");
|
|
if (colorimetry) {
|
|
gst_video_colorimetry_from_string (&self->colorimetry, colorimetry);
|
|
|
|
self->has_hdr_meta =
|
|
gst_video_mastering_display_info_from_caps (&self->mdi, caps);
|
|
|
|
gst_video_content_light_level_from_caps (&self->cll, caps);
|
|
}
|
|
|
|
chroma_site = gst_structure_get_string (s, "chroma-site");
|
|
if (chroma_site) {
|
|
self->chroma_site = gst_video_chroma_from_string (chroma_site);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_rtp_header_extension_colorspace_update_non_rtp_src_caps
|
|
(GstRTPHeaderExtension * ext, GstCaps * caps)
|
|
{
|
|
GstRTPHeaderExtensionColorspace *self =
|
|
GST_RTP_HEADER_EXTENSION_COLORSPACE (ext);
|
|
|
|
gchar *color_str;
|
|
|
|
gst_structure_remove_fields (gst_caps_get_structure (caps, 0),
|
|
"mastering-display-info", "content-light-level", NULL);
|
|
|
|
if ((color_str = gst_video_colorimetry_to_string (&self->colorimetry))) {
|
|
gst_caps_set_simple (caps, "colorimetry", G_TYPE_STRING, color_str, NULL);
|
|
g_free (color_str);
|
|
}
|
|
if (self->chroma_site != GST_VIDEO_CHROMA_SITE_UNKNOWN) {
|
|
gst_caps_set_simple (caps, "chroma-site", G_TYPE_STRING,
|
|
gst_video_chroma_to_string (self->chroma_site), NULL);
|
|
}
|
|
if (self->has_hdr_meta) {
|
|
gst_video_mastering_display_info_add_to_caps (&self->mdi, caps);
|
|
gst_video_content_light_level_add_to_caps (&self->cll, caps);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_rtp_header_extension_colorspace_class_init
|
|
(GstRTPHeaderExtensionColorspaceClass * klass)
|
|
{
|
|
GstRTPHeaderExtensionClass *rtp_hdr_class =
|
|
GST_RTP_HEADER_EXTENSION_CLASS (klass);
|
|
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
|
|
|
rtp_hdr_class->get_supported_flags =
|
|
gst_rtp_header_extension_colorspace_get_supported_flags;
|
|
rtp_hdr_class->get_max_size =
|
|
gst_rtp_header_extension_colorspace_get_max_size;
|
|
rtp_hdr_class->write = gst_rtp_header_extension_colorspace_write;
|
|
rtp_hdr_class->read = gst_rtp_header_extension_colorspace_read;
|
|
rtp_hdr_class->set_non_rtp_sink_caps =
|
|
gst_rtp_header_extension_colorspace_set_non_rtp_sink_caps;
|
|
rtp_hdr_class->update_non_rtp_src_caps =
|
|
gst_rtp_header_extension_colorspace_update_non_rtp_src_caps;
|
|
|
|
gst_element_class_set_static_metadata (gstelement_class,
|
|
"Color Space", GST_RTP_HDREXT_ELEMENT_CLASS,
|
|
"Extends RTP packets with color space and high dynamic range (HDR) information.",
|
|
"Jakub Adam <jakub.adam@collabora.com>");
|
|
gst_rtp_header_extension_class_set_uri (rtp_hdr_class,
|
|
GST_RTP_HDREXT_COLORSPACE_URI);
|
|
}
|