• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * gstmpegtssection.c -
3  * Copyright (C) 2013 Edward Hervey
4  * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
5  * Copyright (C) 2007 Alessandro Decina
6  *               2010 Edward Hervey
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.
10  *
11  * Authors:
12  *   Alessandro Decina <alessandro@nnva.org>
13  *   Zaheer Abbas Merali <zaheerabbas at merali dot org>
14  *   Edward Hervey <edward@collabora.com>
15  *
16  * This library is free software; 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.
20  *
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.
25  *
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.
30  */
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 
35 #include <string.h>
36 #include <stdlib.h>
37 
38 #include "mpegts.h"
39 #include "gstmpegts-private.h"
40 
41 /**
42  * SECTION:gstmpegtssection
43  * @title: Base MPEG-TS sections
44  * @short_description: Sections for ITU H.222.0 | ISO/IEC 13818-1
45  * @include: gst/mpegts/mpegts.h
46  *
47  * ## Generic usage of sections with %GstMpegtsSection
48  *
49  * The %GstMpegtsSection object is the representation of MPEG-TS Section (SI or
50  * PSI).
51  *
52  * Various elements can post those on the bus via %GstMessage of type
53  * %GST_MESSAGE_ELEMENT. The gst_message_parse_mpegts_section() function
54  * provides access to the section.
55  *
56  * Applications (or other elements) can create them either by using one of the
57  * `gst_mpegts_section_from_*` functions, or by providing the raw SI data via
58  * gst_mpegts_section_new().
59  *
60  * Elements outputting MPEG-TS streams can also create sections using the
61  * various convenience functions and then get the packetized data (to be
62  * inserted in MPEG-TS packets) using gst_mpegts_section_packetize().
63  *
64  * For more details, refer to the ITU H.222.0 or ISO/IEC 13818-1 specifications
65  * and other specifications mentioned in the documentation.
66  *
67  * # Supported base MPEG-TS sections
68  * These are the sections for which parsing and packetizing code exists.
69  *
70  * ## Program Association Table (PAT)
71  * See:
72  * * gst_mpegts_section_get_pat()
73  * * gst_mpegts_pat_program_new()
74  * * %GstMpegtsPatProgram
75  *
76  * ## Conditional Access Table (CAT)
77  * See:
78  * * gst_mpegts_section_get_cat()
79  *
80  * ## Program Map Table (PMT)
81  * See:
82  * * %GstMpegtsPMT
83  * * gst_mpegts_section_get_pmt()
84  * * gst_mpegts_pmt_new()
85  * * %GstMpegtsPMTStream
86  *
87  * ## Transport Stream Description Table (TSDT)
88  * See:
89  * * gst_mpegts_section_get_tsdt()
90  * # API
91  */
92 
93 static GQuark QUARK_PAT;
94 static GQuark QUARK_CAT;
95 static GQuark QUARK_BAT;
96 static GQuark QUARK_PMT;
97 static GQuark QUARK_NIT;
98 static GQuark QUARK_SDT;
99 static GQuark QUARK_EIT;
100 static GQuark QUARK_TDT;
101 static GQuark QUARK_TOT;
102 static GQuark QUARK_SCTE_SIT;
103 static GQuark QUARK_SECTION;
104 
105 static GType _gst_mpegts_section_type = 0;
106 #define MPEG_TYPE_TS_SECTION (_gst_mpegts_section_type)
107 GST_DEFINE_MINI_OBJECT_TYPE (GstMpegtsSection, gst_mpegts_section);
108 
109 static const guint32 crc_tab[256] = {
110   0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
111   0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
112   0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
113   0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
114   0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
115   0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
116   0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
117   0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
118   0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
119   0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
120   0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
121   0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
122   0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
123   0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
124   0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
125   0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
126   0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
127   0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
128   0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
129   0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
130   0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
131   0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
132   0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
133   0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
134   0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
135   0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
136   0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
137   0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
138   0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
139   0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
140   0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
141   0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
142   0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
143   0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
144   0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
145   0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
146   0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
147   0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
148   0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
149   0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
150   0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
151   0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
152   0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
153 };
154 
155 /* _calc_crc32 relicensed to LGPL from fluendo ts demuxer */
156 guint32
_calc_crc32(const guint8 * data,guint datalen)157 _calc_crc32 (const guint8 * data, guint datalen)
158 {
159   gint i;
160   guint32 crc = 0xffffffff;
161 
162   for (i = 0; i < datalen; i++) {
163     crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff];
164   }
165   return crc;
166 }
167 
168 gpointer
__common_section_checks(GstMpegtsSection * section,guint min_size,GstMpegtsParseFunc parsefunc,GDestroyNotify destroynotify)169 __common_section_checks (GstMpegtsSection * section, guint min_size,
170     GstMpegtsParseFunc parsefunc, GDestroyNotify destroynotify)
171 {
172   gpointer res;
173 
174   /* Check section is big enough */
175   if (section->section_length < min_size) {
176     GST_WARNING
177         ("PID:0x%04x table_id:0x%02x, section too small (Got %d, need at least %d)",
178         section->pid, section->table_id, section->section_length, min_size);
179     return NULL;
180   }
181 
182   /* If section has a CRC, check it */
183   if (!section->short_section
184       && (_calc_crc32 (section->data, section->section_length) != 0)) {
185     GST_WARNING ("PID:0x%04x table_id:0x%02x, Bad CRC on section", section->pid,
186         section->table_id);
187     return NULL;
188   }
189 
190   /* Finally parse and set the destroy notify */
191   res = parsefunc (section);
192   if (res == NULL)
193     GST_WARNING ("PID:0x%04x table_id:0x%02x, Failed to parse section",
194         section->pid, section->table_id);
195   else
196     section->destroy_parsed = destroynotify;
197   return res;
198 }
199 
200 
201 /*
202  * GENERIC MPEG-TS SECTION
203  */
204 static void
_gst_mpegts_section_free(GstMpegtsSection * section)205 _gst_mpegts_section_free (GstMpegtsSection * section)
206 {
207   GST_DEBUG ("Freeing section type %d", section->section_type);
208 
209   if (section->cached_parsed && section->destroy_parsed)
210     section->destroy_parsed (section->cached_parsed);
211 
212   g_free (section->data);
213 
214   g_slice_free (GstMpegtsSection, section);
215 }
216 
217 static GstMpegtsSection *
_gst_mpegts_section_copy(GstMpegtsSection * section)218 _gst_mpegts_section_copy (GstMpegtsSection * section)
219 {
220   GstMpegtsSection *copy;
221 
222   copy = g_slice_new0 (GstMpegtsSection);
223   gst_mini_object_init (GST_MINI_OBJECT_CAST (copy), 0, MPEG_TYPE_TS_SECTION,
224       (GstMiniObjectCopyFunction) _gst_mpegts_section_copy, NULL,
225       (GstMiniObjectFreeFunction) _gst_mpegts_section_free);
226 
227   copy->section_type = section->section_type;
228   copy->pid = section->pid;
229   copy->table_id = section->table_id;
230   copy->subtable_extension = section->subtable_extension;
231   copy->version_number = section->version_number;
232   copy->current_next_indicator = section->current_next_indicator;
233   copy->section_number = section->section_number;
234   copy->last_section_number = section->last_section_number;
235   copy->crc = section->crc;
236 
237   copy->data = g_memdup2 (section->data, section->section_length);
238   copy->section_length = section->section_length;
239   /* Note: We do not copy the cached parsed item, it will be
240    * reconstructed on that copy */
241   copy->cached_parsed = NULL;
242   copy->offset = section->offset;
243   copy->short_section = section->short_section;
244 
245   return copy;
246 }
247 
248 /**
249  * gst_mpegts_section_get_data:
250  * @section: a #GstMpegtsSection
251  *
252  * Gets the original unparsed section data.
253  *
254  * Returns: (transfer full): The original unparsed section data.
255  */
256 GBytes *
gst_mpegts_section_get_data(GstMpegtsSection * section)257 gst_mpegts_section_get_data (GstMpegtsSection * section)
258 {
259   return g_bytes_new (section->data, section->section_length);
260 }
261 
262 /**
263  * gst_message_parse_mpegts_section:
264  * @message: a #GstMessage
265  *
266  * Returns the #GstMpegtsSection contained in a message.
267  *
268  * Returns: (transfer full): the contained #GstMpegtsSection, or %NULL.
269  */
270 GstMpegtsSection *
gst_message_parse_mpegts_section(GstMessage * message)271 gst_message_parse_mpegts_section (GstMessage * message)
272 {
273   const GstStructure *st;
274   GstMpegtsSection *section;
275 
276   if (message->type != GST_MESSAGE_ELEMENT)
277     return NULL;
278 
279   st = gst_message_get_structure (message);
280   /* FIXME : Add checks against know section names */
281   if (!gst_structure_id_get (st, QUARK_SECTION, GST_TYPE_MPEGTS_SECTION,
282           &section, NULL))
283     return NULL;
284 
285   return section;
286 }
287 
288 static GstStructure *
_mpegts_section_get_structure(GstMpegtsSection * section)289 _mpegts_section_get_structure (GstMpegtsSection * section)
290 {
291   GstStructure *st;
292   GQuark quark;
293 
294   switch (section->section_type) {
295     case GST_MPEGTS_SECTION_PAT:
296       quark = QUARK_PAT;
297       break;
298     case GST_MPEGTS_SECTION_PMT:
299       quark = QUARK_PMT;
300       break;
301     case GST_MPEGTS_SECTION_CAT:
302       quark = QUARK_CAT;
303       break;
304     case GST_MPEGTS_SECTION_EIT:
305       quark = QUARK_EIT;
306       break;
307     case GST_MPEGTS_SECTION_BAT:
308       quark = QUARK_BAT;
309       break;
310     case GST_MPEGTS_SECTION_NIT:
311       quark = QUARK_NIT;
312       break;
313     case GST_MPEGTS_SECTION_SDT:
314       quark = QUARK_SDT;
315       break;
316     case GST_MPEGTS_SECTION_TDT:
317       quark = QUARK_TDT;
318       break;
319     case GST_MPEGTS_SECTION_TOT:
320       quark = QUARK_TOT;
321       break;
322     case GST_MPEGTS_SECTION_SCTE_SIT:
323       quark = QUARK_SCTE_SIT;
324       break;
325     default:
326       GST_DEBUG ("Creating structure for unknown GstMpegtsSection");
327       quark = QUARK_SECTION;
328       break;
329   }
330 
331   st = gst_structure_new_id (quark, QUARK_SECTION, MPEG_TYPE_TS_SECTION,
332       section, NULL);
333 
334   return st;
335 }
336 
337 /**
338  * gst_message_new_mpegts_section:
339  * @parent: (transfer none): The creator of the message
340  * @section: (transfer none): The #GstMpegtsSection to put in a message
341  *
342  * Creates a new #GstMessage for a @GstMpegtsSection.
343  *
344  * Returns: (transfer full): The new #GstMessage to be posted, or %NULL if the
345  * section is not valid.
346  */
347 GstMessage *
gst_message_new_mpegts_section(GstObject * parent,GstMpegtsSection * section)348 gst_message_new_mpegts_section (GstObject * parent, GstMpegtsSection * section)
349 {
350   GstMessage *msg;
351   GstStructure *st;
352 
353   st = _mpegts_section_get_structure (section);
354 
355   msg = gst_message_new_element (parent, st);
356 
357   return msg;
358 }
359 
360 /**
361  * gst_event_new_mpegts_section:
362  * @section: (transfer none): The #GstMpegtsSection to put in a message
363  *
364  * Creates a new #GstEvent for a #GstMpegtsSection.
365  *
366  * Returns: (transfer full): The new custom #GstEvent.
367  * Since: 1.20
368  */
369 GstEvent *
gst_event_new_mpegts_section(GstMpegtsSection * section)370 gst_event_new_mpegts_section (GstMpegtsSection * section)
371 {
372   GstStructure *structure;
373   GstEvent *event;
374 
375   structure = _mpegts_section_get_structure (section);
376 
377   event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
378 
379   return event;
380 }
381 
382 /**
383  * gst_event_parse_mpegts_section:
384  * @event: (transfer none): #GstEvent containing a #GstMpegtsSection
385  *
386  * Extracts the #GstMpegtsSection contained in the @event #GstEvent
387  *
388  * Returns: (transfer full): The extracted #GstMpegtsSection , or %NULL if the
389  * event did not contain a valid #GstMpegtsSection.
390  */
391 GstMpegtsSection *
gst_event_parse_mpegts_section(GstEvent * event)392 gst_event_parse_mpegts_section (GstEvent * event)
393 {
394   const GstStructure *structure;
395   GstMpegtsSection *section;
396 
397   structure = gst_event_get_structure (event);
398 
399   if (!structure)
400     return NULL;
401 
402   if (!gst_structure_id_get (structure, QUARK_SECTION, MPEG_TYPE_TS_SECTION,
403           &section, NULL))
404     return NULL;
405 
406   return section;
407 }
408 
409 /**
410  * gst_mpegts_section_send_event:
411  * @element: (transfer none): The #GstElement to send to section event to
412  * @section: (transfer none): The #GstMpegtsSection to put in the event
413  *
414  * Creates a custom #GstEvent with a @GstMpegtsSection and send it the @element
415  * #GstElement.
416  *
417  * Returns: %TRUE if the event was sent to the element.
418  */
419 gboolean
gst_mpegts_section_send_event(GstMpegtsSection * section,GstElement * element)420 gst_mpegts_section_send_event (GstMpegtsSection * section, GstElement * element)
421 {
422   GstEvent *event;
423 
424   g_return_val_if_fail (section != NULL, FALSE);
425   g_return_val_if_fail (element != NULL, FALSE);
426 
427   event = gst_event_new_mpegts_section (section);
428 
429   if (!gst_element_send_event (element, event)) {
430     gst_event_unref (event);
431     return FALSE;
432   }
433 
434   return TRUE;
435 }
436 
437 static GstMpegtsPatProgram *
_mpegts_pat_program_copy(GstMpegtsPatProgram * orig)438 _mpegts_pat_program_copy (GstMpegtsPatProgram * orig)
439 {
440   return g_slice_dup (GstMpegtsPatProgram, orig);
441 }
442 
443 static void
_mpegts_pat_program_free(GstMpegtsPatProgram * orig)444 _mpegts_pat_program_free (GstMpegtsPatProgram * orig)
445 {
446   g_slice_free (GstMpegtsPatProgram, orig);
447 }
448 
449 G_DEFINE_BOXED_TYPE (GstMpegtsPatProgram, gst_mpegts_pat_program,
450     (GBoxedCopyFunc) _mpegts_pat_program_copy,
451     (GFreeFunc) _mpegts_pat_program_free);
452 
453 /* Program Association Table */
454 static gpointer
_parse_pat(GstMpegtsSection * section)455 _parse_pat (GstMpegtsSection * section)
456 {
457   GPtrArray *pat;
458   guint16 i, nb_programs;
459   GstMpegtsPatProgram *program;
460   guint8 *data, *end;
461 
462   /* Skip already parsed data */
463   data = section->data + 8;
464 
465   /* stop at the CRC */
466   end = section->data + section->section_length;
467 
468   /* Initialize program list */
469   nb_programs = (end - 4 - data) / 4;
470   pat =
471       g_ptr_array_new_full (nb_programs,
472       (GDestroyNotify) _mpegts_pat_program_free);
473 
474   GST_LOG ("nb_programs %u", nb_programs);
475 
476   for (i = 0; i < nb_programs; i++) {
477     program = g_slice_new0 (GstMpegtsPatProgram);
478     program->program_number = GST_READ_UINT16_BE (data);
479     data += 2;
480 
481     program->network_or_program_map_PID = GST_READ_UINT16_BE (data) & 0x1FFF;
482     data += 2;
483 
484     g_ptr_array_index (pat, i) = program;
485   }
486   pat->len = nb_programs;
487 
488   if (data != end - 4) {
489     GST_ERROR ("at the end of PAT data != end - 4");
490     g_ptr_array_unref (pat);
491 
492     return NULL;
493   }
494 
495   return (gpointer) pat;
496 }
497 
498 /**
499  * gst_mpegts_section_get_pat:
500  * @section: a #GstMpegtsSection of type %GST_MPEGTS_SECTION_PAT
501  *
502  * Parses a Program Association Table (ITU H.222.0, ISO/IEC 13818-1).
503  *
504  * Returns the array of #GstMpegtsPatProgram contained in the section.
505  *
506  * Note: The PAT `transport_stream_id` field corresponds to the
507  * "subtable_extension" field of the provided @section.
508  *
509  * Returns: (transfer container) (element-type GstMpegtsPatProgram): The
510  * #GstMpegtsPatProgram contained in the section, or %NULL if an error happened
511  * or the @section did not contain a valid PAT. Release with #g_ptr_array_unref
512  * when done.
513  */
514 GPtrArray *
gst_mpegts_section_get_pat(GstMpegtsSection * section)515 gst_mpegts_section_get_pat (GstMpegtsSection * section)
516 {
517   g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_PAT, NULL);
518   g_return_val_if_fail (section->cached_parsed || section->data, NULL);
519 
520   if (!section->cached_parsed)
521     section->cached_parsed =
522         __common_section_checks (section, 12, _parse_pat,
523         (GDestroyNotify) g_ptr_array_unref);
524 
525   if (section->cached_parsed)
526     return g_ptr_array_ref ((GPtrArray *) section->cached_parsed);
527   return NULL;
528 }
529 
530 /**
531  * gst_mpegts_pat_new:
532  *
533  * Allocates a new #GPtrArray for #GstMpegtsPatProgram. The array can be filled
534  * and then converted to a PAT section with gst_mpegts_section_from_pat().
535  *
536  * Returns: (transfer full) (element-type GstMpegtsPatProgram): A newly allocated #GPtrArray
537  */
538 GPtrArray *
gst_mpegts_pat_new(void)539 gst_mpegts_pat_new (void)
540 {
541   GPtrArray *pat;
542 
543   pat = g_ptr_array_new_with_free_func (
544       (GDestroyNotify) _mpegts_pat_program_free);
545 
546   return pat;
547 }
548 
549 /**
550  * gst_mpegts_pat_program_new:
551  *
552  * Allocates a new #GstMpegtsPatProgram.
553  *
554  * Returns: (transfer full): A newly allocated #GstMpegtsPatProgram
555  */
556 GstMpegtsPatProgram *
gst_mpegts_pat_program_new(void)557 gst_mpegts_pat_program_new (void)
558 {
559   GstMpegtsPatProgram *program;
560 
561   program = g_slice_new0 (GstMpegtsPatProgram);
562 
563   return program;
564 }
565 
566 static gboolean
_packetize_pat(GstMpegtsSection * section)567 _packetize_pat (GstMpegtsSection * section)
568 {
569   GPtrArray *programs;
570   guint8 *data;
571   gsize length;
572   guint i;
573 
574   programs = gst_mpegts_section_get_pat (section);
575 
576   if (programs == NULL)
577     return FALSE;
578 
579   /* 8 byte common section fields
580      4 byte CRC */
581   length = 12;
582 
583   /* 2 byte program number
584      2 byte program/network PID */
585   length += programs->len * 4;
586 
587   _packetize_common_section (section, length);
588   data = section->data + 8;
589 
590   for (i = 0; i < programs->len; i++) {
591     GstMpegtsPatProgram *program;
592 
593     program = g_ptr_array_index (programs, i);
594 
595     /* program_number       - 16 bit uimsbf */
596     GST_WRITE_UINT16_BE (data, program->program_number);
597     data += 2;
598 
599     /* reserved             - 3  bit
600        program/network_PID  - 13 uimsbf */
601     GST_WRITE_UINT16_BE (data, program->network_or_program_map_PID | 0xE000);
602     data += 2;
603   }
604 
605   g_ptr_array_unref (programs);
606 
607   return TRUE;
608 }
609 
610 /**
611  * gst_mpegts_section_from_pat:
612  * @programs: (transfer full) (element-type GstMpegtsPatProgram): an array of #GstMpegtsPatProgram
613  * @ts_id: Transport stream ID of the PAT
614  *
615  * Creates a PAT #GstMpegtsSection from the @programs array of #GstMpegtsPatPrograms
616  *
617  * Returns: (transfer full): a #GstMpegtsSection
618  */
619 GstMpegtsSection *
gst_mpegts_section_from_pat(GPtrArray * programs,guint16 ts_id)620 gst_mpegts_section_from_pat (GPtrArray * programs, guint16 ts_id)
621 {
622   GstMpegtsSection *section;
623 
624   section = _gst_mpegts_section_init (0x00,
625       GST_MTS_TABLE_ID_PROGRAM_ASSOCIATION);
626 
627   section->subtable_extension = ts_id;
628   section->cached_parsed = (gpointer) programs;
629   section->packetizer = _packetize_pat;
630   section->destroy_parsed = (GDestroyNotify) g_ptr_array_unref;
631 
632   return section;
633 }
634 
635 /* Program Map Table */
636 
637 static GstMpegtsPMTStream *
_gst_mpegts_pmt_stream_copy(GstMpegtsPMTStream * pmt)638 _gst_mpegts_pmt_stream_copy (GstMpegtsPMTStream * pmt)
639 {
640   GstMpegtsPMTStream *copy;
641 
642   copy = g_slice_dup (GstMpegtsPMTStream, pmt);
643   copy->descriptors = g_ptr_array_ref (pmt->descriptors);
644 
645   return copy;
646 }
647 
648 static void
_gst_mpegts_pmt_stream_free(GstMpegtsPMTStream * pmt)649 _gst_mpegts_pmt_stream_free (GstMpegtsPMTStream * pmt)
650 {
651   if (pmt->descriptors)
652     g_ptr_array_unref (pmt->descriptors);
653   g_slice_free (GstMpegtsPMTStream, pmt);
654 }
655 
656 G_DEFINE_BOXED_TYPE (GstMpegtsPMTStream, gst_mpegts_pmt_stream,
657     (GBoxedCopyFunc) _gst_mpegts_pmt_stream_copy,
658     (GFreeFunc) _gst_mpegts_pmt_stream_free);
659 
660 static GstMpegtsPMT *
_gst_mpegts_pmt_copy(GstMpegtsPMT * pmt)661 _gst_mpegts_pmt_copy (GstMpegtsPMT * pmt)
662 {
663   GstMpegtsPMT *copy;
664 
665   copy = g_slice_dup (GstMpegtsPMT, pmt);
666   if (pmt->descriptors)
667     copy->descriptors = g_ptr_array_ref (pmt->descriptors);
668   copy->streams = g_ptr_array_ref (pmt->streams);
669 
670   return copy;
671 }
672 
673 static void
_gst_mpegts_pmt_free(GstMpegtsPMT * pmt)674 _gst_mpegts_pmt_free (GstMpegtsPMT * pmt)
675 {
676   if (pmt->descriptors)
677     g_ptr_array_unref (pmt->descriptors);
678   if (pmt->streams)
679     g_ptr_array_unref (pmt->streams);
680   g_slice_free (GstMpegtsPMT, pmt);
681 }
682 
683 G_DEFINE_BOXED_TYPE (GstMpegtsPMT, gst_mpegts_pmt,
684     (GBoxedCopyFunc) _gst_mpegts_pmt_copy, (GFreeFunc) _gst_mpegts_pmt_free);
685 
686 
687 static gpointer
_parse_pmt(GstMpegtsSection * section)688 _parse_pmt (GstMpegtsSection * section)
689 {
690   GstMpegtsPMT *pmt = NULL;
691   guint i = 0, allocated_streams = 8;
692   guint8 *data, *end;
693   guint program_info_length;
694   guint stream_info_length;
695 
696   pmt = g_slice_new0 (GstMpegtsPMT);
697 
698   data = section->data;
699   end = data + section->section_length;
700 
701   GST_DEBUG ("Parsing %d Program Map Table", section->subtable_extension);
702 
703   /* Assign program number from subtable extension,
704      and skip already parsed data */
705   pmt->program_number = section->subtable_extension;
706   data += 8;
707 
708   pmt->pcr_pid = GST_READ_UINT16_BE (data) & 0x1FFF;
709   data += 2;
710 
711   program_info_length = GST_READ_UINT16_BE (data) & 0x0FFF;
712   data += 2;
713 
714   /* check that the buffer is large enough to contain at least
715    * program_info_length bytes + CRC */
716   if (program_info_length && (data + program_info_length + 4 > end)) {
717     GST_WARNING ("PID %d invalid program info length %d left %d",
718         section->pid, program_info_length, (gint) (end - data));
719     goto error;
720   }
721   pmt->descriptors = gst_mpegts_parse_descriptors (data, program_info_length);
722   if (pmt->descriptors == NULL)
723     goto error;
724   data += program_info_length;
725 
726   pmt->streams =
727       g_ptr_array_new_full (allocated_streams,
728       (GDestroyNotify) _gst_mpegts_pmt_stream_free);
729 
730   /* parse entries, cycle until there's space for another entry (at least 5
731    * bytes) plus the CRC */
732   while (data <= end - 4 - 5) {
733     GstMpegtsPMTStream *stream = g_slice_new0 (GstMpegtsPMTStream);
734 
735     g_ptr_array_add (pmt->streams, stream);
736 
737     stream->stream_type = *data++;
738     GST_DEBUG ("[%d] Stream type 0x%02x found", i, stream->stream_type);
739 
740     stream->pid = GST_READ_UINT16_BE (data) & 0x1FFF;
741     data += 2;
742 
743     stream_info_length = GST_READ_UINT16_BE (data) & 0x0FFF;
744     data += 2;
745 
746     if (data + stream_info_length + 4 > end) {
747       GST_WARNING ("PID %d invalid stream info length %d left %d", section->pid,
748           stream_info_length, (gint) (end - data));
749       goto error;
750     }
751 
752     stream->descriptors =
753         gst_mpegts_parse_descriptors (data, stream_info_length);
754     if (stream->descriptors == NULL)
755       goto error;
756     data += stream_info_length;
757 
758     i += 1;
759   }
760 
761   /* Section length was longer than the actual content of the PMT */
762   if (data < end - 4)
763     goto error;
764 
765   /* Ensure we did not read after the end of our array */
766   g_assert (data == end - 4);
767 
768   return (gpointer) pmt;
769 
770 error:
771   if (pmt)
772     _gst_mpegts_pmt_free (pmt);
773 
774   return NULL;
775 }
776 
777 /**
778  * gst_mpegts_section_get_pmt:
779  * @section: a #GstMpegtsSection of type %GST_MPEGTS_SECTION_PMT
780  *
781  * Parses the Program Map Table contained in the @section.
782  *
783  * Returns: The #GstMpegtsPMT contained in the section, or %NULL if an error
784  * happened.
785  */
786 const GstMpegtsPMT *
gst_mpegts_section_get_pmt(GstMpegtsSection * section)787 gst_mpegts_section_get_pmt (GstMpegtsSection * section)
788 {
789   g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_PMT, NULL);
790   g_return_val_if_fail (section->cached_parsed || section->data, NULL);
791 
792   if (!section->cached_parsed)
793     section->cached_parsed =
794         __common_section_checks (section, 16, _parse_pmt,
795         (GDestroyNotify) _gst_mpegts_pmt_free);
796 
797   return (const GstMpegtsPMT *) section->cached_parsed;
798 }
799 
800 /**
801  * gst_mpegts_pmt_new:
802  *
803  * Allocates and initializes a new #GstMpegtsPMT. #GstMpegtsPMTStream can be
804  * added to the streams array, and global PMT #GstMpegtsDescriptor to the
805  * descriptors array.
806  *
807  * Returns: (transfer full): #GstMpegtsPMT
808  */
809 GstMpegtsPMT *
gst_mpegts_pmt_new(void)810 gst_mpegts_pmt_new (void)
811 {
812   GstMpegtsPMT *pmt;
813 
814   pmt = g_slice_new0 (GstMpegtsPMT);
815 
816   pmt->descriptors = g_ptr_array_new_with_free_func ((GDestroyNotify)
817       gst_mpegts_descriptor_free);
818   pmt->streams = g_ptr_array_new_with_free_func ((GDestroyNotify)
819       _gst_mpegts_pmt_stream_free);
820 
821   return pmt;
822 }
823 
824 /**
825  * gst_mpegts_pmt_stream_new:
826  *
827  * Allocates and initializes a new #GstMpegtsPMTStream.
828  *
829  * Returns: (transfer full): #GstMpegtsPMTStream
830  */
831 GstMpegtsPMTStream *
gst_mpegts_pmt_stream_new(void)832 gst_mpegts_pmt_stream_new (void)
833 {
834   GstMpegtsPMTStream *stream;
835 
836   stream = g_slice_new0 (GstMpegtsPMTStream);
837 
838   stream->descriptors = g_ptr_array_new_with_free_func ((GDestroyNotify)
839       gst_mpegts_descriptor_free);
840 
841   return stream;
842 }
843 
844 static gboolean
_packetize_pmt(GstMpegtsSection * section)845 _packetize_pmt (GstMpegtsSection * section)
846 {
847   const GstMpegtsPMT *pmt;
848   GstMpegtsPMTStream *stream;
849   GstMpegtsDescriptor *descriptor;
850   gsize length, pgm_info_length, stream_length;
851   guint8 *data;
852   guint i, j;
853 
854   pmt = gst_mpegts_section_get_pmt (section);
855 
856   if (pmt == NULL)
857     return FALSE;
858 
859   /* 8 byte common section fields
860      2 byte PCR pid
861      2 byte program info length
862      4 byte CRC */
863   length = 16;
864 
865   /* Find length of program info */
866   pgm_info_length = 0;
867   if (pmt->descriptors) {
868     for (i = 0; i < pmt->descriptors->len; i++) {
869       descriptor = g_ptr_array_index (pmt->descriptors, i);
870       pgm_info_length += descriptor->length + 2;
871     }
872   }
873 
874   /* Find length of PMT streams */
875   stream_length = 0;
876   if (pmt->streams) {
877     for (i = 0; i < pmt->streams->len; i++) {
878       stream = g_ptr_array_index (pmt->streams, i);
879 
880       /* 1 byte stream type
881          2 byte PID
882          2 byte ES info length */
883       stream_length += 5;
884 
885       if (stream->descriptors) {
886         for (j = 0; j < stream->descriptors->len; j++) {
887           descriptor = g_ptr_array_index (stream->descriptors, j);
888           stream_length += descriptor->length + 2;
889         }
890       }
891     }
892   }
893 
894   length += pgm_info_length + stream_length;
895 
896   _packetize_common_section (section, length);
897   data = section->data + 8;
898 
899   /* reserved                         - 3  bit
900      PCR_PID                          - 13 uimsbf */
901   GST_WRITE_UINT16_BE (data, pmt->pcr_pid | 0xE000);
902   data += 2;
903 
904   /* reserved                         - 4  bit
905      program_info_length              - 12 uimsbf */
906   GST_WRITE_UINT16_BE (data, pgm_info_length | 0xF000);
907   data += 2;
908 
909   _packetize_descriptor_array (pmt->descriptors, &data);
910 
911   if (pmt->streams) {
912     guint8 *pos;
913 
914     for (i = 0; i < pmt->streams->len; i++) {
915       stream = g_ptr_array_index (pmt->streams, i);
916       /* stream_type                  - 8  bit uimsbf */
917       *data++ = stream->stream_type;
918 
919       /* reserved                     - 3  bit
920          elementary_PID               - 13 bit uimsbf */
921       GST_WRITE_UINT16_BE (data, stream->pid | 0xE000);
922       data += 2;
923 
924       /* reserved                     - 4  bit
925          ES_info_length               - 12 bit uimsbf */
926       pos = data;
927       data += 2;
928       _packetize_descriptor_array (stream->descriptors, &data);
929 
930       /* Go back and update descriptor length */
931       GST_WRITE_UINT16_BE (pos, (data - pos - 2) | 0xF000);
932     }
933   }
934 
935   return TRUE;
936 }
937 
938 /**
939  * gst_mpegts_section_from_pmt:
940  * @pmt: (transfer full): a #GstMpegtsPMT to create a #GstMpegtsSection from
941  * @pid: The PID that the #GstMpegtsPMT belongs to
942  *
943  * Creates a #GstMpegtsSection from @pmt that is bound to @pid
944  *
945  * Returns: (transfer full): #GstMpegtsSection
946  */
947 GstMpegtsSection *
gst_mpegts_section_from_pmt(GstMpegtsPMT * pmt,guint16 pid)948 gst_mpegts_section_from_pmt (GstMpegtsPMT * pmt, guint16 pid)
949 {
950   GstMpegtsSection *section;
951 
952   g_return_val_if_fail (pmt != NULL, NULL);
953 
954   section = _gst_mpegts_section_init (pid, GST_MTS_TABLE_ID_TS_PROGRAM_MAP);
955 
956   section->subtable_extension = pmt->program_number;
957   section->cached_parsed = (gpointer) pmt;
958   section->packetizer = _packetize_pmt;
959   section->destroy_parsed = (GDestroyNotify) _gst_mpegts_pmt_free;
960 
961   return section;
962 }
963 
964 /* Conditional Access Table */
965 static gpointer
_parse_cat(GstMpegtsSection * section)966 _parse_cat (GstMpegtsSection * section)
967 {
968   guint8 *data;
969   guint desc_len;
970 
971   /* Skip parts already parsed */
972   data = section->data + 8;
973 
974   /* descriptors */
975   desc_len = section->section_length - 4 - 8;
976   return (gpointer) gst_mpegts_parse_descriptors (data, desc_len);
977 }
978 
979 /**
980  * gst_mpegts_section_get_cat:
981  * @section: a #GstMpegtsSection of type %GST_MPEGTS_SECTION_CAT
982  *
983  * Parses a Conditional Access Table.
984  *
985  * Returns the array of #GstMpegtsDescriptor contained in the Conditional
986  * Access Table.
987  *
988  * Returns: (transfer container) (element-type GstMpegtsDescriptor): The array
989  * of #GstMpegtsDescriptor contained in the section, or %NULL if an error
990  * happened. Release with #g_array_unref when done.
991  */
992 GPtrArray *
gst_mpegts_section_get_cat(GstMpegtsSection * section)993 gst_mpegts_section_get_cat (GstMpegtsSection * section)
994 {
995   g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_CAT, NULL);
996   g_return_val_if_fail (section->cached_parsed || section->data, NULL);
997 
998   if (!section->cached_parsed)
999     section->cached_parsed =
1000         __common_section_checks (section, 12, _parse_cat,
1001         (GDestroyNotify) g_ptr_array_unref);
1002 
1003   if (section->cached_parsed)
1004     return g_ptr_array_ref ((GPtrArray *) section->cached_parsed);
1005   return NULL;
1006 }
1007 
1008 /* Transport Stream Description Table (TSDT) */
1009 /**
1010  * gst_mpegts_section_get_tsdt:
1011  * @section: a #GstMpegtsSection of type %GST_MPEGTS_SECTION_TSDT
1012  *
1013  * Parses a Transport Stream Description Table.
1014  *
1015  * Returns the array of #GstMpegtsDescriptor contained in the section
1016  *
1017  * Returns: (transfer container) (element-type GstMpegtsDescriptor): The array
1018  * of #GstMpegtsDescriptor contained in the section, or %NULL if an error
1019  * happened. Release with #g_array_unref when done.
1020  */
1021 GPtrArray *
gst_mpegts_section_get_tsdt(GstMpegtsSection * section)1022 gst_mpegts_section_get_tsdt (GstMpegtsSection * section)
1023 {
1024   g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_TSDT, NULL);
1025   g_return_val_if_fail (section->cached_parsed || section->data, NULL);
1026 
1027   if (section->cached_parsed)
1028     return g_ptr_array_ref ((GPtrArray *) section->cached_parsed);
1029 
1030   /* FIXME : parse TSDT */
1031   return NULL;
1032 }
1033 
1034 
1035 
1036 static GstMpegtsSectionType
_identify_section(guint16 pid,guint8 table_id)1037 _identify_section (guint16 pid, guint8 table_id)
1038 {
1039   switch (table_id) {
1040     case GST_MTS_TABLE_ID_PROGRAM_ASSOCIATION:
1041       if (pid == 0x00)
1042         return GST_MPEGTS_SECTION_PAT;
1043       break;
1044     case GST_MTS_TABLE_ID_CONDITIONAL_ACCESS:
1045       if (pid == 0x01)
1046         return GST_MPEGTS_SECTION_CAT;
1047       break;
1048     case GST_MTS_TABLE_ID_TS_PROGRAM_MAP:
1049       return GST_MPEGTS_SECTION_PMT;
1050     case GST_MTS_TABLE_ID_BOUQUET_ASSOCIATION:
1051       if (pid == 0x0011)
1052         return GST_MPEGTS_SECTION_BAT;
1053       break;
1054     case GST_MTS_TABLE_ID_NETWORK_INFORMATION_ACTUAL_NETWORK:
1055     case GST_MTS_TABLE_ID_NETWORK_INFORMATION_OTHER_NETWORK:
1056       if (pid == 0x0010)
1057         return GST_MPEGTS_SECTION_NIT;
1058       break;
1059     case GST_MTS_TABLE_ID_SERVICE_DESCRIPTION_ACTUAL_TS:
1060     case GST_MTS_TABLE_ID_SERVICE_DESCRIPTION_OTHER_TS:
1061       if (pid == 0x0011)
1062         return GST_MPEGTS_SECTION_SDT;
1063       break;
1064     case GST_MTS_TABLE_ID_TIME_DATE:
1065       if (pid == 0x0014)
1066         return GST_MPEGTS_SECTION_TDT;
1067       break;
1068     case GST_MTS_TABLE_ID_TIME_OFFSET:
1069       if (pid == 0x0014)
1070         return GST_MPEGTS_SECTION_TOT;
1071       break;
1072     case GST_MTS_TABLE_ID_ATSC_TERRESTRIAL_VIRTUAL_CHANNEL:
1073       if (pid == 0x1ffb)
1074         return GST_MPEGTS_SECTION_ATSC_TVCT;
1075       break;
1076     case GST_MTS_TABLE_ID_ATSC_CABLE_VIRTUAL_CHANNEL:
1077       if (pid == 0x1ffb)
1078         return GST_MPEGTS_SECTION_ATSC_CVCT;
1079       break;
1080     case GST_MTS_TABLE_ID_ATSC_MASTER_GUIDE:
1081       if (pid == 0x1ffb)
1082         return GST_MPEGTS_SECTION_ATSC_MGT;
1083       break;
1084     case GST_MTS_TABLE_ID_ATSC_EVENT_INFORMATION:
1085       /* FIXME check pids reported on the MGT to confirm expectations */
1086       return GST_MPEGTS_SECTION_ATSC_EIT;
1087     case GST_MTS_TABLE_ID_ATSC_CHANNEL_OR_EVENT_EXTENDED_TEXT:
1088       /* FIXME check pids reported on the MGT to confirm expectations */
1089       return GST_MPEGTS_SECTION_ATSC_ETT;
1090       /* FIXME : FILL */
1091     case GST_MTS_TABLE_ID_ATSC_SYSTEM_TIME:
1092       if (pid == 0x1ffb)
1093         return GST_MPEGTS_SECTION_ATSC_STT;
1094       break;
1095     case GST_MTS_TABLE_ID_ATSC_RATING_REGION:
1096       if (pid == 0x1ffb)
1097         return GST_MPEGTS_SECTION_ATSC_RRT;
1098       break;
1099     case GST_MTS_TABLE_ID_SCTE_SPLICE:
1100       return GST_MPEGTS_SECTION_SCTE_SIT;
1101       break;
1102     case GST_MTS_TABLE_ID_SELECTION_INFORMATION:
1103       if (pid == 0x001f)
1104         return GST_MPEGTS_SECTION_SIT;
1105     default:
1106       /* Handle ranges */
1107       if (table_id >= GST_MTS_TABLE_ID_EVENT_INFORMATION_ACTUAL_TS_PRESENT &&
1108           table_id <= GST_MTS_TABLE_ID_EVENT_INFORMATION_OTHER_TS_SCHEDULE_N) {
1109         if (pid == 0x0012)
1110           return GST_MPEGTS_SECTION_EIT;
1111       }
1112       return GST_MPEGTS_SECTION_UNKNOWN;
1113   }
1114   return GST_MPEGTS_SECTION_UNKNOWN;
1115 
1116 }
1117 
1118 GstMpegtsSection *
_gst_mpegts_section_init(guint16 pid,guint8 table_id)1119 _gst_mpegts_section_init (guint16 pid, guint8 table_id)
1120 {
1121   GstMpegtsSection *section;
1122 
1123   section = g_slice_new0 (GstMpegtsSection);
1124   gst_mini_object_init (GST_MINI_OBJECT_CAST (section), 0, MPEG_TYPE_TS_SECTION,
1125       (GstMiniObjectCopyFunction) _gst_mpegts_section_copy, NULL,
1126       (GstMiniObjectFreeFunction) _gst_mpegts_section_free);
1127 
1128   section->pid = pid;
1129   section->table_id = table_id;
1130   section->current_next_indicator = TRUE;
1131   section->section_type = _identify_section (pid, table_id);
1132 
1133   return section;
1134 }
1135 
1136 void
_packetize_common_section(GstMpegtsSection * section,gsize length)1137 _packetize_common_section (GstMpegtsSection * section, gsize length)
1138 {
1139   guint8 *data;
1140 
1141   section->section_length = length;
1142   data = section->data = g_malloc (length);
1143 
1144   /* table_id                         - 8 bit uimsbf */
1145   *data++ = section->table_id;
1146 
1147   /* section_syntax_indicator         - 1  bit
1148      reserved                         - 3  bit
1149      section_length                   - 12 bit uimsbf */
1150   switch (section->section_type) {
1151     case GST_MPEGTS_SECTION_PAT:
1152     case GST_MPEGTS_SECTION_PMT:
1153     case GST_MPEGTS_SECTION_CAT:
1154     case GST_MPEGTS_SECTION_TSDT:
1155     case GST_MPEGTS_SECTION_SCTE_SIT:
1156       /* Tables from ISO/IEC 13818-1 has a '0' bit
1157        * after the section_syntax_indicator */
1158       /* FIXME : that 'bit' after the section_syntax_indicator is the
1159        * private_indicator field. We should figure out why/when it
1160        * needs to be set *OR* decide that by default it is set to 0
1161        * (i.e. not private data) unless the user/caller decides */
1162       GST_WRITE_UINT16_BE (data, (section->section_length - 3) | 0x3000);
1163       break;
1164     default:
1165       GST_WRITE_UINT16_BE (data, (section->section_length - 3) | 0x7000);
1166   }
1167 
1168   /* short sections do not contain any further fields */
1169   if (section->short_section)
1170     return;
1171 
1172   /* Set section_syntax_indicator bit since we do not have a short section */
1173   *data |= 0x80;
1174   data += 2;
1175 
1176   /* subtable_extension               - 16 bit uimsbf */
1177   GST_WRITE_UINT16_BE (data, section->subtable_extension);
1178   data += 2;
1179 
1180   /* reserved                         - 2  bit
1181      version_number                   - 5  bit uimsbf
1182      current_next_indicator           - 1  bit */
1183   *data++ = 0xC0 |
1184       ((section->version_number & 0x1F) << 1) |
1185       (section->current_next_indicator & 0x01);
1186 
1187   /* section_number                   - 8  bit uimsbf */
1188   *data++ = section->section_number;
1189   /* last_section_number              - 8  bit uimsbf */
1190   *data++ = section->last_section_number;
1191 }
1192 
1193 /**
1194  * gst_mpegts_section_new:
1195  * @pid: the PID to which this section belongs
1196  * @data: (transfer full) (array length=data_size): a pointer to the beginning of
1197  * the section (i.e. the first byte should contain the `table_id` field).
1198  * @data_size: size of the @data argument.
1199  *
1200  * Creates a new #GstMpegtsSection from the provided @data.
1201  *
1202  * Note: Ensuring @data is big enough to contain the full section is the
1203  * responsibility of the caller. If it is not big enough, %NULL will be
1204  * returned.
1205  *
1206  * Note: it is the responsibility of the caller to ensure @data does point
1207  * to the beginning of the section.
1208  *
1209  * Returns: (transfer full): A new #GstMpegtsSection if the data was valid,
1210  * else %NULL
1211  */
1212 GstMpegtsSection *
gst_mpegts_section_new(guint16 pid,guint8 * data,gsize data_size)1213 gst_mpegts_section_new (guint16 pid, guint8 * data, gsize data_size)
1214 {
1215   GstMpegtsSection *res = NULL;
1216   guint8 tmp;
1217   guint8 table_id;
1218   guint16 section_length = 0;
1219 
1220   /* The smallest section ever is 3 bytes */
1221   if (G_UNLIKELY (data_size < 3))
1222     goto short_packet;
1223 
1224   /* Check for length */
1225   section_length = GST_READ_UINT16_BE (data + 1) & 0x0FFF;
1226   if (G_UNLIKELY (data_size < section_length + 3))
1227     goto short_packet;
1228 
1229   GST_LOG ("data_size:%" G_GSIZE_FORMAT " section_length:%u",
1230       data_size, section_length);
1231 
1232   /* Table id is in first byte */
1233   table_id = *data;
1234 
1235   res = _gst_mpegts_section_init (pid, table_id);
1236 
1237   res->data = data;
1238   /* table_id (already parsed)       : 8  bit */
1239   data++;
1240   /* section_syntax_indicator        : 1  bit
1241    * other_fields (reserved)         : 3  bit*/
1242   res->short_section = (*data & 0x80) == 0x00;
1243   /* section_length (already parsed) : 12 bit */
1244   res->section_length = section_length + 3;
1245   if (!res->short_section) {
1246     /* A long packet needs to be at least 11 bytes long
1247      * _ 3 for the bytes above
1248      * _ 5 for the bytes below
1249      * _ 4 for the CRC */
1250     if (G_UNLIKELY (data_size < 11))
1251       goto bad_long_packet;
1252 
1253     /* CRC is after section_length (-4 for the size of the CRC) */
1254     res->crc = GST_READ_UINT32_BE (res->data + res->section_length - 4);
1255     /* Skip to after section_length */
1256     data += 2;
1257     /* subtable extension            : 16 bit */
1258     res->subtable_extension = GST_READ_UINT16_BE (data);
1259     data += 2;
1260     /* reserved                      : 2  bit
1261      * version_number                : 5  bit
1262      * current_next_indicator        : 1  bit */
1263     tmp = *data++;
1264     res->version_number = tmp >> 1 & 0x1f;
1265     res->current_next_indicator = tmp & 0x01;
1266     /* section_number                : 8  bit */
1267     res->section_number = *data++;
1268     /* last_section_number                : 8  bit */
1269     res->last_section_number = *data;
1270   }
1271 
1272   return res;
1273 
1274 short_packet:
1275   {
1276     GST_WARNING
1277         ("PID 0x%04x section extends past provided data (got:%" G_GSIZE_FORMAT
1278         ", need:%d)", pid, data_size, section_length + 3);
1279     g_free (data);
1280     return NULL;
1281   }
1282 bad_long_packet:
1283   {
1284     GST_WARNING ("PID 0x%04x long section is too short (%" G_GSIZE_FORMAT
1285         " bytes, need at least 11)", pid, data_size);
1286     gst_mpegts_section_unref (res);
1287     return NULL;
1288   }
1289 }
1290 
1291 /**
1292  * gst_mpegts_section_packetize:
1293  * @section: (transfer none): the #GstMpegtsSection that holds the data
1294  * @output_size: (out): #gsize to hold the size of the data
1295  *
1296  * Packetize (i.e. serialize) the @section. If the data in @section has already
1297  * been packetized, the data pointer is returned immediately. Otherwise, the
1298  * data field is allocated and populated.
1299  *
1300  * Returns: (transfer none): pointer to section data, or %NULL on failure.
1301  */
1302 guint8 *
gst_mpegts_section_packetize(GstMpegtsSection * section,gsize * output_size)1303 gst_mpegts_section_packetize (GstMpegtsSection * section, gsize * output_size)
1304 {
1305   guint8 *crc;
1306   g_return_val_if_fail (section != NULL, NULL);
1307   g_return_val_if_fail (output_size != NULL, NULL);
1308 
1309   /* Section data has already been packetized */
1310   if (section->data) {
1311     *output_size = section->section_length;
1312     return section->data;
1313   }
1314 
1315   g_return_val_if_fail (section->packetizer != NULL, NULL);
1316 
1317   if (!section->packetizer (section))
1318     return NULL;
1319 
1320   if (!section->short_section) {
1321     /* Update the CRC in the last 4 bytes of the section */
1322     crc = section->data + section->section_length - 4;
1323     GST_WRITE_UINT32_BE (crc, _calc_crc32 (section->data, crc - section->data));
1324   }
1325 
1326   *output_size = section->section_length;
1327 
1328   return section->data;
1329 }
1330 
1331 void
__initialize_sections(void)1332 __initialize_sections (void)
1333 {
1334   /* FIXME : Temporary hack to initialize section gtype */
1335   _gst_mpegts_section_type = gst_mpegts_section_get_type ();
1336 
1337   QUARK_PAT = g_quark_from_string ("pat");
1338   QUARK_CAT = g_quark_from_string ("cat");
1339   QUARK_PMT = g_quark_from_string ("pmt");
1340   QUARK_NIT = g_quark_from_string ("nit");
1341   QUARK_BAT = g_quark_from_string ("bat");
1342   QUARK_SDT = g_quark_from_string ("sdt");
1343   QUARK_EIT = g_quark_from_string ("eit");
1344   QUARK_TDT = g_quark_from_string ("tdt");
1345   QUARK_TOT = g_quark_from_string ("tot");
1346   QUARK_SCTE_SIT = g_quark_from_string ("scte-sit");
1347   QUARK_SECTION = g_quark_from_string ("section");
1348 }
1349