mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
fb16abcd73
The initial specification for the descriptor (from H.222.0 06/21) was wrong and introduced duplicate descriptor_tag/descriptor_length field. This was later corrected in H.222.0 (2021) Amendment 1 (12/22) Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3945 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7775>
1711 lines
48 KiB
C
1711 lines
48 KiB
C
/*
|
|
* gstmpegtsdescriptor.c -
|
|
* Copyright (C) 2013 Edward Hervey
|
|
*
|
|
* Authors:
|
|
* Edward Hervey <edward@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., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "mpegts.h"
|
|
#include "gstmpegts-private.h"
|
|
#include <gst/base/gstbytewriter.h>
|
|
|
|
#define DEFINE_STATIC_COPY_FUNCTION(type, name) \
|
|
static type * _##name##_copy (type * source) \
|
|
{ \
|
|
return g_memdup2 (source, sizeof (type)); \
|
|
}
|
|
|
|
#define DEFINE_STATIC_FREE_FUNCTION(type, name) \
|
|
static void _##name##_free (type * source) \
|
|
{ \
|
|
g_free (source); \
|
|
}
|
|
|
|
/**
|
|
* SECTION:gstmpegtsdescriptor
|
|
* @title: Base MPEG-TS descriptors
|
|
* @short_description: Descriptors for ITU H.222.0 | ISO/IEC 13818-1
|
|
* @include: gst/mpegts/mpegts.h
|
|
*
|
|
* These are the base descriptor types and methods.
|
|
*
|
|
* For more details, refer to the ITU H.222.0 or ISO/IEC 13818-1 specifications
|
|
* and other specifications mentioned in the documentation.
|
|
*/
|
|
|
|
/*
|
|
* TODO
|
|
*
|
|
* * Add common validation code for data presence and minimum/maximum expected
|
|
* size.
|
|
* * Add parsing methods for the following descriptors that were previously
|
|
* handled in mpegtsbase:
|
|
* * GST_MTS_DESC_DVB_DATA_BROADCAST_ID
|
|
* * GST_MTS_DESC_DVB_CAROUSEL_IDENTIFIER
|
|
* * GST_MTS_DESC_DVB_FREQUENCY_LIST
|
|
*/
|
|
|
|
#define MAX_KNOWN_ICONV 25
|
|
|
|
/* First column is the original encoding,
|
|
* second column is the target encoding */
|
|
|
|
static GIConv __iconvs[MAX_KNOWN_ICONV][MAX_KNOWN_ICONV];
|
|
|
|
/* All these conversions will be to UTF8 */
|
|
typedef enum
|
|
{
|
|
_ICONV_UNKNOWN = -1,
|
|
_ICONV_ISO8859_1,
|
|
_ICONV_ISO8859_2,
|
|
_ICONV_ISO8859_3,
|
|
_ICONV_ISO8859_4,
|
|
_ICONV_ISO8859_5,
|
|
_ICONV_ISO8859_6,
|
|
_ICONV_ISO8859_7,
|
|
_ICONV_ISO8859_8,
|
|
_ICONV_ISO8859_9,
|
|
_ICONV_ISO8859_10,
|
|
_ICONV_ISO8859_11,
|
|
_ICONV_ISO8859_12,
|
|
_ICONV_ISO8859_13,
|
|
_ICONV_ISO8859_14,
|
|
_ICONV_ISO8859_15,
|
|
_ICONV_UCS_2BE,
|
|
_ICONV_EUC_KR,
|
|
_ICONV_GB2312,
|
|
_ICONV_UTF_16BE,
|
|
_ICONV_ISO10646_UTF8,
|
|
_ICONV_ISO6937,
|
|
_ICONV_UTF8,
|
|
/* Insert more here if needed */
|
|
_ICONV_MAX
|
|
} LocalIconvCode;
|
|
|
|
static const gchar *iconvtablename[] = {
|
|
"iso-8859-1",
|
|
"iso-8859-2",
|
|
"iso-8859-3",
|
|
"iso-8859-4",
|
|
"iso-8859-5",
|
|
"iso-8859-6",
|
|
"iso-8859-7",
|
|
"iso-8859-8",
|
|
"iso-8859-9",
|
|
"iso-8859-10",
|
|
"iso-8859-11",
|
|
"iso-8859-12",
|
|
"iso-8859-13",
|
|
"iso-8859-14",
|
|
"iso-8859-15",
|
|
"UCS-2BE",
|
|
"EUC-KR",
|
|
"GB2312",
|
|
"UTF-16BE",
|
|
"ISO-10646/UTF8",
|
|
"iso6937",
|
|
"utf-8"
|
|
/* Insert more here if needed */
|
|
};
|
|
|
|
void
|
|
__initialize_descriptors (void)
|
|
{
|
|
guint i, j;
|
|
|
|
/* Initialize converters */
|
|
/* FIXME : How/when should we close them ??? */
|
|
for (i = 0; i < MAX_KNOWN_ICONV; i++) {
|
|
for (j = 0; j < MAX_KNOWN_ICONV; j++)
|
|
__iconvs[i][j] = ((GIConv) - 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @text: The text you want to get the encoding from
|
|
* @start_text: Location where the beginning of the actual text is stored
|
|
* @is_multibyte: Location where information whether it's a multibyte encoding
|
|
* or not is stored
|
|
* @returns: GIconv for conversion or NULL
|
|
*/
|
|
static LocalIconvCode
|
|
get_encoding (const gchar * text, guint * start_text, gboolean * is_multibyte)
|
|
{
|
|
LocalIconvCode encoding;
|
|
guint8 firstbyte;
|
|
|
|
*is_multibyte = FALSE;
|
|
*start_text = 0;
|
|
|
|
firstbyte = (guint8) text[0];
|
|
|
|
/* A wrong value */
|
|
g_return_val_if_fail (firstbyte != 0x00, _ICONV_UNKNOWN);
|
|
|
|
if (firstbyte <= 0x0B) {
|
|
/* 0x01 => iso 8859-5 */
|
|
encoding = firstbyte + _ICONV_ISO8859_4;
|
|
*start_text = 1;
|
|
goto beach;
|
|
}
|
|
|
|
/* ETSI EN 300 468, "Selection of character table" */
|
|
switch (firstbyte) {
|
|
case 0x0C:
|
|
case 0x0D:
|
|
case 0x0E:
|
|
case 0x0F:
|
|
/* RESERVED */
|
|
encoding = _ICONV_UNKNOWN;
|
|
break;
|
|
case 0x10:
|
|
{
|
|
guint16 table;
|
|
|
|
table = GST_READ_UINT16_BE (text + 1);
|
|
|
|
if (table < 17)
|
|
encoding = _ICONV_UNKNOWN + table;
|
|
else
|
|
encoding = _ICONV_UNKNOWN;
|
|
*start_text = 3;
|
|
break;
|
|
}
|
|
case 0x11:
|
|
encoding = _ICONV_UCS_2BE;
|
|
*start_text = 1;
|
|
*is_multibyte = TRUE;
|
|
break;
|
|
case 0x12:
|
|
/* EUC-KR implements KSX1001 */
|
|
encoding = _ICONV_EUC_KR;
|
|
*start_text = 1;
|
|
*is_multibyte = TRUE;
|
|
break;
|
|
case 0x13:
|
|
encoding = _ICONV_GB2312;
|
|
*start_text = 1;
|
|
break;
|
|
case 0x14:
|
|
encoding = _ICONV_UTF_16BE;
|
|
*start_text = 1;
|
|
*is_multibyte = TRUE;
|
|
break;
|
|
case 0x15:
|
|
/* TODO : Where does this come from ?? */
|
|
encoding = _ICONV_ISO10646_UTF8;
|
|
*start_text = 1;
|
|
break;
|
|
case 0x16:
|
|
case 0x17:
|
|
case 0x18:
|
|
case 0x19:
|
|
case 0x1A:
|
|
case 0x1B:
|
|
case 0x1C:
|
|
case 0x1D:
|
|
case 0x1E:
|
|
case 0x1F:
|
|
/* RESERVED */
|
|
encoding = _ICONV_UNKNOWN;
|
|
break;
|
|
default:
|
|
encoding = _ICONV_ISO6937;
|
|
break;
|
|
}
|
|
|
|
beach:
|
|
GST_DEBUG
|
|
("Found encoding %d, first byte is 0x%02x, start_text: %u, is_multibyte: %d",
|
|
encoding, firstbyte, *start_text, *is_multibyte);
|
|
|
|
return encoding;
|
|
}
|
|
|
|
static GIConv
|
|
_get_iconv (LocalIconvCode from, LocalIconvCode to)
|
|
{
|
|
if (__iconvs[from][to] == (GIConv) - 1)
|
|
__iconvs[from][to] = g_iconv_open (iconvtablename[to],
|
|
iconvtablename[from]);
|
|
return __iconvs[from][to];
|
|
}
|
|
|
|
static void
|
|
_encode_control_codes (gchar * text, gsize length, gboolean is_multibyte)
|
|
{
|
|
gsize pos = 0;
|
|
|
|
while (pos < length) {
|
|
if (is_multibyte) {
|
|
guint16 code = GST_READ_UINT16_BE (text + pos);
|
|
if (code == 0x000A) {
|
|
text[pos] = 0xE0;
|
|
text[pos + 1] = 0x8A;
|
|
}
|
|
pos += 2;
|
|
} else {
|
|
guint8 code = text[pos];
|
|
if (code == 0x0A)
|
|
text[pos] = 0x8A;
|
|
pos++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* dvb_text_from_utf8:
|
|
* @text: The text to convert. This should be in UTF-8 format
|
|
* @out_size: (out): the byte length of the new text
|
|
*
|
|
* Converts UTF-8 strings to text characters compliant with EN 300 468.
|
|
* The converted text can be used directly in DVB #GstMpegtsDescriptor
|
|
*
|
|
* The function will try different character maps until the string is
|
|
* completely converted.
|
|
*
|
|
* The function tries the default ISO 6937 character map first.
|
|
*
|
|
* If no character map that contains all characters could be found, the
|
|
* string is converted to ISO 6937 with unknown characters set to `?`.
|
|
*
|
|
* Returns: (transfer full): byte array of size @out_size
|
|
*/
|
|
guint8 *
|
|
dvb_text_from_utf8 (const gchar * text, gsize * out_size)
|
|
{
|
|
GError *error = NULL;
|
|
gchar *out_text = NULL;
|
|
guint8 *out_buffer;
|
|
guint encoding;
|
|
GIConv giconv = (GIConv) - 1;
|
|
|
|
/* We test character maps one-by-one. Start with the default */
|
|
encoding = _ICONV_ISO6937;
|
|
giconv = _get_iconv (_ICONV_UTF8, encoding);
|
|
if (giconv != (GIConv) - 1)
|
|
out_text = g_convert_with_iconv (text, -1, giconv, NULL, out_size, &error);
|
|
|
|
if (out_text) {
|
|
GST_DEBUG ("Using default ISO6937 encoding");
|
|
goto out;
|
|
}
|
|
|
|
g_clear_error (&error);
|
|
|
|
for (encoding = _ICONV_ISO8859_1; encoding <= _ICONV_ISO10646_UTF8;
|
|
encoding++) {
|
|
giconv = _get_iconv (_ICONV_UTF8, encoding);
|
|
if (giconv == (GIConv) - 1)
|
|
continue;
|
|
out_text = g_convert_with_iconv (text, -1, giconv, NULL, out_size, &error);
|
|
|
|
if (out_text) {
|
|
GST_DEBUG ("Found suitable character map - %s", iconvtablename[encoding]);
|
|
goto out;
|
|
}
|
|
|
|
g_clear_error (&error);
|
|
}
|
|
|
|
out_text = g_convert_with_fallback (text, -1, iconvtablename[_ICONV_ISO6937],
|
|
iconvtablename[_ICONV_UTF8], "?", NULL, out_size, &error);
|
|
|
|
out:
|
|
|
|
if (error) {
|
|
GST_WARNING ("Could not convert from utf-8: %s", error->message);
|
|
g_error_free (error);
|
|
g_free (out_text);
|
|
return NULL;
|
|
}
|
|
|
|
switch (encoding) {
|
|
case _ICONV_ISO6937:
|
|
/* Default encoding contains no selection bytes. */
|
|
_encode_control_codes (out_text, *out_size, FALSE);
|
|
return (guint8 *) out_text;
|
|
case _ICONV_ISO8859_1:
|
|
case _ICONV_ISO8859_2:
|
|
case _ICONV_ISO8859_3:
|
|
case _ICONV_ISO8859_4:
|
|
/* These character sets requires 3 selection bytes */
|
|
_encode_control_codes (out_text, *out_size, FALSE);
|
|
out_buffer = g_malloc (*out_size + 3);
|
|
out_buffer[0] = 0x10;
|
|
out_buffer[1] = 0x00;
|
|
out_buffer[2] = encoding - _ICONV_ISO8859_1 + 1;
|
|
memcpy (out_buffer + 3, out_text, *out_size);
|
|
*out_size += 3;
|
|
g_free (out_text);
|
|
return out_buffer;
|
|
case _ICONV_ISO8859_5:
|
|
case _ICONV_ISO8859_6:
|
|
case _ICONV_ISO8859_7:
|
|
case _ICONV_ISO8859_8:
|
|
case _ICONV_ISO8859_9:
|
|
case _ICONV_ISO8859_10:
|
|
case _ICONV_ISO8859_11:
|
|
case _ICONV_ISO8859_12:
|
|
case _ICONV_ISO8859_13:
|
|
case _ICONV_ISO8859_14:
|
|
case _ICONV_ISO8859_15:
|
|
/* These character sets requires 1 selection byte */
|
|
_encode_control_codes (out_text, *out_size, FALSE);
|
|
out_buffer = g_malloc (*out_size + 1);
|
|
out_buffer[0] = encoding - _ICONV_ISO8859_5 + 1;
|
|
memcpy (out_buffer + 1, out_text, *out_size);
|
|
*out_size += 1;
|
|
g_free (out_text);
|
|
return out_buffer;
|
|
case _ICONV_UCS_2BE:
|
|
case _ICONV_EUC_KR:
|
|
case _ICONV_UTF_16BE:
|
|
/* These character sets requires 1 selection byte */
|
|
_encode_control_codes (out_text, *out_size, TRUE);
|
|
out_buffer = g_malloc (*out_size + 1);
|
|
out_buffer[0] = encoding - _ICONV_UCS_2BE + 0x11;
|
|
memcpy (out_buffer + 1, out_text, *out_size);
|
|
*out_size += 1;
|
|
g_free (out_text);
|
|
return out_buffer;
|
|
case _ICONV_GB2312:
|
|
case _ICONV_ISO10646_UTF8:
|
|
/* These character sets requires 1 selection byte */
|
|
_encode_control_codes (out_text, *out_size, FALSE);
|
|
out_buffer = g_malloc (*out_size + 1);
|
|
out_buffer[0] = encoding - _ICONV_UCS_2BE + 0x11;
|
|
memcpy (out_buffer + 1, out_text, *out_size);
|
|
*out_size += 1;
|
|
g_free (out_text);
|
|
return out_buffer;
|
|
default:
|
|
g_free (out_text);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @text: The text to convert. It may include pango markup (<b> and </b>)
|
|
* @length: The length of the string -1 if it's nul-terminated
|
|
* @start: Where to start converting in the text
|
|
* @encoding: The encoding of text
|
|
* @is_multibyte: Whether the encoding is a multibyte encoding
|
|
* @error: The location to store the error, or NULL to ignore errors
|
|
* @returns: UTF-8 encoded string
|
|
*
|
|
* Convert text to UTF-8.
|
|
*/
|
|
static gchar *
|
|
convert_to_utf8 (const gchar * text, gint length, guint start,
|
|
GIConv giconv, gboolean is_multibyte, GError ** error)
|
|
{
|
|
gchar *new_text;
|
|
gchar *tmp, *pos;
|
|
gint i;
|
|
|
|
text += start;
|
|
|
|
pos = tmp = g_malloc (length * 2);
|
|
|
|
if (is_multibyte) {
|
|
if (length == -1) {
|
|
while (*text != '\0') {
|
|
guint16 code = GST_READ_UINT16_BE (text);
|
|
|
|
switch (code) {
|
|
case 0xE086: /* emphasis on */
|
|
case 0xE087: /* emphasis off */
|
|
/* skip it */
|
|
break;
|
|
case 0xE08A:{
|
|
pos[0] = 0x00; /* 0x00 0x0A is a new line */
|
|
pos[1] = 0x0A;
|
|
pos += 2;
|
|
break;
|
|
}
|
|
default:
|
|
pos[0] = text[0];
|
|
pos[1] = text[1];
|
|
pos += 2;
|
|
break;
|
|
}
|
|
|
|
text += 2;
|
|
}
|
|
} else {
|
|
for (i = 0; i < length; i += 2) {
|
|
guint16 code = GST_READ_UINT16_BE (text);
|
|
|
|
switch (code) {
|
|
case 0xE086: /* emphasis on */
|
|
case 0xE087: /* emphasis off */
|
|
/* skip it */
|
|
break;
|
|
case 0xE08A:{
|
|
pos[0] = 0x00; /* 0x00 0x0A is a new line */
|
|
pos[1] = 0x0A;
|
|
pos += 2;
|
|
break;
|
|
}
|
|
default:
|
|
pos[0] = text[0];
|
|
pos[1] = text[1];
|
|
pos += 2;
|
|
break;
|
|
}
|
|
|
|
text += 2;
|
|
}
|
|
}
|
|
} else {
|
|
if (length == -1) {
|
|
while (*text != '\0') {
|
|
guint8 code = (guint8) (*text);
|
|
|
|
switch (code) {
|
|
case 0x86: /* emphasis on */
|
|
case 0x87: /* emphasis off */
|
|
/* skip it */
|
|
break;
|
|
case 0x8A:
|
|
*pos = '\n';
|
|
pos += 1;
|
|
break;
|
|
default:
|
|
*pos = *text;
|
|
pos += 1;
|
|
break;
|
|
}
|
|
|
|
text++;
|
|
}
|
|
} else {
|
|
for (i = 0; i < length; i++) {
|
|
guint8 code = (guint8) (*text);
|
|
|
|
switch (code) {
|
|
case 0x86: /* emphasis on */
|
|
case 0x87: /* emphasis off */
|
|
/* skip it */
|
|
break;
|
|
case 0x8A:
|
|
*pos = '\n';
|
|
pos += 1;
|
|
break;
|
|
default:
|
|
*pos = *text;
|
|
pos += 1;
|
|
break;
|
|
}
|
|
|
|
text++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pos > tmp) {
|
|
gsize bread = 0;
|
|
new_text =
|
|
g_convert_with_iconv (tmp, pos - tmp, giconv, &bread, NULL, error);
|
|
GST_DEBUG ("Converted to : %s", new_text);
|
|
} else {
|
|
new_text = g_strdup ("");
|
|
}
|
|
|
|
g_free (tmp);
|
|
|
|
return new_text;
|
|
}
|
|
|
|
gchar *
|
|
get_encoding_and_convert (const gchar * text, guint length)
|
|
{
|
|
GError *error = NULL;
|
|
gchar *converted_str;
|
|
guint start_text = 0;
|
|
gboolean is_multibyte;
|
|
LocalIconvCode encoding;
|
|
GIConv giconv = (GIConv) - 1;
|
|
|
|
g_return_val_if_fail (text != NULL, NULL);
|
|
|
|
if (text == NULL || length == 0)
|
|
return g_strdup ("");
|
|
|
|
encoding = get_encoding (text, &start_text, &is_multibyte);
|
|
|
|
if (encoding > _ICONV_UNKNOWN && encoding < _ICONV_MAX) {
|
|
GST_DEBUG ("Encoding %s", iconvtablename[encoding]);
|
|
giconv = _get_iconv (encoding, _ICONV_UTF8);
|
|
} else {
|
|
GST_FIXME ("Could not detect encoding. Returning NULL string");
|
|
converted_str = NULL;
|
|
goto beach;
|
|
}
|
|
|
|
converted_str = convert_to_utf8 (text, length - start_text, start_text,
|
|
giconv, is_multibyte, &error);
|
|
if (error != NULL) {
|
|
GST_WARNING ("Could not convert string: %s", error->message);
|
|
g_free (converted_str);
|
|
g_error_free (error);
|
|
error = NULL;
|
|
|
|
if (encoding >= _ICONV_ISO8859_2 && encoding <= _ICONV_ISO8859_15) {
|
|
/* Sometimes using the standard 8859-1 set fixes issues */
|
|
GST_DEBUG ("Encoding %s", iconvtablename[_ICONV_ISO8859_1]);
|
|
giconv = _get_iconv (_ICONV_ISO8859_1, _ICONV_UTF8);
|
|
|
|
GST_INFO ("Trying encoding ISO 8859-1");
|
|
converted_str = convert_to_utf8 (text, length, 1, giconv, FALSE, &error);
|
|
if (error != NULL) {
|
|
GST_WARNING
|
|
("Could not convert string while assuming encoding ISO 8859-1: %s",
|
|
error->message);
|
|
g_error_free (error);
|
|
goto failed;
|
|
}
|
|
} else if (encoding == _ICONV_ISO6937) {
|
|
|
|
/* The first part of ISO 6937 is identical to ISO 8859-9, but
|
|
* they differ in the second part. Some channels don't
|
|
* provide the first byte that indicates ISO 8859-9 encoding.
|
|
* If decoding from ISO 6937 failed, we try ISO 8859-9 here.
|
|
*/
|
|
giconv = _get_iconv (_ICONV_ISO8859_9, _ICONV_UTF8);
|
|
|
|
GST_INFO ("Trying encoding ISO 8859-9");
|
|
converted_str = convert_to_utf8 (text, length, 0, giconv, FALSE, &error);
|
|
if (error != NULL) {
|
|
GST_WARNING
|
|
("Could not convert string while assuming encoding ISO 8859-9: %s",
|
|
error->message);
|
|
g_error_free (error);
|
|
goto failed;
|
|
}
|
|
} else
|
|
goto failed;
|
|
}
|
|
|
|
beach:
|
|
return converted_str;
|
|
|
|
failed:
|
|
{
|
|
text += start_text;
|
|
return g_strndup (text, length - start_text);
|
|
}
|
|
}
|
|
|
|
gchar *
|
|
convert_lang_code (guint8 * data)
|
|
{
|
|
gchar *code;
|
|
/* the iso language code and country code is always 3 byte long */
|
|
code = g_malloc0 (4);
|
|
memcpy (code, data, 3);
|
|
|
|
return code;
|
|
}
|
|
|
|
void
|
|
_packetize_descriptor_array (GPtrArray * array, guint8 ** out_data)
|
|
{
|
|
guint i;
|
|
GstMpegtsDescriptor *descriptor;
|
|
|
|
g_return_if_fail (out_data != NULL);
|
|
g_return_if_fail (*out_data != NULL);
|
|
|
|
if (array == NULL)
|
|
return;
|
|
|
|
for (i = 0; i < array->len; i++) {
|
|
descriptor = g_ptr_array_index (array, i);
|
|
|
|
memcpy (*out_data, descriptor->data, descriptor->length + 2);
|
|
*out_data += descriptor->length + 2;
|
|
}
|
|
}
|
|
|
|
GstMpegtsDescriptor *
|
|
_new_descriptor (guint8 tag, guint8 length)
|
|
{
|
|
GstMpegtsDescriptor *descriptor;
|
|
guint8 *data;
|
|
|
|
descriptor = g_new (GstMpegtsDescriptor, 1);
|
|
|
|
descriptor->tag = tag;
|
|
descriptor->tag_extension = 0;
|
|
descriptor->length = length;
|
|
|
|
descriptor->data = g_malloc (length + 2);
|
|
|
|
data = descriptor->data;
|
|
|
|
*data++ = descriptor->tag;
|
|
*data = descriptor->length;
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
GstMpegtsDescriptor *
|
|
_new_descriptor_with_extension (guint8 tag, guint8 tag_extension, guint8 length)
|
|
{
|
|
GstMpegtsDescriptor *descriptor;
|
|
guint8 *data;
|
|
|
|
descriptor = g_new (GstMpegtsDescriptor, 1);
|
|
|
|
descriptor->tag = tag;
|
|
descriptor->tag_extension = tag_extension;
|
|
descriptor->length = length + 1;
|
|
|
|
descriptor->data = g_malloc (length + 3);
|
|
|
|
data = descriptor->data;
|
|
|
|
*data++ = descriptor->tag;
|
|
*data++ = descriptor->length;
|
|
*data = descriptor->tag_extension;
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_copy:
|
|
* @desc: (transfer none): A #GstMpegtsDescriptor:
|
|
*
|
|
* Copy the given descriptor.
|
|
*
|
|
* Returns: (transfer full): A copy of @desc.
|
|
*
|
|
* Since: 1.26
|
|
*/
|
|
GstMpegtsDescriptor *
|
|
gst_mpegts_descriptor_copy (GstMpegtsDescriptor * desc)
|
|
{
|
|
GstMpegtsDescriptor *copy;
|
|
|
|
copy = g_memdup2 (desc, sizeof (GstMpegtsDescriptor));
|
|
copy->data = g_memdup2 (desc->data, desc->length + 2);
|
|
|
|
return copy;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_free:
|
|
* @desc: The descriptor to free
|
|
*
|
|
* Frees @desc
|
|
*/
|
|
void
|
|
gst_mpegts_descriptor_free (GstMpegtsDescriptor * desc)
|
|
{
|
|
g_free ((gpointer) desc->data);
|
|
g_free (desc);
|
|
}
|
|
|
|
G_DEFINE_BOXED_TYPE (GstMpegtsDescriptor, gst_mpegts_descriptor,
|
|
(GBoxedCopyFunc) gst_mpegts_descriptor_copy,
|
|
(GBoxedFreeFunc) gst_mpegts_descriptor_free);
|
|
|
|
/**
|
|
* gst_mpegts_parse_descriptors:
|
|
* @buffer: (transfer none): descriptors to parse
|
|
* @buf_len: Size of @buffer
|
|
*
|
|
* Parses the descriptors present in @buffer and returns them as an
|
|
* array.
|
|
*
|
|
* Note: The data provided in @buffer will not be copied.
|
|
*
|
|
* Returns: (transfer full) (element-type GstMpegtsDescriptor): an
|
|
* array of the parsed descriptors or %NULL if there was an error.
|
|
* Release with #g_array_unref when done with it.
|
|
*/
|
|
GPtrArray *
|
|
gst_mpegts_parse_descriptors (guint8 * buffer, gsize buf_len)
|
|
{
|
|
GPtrArray *res;
|
|
guint8 length;
|
|
guint8 *data;
|
|
guint i, nb_desc = 0;
|
|
|
|
/* fast-path */
|
|
if (buf_len == 0)
|
|
return g_ptr_array_new ();
|
|
|
|
data = buffer;
|
|
|
|
GST_MEMDUMP ("Full descriptor array", buffer, buf_len);
|
|
|
|
while (data - buffer < buf_len) {
|
|
data++; /* skip tag */
|
|
length = *data++;
|
|
|
|
if (data - buffer > buf_len) {
|
|
GST_WARNING ("invalid descriptor length %d now at %d max %"
|
|
G_GSIZE_FORMAT, length, (gint) (data - buffer), buf_len);
|
|
return NULL;
|
|
}
|
|
|
|
data += length;
|
|
nb_desc++;
|
|
}
|
|
|
|
GST_DEBUG ("Saw %d descriptors, read %" G_GSIZE_FORMAT " bytes",
|
|
nb_desc, (gsize) (data - buffer));
|
|
|
|
if (data - buffer != buf_len) {
|
|
GST_WARNING ("descriptors size %d expected %" G_GSIZE_FORMAT,
|
|
(gint) (data - buffer), buf_len);
|
|
return NULL;
|
|
}
|
|
|
|
res =
|
|
g_ptr_array_new_full (nb_desc + 1,
|
|
(GDestroyNotify) gst_mpegts_descriptor_free);
|
|
|
|
data = buffer;
|
|
|
|
for (i = 0; i < nb_desc; i++) {
|
|
GstMpegtsDescriptor *desc = g_new0 (GstMpegtsDescriptor, 1);
|
|
|
|
desc->data = data;
|
|
desc->tag = *data++;
|
|
desc->length = *data++;
|
|
/* Copy the data now that we known the size */
|
|
desc->data = g_memdup2 (desc->data, desc->length + 2);
|
|
GST_LOG ("descriptor 0x%02x length:%d", desc->tag, desc->length);
|
|
GST_MEMDUMP ("descriptor", desc->data + 2, desc->length);
|
|
/* extended descriptors */
|
|
if (G_UNLIKELY (desc->tag == GST_MTS_DESC_DVB_EXTENSION
|
|
|| desc->tag == GST_MTS_DESC_EXTENSION))
|
|
desc->tag_extension = *data;
|
|
|
|
data += desc->length;
|
|
|
|
/* Set the descriptor in the array */
|
|
g_ptr_array_index (res, i) = desc;
|
|
}
|
|
|
|
res->len = nb_desc;
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_find_descriptor:
|
|
* @descriptors: (element-type GstMpegtsDescriptor) (transfer none): an array
|
|
* of #GstMpegtsDescriptor
|
|
* @tag: the tag to look for
|
|
*
|
|
* Finds the first descriptor of type @tag in the array.
|
|
*
|
|
* Note: To look for descriptors that can be present more than once in an
|
|
* array of descriptors, iterate the #GArray manually.
|
|
*
|
|
* Returns: (transfer none): the first descriptor matching @tag, else %NULL.
|
|
*/
|
|
const GstMpegtsDescriptor *
|
|
gst_mpegts_find_descriptor (GPtrArray * descriptors, guint8 tag)
|
|
{
|
|
guint i, nb_desc;
|
|
|
|
g_return_val_if_fail (descriptors != NULL, NULL);
|
|
|
|
nb_desc = descriptors->len;
|
|
for (i = 0; i < nb_desc; i++) {
|
|
GstMpegtsDescriptor *desc = g_ptr_array_index (descriptors, i);
|
|
if (desc->tag == tag)
|
|
return (const GstMpegtsDescriptor *) desc;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_find_descriptor_with_extension:
|
|
* @descriptors: (element-type GstMpegtsDescriptor) (transfer none): an array
|
|
* of #GstMpegtsDescriptor
|
|
* @tag: the tag to look for
|
|
*
|
|
* Finds the first descriptor of type @tag with @tag_extension in the array.
|
|
*
|
|
* Note: To look for descriptors that can be present more than once in an
|
|
* array of descriptors, iterate the #GArray manually.
|
|
*
|
|
* Returns: (transfer none): the first descriptor matchin @tag with @tag_extension, else %NULL.
|
|
*
|
|
* Since: 1.20
|
|
*/
|
|
const GstMpegtsDescriptor *
|
|
gst_mpegts_find_descriptor_with_extension (GPtrArray * descriptors, guint8 tag,
|
|
guint8 tag_extension)
|
|
{
|
|
guint i, nb_desc;
|
|
|
|
g_return_val_if_fail (descriptors != NULL, NULL);
|
|
|
|
nb_desc = descriptors->len;
|
|
for (i = 0; i < nb_desc; i++) {
|
|
GstMpegtsDescriptor *desc = g_ptr_array_index (descriptors, i);
|
|
if ((desc->tag == tag) && (desc->tag_extension == tag_extension))
|
|
return (const GstMpegtsDescriptor *) desc;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* GST_MTS_DESC_REGISTRATION (0x05) */
|
|
/**
|
|
* gst_mpegts_descriptor_from_registration:
|
|
* @format_identifier: (transfer none): a 4 character format identifier string
|
|
* @additional_info: (transfer none) (allow-none) (array length=additional_info_length): pointer to optional additional info
|
|
* @additional_info_length: length of the optional @additional_info
|
|
*
|
|
* Creates a %GST_MTS_DESC_REGISTRATION #GstMpegtsDescriptor
|
|
*
|
|
* Return: #GstMpegtsDescriptor, %NULL on failure
|
|
*/
|
|
GstMpegtsDescriptor *
|
|
gst_mpegts_descriptor_from_registration (const gchar * format_identifier,
|
|
guint8 * additional_info, gsize additional_info_length)
|
|
{
|
|
GstMpegtsDescriptor *descriptor;
|
|
|
|
g_return_val_if_fail (format_identifier != NULL, NULL);
|
|
g_return_val_if_fail (additional_info_length > 0 || !additional_info, NULL);
|
|
|
|
descriptor = _new_descriptor (GST_MTS_DESC_REGISTRATION,
|
|
4 + additional_info_length);
|
|
|
|
memcpy (descriptor->data + 2, format_identifier, 4);
|
|
if (additional_info && (additional_info_length > 0))
|
|
memcpy (descriptor->data + 6, additional_info, additional_info_length);
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_parse_registration:
|
|
* @descriptor: a %GST_MTS_DESC_REGISTRATION #GstMpegtsDescriptor
|
|
* @registration_id: (out): The registration ID (in host endiannes)
|
|
* @additional_info: (out) (allow-none) (array length=additional_info_length): The additional information
|
|
* @additional_info_length: (out) (allow-none): The size of @additional_info in bytes.
|
|
*
|
|
* Extracts the Registration information from @descriptor.
|
|
*
|
|
* Returns: %TRUE if parsing succeeded, else %FALSE.
|
|
*
|
|
* Since: 1.20
|
|
*/
|
|
|
|
gboolean
|
|
gst_mpegts_descriptor_parse_registration (GstMpegtsDescriptor * descriptor,
|
|
guint32 * registration_id,
|
|
guint8 ** additional_info, gsize * additional_info_length)
|
|
{
|
|
guint8 *data;
|
|
|
|
g_return_val_if_fail (descriptor != NULL && registration_id != NULL, FALSE);
|
|
|
|
/* The smallest registration is 4 bytes */
|
|
__common_desc_checks (descriptor, GST_MTS_DESC_REGISTRATION, 4, FALSE);
|
|
|
|
data = (guint8 *) descriptor->data + 2;
|
|
*registration_id = GST_READ_UINT32_BE (data);
|
|
data += 4;
|
|
if (additional_info && additional_info_length) {
|
|
*additional_info_length = descriptor->length - 4;
|
|
if (descriptor->length > 4) {
|
|
*additional_info = data;
|
|
} else {
|
|
*additional_info = NULL;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* GST_MTS_DESC_CA (0x09) */
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_parse_ca:
|
|
* @descriptor: a %GST_MTS_DESC_CA #GstMpegtsDescriptor
|
|
* @ca_system_id: (out): the type of CA system used
|
|
* @ca_pid: (out): The PID containing ECM or EMM data
|
|
* @private_data: (out) (allow-none) (array length=private_data_size): The private data
|
|
* @private_data_size: (out) (allow-none): The size of @private_data in bytes
|
|
*
|
|
* Extracts the Conditional Access information from @descriptor.
|
|
*
|
|
* Returns: %TRUE if parsing succeeded, else %FALSE.
|
|
*/
|
|
|
|
gboolean
|
|
gst_mpegts_descriptor_parse_ca (GstMpegtsDescriptor * descriptor,
|
|
guint16 * ca_system_id, guint16 * ca_pid,
|
|
const guint8 ** private_data, gsize * private_data_size)
|
|
{
|
|
guint8 *data;
|
|
|
|
g_return_val_if_fail (descriptor != NULL && ca_system_id != NULL
|
|
&& ca_pid != NULL, FALSE);
|
|
/* The smallest CA is 4 bytes (though not having any private data
|
|
* sounds a bit ... weird) */
|
|
__common_desc_checks (descriptor, GST_MTS_DESC_CA, 4, FALSE);
|
|
|
|
data = (guint8 *) descriptor->data + 2;
|
|
*ca_system_id = GST_READ_UINT16_BE (data);
|
|
data += 2;
|
|
*ca_pid = GST_READ_UINT16_BE (data) & 0x1fff;
|
|
data += 2;
|
|
if (private_data && private_data_size) {
|
|
*private_data = data;
|
|
*private_data_size = descriptor->length - 4;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* GST_MTS_DESC_ISO_639_LANGUAGE (0x0A) */
|
|
static GstMpegtsISO639LanguageDescriptor *
|
|
_gst_mpegts_iso_639_language_descriptor_copy (GstMpegtsISO639LanguageDescriptor
|
|
* source)
|
|
{
|
|
GstMpegtsISO639LanguageDescriptor *copy;
|
|
guint i;
|
|
|
|
copy = g_memdup2 (source, sizeof (GstMpegtsISO639LanguageDescriptor));
|
|
|
|
for (i = 0; i < source->nb_language; i++) {
|
|
copy->language[i] = g_strdup (source->language[i]);
|
|
}
|
|
|
|
return copy;
|
|
}
|
|
|
|
void
|
|
gst_mpegts_iso_639_language_descriptor_free (GstMpegtsISO639LanguageDescriptor
|
|
* desc)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < desc->nb_language; i++) {
|
|
g_free (desc->language[i]);
|
|
}
|
|
g_free (desc);
|
|
}
|
|
|
|
G_DEFINE_BOXED_TYPE (GstMpegtsISO639LanguageDescriptor,
|
|
gst_mpegts_iso_639_language,
|
|
(GBoxedCopyFunc) _gst_mpegts_iso_639_language_descriptor_copy,
|
|
(GFreeFunc) gst_mpegts_iso_639_language_descriptor_free);
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_parse_iso_639_language:
|
|
* @descriptor: a %GST_MTS_DESC_ISO_639_LANGUAGE #GstMpegtsDescriptor
|
|
* @res: (out) (transfer full): the #GstMpegtsISO639LanguageDescriptor to fill
|
|
*
|
|
* Extracts the iso 639-2 language information from @descriptor.
|
|
*
|
|
* Note: Use #gst_tag_get_language_code if you want to get the the
|
|
* ISO 639-1 language code from the returned ISO 639-2 one.
|
|
*
|
|
* Returns: %TRUE if parsing succeeded, else %FALSE.
|
|
*/
|
|
gboolean
|
|
gst_mpegts_descriptor_parse_iso_639_language (const GstMpegtsDescriptor *
|
|
descriptor, GstMpegtsISO639LanguageDescriptor ** desc)
|
|
{
|
|
guint i;
|
|
guint8 *data;
|
|
GstMpegtsISO639LanguageDescriptor *res;
|
|
|
|
g_return_val_if_fail (descriptor != NULL && desc != NULL, FALSE);
|
|
/* This descriptor can be empty, no size check needed */
|
|
__common_desc_check_base (descriptor, GST_MTS_DESC_ISO_639_LANGUAGE, FALSE);
|
|
|
|
data = (guint8 *) descriptor->data + 2;
|
|
|
|
res = g_new0 (GstMpegtsISO639LanguageDescriptor, 1);
|
|
|
|
/* Each language is 3 + 1 bytes */
|
|
res->nb_language = descriptor->length / 4;
|
|
for (i = 0; i < res->nb_language; i++) {
|
|
res->language[i] = convert_lang_code (data);
|
|
res->audio_type[i] = data[3];
|
|
data += 4;
|
|
}
|
|
|
|
*desc = res;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_parse_iso_639_language_idx:
|
|
* @descriptor: a %GST_MTS_DESC_ISO_639_LANGUAGE #GstMpegtsDescriptor
|
|
* @idx: Table id of the language to parse
|
|
* @lang: (out) (transfer full): 4-byte gchar array to hold the language code
|
|
* @audio_type: (out) (transfer none) (allow-none): the #GstMpegtsIso639AudioType to set
|
|
*
|
|
* Extracts the iso 639-2 language information from specific table id in @descriptor.
|
|
*
|
|
* Note: Use #gst_tag_get_language_code if you want to get the the
|
|
* ISO 639-1 language code from the returned ISO 639-2 one.
|
|
*
|
|
* Returns: %TRUE if parsing succeeded, else %FALSE.
|
|
*/
|
|
gboolean
|
|
gst_mpegts_descriptor_parse_iso_639_language_idx (const GstMpegtsDescriptor *
|
|
descriptor, guint idx, gchar ** lang, GstMpegtsIso639AudioType * audio_type)
|
|
{
|
|
guint8 *data;
|
|
|
|
g_return_val_if_fail (descriptor != NULL && lang != NULL, FALSE);
|
|
/* This descriptor can be empty, no size check needed */
|
|
__common_desc_check_base (descriptor, GST_MTS_DESC_ISO_639_LANGUAGE, FALSE);
|
|
|
|
if (descriptor->length / 4 <= idx)
|
|
return FALSE;
|
|
|
|
data = (guint8 *) descriptor->data + 2 + idx * 4;
|
|
|
|
*lang = convert_lang_code (data);
|
|
|
|
data += 3;
|
|
|
|
if (audio_type)
|
|
*audio_type = *data;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_parse_iso_639_language_nb:
|
|
* @descriptor: a %GST_MTS_DESC_ISO_639_LANGUAGE #GstMpegtsDescriptor
|
|
*
|
|
* Returns: The number of languages in @descriptor
|
|
*/
|
|
guint
|
|
gst_mpegts_descriptor_parse_iso_639_language_nb (const GstMpegtsDescriptor *
|
|
descriptor)
|
|
{
|
|
g_return_val_if_fail (descriptor != NULL, 0);
|
|
/* This descriptor can be empty, no size check needed */
|
|
__common_desc_check_base (descriptor, GST_MTS_DESC_ISO_639_LANGUAGE, FALSE);
|
|
|
|
return descriptor->length / 4;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_from_iso_639_language:
|
|
* @language: (transfer none): ISO-639-2 language 3-char code
|
|
*
|
|
* Creates a %GST_MTS_DESC_ISO_639_LANGUAGE #GstMpegtsDescriptor with
|
|
* a single language
|
|
*
|
|
* Return: #GstMpegtsDescriptor, %NULL on failure
|
|
*/
|
|
GstMpegtsDescriptor *
|
|
gst_mpegts_descriptor_from_iso_639_language (const gchar * language)
|
|
{
|
|
GstMpegtsDescriptor *descriptor;
|
|
|
|
g_return_val_if_fail (language != NULL, NULL);
|
|
|
|
descriptor = _new_descriptor (GST_MTS_DESC_ISO_639_LANGUAGE, 4); /* a language takes 4 bytes */
|
|
|
|
memcpy (descriptor->data + 2, language, 3);
|
|
descriptor->data[2 + 3] = 0; /* set audio type to undefined */
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
DEFINE_STATIC_COPY_FUNCTION (GstMpegtsLogicalChannelDescriptor,
|
|
gst_mpegts_logical_channel_descriptor);
|
|
|
|
DEFINE_STATIC_FREE_FUNCTION (GstMpegtsLogicalChannelDescriptor,
|
|
gst_mpegts_logical_channel_descriptor);
|
|
|
|
G_DEFINE_BOXED_TYPE (GstMpegtsLogicalChannelDescriptor,
|
|
gst_mpegts_logical_channel_descriptor,
|
|
(GBoxedCopyFunc) _gst_mpegts_logical_channel_descriptor_copy,
|
|
(GFreeFunc) _gst_mpegts_logical_channel_descriptor_free);
|
|
|
|
DEFINE_STATIC_COPY_FUNCTION (GstMpegtsLogicalChannel,
|
|
gst_mpegts_logical_channel);
|
|
|
|
DEFINE_STATIC_FREE_FUNCTION (GstMpegtsLogicalChannel,
|
|
gst_mpegts_logical_channel);
|
|
|
|
G_DEFINE_BOXED_TYPE (GstMpegtsLogicalChannel,
|
|
gst_mpegts_logical_channel,
|
|
(GBoxedCopyFunc) _gst_mpegts_logical_channel_copy,
|
|
(GFreeFunc) _gst_mpegts_logical_channel_free);
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_parse_logical_channel:
|
|
* @descriptor: a %GST_MTS_DESC_DTG_LOGICAL_CHANNEL #GstMpegtsDescriptor
|
|
* @res: (out) (transfer none): the #GstMpegtsLogicalChannelDescriptor to fill
|
|
*
|
|
* Extracts the logical channels from @descriptor.
|
|
*
|
|
* Returns: %TRUE if parsing succeeded, else %FALSE.
|
|
*/
|
|
gboolean
|
|
gst_mpegts_descriptor_parse_logical_channel (const GstMpegtsDescriptor *
|
|
descriptor, GstMpegtsLogicalChannelDescriptor * res)
|
|
{
|
|
guint i;
|
|
guint8 *data;
|
|
|
|
g_return_val_if_fail (descriptor != NULL && res != NULL, FALSE);
|
|
/* This descriptor loop can be empty, no size check required */
|
|
__common_desc_check_base (descriptor, GST_MTS_DESC_DTG_LOGICAL_CHANNEL,
|
|
FALSE);
|
|
|
|
data = (guint8 *) descriptor->data + 2;
|
|
|
|
res->nb_channels = descriptor->length / 4;
|
|
|
|
for (i = 0; i < res->nb_channels; i++) {
|
|
res->channels[i].service_id = GST_READ_UINT16_BE (data);
|
|
data += 2;
|
|
res->channels[i].visible_service = *data >> 7;
|
|
res->channels[i].logical_channel_number =
|
|
GST_READ_UINT16_BE (data) & 0x03ff;
|
|
data += 2;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_from_custom:
|
|
* @tag: descriptor tag
|
|
* @data: (transfer none) (array length=length): descriptor data (after tag and length field)
|
|
* @length: length of @data
|
|
*
|
|
* Creates a #GstMpegtsDescriptor with custom @tag and @data
|
|
*
|
|
* Returns: #GstMpegtsDescriptor
|
|
*/
|
|
GstMpegtsDescriptor *
|
|
gst_mpegts_descriptor_from_custom (guint8 tag, const guint8 * data,
|
|
gsize length)
|
|
{
|
|
GstMpegtsDescriptor *descriptor;
|
|
|
|
g_return_val_if_fail (length > 0 || !data, NULL);
|
|
|
|
descriptor = _new_descriptor (tag, length);
|
|
|
|
if (data && (length > 0))
|
|
memcpy (descriptor->data + 2, data, length);
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_from_custom_with_extension:
|
|
* @tag: descriptor tag
|
|
* @tag_extension: descriptor tag extension
|
|
* @data: (transfer none) (array length=length): descriptor data (after tag and length field)
|
|
* @length: length of @data
|
|
*
|
|
* Creates a #GstMpegtsDescriptor with custom @tag, @tag_extension and @data
|
|
*
|
|
* Returns: #GstMpegtsDescriptor
|
|
*
|
|
* Since: 1.20
|
|
*/
|
|
GstMpegtsDescriptor *
|
|
gst_mpegts_descriptor_from_custom_with_extension (guint8 tag,
|
|
guint8 tag_extension, const guint8 * data, gsize length)
|
|
{
|
|
GstMpegtsDescriptor *descriptor;
|
|
|
|
descriptor = _new_descriptor_with_extension (tag, tag_extension, length);
|
|
|
|
if (data && (length > 0))
|
|
memcpy (descriptor->data + 3, data, length);
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
static GstMpegtsMetadataDescriptor *
|
|
_gst_mpegts_metadata_descriptor_copy (GstMpegtsMetadataDescriptor * source)
|
|
{
|
|
GstMpegtsMetadataDescriptor *copy =
|
|
g_memdup2 (source, sizeof (GstMpegtsMetadataDescriptor));
|
|
return copy;
|
|
}
|
|
|
|
static void
|
|
_gst_mpegts_metadata_descriptor_free (GstMpegtsMetadataDescriptor * desc)
|
|
{
|
|
g_free (desc);
|
|
}
|
|
|
|
G_DEFINE_BOXED_TYPE (GstMpegtsMetadataDescriptor,
|
|
gst_mpegts_metadata_descriptor,
|
|
(GBoxedCopyFunc) _gst_mpegts_metadata_descriptor_copy,
|
|
(GFreeFunc) _gst_mpegts_metadata_descriptor_free);
|
|
|
|
GstMpegtsDescriptor *
|
|
gst_mpegts_descriptor_from_metadata (const GstMpegtsMetadataDescriptor *
|
|
metadata_descriptor)
|
|
{
|
|
g_return_val_if_fail (metadata_descriptor != NULL, NULL);
|
|
|
|
int wr_size = 0;
|
|
guint8 *add_info = NULL;
|
|
GstByteWriter writer;
|
|
|
|
// metadata_descriptor
|
|
gst_byte_writer_init_with_size (&writer, 32, FALSE);
|
|
|
|
gst_byte_writer_put_uint16_be (&writer,
|
|
metadata_descriptor->metadata_application_format);
|
|
if (metadata_descriptor->metadata_application_format ==
|
|
GST_MPEGTS_METADATA_APPLICATION_FORMAT_IDENTIFIER_FIELD) {
|
|
gst_byte_writer_put_uint32_be (&writer, metadata_descriptor->metadata_format_identifier); // metadata_application_format_identifier
|
|
}
|
|
|
|
gst_byte_writer_put_uint8 (&writer, metadata_descriptor->metadata_format);
|
|
if (metadata_descriptor->metadata_format ==
|
|
GST_MPEGTS_METADATA_FORMAT_IDENTIFIER_FIELD) {
|
|
gst_byte_writer_put_uint32_be (&writer, metadata_descriptor->metadata_format_identifier); // metadata_format_identifier
|
|
}
|
|
|
|
gst_byte_writer_put_uint8 (&writer, metadata_descriptor->metadata_service_id);
|
|
gst_byte_writer_put_uint8 (&writer, 0x0F); // decoder_config_flags = 000, DSM_CC_flag = 0, reserved = 1111
|
|
|
|
wr_size = gst_byte_writer_get_size (&writer);
|
|
add_info = gst_byte_writer_reset_and_get_data (&writer);
|
|
|
|
GstMpegtsDescriptor *descriptor =
|
|
_new_descriptor (GST_MTS_DESC_METADATA, wr_size);
|
|
memcpy (descriptor->data + 2, add_info, wr_size);
|
|
g_free (add_info);
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_parse_metadata:
|
|
* @descriptor: a %GST_TYPE_MPEGTS_METADATA_DESCRIPTOR #GstMpegtsDescriptor
|
|
* @res: (out) (transfer full): #GstMpegtsMetadataDescriptor
|
|
*
|
|
* Parses out the metadata descriptor from the @descriptor.
|
|
*
|
|
* See ISO/IEC 13818-1:2018 Section 2.6.60 and 2.6.61 for details.
|
|
* metadata_application_format is provided in Table 2-82. metadata_format is
|
|
* provided in Table 2-85.
|
|
*
|
|
* Returns: %TRUE if the parsing worked correctly, else %FALSE.
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
gboolean
|
|
gst_mpegts_descriptor_parse_metadata (const GstMpegtsDescriptor * descriptor,
|
|
GstMpegtsMetadataDescriptor ** desc)
|
|
{
|
|
guint8 *data;
|
|
guint8 flag;
|
|
GstMpegtsMetadataDescriptor *res;
|
|
|
|
g_return_val_if_fail (descriptor != NULL && desc != NULL, FALSE);
|
|
__common_desc_checks (descriptor, GST_MTS_DESC_METADATA, 5, FALSE);
|
|
|
|
data = (guint8 *) descriptor->data + 2;
|
|
|
|
res = g_new0 (GstMpegtsMetadataDescriptor, 1);
|
|
|
|
res->metadata_application_format = GST_READ_UINT16_BE (data);
|
|
data += 2;
|
|
if (res->metadata_application_format ==
|
|
GST_MPEGTS_METADATA_APPLICATION_FORMAT_IDENTIFIER_FIELD) {
|
|
// skip over metadata_application_format_identifier if it is provided
|
|
data += 4;
|
|
}
|
|
res->metadata_format = *data;
|
|
data += 1;
|
|
if (res->metadata_format == GST_MPEGTS_METADATA_FORMAT_IDENTIFIER_FIELD) {
|
|
res->metadata_format_identifier = GST_READ_UINT32_BE (data);
|
|
data += 4;
|
|
}
|
|
res->metadata_service_id = *data;
|
|
data += 1;
|
|
flag = *data;
|
|
res->decoder_config_flags = flag >> 5;
|
|
res->dsm_cc_flag = (flag & 0x10);
|
|
|
|
// There are more values if the dsm_cc_flag or decoder flags are set.
|
|
|
|
*desc = res;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_parse_metadata_std:
|
|
* @descriptor: a %GST_MTS_DESC_METADATA_STD #GstMpegtsDescriptor
|
|
* @metadata_input_leak_rate (out): the input leak rate in units of 400bits/sec.
|
|
* @metadata_buffer_size (out): the buffer size in units of 1024 bytes
|
|
* @metadata_output_leak_rate (out): the output leak rate in units of 400bits/sec.
|
|
*
|
|
* Extracts the metadata STD descriptor from @descriptor.
|
|
*
|
|
* See ISO/IEC 13818-1:2018 Section 2.6.62 and 2.6.63 for details.
|
|
*
|
|
* Returns: %TRUE if parsing succeeded, else %FALSE.
|
|
*
|
|
* Since: 1.24
|
|
*/
|
|
gboolean
|
|
gst_mpegts_descriptor_parse_metadata_std (const GstMpegtsDescriptor *
|
|
descriptor,
|
|
guint32 * metadata_input_leak_rate,
|
|
guint32 * metadata_buffer_size, guint32 * metadata_output_leak_rate)
|
|
{
|
|
guint8 *data;
|
|
|
|
g_return_val_if_fail (descriptor != NULL && metadata_input_leak_rate != NULL
|
|
&& metadata_buffer_size != NULL
|
|
&& metadata_output_leak_rate != NULL, FALSE);
|
|
__common_desc_checks (descriptor, GST_MTS_DESC_METADATA_STD, 9, FALSE);
|
|
data = (guint8 *) descriptor->data + 2;
|
|
*metadata_input_leak_rate = GST_READ_UINT24_BE (data) & 0x3FFFFF;
|
|
data += 3;
|
|
*metadata_buffer_size = GST_READ_UINT24_BE (data) & 0x3FFFFF;
|
|
data += 3;
|
|
*metadata_output_leak_rate = GST_READ_UINT24_BE (data) & 0x3FFFFF;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_mpegts_pes_metadata_meta_init (GstMpegtsPESMetadataMeta * meta,
|
|
gpointer params, GstBuffer * buffer)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_mpegts_pes_metadata_meta_free (GstMpegtsPESMetadataMeta * meta,
|
|
GstBuffer * buffer)
|
|
{
|
|
}
|
|
|
|
static gboolean
|
|
gst_mpegts_pes_metadata_meta_transform (GstBuffer * dest, GstMeta * meta,
|
|
GstBuffer * buffer, GQuark type, gpointer data)
|
|
{
|
|
GstMpegtsPESMetadataMeta *source_meta, *dest_meta;
|
|
|
|
source_meta = (GstMpegtsPESMetadataMeta *) meta;
|
|
|
|
if (GST_META_TRANSFORM_IS_COPY (type)) {
|
|
GstMetaTransformCopy *copy = data;
|
|
if (!copy->region) {
|
|
dest_meta = gst_buffer_add_mpegts_pes_metadata_meta (dest);
|
|
if (!dest_meta)
|
|
return FALSE;
|
|
dest_meta->metadata_service_id = source_meta->metadata_service_id;
|
|
dest_meta->flags = source_meta->flags;
|
|
}
|
|
} else {
|
|
/* return FALSE, if transform type is not supported */
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
GType
|
|
gst_mpegts_pes_metadata_meta_api_get_type (void)
|
|
{
|
|
static GType type;
|
|
static const gchar *tags[] = { NULL };
|
|
|
|
if (g_once_init_enter (&type)) {
|
|
GType _type =
|
|
gst_meta_api_type_register ("GstMpegtsPESMetadataMetaAPI", tags);
|
|
g_once_init_leave (&type, _type);
|
|
}
|
|
return type;
|
|
}
|
|
|
|
const GstMetaInfo *
|
|
gst_mpegts_pes_metadata_meta_get_info (void)
|
|
{
|
|
static const GstMetaInfo *mpegts_pes_metadata_meta_info = NULL;
|
|
|
|
if (g_once_init_enter ((GstMetaInfo **) & mpegts_pes_metadata_meta_info)) {
|
|
const GstMetaInfo *meta =
|
|
gst_meta_register (GST_MPEGTS_PES_METADATA_META_API_TYPE,
|
|
"GstMpegtsPESMetadataMeta", sizeof (GstMpegtsPESMetadataMeta),
|
|
(GstMetaInitFunction) gst_mpegts_pes_metadata_meta_init,
|
|
(GstMetaFreeFunction) gst_mpegts_pes_metadata_meta_free,
|
|
(GstMetaTransformFunction) gst_mpegts_pes_metadata_meta_transform);
|
|
g_once_init_leave ((GstMetaInfo **) & mpegts_pes_metadata_meta_info,
|
|
(GstMetaInfo *) meta);
|
|
}
|
|
|
|
return mpegts_pes_metadata_meta_info;
|
|
}
|
|
|
|
GstMpegtsPESMetadataMeta *
|
|
gst_buffer_add_mpegts_pes_metadata_meta (GstBuffer * buffer)
|
|
{
|
|
GstMpegtsPESMetadataMeta *meta;
|
|
meta =
|
|
(GstMpegtsPESMetadataMeta *) gst_buffer_add_meta (buffer,
|
|
GST_MPEGTS_PES_METADATA_META_INFO, NULL);
|
|
return meta;
|
|
}
|
|
|
|
static GstMpegtsMetadataPointerDescriptor *
|
|
_gst_mpegts_metadata_pointer_descriptor_copy (GstMpegtsMetadataPointerDescriptor
|
|
* source)
|
|
{
|
|
GstMpegtsMetadataPointerDescriptor *copy =
|
|
g_memdup2 (source, sizeof (GstMpegtsMetadataPointerDescriptor));
|
|
return copy;
|
|
}
|
|
|
|
static void
|
|
_gst_mpegts_metadata_pointer_descriptor_free (GstMpegtsMetadataPointerDescriptor
|
|
* desc)
|
|
{
|
|
g_free (desc);
|
|
}
|
|
|
|
G_DEFINE_BOXED_TYPE (GstMpegtsMetadataPointerDescriptor,
|
|
gst_mpegts_metadata_pointer_descriptor,
|
|
(GBoxedCopyFunc) _gst_mpegts_metadata_pointer_descriptor_copy,
|
|
(GFreeFunc) _gst_mpegts_metadata_pointer_descriptor_free);
|
|
|
|
GstMpegtsDescriptor *
|
|
gst_mpegts_descriptor_from_metadata_pointer (const
|
|
GstMpegtsMetadataPointerDescriptor * metadata_pointer_descriptor)
|
|
{
|
|
g_return_val_if_fail (metadata_pointer_descriptor != NULL, NULL);
|
|
|
|
int wr_size = 0;
|
|
guint8 *add_info = NULL;
|
|
GstByteWriter writer;
|
|
|
|
// metadata_pointer_descriptor
|
|
gst_byte_writer_init_with_size (&writer, 32, FALSE);
|
|
|
|
gst_byte_writer_put_uint16_be (&writer,
|
|
metadata_pointer_descriptor->metadata_application_format);
|
|
if (metadata_pointer_descriptor->metadata_application_format ==
|
|
GST_MPEGTS_METADATA_APPLICATION_FORMAT_IDENTIFIER_FIELD) {
|
|
gst_byte_writer_put_uint32_be (&writer, metadata_pointer_descriptor->metadata_format_identifier); // metadata_application_format_identifier
|
|
}
|
|
|
|
gst_byte_writer_put_uint8 (&writer,
|
|
metadata_pointer_descriptor->metadata_format);
|
|
if (metadata_pointer_descriptor->metadata_format ==
|
|
GST_MPEGTS_METADATA_FORMAT_IDENTIFIER_FIELD) {
|
|
gst_byte_writer_put_uint32_be (&writer, metadata_pointer_descriptor->metadata_format_identifier); // metadata_format_identifier
|
|
}
|
|
|
|
gst_byte_writer_put_uint8 (&writer,
|
|
metadata_pointer_descriptor->metadata_service_id);
|
|
gst_byte_writer_put_uint8 (&writer, 0x1F); // metadata_locator_record_flag = 0, MPEG_carriage_flag = 00, reserved = 11111
|
|
gst_byte_writer_put_uint16_be (&writer, metadata_pointer_descriptor->program_number); // program_number
|
|
|
|
wr_size = gst_byte_writer_get_size (&writer);
|
|
add_info = gst_byte_writer_reset_and_get_data (&writer);
|
|
|
|
GstMpegtsDescriptor *descriptor =
|
|
_new_descriptor (GST_MTS_DESC_METADATA_POINTER, wr_size);
|
|
memcpy (descriptor->data + 2, add_info, wr_size);
|
|
g_free (add_info);
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
DEFINE_STATIC_COPY_FUNCTION (GstMpegtsJpegXsDescriptor,
|
|
gst_mpegts_jpeg_xs_descriptor);
|
|
DEFINE_STATIC_FREE_FUNCTION (GstMpegtsJpegXsDescriptor,
|
|
gst_mpegts_jpeg_xs_descriptor);
|
|
|
|
G_DEFINE_BOXED_TYPE (GstMpegtsJpegXsDescriptor, gst_mpegts_jpeg_xs_descriptor,
|
|
(GBoxedCopyFunc) _gst_mpegts_jpeg_xs_descriptor_copy,
|
|
(GFreeFunc) _gst_mpegts_jpeg_xs_descriptor_free);
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_parse_jpeg_xs:
|
|
* @descriptor: A #GstMpegtsDescriptor
|
|
* @res: (out): A parsed #GstMpegtsJpegXsDescriptor
|
|
*
|
|
* Parses the JPEG-XS descriptor information from @descriptor:
|
|
*
|
|
* Returns: TRUE if the information could be parsed, else FALSE.
|
|
*
|
|
* Since: 1.26
|
|
*/
|
|
|
|
gboolean
|
|
gst_mpegts_descriptor_parse_jpeg_xs (const GstMpegtsDescriptor * descriptor,
|
|
GstMpegtsJpegXsDescriptor * res)
|
|
{
|
|
GstByteReader br;
|
|
guint8 flags;
|
|
g_return_val_if_fail (descriptor != NULL && res != NULL, FALSE);
|
|
|
|
/* The smallest jpegxs descriptor doesn't contain the MDM,
|
|
* but is an H.222.0 extension (so additional one byte) */
|
|
__common_desc_ext_checks (descriptor, GST_MTS_DESC_EXT_JXS_VIDEO, 30, FALSE);
|
|
|
|
/* Skip tag/length/extension */
|
|
gst_byte_reader_init (&br, descriptor->data + 3, descriptor->length - 1);
|
|
memset (res, 0, sizeof (*res));
|
|
|
|
/* First part can be scanned out with unchecked reader */
|
|
res->descriptor_version = gst_byte_reader_get_uint8_unchecked (&br);
|
|
if (res->descriptor_version != 0) {
|
|
GST_WARNING ("Unsupported JPEG-XS descriptor version (%d != 0)",
|
|
res->descriptor_version);
|
|
return FALSE;
|
|
}
|
|
res->horizontal_size = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->vertical_size = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->brat = gst_byte_reader_get_uint32_be_unchecked (&br);
|
|
res->frat = gst_byte_reader_get_uint32_be_unchecked (&br);
|
|
res->schar = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->Ppih = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->Plev = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->max_buffer_size = gst_byte_reader_get_uint32_be_unchecked (&br);
|
|
res->buffer_model_type = gst_byte_reader_get_uint8_unchecked (&br);
|
|
res->colour_primaries = gst_byte_reader_get_uint8_unchecked (&br);
|
|
res->transfer_characteristics = gst_byte_reader_get_uint8_unchecked (&br);
|
|
res->matrix_coefficients = gst_byte_reader_get_uint8_unchecked (&br);
|
|
|
|
res->video_full_range_flag =
|
|
(gst_byte_reader_get_uint8_unchecked (&br) & 0x80) == 0x80;
|
|
flags = gst_byte_reader_get_uint8_unchecked (&br);
|
|
res->still_mode = flags >> 7;
|
|
if ((flags & 0x40) == 0x40) {
|
|
if (gst_byte_reader_get_remaining (&br) < 28) {
|
|
GST_ERROR ("MDM present on JPEG-XS descriptor but not enough bytes");
|
|
return FALSE;
|
|
}
|
|
res->X_c0 = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->Y_c0 = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->X_c1 = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->Y_c1 = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->X_c2 = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->Y_c2 = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->X_wp = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->Y_wp = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->L_max = gst_byte_reader_get_uint32_be_unchecked (&br);
|
|
res->L_min = gst_byte_reader_get_uint32_be_unchecked (&br);
|
|
res->MaxCLL = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
res->MaxFALL = gst_byte_reader_get_uint16_be_unchecked (&br);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_descriptor_from_jpeg_xs:
|
|
* @jpegxs: A #GstMpegtsJpegXsDescriptor
|
|
*
|
|
* Create a new #GstMpegtsDescriptor based on the information in @jpegxs
|
|
*
|
|
* Returns: (transfer full): The #GstMpegtsDescriptor
|
|
*
|
|
* Since: 1.26
|
|
*/
|
|
GstMpegtsDescriptor *
|
|
gst_mpegts_descriptor_from_jpeg_xs (const GstMpegtsJpegXsDescriptor * jpegxs)
|
|
{
|
|
gsize desc_size = 29;
|
|
GstByteWriter writer;
|
|
guint8 *desc_data;
|
|
GstMpegtsDescriptor *descriptor;
|
|
|
|
gst_byte_writer_init_with_size (&writer, desc_size, FALSE);
|
|
|
|
/* descriptor version: 0 */
|
|
gst_byte_writer_put_uint8 (&writer, 0);
|
|
/* horizontal/vertical size */
|
|
gst_byte_writer_put_uint16_be (&writer, jpegxs->horizontal_size);
|
|
gst_byte_writer_put_uint16_be (&writer, jpegxs->vertical_size);
|
|
/* brat/frat */
|
|
gst_byte_writer_put_uint32_be (&writer, jpegxs->brat);
|
|
gst_byte_writer_put_uint32_be (&writer, jpegxs->frat);
|
|
|
|
/* schar, Ppih, Plev */
|
|
gst_byte_writer_put_uint16_be (&writer, jpegxs->schar);
|
|
gst_byte_writer_put_uint16_be (&writer, jpegxs->Ppih);
|
|
gst_byte_writer_put_uint16_be (&writer, jpegxs->Plev);
|
|
|
|
gst_byte_writer_put_uint32_be (&writer, jpegxs->max_buffer_size);
|
|
|
|
/* Buffer model type */
|
|
gst_byte_writer_put_uint8 (&writer, jpegxs->buffer_model_type);
|
|
|
|
/* color_primaries */
|
|
gst_byte_writer_put_uint8 (&writer, jpegxs->colour_primaries);
|
|
|
|
/* transfer_characteristics */
|
|
gst_byte_writer_put_uint8 (&writer, jpegxs->transfer_characteristics);
|
|
|
|
/* matrix_coefficients */
|
|
gst_byte_writer_put_uint8 (&writer, jpegxs->matrix_coefficients);
|
|
|
|
/* video_full_range_flag */
|
|
gst_byte_writer_put_uint8 (&writer,
|
|
jpegxs->video_full_range_flag ? 1 << 7 : 0);
|
|
|
|
/* still_mode_flag : off
|
|
* mdm_flag : off */
|
|
gst_byte_writer_put_uint8 (&writer, jpegxs->still_mode ? 1 : 0);
|
|
|
|
if (jpegxs->mdm_flag) {
|
|
GST_ERROR ("Mastering Display Metadata not supported yet !");
|
|
}
|
|
|
|
desc_size = gst_byte_writer_get_size (&writer);
|
|
g_assert (desc_size == 29);
|
|
desc_data = gst_byte_writer_reset_and_get_data (&writer);
|
|
|
|
descriptor =
|
|
gst_mpegts_descriptor_from_custom_with_extension (GST_MTS_DESC_EXTENSION,
|
|
GST_MTS_DESC_EXT_JXS_VIDEO, desc_data, desc_size);
|
|
g_free (desc_data);
|
|
|
|
return descriptor;
|
|
}
|