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 §ion, 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 §ion, 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