blob: 079716e7fd21cba1b445384bba7c9428ec005427 [file] [log] [blame]
/*
* 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.
*/
#include <stdlib.h>
#include <string.h>
#include "mpegts.h"
#include "gstmpegts-private.h"
/**
* 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 mentionned in the documentation.
*/
/* FIXME : Move this to proper file once we have a C file for ATSC/ISDB descriptors */
/**
* SECTION:gst-atsc-descriptor
* @title: ATSC variants of MPEG-TS descriptors
* @short_description: Descriptors for the various ATSC specifications
* @include: gst/mpegts/mpegts.h
*
*/
/**
* SECTION:gst-isdb-descriptor
* @title: ISDB variants of MPEG-TS descriptors
* @short_description: Descriptors for the various ISDB specifications
* @include: gst/mpegts/mpegts.h
*
*/
/*
* 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;
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);
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_slice_new (GstMpegtsDescriptor);
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_slice_new (GstMpegtsDescriptor);
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;
}
static GstMpegtsDescriptor *
_copy_descriptor (GstMpegtsDescriptor * desc)
{
GstMpegtsDescriptor *copy;
copy = g_slice_dup (GstMpegtsDescriptor, desc);
copy->data = g_memdup (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_slice_free (GstMpegtsDescriptor, desc);
}
G_DEFINE_BOXED_TYPE (GstMpegtsDescriptor, gst_mpegts_descriptor,
(GBoxedCopyFunc) _copy_descriptor,
(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_slice_new0 (GstMpegtsDescriptor);
desc->data = data;
desc->tag = *data++;
desc->length = *data++;
/* Copy the data now that we known the size */
desc->data = g_memdup (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 == 0x7f))
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 matchin @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_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): 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_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): 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_slice_dup (GstMpegtsISO639LanguageDescriptor, source);
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_slice_free (GstMpegtsISO639LanguageDescriptor, 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_slice_new0 (GstMpegtsISO639LanguageDescriptor);
/* 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;
}
/**
* 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): 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): descriptor data (after tag and length field)
* @length: length of @data
*
* Creates a #GstMpegtsDescriptor with custom @tag, @tag_extension and @data
*
* Returns: #GstMpegtsDescriptor
*/
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;
}