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