3 * Copyright (C) 2013 Edward Hervey
4 * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
5 * Copyright (C) 2007 Alessandro Decina
7 * Author: Youness Alaoui <youness.alaoui@collabora.co.uk>, Collabora Ltd.
8 * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
9 * Author: Edward Hervey <bilboed@bilboed.com>, Collabora Ltd.
12 * Alessandro Decina <alessandro@nnva.org>
13 * Zaheer Abbas Merali <zaheerabbas at merali dot org>
14 * Edward Hervey <edward@collabora.com>
16 * This library is free softwagre; you can redistribute it and/or
17 * modify it under the terms of the GNU Library General Public
18 * License as published by the Free Software Foundation; either
19 * version 2 of the License, or (at your option) any later version.
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Library General Public License for more details.
26 * You should have received a copy of the GNU Library General Public
27 * License along with this library; if not, write to the
28 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
29 * Boston, MA 02110-1301, USA.
36 #include "gstmpegts-private.h"
40 * @title: Mpeg-ts helper library
41 * @short_description: Mpeg-ts helper library for plugins and applications
42 * @include: gst/mpegts/mpegts.h
46 * SECTION:gstmpegtssection
47 * @title: Base MPEG-TS sections
48 * @short_description: Sections for ITU H.222.0 | ISO/IEC 13818-1
49 * @include: gst/mpegts/mpegts.h
51 * For more details, refer to the ITU H.222.0 or ISO/IEC 13818-1 specifications
52 * and other specifications mentionned in the documentation.
55 /* FIXME : Move this to proper file once we have a C file for it */
57 * SECTION:gst-atsc-section
58 * @title: ATSC variants of MPEG-TS sections
59 * @short_description: Sections for the various ATSC specifications
60 * @include: gst/mpegts/mpegts.h
67 * * Check minimum size for section parsing in the various
68 * gst_mpegts_section_get_<tabld>() methods
70 * * Implement parsing code for
76 GST_DEBUG_CATEGORY (gst_mpegts_debug);
78 static GQuark QUARK_PAT;
79 static GQuark QUARK_CAT;
80 static GQuark QUARK_BAT;
81 static GQuark QUARK_PMT;
82 static GQuark QUARK_NIT;
83 static GQuark QUARK_SDT;
84 static GQuark QUARK_EIT;
85 static GQuark QUARK_TDT;
86 static GQuark QUARK_TOT;
87 static GQuark QUARK_SECTION;
89 static GType _gst_mpegts_section_type = 0;
90 #define MPEG_TYPE_TS_SECTION (_gst_mpegts_section_type)
91 GST_DEFINE_MINI_OBJECT_TYPE (GstMpegTsSection, gst_mpegts_section);
93 static const guint32 crc_tab[256] = {
94 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
95 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
96 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
97 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
98 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
99 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
100 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
101 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
102 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
103 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
104 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
105 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
106 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
107 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
108 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
109 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
110 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
111 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
112 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
113 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
114 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
115 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
116 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
117 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
118 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
119 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
120 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
121 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
122 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
123 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
124 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
125 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
126 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
127 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
128 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
129 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
130 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
131 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
132 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
133 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
134 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
135 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
136 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
139 /* _calc_crc32 relicenced to LGPL from fluendo ts demuxer */
141 _calc_crc32 (const guint8 * data, guint datalen)
144 guint32 crc = 0xffffffff;
146 for (i = 0; i < datalen; i++) {
147 crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff];
153 __common_desc_checks (GstMpegTsSection * section, guint min_size,
154 GstMpegTsParseFunc parsefunc, GDestroyNotify destroynotify)
158 /* Check section is big enough */
159 if (section->section_length < min_size) {
161 ("PID:0x%04x table_id:0x%02x, section too small (Got %d, need at least %d)",
162 section->pid, section->table_id, section->section_length, min_size);
166 /* If section has a CRC, check it */
167 if (!section->short_section
168 && (_calc_crc32 (section->data, section->section_length) != 0)) {
169 GST_WARNING ("PID:0x%04x table_id:0x%02x, Bad CRC on section", section->pid,
174 /* Finally parse and set the destroy notify */
175 res = parsefunc (section);
177 GST_WARNING ("PID:0x%04x table_id:0x%02x, Failed to parse section",
178 section->pid, section->table_id);
180 section->destroy_parsed = destroynotify;
186 * GENERIC MPEG-TS SECTION
189 _gst_mpegts_section_free (GstMpegTsSection * section)
191 GST_DEBUG ("Freeing section type %d", section->section_type);
193 if (section->cached_parsed && section->destroy_parsed)
194 section->destroy_parsed (section->cached_parsed);
197 g_free (section->data);
199 g_slice_free (GstMpegTsSection, section);
202 static GstMpegTsSection *
203 _gst_mpegts_section_copy (GstMpegTsSection * section)
205 GstMpegTsSection *copy;
207 copy = g_slice_new0 (GstMpegTsSection);
208 gst_mini_object_init (GST_MINI_OBJECT_CAST (copy), 0, MPEG_TYPE_TS_SECTION,
209 (GstMiniObjectCopyFunction) _gst_mpegts_section_copy, NULL,
210 (GstMiniObjectFreeFunction) _gst_mpegts_section_free);
212 copy->section_type = section->section_type;
213 copy->pid = section->pid;
214 copy->table_id = section->table_id;
215 copy->subtable_extension = section->subtable_extension;
216 copy->version_number = section->version_number;
217 copy->current_next_indicator = section->current_next_indicator;
218 copy->section_number = section->section_number;
219 copy->last_section_number = section->last_section_number;
220 copy->crc = section->crc;
222 copy->data = g_memdup (section->data, section->section_length);
223 copy->section_length = section->section_length;
224 /* Note: We do not copy the cached parsed item, it will be
225 * reconstructed on that copy */
226 copy->cached_parsed = NULL;
227 copy->offset = section->offset;
228 copy->short_section = section->short_section;
235 * gst_message_parse_mpegts_section:
236 * @message: a #GstMessage
238 * Returns the #GstMpegTsSection contained in a message.
240 * Returns: (transfer full): the contained #GstMpegTsSection, or %NULL.
243 gst_message_parse_mpegts_section (GstMessage * message)
245 const GstStructure *st;
246 GstMpegTsSection *section;
248 if (message->type != GST_MESSAGE_ELEMENT)
251 st = gst_message_get_structure (message);
252 /* FIXME : Add checks against know section names */
253 if (!gst_structure_id_get (st, QUARK_SECTION, GST_TYPE_MPEGTS_SECTION,
260 static GstStructure *
261 _mpegts_section_get_structure (GstMpegTsSection * section)
266 switch (section->section_type) {
267 case GST_MPEGTS_SECTION_PAT:
270 case GST_MPEGTS_SECTION_PMT:
273 case GST_MPEGTS_SECTION_CAT:
276 case GST_MPEGTS_SECTION_EIT:
279 case GST_MPEGTS_SECTION_BAT:
282 case GST_MPEGTS_SECTION_NIT:
285 case GST_MPEGTS_SECTION_SDT:
288 case GST_MPEGTS_SECTION_TDT:
291 case GST_MPEGTS_SECTION_TOT:
295 GST_DEBUG ("Creating structure for unknown GstMpegTsSection");
296 quark = QUARK_SECTION;
300 st = gst_structure_new_id (quark, QUARK_SECTION, MPEG_TYPE_TS_SECTION,
307 * gst_message_new_mpegts_section:
308 * @parent: (transfer none): The creator of the message
309 * @section: (transfer none): The #GstMpegTsSection to put in a message
311 * Creates a new #GstMessage for a @GstMpegTsSection.
313 * Returns: (transfer full): The new #GstMessage to be posted, or %NULL if the
314 * section is not valid.
317 gst_message_new_mpegts_section (GstObject * parent, GstMpegTsSection * section)
322 st = _mpegts_section_get_structure (section);
324 msg = gst_message_new_element (parent, st);
330 _mpegts_section_get_event (GstMpegTsSection * section)
332 GstStructure *structure;
335 structure = _mpegts_section_get_structure (section);
337 event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
343 * gst_event_parse_mpegts_section:
344 * @event: (transfer none): #GstEvent containing a #GstMpegTsSection
346 * Extracts the #GstMpegTsSection contained in the @event #GstEvent
348 * Returns: (transfer full): The extracted #GstMpegTsSection
351 gst_event_parse_mpegts_section (GstEvent * event)
353 const GstStructure *structure;
354 GstMpegTsSection *section;
356 structure = gst_event_get_structure (event);
358 if (!gst_structure_id_get (structure, QUARK_SECTION, MPEG_TYPE_TS_SECTION,
366 * gst_mpegts_section_send_event:
367 * @element: (transfer none): The #GstElement to send to section event to
368 * @section: (transfer none): The #GstMpegTsSection to put in the event
370 * Creates a custom #GstEvent with a @GstMpegTsSection.
371 * The #GstEvent is sent to the @element #GstElement.
373 * Returns: %TRUE if the event is sent
376 gst_mpegts_section_send_event (GstMpegTsSection * section, GstElement * element)
380 g_return_val_if_fail (section != NULL, FALSE);
381 g_return_val_if_fail (element != NULL, FALSE);
383 event = _mpegts_section_get_event (section);
385 if (!gst_element_send_event (element, event)) {
386 gst_event_unref (event);
393 static GstMpegTsPatProgram *
394 _mpegts_pat_program_copy (GstMpegTsPatProgram * orig)
396 return g_slice_dup (GstMpegTsPatProgram, orig);
400 _mpegts_pat_program_free (GstMpegTsPatProgram * orig)
402 g_slice_free (GstMpegTsPatProgram, orig);
405 G_DEFINE_BOXED_TYPE (GstMpegTsPatProgram, gst_mpegts_pat_program,
406 (GBoxedCopyFunc) _mpegts_pat_program_copy,
407 (GFreeFunc) _mpegts_pat_program_free);
409 /* Program Association Table */
411 _parse_pat (GstMpegTsSection * section)
414 guint16 i = 0, nb_programs;
415 GstMpegTsPatProgram *program;
418 /* Skip already parsed data */
419 data = section->data + 8;
421 /* stop at the CRC */
422 end = section->data + section->section_length;
424 /* Initialize program list */
425 nb_programs = (end - 4 - data) / 4;
427 g_ptr_array_new_full (nb_programs,
428 (GDestroyNotify) _mpegts_pat_program_free);
430 while (data < end - 4) {
431 program = g_slice_new0 (GstMpegTsPatProgram);
432 program->program_number = GST_READ_UINT16_BE (data);
435 program->network_or_program_map_PID = GST_READ_UINT16_BE (data) & 0x1FFF;
438 g_ptr_array_index (pat, i) = program;
442 pat->len = nb_programs;
444 if (data != end - 4) {
445 GST_ERROR ("at the end of PAT data != end - 4");
446 g_ptr_array_unref (pat);
451 return (gpointer) pat;
455 * gst_mpegts_section_get_pat:
456 * @section: a #GstMpegTsSection of type %GST_MPEGTS_SECTION_PAT
458 * Parses a Program Association Table (ITU H.222.0, ISO/IEC 13818-1).
460 * Returns the array of #GstMpegTsPatProgram contained in the section.
462 * Note: The PAT "transport_id" field corresponds to the "subtable_extension"
463 * field of the provided @section.
465 * Returns: (transfer container) (element-type GstMpegTsPatProgram): The
466 * #GstMpegTsPatProgram contained in the section, or %NULL if an error
467 * happened. Release with #g_ptr_array_unref when done.
470 gst_mpegts_section_get_pat (GstMpegTsSection * section)
472 g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_PAT, NULL);
473 g_return_val_if_fail (section->cached_parsed || section->data, NULL);
475 if (!section->cached_parsed)
476 section->cached_parsed =
477 __common_desc_checks (section, 12, _parse_pat,
478 (GDestroyNotify) g_ptr_array_unref);
480 if (section->cached_parsed)
481 return g_ptr_array_ref ((GPtrArray *) section->cached_parsed);
486 /* Program Map Table */
488 static GstMpegTsPMTStream *
489 _gst_mpegts_pmt_stream_copy (GstMpegTsPMTStream * pmt)
491 GstMpegTsPMTStream *copy;
493 copy = g_slice_dup (GstMpegTsPMTStream, pmt);
494 copy->descriptors = g_ptr_array_ref (pmt->descriptors);
500 _gst_mpegts_pmt_stream_free (GstMpegTsPMTStream * pmt)
502 g_ptr_array_unref (pmt->descriptors);
503 g_slice_free (GstMpegTsPMTStream, pmt);
506 G_DEFINE_BOXED_TYPE (GstMpegTsPMTStream, gst_mpegts_pmt_stream,
507 (GBoxedCopyFunc) _gst_mpegts_pmt_stream_copy,
508 (GFreeFunc) _gst_mpegts_pmt_stream_free);
510 static GstMpegTsPMT *
511 _gst_mpegts_pmt_copy (GstMpegTsPMT * pmt)
515 copy = g_slice_dup (GstMpegTsPMT, pmt);
516 copy->descriptors = g_ptr_array_ref (pmt->descriptors);
517 copy->streams = g_ptr_array_ref (pmt->streams);
523 _gst_mpegts_pmt_free (GstMpegTsPMT * pmt)
525 g_ptr_array_unref (pmt->descriptors);
526 g_ptr_array_unref (pmt->streams);
527 g_slice_free (GstMpegTsPMT, pmt);
530 G_DEFINE_BOXED_TYPE (GstMpegTsPMT, gst_mpegts_pmt,
531 (GBoxedCopyFunc) _gst_mpegts_pmt_copy, (GFreeFunc) _gst_mpegts_pmt_free);
535 _parse_pmt (GstMpegTsSection * section)
537 GstMpegTsPMT *pmt = NULL;
538 guint i = 0, allocated_streams = 8;
540 guint program_info_length;
541 guint stream_info_length;
543 pmt = g_slice_new0 (GstMpegTsPMT);
545 data = section->data;
546 end = data + section->section_length;
548 GST_DEBUG ("Parsing %d Program Map Table", section->subtable_extension);
550 /* Skip already parsed data */
553 pmt->pcr_pid = GST_READ_UINT16_BE (data) & 0x1FFF;
556 program_info_length = GST_READ_UINT16_BE (data) & 0x0FFF;
559 /* check that the buffer is large enough to contain at least
560 * program_info_length bytes + CRC */
561 if (program_info_length && (data + program_info_length + 4 > end)) {
562 GST_WARNING ("PID %d invalid program info length %d left %d",
563 section->pid, program_info_length, (gint) (end - data));
566 pmt->descriptors = gst_mpegts_parse_descriptors (data, program_info_length);
567 if (pmt->descriptors == NULL)
569 data += program_info_length;
572 g_ptr_array_new_full (allocated_streams,
573 (GDestroyNotify) _gst_mpegts_pmt_stream_free);
575 /* parse entries, cycle until there's space for another entry (at least 5
576 * bytes) plus the CRC */
577 while (data <= end - 4 - 5) {
578 GstMpegTsPMTStream *stream = g_slice_new0 (GstMpegTsPMTStream);
580 g_ptr_array_add (pmt->streams, stream);
582 stream->stream_type = *data++;
583 GST_DEBUG ("[%d] Stream type 0x%02x found", i, stream->stream_type);
585 stream->pid = GST_READ_UINT16_BE (data) & 0x1FFF;
588 stream_info_length = GST_READ_UINT16_BE (data) & 0x0FFF;
591 if (data + stream_info_length + 4 > end) {
592 GST_WARNING ("PID %d invalid stream info length %d left %d", section->pid,
593 stream_info_length, (gint) (end - data));
597 stream->descriptors =
598 gst_mpegts_parse_descriptors (data, stream_info_length);
599 if (stream->descriptors == NULL)
601 data += stream_info_length;
606 g_assert (data == end - 4);
608 return (gpointer) pmt;
612 _gst_mpegts_pmt_free (pmt);
618 * gst_mpegts_section_get_pmt:
619 * @section: a #GstMpegTsSection of type %GST_MPEGTS_SECTION_PMT
621 * Returns the #GstMpegTsPMT contained in the @section.
623 * Returns: The #GstMpegTsPMT contained in the section, or %NULL if an error
627 gst_mpegts_section_get_pmt (GstMpegTsSection * section)
629 g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_PMT, NULL);
630 g_return_val_if_fail (section->cached_parsed || section->data, NULL);
632 if (!section->cached_parsed)
633 section->cached_parsed =
634 __common_desc_checks (section, 16, _parse_pmt,
635 (GDestroyNotify) _gst_mpegts_pmt_free);
637 return (const GstMpegTsPMT *) section->cached_parsed;
641 /* Conditional Access Table */
643 _parse_cat (GstMpegTsSection * section)
648 /* Skip parts already parsed */
649 data = section->data + 8;
652 desc_len = section->section_length - 4 - 8;
653 return (gpointer) gst_mpegts_parse_descriptors (data, desc_len);
657 * gst_mpegts_section_get_cat:
658 * @section: a #GstMpegTsSection of type %GST_MPEGTS_SECTION_CAT
660 * Returns the array of #GstMpegTsDescriptor contained in the Condtional
663 * Returns: (transfer container) (element-type GstMpegTsDescriptor): The
664 * #GstMpegTsDescriptor contained in the section, or %NULL if an error
665 * happened. Release with #g_array_unref when done.
668 gst_mpegts_section_get_cat (GstMpegTsSection * section)
670 g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_CAT, NULL);
671 g_return_val_if_fail (section->cached_parsed || section->data, NULL);
673 if (!section->cached_parsed)
674 section->cached_parsed =
675 __common_desc_checks (section, 12, _parse_cat,
676 (GDestroyNotify) g_ptr_array_unref);
678 if (section->cached_parsed)
679 return g_ptr_array_ref ((GPtrArray *) section->cached_parsed);
683 /* Transport Stream Description Table (TSDT) */
685 * gst_mpegts_section_get_tsdt:
686 * @section: a #GstMpegTsSection of type %GST_MPEGTS_SECTION_TSDT
688 * Returns the array of #GstMpegTsDescriptor contained in the section
690 * Returns: (transfer container) (element-type GstMpegTsDescriptor): The
691 * #GstMpegTsDescriptor contained in the section, or %NULL if an error
692 * happened. Release with #g_array_unref when done.
695 gst_mpegts_section_get_tsdt (GstMpegTsSection * section)
697 g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_TSDT, NULL);
698 g_return_val_if_fail (section->cached_parsed || section->data, NULL);
700 if (section->cached_parsed)
701 return g_ptr_array_ref ((GPtrArray *) section->cached_parsed);
703 /* FIXME : parse TSDT */
709 * gst_mpegts_initialize:
711 * Initializes the MPEG-TS helper library. Must be called before any
715 gst_mpegts_initialize (void)
717 if (_gst_mpegts_section_type)
720 GST_DEBUG_CATEGORY_INIT (gst_mpegts_debug, "mpegts", 0,
721 "MPEG-TS helper library");
723 /* FIXME : Temporary hack to initialize section gtype */
724 _gst_mpegts_section_type = gst_mpegts_section_get_type ();
726 QUARK_PAT = g_quark_from_string ("pat");
727 QUARK_CAT = g_quark_from_string ("cat");
728 QUARK_PMT = g_quark_from_string ("pmt");
729 QUARK_NIT = g_quark_from_string ("nit");
730 QUARK_BAT = g_quark_from_string ("bat");
731 QUARK_SDT = g_quark_from_string ("sdt");
732 QUARK_EIT = g_quark_from_string ("eit");
733 QUARK_TDT = g_quark_from_string ("tdt");
734 QUARK_TOT = g_quark_from_string ("tot");
735 QUARK_SECTION = g_quark_from_string ("section");
737 __initialize_descriptors ();
740 /* FIXME : Later on we might need to use more than just the table_id
741 * to figure out which type of section this is. */
742 static GstMpegTsSectionType
743 _identify_section (guint16 pid, guint8 table_id)
746 case GST_MTS_TABLE_ID_PROGRAM_ASSOCIATION:
748 return GST_MPEGTS_SECTION_PAT;
750 case GST_MTS_TABLE_ID_CONDITIONAL_ACCESS:
752 return GST_MPEGTS_SECTION_CAT;
754 case GST_MTS_TABLE_ID_TS_PROGRAM_MAP:
755 return GST_MPEGTS_SECTION_PMT;
756 case GST_MTS_TABLE_ID_BOUQUET_ASSOCIATION:
758 return GST_MPEGTS_SECTION_BAT;
760 case GST_MTS_TABLE_ID_NETWORK_INFORMATION_ACTUAL_NETWORK:
761 case GST_MTS_TABLE_ID_NETWORK_INFORMATION_OTHER_NETWORK:
763 return GST_MPEGTS_SECTION_NIT;
765 case GST_MTS_TABLE_ID_SERVICE_DESCRIPTION_ACTUAL_TS:
766 case GST_MTS_TABLE_ID_SERVICE_DESCRIPTION_OTHER_TS:
768 return GST_MPEGTS_SECTION_SDT;
770 case GST_MTS_TABLE_ID_TIME_DATE:
772 return GST_MPEGTS_SECTION_TDT;
774 case GST_MTS_TABLE_ID_TIME_OFFSET:
776 return GST_MPEGTS_SECTION_TOT;
781 if (table_id >= GST_MTS_TABLE_ID_EVENT_INFORMATION_ACTUAL_TS_PRESENT &&
782 table_id <= GST_MTS_TABLE_ID_EVENT_INFORMATION_OTHER_TS_SCHEDULE_N) {
784 return GST_MPEGTS_SECTION_EIT;
786 return GST_MPEGTS_SECTION_UNKNOWN;
788 return GST_MPEGTS_SECTION_UNKNOWN;
793 _gst_mpegts_section_init (guint16 pid, guint8 table_id)
795 GstMpegTsSection *section;
797 section = g_slice_new0 (GstMpegTsSection);
798 gst_mini_object_init (GST_MINI_OBJECT_CAST (section), 0, MPEG_TYPE_TS_SECTION,
799 (GstMiniObjectCopyFunction) _gst_mpegts_section_copy, NULL,
800 (GstMiniObjectFreeFunction) _gst_mpegts_section_free);
803 section->table_id = table_id;
804 section->current_next_indicator = TRUE;
805 section->section_type = _identify_section (pid, table_id);
811 _packetize_common_section (GstMpegTsSection * section, gsize length)
815 section->section_length = length;
816 data = section->data = g_malloc (length);
818 /* table_id - 8 bit uimsbf */
819 *data++ = section->table_id;
821 /* section_syntax_indicator - 1 bit
823 section_length - 12 bit uimsbf */
824 GST_WRITE_UINT16_BE (data, (section->section_length - 3) | 0x7000);
826 if (!section->short_section)
831 /* subtable_extension - 16 bit uimsbf */
832 GST_WRITE_UINT16_BE (data, section->subtable_extension);
836 version_number - 5 bit uimsbf
837 current_next_indicator - 1 bit */
839 ((section->version_number & 0x1F) << 1) |
840 (section->current_next_indicator & 0x01);
842 /* section_number - 8 bit uimsbf */
843 *data++ = section->section_number;
844 /* last_section_number - 8 bit uimsbf */
845 *data++ = section->last_section_number;
849 * gst_mpegts_section_new:
850 * @pid: the PID to which this section belongs
851 * @data: (transfer full): a pointer to the beginning of the section (i.e. the first byte
852 * should contain the table_id field).
853 * @data_size: size of the @data argument.
855 * Creates a new #GstMpegTsSection from the provided @data.
857 * Note: Ensuring @data is big enough to contain the full section is the
858 * responsibility of the caller. If it is not big enough, %NULL will be
861 * Note: it is the responsibility of the caller to ensure @data does point
862 * to the beginning of the section.
864 * Returns: (transfer full): A new #GstMpegTsSection if the data was valid,
868 gst_mpegts_section_new (guint16 pid, guint8 * data, gsize data_size)
870 GstMpegTsSection *res = NULL;
873 guint16 section_length;
875 /* Check for length */
876 section_length = GST_READ_UINT16_BE (data + 1) & 0x0FFF;
877 if (G_UNLIKELY (data_size < section_length + 3))
880 /* Table id is in first byte */
883 res = _gst_mpegts_section_init (pid, table_id);
886 /* table_id (already parsed) : 8 bit */
888 /* section_syntax_indicator : 1 bit
889 * other_fields (reserved) : 3 bit*/
890 res->short_section = (*data & 0x80) == 0x00;
891 /* section_length (already parsed) : 12 bit */
892 res->section_length = section_length + 3;
893 if (!res->short_section) {
894 /* CRC is after section_length (-4 for the size of the CRC) */
895 res->crc = GST_READ_UINT32_BE (res->data + res->section_length - 4);
896 /* Skip to after section_length */
898 /* subtable extension : 16 bit */
899 res->subtable_extension = GST_READ_UINT16_BE (data);
902 * version_number : 5 bit
903 * current_next_indicator : 1 bit */
905 res->version_number = tmp >> 1 & 0x1f;
906 res->current_next_indicator = tmp & 0x01;
907 /* section_number : 8 bit */
908 res->section_number = *data++;
909 /* last_section_number : 8 bit */
910 res->last_section_number = *data;
918 ("PID 0x%04x section extends past provided data (got:%" G_GSIZE_FORMAT
919 ", need:%d)", pid, data_size, section_length + 3);
925 * gst_mpegts_section_packetize:
926 * @section: (transfer none): the #GstMpegTsSection that holds the data
927 * @output_size: (out): #gsize to hold the size of the data
929 * If the data in @section has aldready been packetized, the data pointer is returned
930 * immediately. Otherwise, the data field is allocated and populated.
932 * Returns: (transfer none): pointer to section data, or %NULL on fail
935 gst_mpegts_section_packetize (GstMpegTsSection * section, gsize * output_size)
938 g_return_val_if_fail (section != NULL, NULL);
939 g_return_val_if_fail (output_size != NULL, NULL);
940 g_return_val_if_fail (section->packetizer != NULL, NULL);
942 /* Section data has already been packetized */
944 return section->data;
946 if (!section->packetizer (section))
949 if (!section->short_section) {
950 /* Update the CRC in the last 4 bytes of the section */
951 crc = section->data + section->section_length - 4;
952 GST_WRITE_UINT32_BE (crc, _calc_crc32 (section->data, crc - section->data));
955 *output_size = section->section_length;
957 return section->data;