1 /*
2 * gstmpegtsdescriptor.c -
3 * Copyright (C) 2013 Edward Hervey
4 *
5 * Authors:
6 * Edward Hervey <edward@collabora.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "mpegts.h"
31 #include "gstmpegts-private.h"
32
33 #define DEFINE_STATIC_COPY_FUNCTION(type, name) \
34 static type * _##name##_copy (type * source) \
35 { \
36 return g_slice_dup (type, source); \
37 }
38
39 #define DEFINE_STATIC_FREE_FUNCTION(type, name) \
40 static void _##name##_free (type * source) \
41 { \
42 g_slice_free (type, source); \
43 }
44
45 /**
46 * SECTION:gstmpegtsdescriptor
47 * @title: Base MPEG-TS descriptors
48 * @short_description: Descriptors for ITU H.222.0 | ISO/IEC 13818-1
49 * @include: gst/mpegts/mpegts.h
50 *
51 * These are the base descriptor types and methods.
52 *
53 * For more details, refer to the ITU H.222.0 or ISO/IEC 13818-1 specifications
54 * and other specifications mentionned in the documentation.
55 */
56
57 /* FIXME : Move this to proper file once we have a C file for ATSC/ISDB descriptors */
58 /**
59 * SECTION:gst-atsc-descriptor
60 * @title: ATSC variants of MPEG-TS descriptors
61 * @short_description: Descriptors for the various ATSC specifications
62 * @include: gst/mpegts/mpegts.h
63 *
64 */
65
66 /**
67 * SECTION:gst-isdb-descriptor
68 * @title: ISDB variants of MPEG-TS descriptors
69 * @short_description: Descriptors for the various ISDB specifications
70 * @include: gst/mpegts/mpegts.h
71 *
72 */
73
74
75 /*
76 * TODO
77 *
78 * * Add common validation code for data presence and minimum/maximum expected
79 * size.
80 * * Add parsing methods for the following descriptors that were previously
81 * handled in mpegtsbase:
82 * * GST_MTS_DESC_DVB_DATA_BROADCAST_ID
83 * * GST_MTS_DESC_DVB_CAROUSEL_IDENTIFIER
84 * * GST_MTS_DESC_DVB_FREQUENCY_LIST
85 */
86
87 #define MAX_KNOWN_ICONV 25
88
89 /* First column is the original encoding,
90 * second column is the target encoding */
91
92 static GIConv __iconvs[MAX_KNOWN_ICONV][MAX_KNOWN_ICONV];
93
94 /* All these conversions will be to UTF8 */
95 typedef enum
96 {
97 _ICONV_UNKNOWN = -1,
98 _ICONV_ISO8859_1,
99 _ICONV_ISO8859_2,
100 _ICONV_ISO8859_3,
101 _ICONV_ISO8859_4,
102 _ICONV_ISO8859_5,
103 _ICONV_ISO8859_6,
104 _ICONV_ISO8859_7,
105 _ICONV_ISO8859_8,
106 _ICONV_ISO8859_9,
107 _ICONV_ISO8859_10,
108 _ICONV_ISO8859_11,
109 _ICONV_ISO8859_12,
110 _ICONV_ISO8859_13,
111 _ICONV_ISO8859_14,
112 _ICONV_ISO8859_15,
113 _ICONV_UCS_2BE,
114 _ICONV_EUC_KR,
115 _ICONV_GB2312,
116 _ICONV_UTF_16BE,
117 _ICONV_ISO10646_UTF8,
118 _ICONV_ISO6937,
119 _ICONV_UTF8,
120 /* Insert more here if needed */
121 _ICONV_MAX
122 } LocalIconvCode;
123
124 static const gchar *iconvtablename[] = {
125 "iso-8859-1",
126 "iso-8859-2",
127 "iso-8859-3",
128 "iso-8859-4",
129 "iso-8859-5",
130 "iso-8859-6",
131 "iso-8859-7",
132 "iso-8859-8",
133 "iso-8859-9",
134 "iso-8859-10",
135 "iso-8859-11",
136 "iso-8859-12",
137 "iso-8859-13",
138 "iso-8859-14",
139 "iso-8859-15",
140 "UCS-2BE",
141 "EUC-KR",
142 "GB2312",
143 "UTF-16BE",
144 "ISO-10646/UTF8",
145 "iso6937",
146 "utf-8"
147 /* Insert more here if needed */
148 };
149
150 void
__initialize_descriptors(void)151 __initialize_descriptors (void)
152 {
153 guint i, j;
154
155 /* Initialize converters */
156 /* FIXME : How/when should we close them ??? */
157 for (i = 0; i < MAX_KNOWN_ICONV; i++) {
158 for (j = 0; j < MAX_KNOWN_ICONV; j++)
159 __iconvs[i][j] = ((GIConv) - 1);
160 }
161 }
162
163 /*
164 * @text: The text you want to get the encoding from
165 * @start_text: Location where the beginning of the actual text is stored
166 * @is_multibyte: Location where information whether it's a multibyte encoding
167 * or not is stored
168 * @returns: GIconv for conversion or NULL
169 */
170 static LocalIconvCode
get_encoding(const gchar * text,guint * start_text,gboolean * is_multibyte)171 get_encoding (const gchar * text, guint * start_text, gboolean * is_multibyte)
172 {
173 LocalIconvCode encoding;
174 guint8 firstbyte;
175
176 *is_multibyte = FALSE;
177 *start_text = 0;
178
179 firstbyte = (guint8) text[0];
180
181 /* A wrong value */
182 g_return_val_if_fail (firstbyte != 0x00, _ICONV_UNKNOWN);
183
184 if (firstbyte <= 0x0B) {
185 /* 0x01 => iso 8859-5 */
186 encoding = firstbyte + _ICONV_ISO8859_4;
187 *start_text = 1;
188 goto beach;
189 }
190
191 /* ETSI EN 300 468, "Selection of character table" */
192 switch (firstbyte) {
193 case 0x0C:
194 case 0x0D:
195 case 0x0E:
196 case 0x0F:
197 /* RESERVED */
198 encoding = _ICONV_UNKNOWN;
199 break;
200 case 0x10:
201 {
202 guint16 table;
203
204 table = GST_READ_UINT16_BE (text + 1);
205
206 if (table < 17)
207 encoding = _ICONV_UNKNOWN + table;
208 else
209 encoding = _ICONV_UNKNOWN;
210 *start_text = 3;
211 break;
212 }
213 case 0x11:
214 encoding = _ICONV_UCS_2BE;
215 *start_text = 1;
216 *is_multibyte = TRUE;
217 break;
218 case 0x12:
219 /* EUC-KR implements KSX1001 */
220 encoding = _ICONV_EUC_KR;
221 *start_text = 1;
222 *is_multibyte = TRUE;
223 break;
224 case 0x13:
225 encoding = _ICONV_GB2312;
226 *start_text = 1;
227 break;
228 case 0x14:
229 encoding = _ICONV_UTF_16BE;
230 *start_text = 1;
231 *is_multibyte = TRUE;
232 break;
233 case 0x15:
234 /* TODO : Where does this come from ?? */
235 encoding = _ICONV_ISO10646_UTF8;
236 *start_text = 1;
237 break;
238 case 0x16:
239 case 0x17:
240 case 0x18:
241 case 0x19:
242 case 0x1A:
243 case 0x1B:
244 case 0x1C:
245 case 0x1D:
246 case 0x1E:
247 case 0x1F:
248 /* RESERVED */
249 encoding = _ICONV_UNKNOWN;
250 break;
251 default:
252 encoding = _ICONV_ISO6937;
253 break;
254 }
255
256 beach:
257 GST_DEBUG
258 ("Found encoding %d, first byte is 0x%02x, start_text: %u, is_multibyte: %d",
259 encoding, firstbyte, *start_text, *is_multibyte);
260
261 return encoding;
262 }
263
264 static GIConv
_get_iconv(LocalIconvCode from,LocalIconvCode to)265 _get_iconv (LocalIconvCode from, LocalIconvCode to)
266 {
267 if (__iconvs[from][to] == (GIConv) - 1)
268 __iconvs[from][to] = g_iconv_open (iconvtablename[to],
269 iconvtablename[from]);
270 return __iconvs[from][to];
271 }
272
273 static void
_encode_control_codes(gchar * text,gsize length,gboolean is_multibyte)274 _encode_control_codes (gchar * text, gsize length, gboolean is_multibyte)
275 {
276 gsize pos = 0;
277
278 while (pos < length) {
279 if (is_multibyte) {
280 guint16 code = GST_READ_UINT16_BE (text + pos);
281 if (code == 0x000A) {
282 text[pos] = 0xE0;
283 text[pos + 1] = 0x8A;
284 }
285 pos += 2;
286 } else {
287 guint8 code = text[pos];
288 if (code == 0x0A)
289 text[pos] = 0x8A;
290 pos++;
291 }
292 }
293 }
294
295 /**
296 * dvb_text_from_utf8:
297 * @text: The text to convert. This should be in UTF-8 format
298 * @out_size: (out): the byte length of the new text
299 *
300 * Converts UTF-8 strings to text characters compliant with EN 300 468.
301 * The converted text can be used directly in DVB #GstMpegtsDescriptor
302 *
303 * The function will try different character maps until the string is
304 * completely converted.
305 *
306 * The function tries the default ISO 6937 character map first.
307 *
308 * If no character map that contains all characters could be found, the
309 * string is converted to ISO 6937 with unknown characters set to `?`.
310 *
311 * Returns: (transfer full): byte array of size @out_size
312 */
313 guint8 *
dvb_text_from_utf8(const gchar * text,gsize * out_size)314 dvb_text_from_utf8 (const gchar * text, gsize * out_size)
315 {
316 GError *error = NULL;
317 gchar *out_text;
318 guint8 *out_buffer;
319 guint encoding;
320 GIConv giconv = (GIConv) - 1;
321
322 /* We test character maps one-by-one. Start with the default */
323 encoding = _ICONV_ISO6937;
324 giconv = _get_iconv (_ICONV_UTF8, encoding);
325 out_text = g_convert_with_iconv (text, -1, giconv, NULL, out_size, &error);
326
327 if (out_text) {
328 GST_DEBUG ("Using default ISO6937 encoding");
329 goto out;
330 }
331
332 g_clear_error (&error);
333
334 for (encoding = _ICONV_ISO8859_1; encoding <= _ICONV_ISO10646_UTF8;
335 encoding++) {
336 giconv = _get_iconv (_ICONV_UTF8, encoding);
337 if (giconv == (GIConv) - 1)
338 continue;
339 out_text = g_convert_with_iconv (text, -1, giconv, NULL, out_size, &error);
340
341 if (out_text) {
342 GST_DEBUG ("Found suitable character map - %s", iconvtablename[encoding]);
343 goto out;
344 }
345
346 g_clear_error (&error);
347 }
348
349 out_text = g_convert_with_fallback (text, -1, iconvtablename[_ICONV_ISO6937],
350 iconvtablename[_ICONV_UTF8], "?", NULL, out_size, &error);
351
352 out:
353
354 if (error) {
355 GST_WARNING ("Could not convert from utf-8: %s", error->message);
356 g_error_free (error);
357 g_free (out_text);
358 return NULL;
359 }
360
361 switch (encoding) {
362 case _ICONV_ISO6937:
363 /* Default encoding contains no selection bytes. */
364 _encode_control_codes (out_text, *out_size, FALSE);
365 return (guint8 *) out_text;
366 case _ICONV_ISO8859_1:
367 case _ICONV_ISO8859_2:
368 case _ICONV_ISO8859_3:
369 case _ICONV_ISO8859_4:
370 /* These character sets requires 3 selection bytes */
371 _encode_control_codes (out_text, *out_size, FALSE);
372 out_buffer = g_malloc (*out_size + 3);
373 out_buffer[0] = 0x10;
374 out_buffer[1] = 0x00;
375 out_buffer[2] = encoding - _ICONV_ISO8859_1 + 1;
376 memcpy (out_buffer + 3, out_text, *out_size);
377 *out_size += 3;
378 g_free (out_text);
379 return out_buffer;
380 case _ICONV_ISO8859_5:
381 case _ICONV_ISO8859_6:
382 case _ICONV_ISO8859_7:
383 case _ICONV_ISO8859_8:
384 case _ICONV_ISO8859_9:
385 case _ICONV_ISO8859_10:
386 case _ICONV_ISO8859_11:
387 case _ICONV_ISO8859_12:
388 case _ICONV_ISO8859_13:
389 case _ICONV_ISO8859_14:
390 case _ICONV_ISO8859_15:
391 /* These character sets requires 1 selection byte */
392 _encode_control_codes (out_text, *out_size, FALSE);
393 out_buffer = g_malloc (*out_size + 1);
394 out_buffer[0] = encoding - _ICONV_ISO8859_5 + 1;
395 memcpy (out_buffer + 1, out_text, *out_size);
396 *out_size += 1;
397 g_free (out_text);
398 return out_buffer;
399 case _ICONV_UCS_2BE:
400 case _ICONV_EUC_KR:
401 case _ICONV_UTF_16BE:
402 /* These character sets requires 1 selection byte */
403 _encode_control_codes (out_text, *out_size, TRUE);
404 out_buffer = g_malloc (*out_size + 1);
405 out_buffer[0] = encoding - _ICONV_UCS_2BE + 0x11;
406 memcpy (out_buffer + 1, out_text, *out_size);
407 *out_size += 1;
408 g_free (out_text);
409 return out_buffer;
410 case _ICONV_GB2312:
411 case _ICONV_ISO10646_UTF8:
412 /* These character sets requires 1 selection byte */
413 _encode_control_codes (out_text, *out_size, FALSE);
414 out_buffer = g_malloc (*out_size + 1);
415 out_buffer[0] = encoding - _ICONV_UCS_2BE + 0x11;
416 memcpy (out_buffer + 1, out_text, *out_size);
417 *out_size += 1;
418 g_free (out_text);
419 return out_buffer;
420 default:
421 g_free (out_text);
422 return NULL;
423 }
424 }
425
426 /*
427 * @text: The text to convert. It may include pango markup (<b> and </b>)
428 * @length: The length of the string -1 if it's nul-terminated
429 * @start: Where to start converting in the text
430 * @encoding: The encoding of text
431 * @is_multibyte: Whether the encoding is a multibyte encoding
432 * @error: The location to store the error, or NULL to ignore errors
433 * @returns: UTF-8 encoded string
434 *
435 * Convert text to UTF-8.
436 */
437 static gchar *
convert_to_utf8(const gchar * text,gint length,guint start,GIConv giconv,gboolean is_multibyte,GError ** error)438 convert_to_utf8 (const gchar * text, gint length, guint start,
439 GIConv giconv, gboolean is_multibyte, GError ** error)
440 {
441 gchar *new_text;
442 gchar *tmp, *pos;
443 gint i;
444
445 text += start;
446
447 pos = tmp = g_malloc (length * 2);
448
449 if (is_multibyte) {
450 if (length == -1) {
451 while (*text != '\0') {
452 guint16 code = GST_READ_UINT16_BE (text);
453
454 switch (code) {
455 case 0xE086: /* emphasis on */
456 case 0xE087: /* emphasis off */
457 /* skip it */
458 break;
459 case 0xE08A:{
460 pos[0] = 0x00; /* 0x00 0x0A is a new line */
461 pos[1] = 0x0A;
462 pos += 2;
463 break;
464 }
465 default:
466 pos[0] = text[0];
467 pos[1] = text[1];
468 pos += 2;
469 break;
470 }
471
472 text += 2;
473 }
474 } else {
475 for (i = 0; i < length; i += 2) {
476 guint16 code = GST_READ_UINT16_BE (text);
477
478 switch (code) {
479 case 0xE086: /* emphasis on */
480 case 0xE087: /* emphasis off */
481 /* skip it */
482 break;
483 case 0xE08A:{
484 pos[0] = 0x00; /* 0x00 0x0A is a new line */
485 pos[1] = 0x0A;
486 pos += 2;
487 break;
488 }
489 default:
490 pos[0] = text[0];
491 pos[1] = text[1];
492 pos += 2;
493 break;
494 }
495
496 text += 2;
497 }
498 }
499 } else {
500 if (length == -1) {
501 while (*text != '\0') {
502 guint8 code = (guint8) (*text);
503
504 switch (code) {
505 case 0x86: /* emphasis on */
506 case 0x87: /* emphasis off */
507 /* skip it */
508 break;
509 case 0x8A:
510 *pos = '\n';
511 pos += 1;
512 break;
513 default:
514 *pos = *text;
515 pos += 1;
516 break;
517 }
518
519 text++;
520 }
521 } else {
522 for (i = 0; i < length; i++) {
523 guint8 code = (guint8) (*text);
524
525 switch (code) {
526 case 0x86: /* emphasis on */
527 case 0x87: /* emphasis off */
528 /* skip it */
529 break;
530 case 0x8A:
531 *pos = '\n';
532 pos += 1;
533 break;
534 default:
535 *pos = *text;
536 pos += 1;
537 break;
538 }
539
540 text++;
541 }
542 }
543 }
544
545 if (pos > tmp) {
546 gsize bread = 0;
547 new_text =
548 g_convert_with_iconv (tmp, pos - tmp, giconv, &bread, NULL, error);
549 GST_DEBUG ("Converted to : %s", new_text);
550 } else {
551 new_text = g_strdup ("");
552 }
553
554 g_free (tmp);
555
556 return new_text;
557 }
558
559 gchar *
get_encoding_and_convert(const gchar * text,guint length)560 get_encoding_and_convert (const gchar * text, guint length)
561 {
562 GError *error = NULL;
563 gchar *converted_str;
564 guint start_text = 0;
565 gboolean is_multibyte;
566 LocalIconvCode encoding;
567 GIConv giconv = (GIConv) - 1;
568
569 g_return_val_if_fail (text != NULL, NULL);
570
571 if (text == NULL || length == 0)
572 return g_strdup ("");
573
574 encoding = get_encoding (text, &start_text, &is_multibyte);
575
576 if (encoding > _ICONV_UNKNOWN && encoding < _ICONV_MAX) {
577 GST_DEBUG ("Encoding %s", iconvtablename[encoding]);
578 giconv = _get_iconv (encoding, _ICONV_UTF8);
579 } else {
580 GST_FIXME ("Could not detect encoding. Returning NULL string");
581 converted_str = NULL;
582 goto beach;
583 }
584
585 converted_str = convert_to_utf8 (text, length - start_text, start_text,
586 giconv, is_multibyte, &error);
587 if (error != NULL) {
588 GST_WARNING ("Could not convert string: %s", error->message);
589 g_free (converted_str);
590 g_error_free (error);
591 error = NULL;
592
593 if (encoding >= _ICONV_ISO8859_2 && encoding <= _ICONV_ISO8859_15) {
594 /* Sometimes using the standard 8859-1 set fixes issues */
595 GST_DEBUG ("Encoding %s", iconvtablename[_ICONV_ISO8859_1]);
596 giconv = _get_iconv (_ICONV_ISO8859_1, _ICONV_UTF8);
597
598 GST_INFO ("Trying encoding ISO 8859-1");
599 converted_str = convert_to_utf8 (text, length, 1, giconv, FALSE, &error);
600 if (error != NULL) {
601 GST_WARNING
602 ("Could not convert string while assuming encoding ISO 8859-1: %s",
603 error->message);
604 g_error_free (error);
605 goto failed;
606 }
607 } else if (encoding == _ICONV_ISO6937) {
608
609 /* The first part of ISO 6937 is identical to ISO 8859-9, but
610 * they differ in the second part. Some channels don't
611 * provide the first byte that indicates ISO 8859-9 encoding.
612 * If decoding from ISO 6937 failed, we try ISO 8859-9 here.
613 */
614 giconv = _get_iconv (_ICONV_ISO8859_9, _ICONV_UTF8);
615
616 GST_INFO ("Trying encoding ISO 8859-9");
617 converted_str = convert_to_utf8 (text, length, 0, giconv, FALSE, &error);
618 if (error != NULL) {
619 GST_WARNING
620 ("Could not convert string while assuming encoding ISO 8859-9: %s",
621 error->message);
622 g_error_free (error);
623 goto failed;
624 }
625 } else
626 goto failed;
627 }
628
629 beach:
630 return converted_str;
631
632 failed:
633 {
634 text += start_text;
635 return g_strndup (text, length - start_text);
636 }
637 }
638
639 gchar *
convert_lang_code(guint8 * data)640 convert_lang_code (guint8 * data)
641 {
642 gchar *code;
643 /* the iso language code and country code is always 3 byte long */
644 code = g_malloc0 (4);
645 memcpy (code, data, 3);
646
647 return code;
648 }
649
650 void
_packetize_descriptor_array(GPtrArray * array,guint8 ** out_data)651 _packetize_descriptor_array (GPtrArray * array, guint8 ** out_data)
652 {
653 guint i;
654 GstMpegtsDescriptor *descriptor;
655
656 g_return_if_fail (out_data != NULL);
657 g_return_if_fail (*out_data != NULL);
658
659 if (array == NULL)
660 return;
661
662 for (i = 0; i < array->len; i++) {
663 descriptor = g_ptr_array_index (array, i);
664
665 memcpy (*out_data, descriptor->data, descriptor->length + 2);
666 *out_data += descriptor->length + 2;
667 }
668 }
669
670 GstMpegtsDescriptor *
_new_descriptor(guint8 tag,guint8 length)671 _new_descriptor (guint8 tag, guint8 length)
672 {
673 GstMpegtsDescriptor *descriptor;
674 guint8 *data;
675
676 descriptor = g_slice_new (GstMpegtsDescriptor);
677
678 descriptor->tag = tag;
679 descriptor->tag_extension = 0;
680 descriptor->length = length;
681
682 descriptor->data = g_malloc (length + 2);
683
684 data = descriptor->data;
685
686 *data++ = descriptor->tag;
687 *data = descriptor->length;
688
689 return descriptor;
690 }
691
692 GstMpegtsDescriptor *
_new_descriptor_with_extension(guint8 tag,guint8 tag_extension,guint8 length)693 _new_descriptor_with_extension (guint8 tag, guint8 tag_extension, guint8 length)
694 {
695 GstMpegtsDescriptor *descriptor;
696 guint8 *data;
697
698 descriptor = g_slice_new (GstMpegtsDescriptor);
699
700 descriptor->tag = tag;
701 descriptor->tag_extension = tag_extension;
702 descriptor->length = length + 1;
703
704 descriptor->data = g_malloc (length + 3);
705
706 data = descriptor->data;
707
708 *data++ = descriptor->tag;
709 *data++ = descriptor->length;
710 *data = descriptor->tag_extension;
711
712 return descriptor;
713 }
714
715 static GstMpegtsDescriptor *
_copy_descriptor(GstMpegtsDescriptor * desc)716 _copy_descriptor (GstMpegtsDescriptor * desc)
717 {
718 GstMpegtsDescriptor *copy;
719
720 copy = g_slice_dup (GstMpegtsDescriptor, desc);
721 copy->data = g_memdup (desc->data, desc->length + 2);
722
723 return copy;
724 }
725
726 /**
727 * gst_mpegts_descriptor_free:
728 * @desc: The descriptor to free
729 *
730 * Frees @desc
731 */
732 void
gst_mpegts_descriptor_free(GstMpegtsDescriptor * desc)733 gst_mpegts_descriptor_free (GstMpegtsDescriptor * desc)
734 {
735 g_free ((gpointer) desc->data);
736 g_slice_free (GstMpegtsDescriptor, desc);
737 }
738
739 G_DEFINE_BOXED_TYPE (GstMpegtsDescriptor, gst_mpegts_descriptor,
740 (GBoxedCopyFunc) _copy_descriptor,
741 (GBoxedFreeFunc) gst_mpegts_descriptor_free);
742
743 /**
744 * gst_mpegts_parse_descriptors:
745 * @buffer: (transfer none): descriptors to parse
746 * @buf_len: Size of @buffer
747 *
748 * Parses the descriptors present in @buffer and returns them as an
749 * array.
750 *
751 * Note: The data provided in @buffer will not be copied.
752 *
753 * Returns: (transfer full) (element-type GstMpegtsDescriptor): an
754 * array of the parsed descriptors or %NULL if there was an error.
755 * Release with #g_array_unref when done with it.
756 */
757 GPtrArray *
gst_mpegts_parse_descriptors(guint8 * buffer,gsize buf_len)758 gst_mpegts_parse_descriptors (guint8 * buffer, gsize buf_len)
759 {
760 GPtrArray *res;
761 guint8 length;
762 guint8 *data;
763 guint i, nb_desc = 0;
764
765 /* fast-path */
766 if (buf_len == 0)
767 return g_ptr_array_new ();
768
769 data = buffer;
770
771 GST_MEMDUMP ("Full descriptor array", buffer, buf_len);
772
773 while (data - buffer < buf_len) {
774 data++; /* skip tag */
775 length = *data++;
776
777 if (data - buffer > buf_len) {
778 GST_WARNING ("invalid descriptor length %d now at %d max %"
779 G_GSIZE_FORMAT, length, (gint) (data - buffer), buf_len);
780 return NULL;
781 }
782
783 data += length;
784 nb_desc++;
785 }
786
787 GST_DEBUG ("Saw %d descriptors, read %" G_GSIZE_FORMAT " bytes",
788 nb_desc, (gsize) (data - buffer));
789
790 if (data - buffer != buf_len) {
791 GST_WARNING ("descriptors size %d expected %" G_GSIZE_FORMAT,
792 (gint) (data - buffer), buf_len);
793 return NULL;
794 }
795
796 res =
797 g_ptr_array_new_full (nb_desc + 1,
798 (GDestroyNotify) gst_mpegts_descriptor_free);
799
800 data = buffer;
801
802 for (i = 0; i < nb_desc; i++) {
803 GstMpegtsDescriptor *desc = g_slice_new0 (GstMpegtsDescriptor);
804
805 desc->data = data;
806 desc->tag = *data++;
807 desc->length = *data++;
808 /* Copy the data now that we known the size */
809 desc->data = g_memdup (desc->data, desc->length + 2);
810 GST_LOG ("descriptor 0x%02x length:%d", desc->tag, desc->length);
811 GST_MEMDUMP ("descriptor", desc->data + 2, desc->length);
812 /* extended descriptors */
813 if (G_UNLIKELY (desc->tag == 0x7f))
814 desc->tag_extension = *data;
815
816 data += desc->length;
817
818 /* Set the descriptor in the array */
819 g_ptr_array_index (res, i) = desc;
820 }
821
822 res->len = nb_desc;
823
824 return res;
825 }
826
827 /**
828 * gst_mpegts_find_descriptor:
829 * @descriptors: (element-type GstMpegtsDescriptor) (transfer none): an array
830 * of #GstMpegtsDescriptor
831 * @tag: the tag to look for
832 *
833 * Finds the first descriptor of type @tag in the array.
834 *
835 * Note: To look for descriptors that can be present more than once in an
836 * array of descriptors, iterate the #GArray manually.
837 *
838 * Returns: (transfer none): the first descriptor matchin @tag, else %NULL.
839 */
840 const GstMpegtsDescriptor *
gst_mpegts_find_descriptor(GPtrArray * descriptors,guint8 tag)841 gst_mpegts_find_descriptor (GPtrArray * descriptors, guint8 tag)
842 {
843 guint i, nb_desc;
844
845 g_return_val_if_fail (descriptors != NULL, NULL);
846
847 nb_desc = descriptors->len;
848 for (i = 0; i < nb_desc; i++) {
849 GstMpegtsDescriptor *desc = g_ptr_array_index (descriptors, i);
850 if (desc->tag == tag)
851 return (const GstMpegtsDescriptor *) desc;
852 }
853 return NULL;
854 }
855
856 /* GST_MTS_DESC_REGISTRATION (0x05) */
857 /**
858 * gst_mpegts_descriptor_from_registration:
859 * @format_identifier: (transfer none): a 4 character format identifier string
860 * @additional_info: (transfer none) (allow-none) (array length=additional_info_length): pointer to optional additional info
861 * @additional_info_length: length of the optional @additional_info
862 *
863 * Creates a %GST_MTS_DESC_REGISTRATION #GstMpegtsDescriptor
864 *
865 * Return: #GstMpegtsDescriptor, %NULL on failure
866 */
867 GstMpegtsDescriptor *
gst_mpegts_descriptor_from_registration(const gchar * format_identifier,guint8 * additional_info,gsize additional_info_length)868 gst_mpegts_descriptor_from_registration (const gchar * format_identifier,
869 guint8 * additional_info, gsize additional_info_length)
870 {
871 GstMpegtsDescriptor *descriptor;
872
873 g_return_val_if_fail (format_identifier != NULL, NULL);
874 g_return_val_if_fail (additional_info_length > 0 || !additional_info, NULL);
875
876 descriptor = _new_descriptor (GST_MTS_DESC_REGISTRATION,
877 4 + additional_info_length);
878
879 memcpy (descriptor->data + 2, format_identifier, 4);
880 if (additional_info && (additional_info_length > 0))
881 memcpy (descriptor->data + 6, additional_info, additional_info_length);
882
883 return descriptor;
884 }
885
886 /* GST_MTS_DESC_CA (0x09) */
887
888 /**
889 * gst_mpegts_descriptor_parse_ca:
890 * @descriptor: a %GST_MTS_DESC_CA #GstMpegtsDescriptor
891 * @ca_system_id: (out): the type of CA system used
892 * @ca_pid: (out): The PID containing ECM or EMM data
893 * @private_data: (out) (allow-none) (array length=private_data_size): The private data
894 * @private_data_size: (out) (allow-none): The size of @private_data in bytes
895 *
896 * Extracts the Conditional Access information from @descriptor.
897 *
898 * Returns: %TRUE if parsing succeeded, else %FALSE.
899 */
900
901 gboolean
gst_mpegts_descriptor_parse_ca(GstMpegtsDescriptor * descriptor,guint16 * ca_system_id,guint16 * ca_pid,const guint8 ** private_data,gsize * private_data_size)902 gst_mpegts_descriptor_parse_ca (GstMpegtsDescriptor * descriptor,
903 guint16 * ca_system_id, guint16 * ca_pid,
904 const guint8 ** private_data, gsize * private_data_size)
905 {
906 guint8 *data;
907
908 g_return_val_if_fail (descriptor != NULL && ca_system_id != NULL
909 && ca_pid != NULL, FALSE);
910 /* The smallest CA is 4 bytes (though not having any private data
911 * sounds a bit ... weird) */
912 __common_desc_checks (descriptor, GST_MTS_DESC_CA, 4, FALSE);
913
914 data = (guint8 *) descriptor->data + 2;
915 *ca_system_id = GST_READ_UINT16_BE (data);
916 data += 2;
917 *ca_pid = GST_READ_UINT16_BE (data) & 0x1fff;
918 data += 2;
919 if (private_data && private_data_size) {
920 *private_data = data;
921 *private_data_size = descriptor->length - 4;
922 }
923
924 return TRUE;
925 }
926
927 /* GST_MTS_DESC_ISO_639_LANGUAGE (0x0A) */
928 static GstMpegtsISO639LanguageDescriptor *
_gst_mpegts_iso_639_language_descriptor_copy(GstMpegtsISO639LanguageDescriptor * source)929 _gst_mpegts_iso_639_language_descriptor_copy (GstMpegtsISO639LanguageDescriptor
930 * source)
931 {
932 GstMpegtsISO639LanguageDescriptor *copy;
933 guint i;
934
935 copy = g_slice_dup (GstMpegtsISO639LanguageDescriptor, source);
936
937 for (i = 0; i < source->nb_language; i++) {
938 copy->language[i] = g_strdup (source->language[i]);
939 }
940
941 return copy;
942 }
943
944 void
gst_mpegts_iso_639_language_descriptor_free(GstMpegtsISO639LanguageDescriptor * desc)945 gst_mpegts_iso_639_language_descriptor_free (GstMpegtsISO639LanguageDescriptor
946 * desc)
947 {
948 guint i;
949
950 for (i = 0; i < desc->nb_language; i++) {
951 g_free (desc->language[i]);
952 }
953 g_slice_free (GstMpegtsISO639LanguageDescriptor, desc);
954 }
955
956 G_DEFINE_BOXED_TYPE (GstMpegtsISO639LanguageDescriptor,
957 gst_mpegts_iso_639_language,
958 (GBoxedCopyFunc) _gst_mpegts_iso_639_language_descriptor_copy,
959 (GFreeFunc) gst_mpegts_iso_639_language_descriptor_free);
960
961 /**
962 * gst_mpegts_descriptor_parse_iso_639_language:
963 * @descriptor: a %GST_MTS_DESC_ISO_639_LANGUAGE #GstMpegtsDescriptor
964 * @res: (out) (transfer full): the #GstMpegtsISO639LanguageDescriptor to fill
965 *
966 * Extracts the iso 639-2 language information from @descriptor.
967 *
968 * Note: Use #gst_tag_get_language_code if you want to get the the
969 * ISO 639-1 language code from the returned ISO 639-2 one.
970 *
971 * Returns: %TRUE if parsing succeeded, else %FALSE.
972 */
973 gboolean
gst_mpegts_descriptor_parse_iso_639_language(const GstMpegtsDescriptor * descriptor,GstMpegtsISO639LanguageDescriptor ** desc)974 gst_mpegts_descriptor_parse_iso_639_language (const GstMpegtsDescriptor *
975 descriptor, GstMpegtsISO639LanguageDescriptor ** desc)
976 {
977 guint i;
978 guint8 *data;
979 GstMpegtsISO639LanguageDescriptor *res;
980
981 g_return_val_if_fail (descriptor != NULL && desc != NULL, FALSE);
982 /* This descriptor can be empty, no size check needed */
983 __common_desc_check_base (descriptor, GST_MTS_DESC_ISO_639_LANGUAGE, FALSE);
984
985 data = (guint8 *) descriptor->data + 2;
986
987 res = g_slice_new0 (GstMpegtsISO639LanguageDescriptor);
988
989 /* Each language is 3 + 1 bytes */
990 res->nb_language = descriptor->length / 4;
991 for (i = 0; i < res->nb_language; i++) {
992 res->language[i] = convert_lang_code (data);
993 res->audio_type[i] = data[3];
994 data += 4;
995 }
996
997 *desc = res;
998
999 return TRUE;
1000
1001 }
1002
1003 /**
1004 * gst_mpegts_descriptor_parse_iso_639_language_idx:
1005 * @descriptor: a %GST_MTS_DESC_ISO_639_LANGUAGE #GstMpegtsDescriptor
1006 * @idx: Table id of the language to parse
1007 * @lang: (out) (transfer full): 4-byte gchar array to hold the language code
1008 * @audio_type: (out) (transfer none) (allow-none): the #GstMpegtsIso639AudioType to set
1009 *
1010 * Extracts the iso 639-2 language information from specific table id in @descriptor.
1011 *
1012 * Note: Use #gst_tag_get_language_code if you want to get the the
1013 * ISO 639-1 language code from the returned ISO 639-2 one.
1014 *
1015 * Returns: %TRUE if parsing succeeded, else %FALSE.
1016 */
1017 gboolean
gst_mpegts_descriptor_parse_iso_639_language_idx(const GstMpegtsDescriptor * descriptor,guint idx,gchar ** lang,GstMpegtsIso639AudioType * audio_type)1018 gst_mpegts_descriptor_parse_iso_639_language_idx (const GstMpegtsDescriptor *
1019 descriptor, guint idx, gchar ** lang, GstMpegtsIso639AudioType * audio_type)
1020 {
1021 guint8 *data;
1022
1023 g_return_val_if_fail (descriptor != NULL && lang != NULL, FALSE);
1024 /* This descriptor can be empty, no size check needed */
1025 __common_desc_check_base (descriptor, GST_MTS_DESC_ISO_639_LANGUAGE, FALSE);
1026
1027 if (descriptor->length / 4 <= idx)
1028 return FALSE;
1029
1030 data = (guint8 *) descriptor->data + 2 + idx * 4;
1031
1032 *lang = convert_lang_code (data);
1033
1034 data += 3;
1035
1036 if (audio_type)
1037 *audio_type = *data;
1038
1039 return TRUE;
1040 }
1041
1042 /**
1043 * gst_mpegts_descriptor_parse_iso_639_language_nb:
1044 * @descriptor: a %GST_MTS_DESC_ISO_639_LANGUAGE #GstMpegtsDescriptor
1045 *
1046 * Returns: The number of languages in @descriptor
1047 */
1048 guint
gst_mpegts_descriptor_parse_iso_639_language_nb(const GstMpegtsDescriptor * descriptor)1049 gst_mpegts_descriptor_parse_iso_639_language_nb (const GstMpegtsDescriptor *
1050 descriptor)
1051 {
1052 g_return_val_if_fail (descriptor != NULL, 0);
1053 /* This descriptor can be empty, no size check needed */
1054 __common_desc_check_base (descriptor, GST_MTS_DESC_ISO_639_LANGUAGE, FALSE);
1055
1056 return descriptor->length / 4;
1057 }
1058
1059 /**
1060 * gst_mpegts_descriptor_from_iso_639_language:
1061 * @language: (transfer none): ISO-639-2 language 3-char code
1062 *
1063 * Creates a %GST_MTS_DESC_ISO_639_LANGUAGE #GstMpegtsDescriptor with
1064 * a single language
1065 *
1066 * Return: #GstMpegtsDescriptor, %NULL on failure
1067 */
1068 GstMpegtsDescriptor *
gst_mpegts_descriptor_from_iso_639_language(const gchar * language)1069 gst_mpegts_descriptor_from_iso_639_language (const gchar * language)
1070 {
1071 GstMpegtsDescriptor *descriptor;
1072
1073 g_return_val_if_fail (language != NULL, NULL);
1074
1075 descriptor = _new_descriptor (GST_MTS_DESC_ISO_639_LANGUAGE, 4); /* a language takes 4 bytes */
1076
1077 memcpy (descriptor->data + 2, language, 3);
1078 descriptor->data[2 + 3] = 0; /* set audio type to undefined */
1079
1080 return descriptor;
1081 }
1082
1083 DEFINE_STATIC_COPY_FUNCTION (GstMpegtsLogicalChannelDescriptor,
1084 gst_mpegts_logical_channel_descriptor);
1085
1086 DEFINE_STATIC_FREE_FUNCTION (GstMpegtsLogicalChannelDescriptor,
1087 gst_mpegts_logical_channel_descriptor);
1088
1089 G_DEFINE_BOXED_TYPE (GstMpegtsLogicalChannelDescriptor,
1090 gst_mpegts_logical_channel_descriptor,
1091 (GBoxedCopyFunc) _gst_mpegts_logical_channel_descriptor_copy,
1092 (GFreeFunc) _gst_mpegts_logical_channel_descriptor_free);
1093
1094 DEFINE_STATIC_COPY_FUNCTION (GstMpegtsLogicalChannel,
1095 gst_mpegts_logical_channel);
1096
1097 DEFINE_STATIC_FREE_FUNCTION (GstMpegtsLogicalChannel,
1098 gst_mpegts_logical_channel);
1099
1100 G_DEFINE_BOXED_TYPE (GstMpegtsLogicalChannel,
1101 gst_mpegts_logical_channel,
1102 (GBoxedCopyFunc) _gst_mpegts_logical_channel_copy,
1103 (GFreeFunc) _gst_mpegts_logical_channel_free);
1104
1105 /**
1106 * gst_mpegts_descriptor_parse_logical_channel:
1107 * @descriptor: a %GST_MTS_DESC_DTG_LOGICAL_CHANNEL #GstMpegtsDescriptor
1108 * @res: (out) (transfer none): the #GstMpegtsLogicalChannelDescriptor to fill
1109 *
1110 * Extracts the logical channels from @descriptor.
1111 *
1112 * Returns: %TRUE if parsing succeeded, else %FALSE.
1113 */
1114 gboolean
gst_mpegts_descriptor_parse_logical_channel(const GstMpegtsDescriptor * descriptor,GstMpegtsLogicalChannelDescriptor * res)1115 gst_mpegts_descriptor_parse_logical_channel (const GstMpegtsDescriptor *
1116 descriptor, GstMpegtsLogicalChannelDescriptor * res)
1117 {
1118 guint i;
1119 guint8 *data;
1120
1121 g_return_val_if_fail (descriptor != NULL && res != NULL, FALSE);
1122 /* This descriptor loop can be empty, no size check required */
1123 __common_desc_check_base (descriptor, GST_MTS_DESC_DTG_LOGICAL_CHANNEL,
1124 FALSE);
1125
1126 data = (guint8 *) descriptor->data + 2;
1127
1128 res->nb_channels = descriptor->length / 4;
1129
1130 for (i = 0; i < res->nb_channels; i++) {
1131 res->channels[i].service_id = GST_READ_UINT16_BE (data);
1132 data += 2;
1133 res->channels[i].visible_service = *data >> 7;
1134 res->channels[i].logical_channel_number =
1135 GST_READ_UINT16_BE (data) & 0x03ff;
1136 data += 2;
1137 }
1138
1139 return TRUE;
1140 }
1141
1142 /**
1143 * gst_mpegts_descriptor_from_custom:
1144 * @tag: descriptor tag
1145 * @data: (transfer none) (array length=length): descriptor data (after tag and length field)
1146 * @length: length of @data
1147 *
1148 * Creates a #GstMpegtsDescriptor with custom @tag and @data
1149 *
1150 * Returns: #GstMpegtsDescriptor
1151 */
1152 GstMpegtsDescriptor *
gst_mpegts_descriptor_from_custom(guint8 tag,const guint8 * data,gsize length)1153 gst_mpegts_descriptor_from_custom (guint8 tag, const guint8 * data,
1154 gsize length)
1155 {
1156 GstMpegtsDescriptor *descriptor;
1157
1158 g_return_val_if_fail (length > 0 || !data, NULL);
1159
1160 descriptor = _new_descriptor (tag, length);
1161
1162 if (data && (length > 0))
1163 memcpy (descriptor->data + 2, data, length);
1164
1165 return descriptor;
1166 }
1167
1168 /**
1169 * gst_mpegts_descriptor_from_custom_with_extension:
1170 * @tag: descriptor tag
1171 * @tag_extension: descriptor tag extension
1172 * @data: (transfer none) (array length=length): descriptor data (after tag and length field)
1173 * @length: length of @data
1174 *
1175 * Creates a #GstMpegtsDescriptor with custom @tag, @tag_extension and @data
1176 *
1177 * Returns: #GstMpegtsDescriptor
1178 */
1179 GstMpegtsDescriptor *
gst_mpegts_descriptor_from_custom_with_extension(guint8 tag,guint8 tag_extension,const guint8 * data,gsize length)1180 gst_mpegts_descriptor_from_custom_with_extension (guint8 tag,
1181 guint8 tag_extension, const guint8 * data, gsize length)
1182 {
1183 GstMpegtsDescriptor *descriptor;
1184
1185 descriptor = _new_descriptor_with_extension (tag, tag_extension, length);
1186
1187 if (data && (length > 0))
1188 memcpy (descriptor->data + 3, data, length);
1189
1190 return descriptor;
1191 }
1192