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