mpegts: Add functions to send sections as events
[platform/upstream/gstreamer.git] / gst-libs / gst / mpegts / gstmpegtssection.c
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 softwagre; you can redistribute it and/or
17  * modify it under the terms of the GNU Library General Public
18  * License as published by the Free Software Foundation; either
19  * version 2 of the License, or (at your option) any later version.
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
32 #include <string.h>
33 #include <stdlib.h>
34
35 #include "mpegts.h"
36 #include "gstmpegts-private.h"
37
38 /**
39  * SECTION:gstmpegts
40  * @title: Mpeg-ts helper library
41  * @short_description: Mpeg-ts helper library for plugins and applications
42  * @include: gst/mpegts/mpegts.h
43  */
44
45 /**
46  * SECTION:gstmpegtssection
47  * @title: Base MPEG-TS sections
48  * @short_description: Sections for ITU H.222.0 | ISO/IEC 13818-1 
49  * @include: gst/mpegts/mpegts.h
50  *
51  * For more details, refer to the ITU H.222.0 or ISO/IEC 13818-1 specifications
52  * and other specifications mentionned in the documentation.
53  */
54
55 /* FIXME : Move this to proper file once we have a C file for it */
56 /**
57  * SECTION:gst-atsc-section
58  * @title: ATSC variants of MPEG-TS sections
59  * @short_description: Sections for the various ATSC specifications
60  * @include: gst/mpegts/mpegts.h
61  *
62  */
63
64 /*
65  * TODO
66  *
67  * * Check minimum size for section parsing in the various 
68  *   gst_mpegts_section_get_<tabld>() methods
69  *
70  * * Implement parsing code for
71  *   * BAT
72  *   * CAT
73  *   * TSDT
74  */
75
76 GST_DEBUG_CATEGORY (gst_mpegts_debug);
77
78 static GQuark QUARK_PAT;
79 static GQuark QUARK_CAT;
80 static GQuark QUARK_BAT;
81 static GQuark QUARK_PMT;
82 static GQuark QUARK_NIT;
83 static GQuark QUARK_SDT;
84 static GQuark QUARK_EIT;
85 static GQuark QUARK_TDT;
86 static GQuark QUARK_TOT;
87 static GQuark QUARK_SECTION;
88
89 static GType _gst_mpegts_section_type = 0;
90 #define MPEG_TYPE_TS_SECTION (_gst_mpegts_section_type)
91 GST_DEFINE_MINI_OBJECT_TYPE (GstMpegTsSection, gst_mpegts_section);
92
93 static const guint32 crc_tab[256] = {
94   0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
95   0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
96   0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
97   0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
98   0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
99   0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
100   0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
101   0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
102   0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
103   0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
104   0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
105   0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
106   0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
107   0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
108   0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
109   0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
110   0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
111   0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
112   0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
113   0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
114   0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
115   0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
116   0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
117   0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
118   0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
119   0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
120   0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
121   0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
122   0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
123   0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
124   0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
125   0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
126   0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
127   0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
128   0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
129   0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
130   0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
131   0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
132   0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
133   0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
134   0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
135   0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
136   0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
137 };
138
139 /* _calc_crc32 relicenced to LGPL from fluendo ts demuxer */
140 guint32
141 _calc_crc32 (const guint8 * data, guint datalen)
142 {
143   gint i;
144   guint32 crc = 0xffffffff;
145
146   for (i = 0; i < datalen; i++) {
147     crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff];
148   }
149   return crc;
150 }
151
152 gpointer
153 __common_desc_checks (GstMpegTsSection * section, guint min_size,
154     GstMpegTsParseFunc parsefunc, GDestroyNotify destroynotify)
155 {
156   gpointer res;
157
158   /* Check section is big enough */
159   if (section->section_length < min_size) {
160     GST_WARNING
161         ("PID:0x%04x table_id:0x%02x, section too small (Got %d, need at least %d)",
162         section->pid, section->table_id, section->section_length, min_size);
163     return NULL;
164   }
165
166   /* If section has a CRC, check it */
167   if (!section->short_section
168       && (_calc_crc32 (section->data, section->section_length) != 0)) {
169     GST_WARNING ("PID:0x%04x table_id:0x%02x, Bad CRC on section", section->pid,
170         section->table_id);
171     return NULL;
172   }
173
174   /* Finally parse and set the destroy notify */
175   res = parsefunc (section);
176   if (res == NULL)
177     GST_WARNING ("PID:0x%04x table_id:0x%02x, Failed to parse section",
178         section->pid, section->table_id);
179   else
180     section->destroy_parsed = destroynotify;
181   return res;
182 }
183
184
185 /*
186  * GENERIC MPEG-TS SECTION
187  */
188 static void
189 _gst_mpegts_section_free (GstMpegTsSection * section)
190 {
191   GST_DEBUG ("Freeing section type %d", section->section_type);
192
193   if (section->cached_parsed && section->destroy_parsed)
194     section->destroy_parsed (section->cached_parsed);
195
196   if (section->data)
197     g_free (section->data);
198
199   g_slice_free (GstMpegTsSection, section);
200 }
201
202 static GstMpegTsSection *
203 _gst_mpegts_section_copy (GstMpegTsSection * section)
204 {
205   GstMpegTsSection *copy;
206
207   copy = g_slice_new0 (GstMpegTsSection);
208   gst_mini_object_init (GST_MINI_OBJECT_CAST (copy), 0, MPEG_TYPE_TS_SECTION,
209       (GstMiniObjectCopyFunction) _gst_mpegts_section_copy, NULL,
210       (GstMiniObjectFreeFunction) _gst_mpegts_section_free);
211
212   copy->section_type = section->section_type;
213   copy->pid = section->pid;
214   copy->table_id = section->table_id;
215   copy->subtable_extension = section->subtable_extension;
216   copy->version_number = section->version_number;
217   copy->current_next_indicator = section->current_next_indicator;
218   copy->section_number = section->section_number;
219   copy->last_section_number = section->last_section_number;
220   copy->crc = section->crc;
221
222   copy->data = g_memdup (section->data, section->section_length);
223   copy->section_length = section->section_length;
224   /* Note: We do not copy the cached parsed item, it will be
225    * reconstructed on that copy */
226   copy->cached_parsed = NULL;
227   copy->offset = section->offset;
228   copy->short_section = section->short_section;
229
230   return copy;
231 }
232
233
234 /**
235  * gst_message_parse_mpegts_section:
236  * @message: a #GstMessage
237  *
238  * Returns the #GstMpegTsSection contained in a message.
239  *
240  * Returns: (transfer full): the contained #GstMpegTsSection, or %NULL.
241  */
242 GstMpegTsSection *
243 gst_message_parse_mpegts_section (GstMessage * message)
244 {
245   const GstStructure *st;
246   GstMpegTsSection *section;
247
248   if (message->type != GST_MESSAGE_ELEMENT)
249     return NULL;
250
251   st = gst_message_get_structure (message);
252   /* FIXME : Add checks against know section names */
253   if (!gst_structure_id_get (st, QUARK_SECTION, GST_TYPE_MPEGTS_SECTION,
254           &section, NULL))
255     return NULL;
256
257   return section;
258 }
259
260 static GstStructure *
261 _mpegts_section_get_structure (GstMpegTsSection * section)
262 {
263   GstStructure *st;
264   GQuark quark;
265
266   switch (section->section_type) {
267     case GST_MPEGTS_SECTION_PAT:
268       quark = QUARK_PAT;
269       break;
270     case GST_MPEGTS_SECTION_PMT:
271       quark = QUARK_PMT;
272       break;
273     case GST_MPEGTS_SECTION_CAT:
274       quark = QUARK_CAT;
275       break;
276     case GST_MPEGTS_SECTION_EIT:
277       quark = QUARK_EIT;
278       break;
279     case GST_MPEGTS_SECTION_BAT:
280       quark = QUARK_BAT;
281       break;
282     case GST_MPEGTS_SECTION_NIT:
283       quark = QUARK_NIT;
284       break;
285     case GST_MPEGTS_SECTION_SDT:
286       quark = QUARK_SDT;
287       break;
288     case GST_MPEGTS_SECTION_TDT:
289       quark = QUARK_TDT;
290       break;
291     case GST_MPEGTS_SECTION_TOT:
292       quark = QUARK_TOT;
293       break;
294     default:
295       GST_DEBUG ("Creating structure for unknown GstMpegTsSection");
296       quark = QUARK_SECTION;
297       break;
298   }
299
300   st = gst_structure_new_id (quark, QUARK_SECTION, MPEG_TYPE_TS_SECTION,
301       section, NULL);
302
303   return st;
304 }
305
306 /**
307  * gst_message_new_mpegts_section:
308  * @parent: (transfer none): The creator of the message
309  * @section: (transfer none): The #GstMpegTsSection to put in a message
310  *
311  * Creates a new #GstMessage for a @GstMpegTsSection.
312  *
313  * Returns: (transfer full): The new #GstMessage to be posted, or %NULL if the
314  * section is not valid.
315  */
316 GstMessage *
317 gst_message_new_mpegts_section (GstObject * parent, GstMpegTsSection * section)
318 {
319   GstMessage *msg;
320   GstStructure *st;
321
322   st = _mpegts_section_get_structure (section);
323
324   msg = gst_message_new_element (parent, st);
325
326   return msg;
327 }
328
329 static GstEvent *
330 _mpegts_section_get_event (GstMpegTsSection * section)
331 {
332   GstStructure *structure;
333   GstEvent *event;
334
335   structure = _mpegts_section_get_structure (section);
336
337   event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
338
339   return event;
340 }
341
342 /**
343  * gst_event_parse_mpegts_section:
344  * @event: (transfer none): #GstEvent containing a #GstMpegTsSection
345  *
346  * Extracts the #GstMpegTsSection contained in the @event #GstEvent
347  *
348  * Returns: (transfer full): The extracted #GstMpegTsSection
349  */
350 GstMpegTsSection *
351 gst_event_parse_mpegts_section (GstEvent * event)
352 {
353   const GstStructure *structure;
354   GstMpegTsSection *section;
355
356   structure = gst_event_get_structure (event);
357
358   if (!gst_structure_id_get (structure, QUARK_SECTION, MPEG_TYPE_TS_SECTION,
359           &section, NULL))
360     return NULL;
361
362   return section;
363 }
364
365 /**
366  * gst_mpegts_section_send_event:
367  * @element: (transfer none): The #GstElement to send to section event to
368  * @section: (transfer none): The #GstMpegTsSection to put in the event
369  *
370  * Creates a custom #GstEvent with a @GstMpegTsSection.
371  * The #GstEvent is sent to the @element #GstElement.
372  *
373  * Returns: %TRUE if the event is sent
374  */
375 gboolean
376 gst_mpegts_section_send_event (GstMpegTsSection * section, GstElement * element)
377 {
378   GstEvent *event;
379
380   g_return_val_if_fail (section != NULL, FALSE);
381   g_return_val_if_fail (element != NULL, FALSE);
382
383   event = _mpegts_section_get_event (section);
384
385   if (!gst_element_send_event (element, event)) {
386     gst_event_unref (event);
387     return FALSE;
388   }
389
390   return TRUE;
391 }
392
393 static GstMpegTsPatProgram *
394 _mpegts_pat_program_copy (GstMpegTsPatProgram * orig)
395 {
396   return g_slice_dup (GstMpegTsPatProgram, orig);
397 }
398
399 static void
400 _mpegts_pat_program_free (GstMpegTsPatProgram * orig)
401 {
402   g_slice_free (GstMpegTsPatProgram, orig);
403 }
404
405 G_DEFINE_BOXED_TYPE (GstMpegTsPatProgram, gst_mpegts_pat_program,
406     (GBoxedCopyFunc) _mpegts_pat_program_copy,
407     (GFreeFunc) _mpegts_pat_program_free);
408
409 /* Program Association Table */
410 static gpointer
411 _parse_pat (GstMpegTsSection * section)
412 {
413   GPtrArray *pat;
414   guint16 i = 0, nb_programs;
415   GstMpegTsPatProgram *program;
416   guint8 *data, *end;
417
418   /* Skip already parsed data */
419   data = section->data + 8;
420
421   /* stop at the CRC */
422   end = section->data + section->section_length;
423
424   /* Initialize program list */
425   nb_programs = (end - 4 - data) / 4;
426   pat =
427       g_ptr_array_new_full (nb_programs,
428       (GDestroyNotify) _mpegts_pat_program_free);
429
430   while (data < end - 4) {
431     program = g_slice_new0 (GstMpegTsPatProgram);
432     program->program_number = GST_READ_UINT16_BE (data);
433     data += 2;
434
435     program->network_or_program_map_PID = GST_READ_UINT16_BE (data) & 0x1FFF;
436     data += 2;
437
438     g_ptr_array_index (pat, i) = program;
439
440     i++;
441   }
442   pat->len = nb_programs;
443
444   if (data != end - 4) {
445     GST_ERROR ("at the end of PAT data != end - 4");
446     g_ptr_array_unref (pat);
447
448     return NULL;
449   }
450
451   return (gpointer) pat;
452 }
453
454 /**
455  * gst_mpegts_section_get_pat:
456  * @section: a #GstMpegTsSection of type %GST_MPEGTS_SECTION_PAT
457  *
458  * Parses a Program Association Table (ITU H.222.0, ISO/IEC 13818-1).
459  *
460  * Returns the array of #GstMpegTsPatProgram contained in the section.
461  *
462  * Note: The PAT "transport_id" field corresponds to the "subtable_extension"
463  * field of the provided @section.
464  *
465  * Returns: (transfer container) (element-type GstMpegTsPatProgram): The
466  * #GstMpegTsPatProgram contained in the section, or %NULL if an error
467  * happened. Release with #g_ptr_array_unref when done.
468  */
469 GPtrArray *
470 gst_mpegts_section_get_pat (GstMpegTsSection * section)
471 {
472   g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_PAT, NULL);
473   g_return_val_if_fail (section->cached_parsed || section->data, NULL);
474
475   if (!section->cached_parsed)
476     section->cached_parsed =
477         __common_desc_checks (section, 12, _parse_pat,
478         (GDestroyNotify) g_ptr_array_unref);
479
480   if (section->cached_parsed)
481     return g_ptr_array_ref ((GPtrArray *) section->cached_parsed);
482   return NULL;
483 }
484
485
486 /* Program Map Table */
487
488 static GstMpegTsPMTStream *
489 _gst_mpegts_pmt_stream_copy (GstMpegTsPMTStream * pmt)
490 {
491   GstMpegTsPMTStream *copy;
492
493   copy = g_slice_dup (GstMpegTsPMTStream, pmt);
494   copy->descriptors = g_ptr_array_ref (pmt->descriptors);
495
496   return copy;
497 }
498
499 static void
500 _gst_mpegts_pmt_stream_free (GstMpegTsPMTStream * pmt)
501 {
502   g_ptr_array_unref (pmt->descriptors);
503   g_slice_free (GstMpegTsPMTStream, pmt);
504 }
505
506 G_DEFINE_BOXED_TYPE (GstMpegTsPMTStream, gst_mpegts_pmt_stream,
507     (GBoxedCopyFunc) _gst_mpegts_pmt_stream_copy,
508     (GFreeFunc) _gst_mpegts_pmt_stream_free);
509
510 static GstMpegTsPMT *
511 _gst_mpegts_pmt_copy (GstMpegTsPMT * pmt)
512 {
513   GstMpegTsPMT *copy;
514
515   copy = g_slice_dup (GstMpegTsPMT, pmt);
516   copy->descriptors = g_ptr_array_ref (pmt->descriptors);
517   copy->streams = g_ptr_array_ref (pmt->streams);
518
519   return copy;
520 }
521
522 static void
523 _gst_mpegts_pmt_free (GstMpegTsPMT * pmt)
524 {
525   g_ptr_array_unref (pmt->descriptors);
526   g_ptr_array_unref (pmt->streams);
527   g_slice_free (GstMpegTsPMT, pmt);
528 }
529
530 G_DEFINE_BOXED_TYPE (GstMpegTsPMT, gst_mpegts_pmt,
531     (GBoxedCopyFunc) _gst_mpegts_pmt_copy, (GFreeFunc) _gst_mpegts_pmt_free);
532
533
534 static gpointer
535 _parse_pmt (GstMpegTsSection * section)
536 {
537   GstMpegTsPMT *pmt = NULL;
538   guint i = 0, allocated_streams = 8;
539   guint8 *data, *end;
540   guint program_info_length;
541   guint stream_info_length;
542
543   pmt = g_slice_new0 (GstMpegTsPMT);
544
545   data = section->data;
546   end = data + section->section_length;
547
548   GST_DEBUG ("Parsing %d Program Map Table", section->subtable_extension);
549
550   /* Skip already parsed data */
551   data += 8;
552
553   pmt->pcr_pid = GST_READ_UINT16_BE (data) & 0x1FFF;
554   data += 2;
555
556   program_info_length = GST_READ_UINT16_BE (data) & 0x0FFF;
557   data += 2;
558
559   /* check that the buffer is large enough to contain at least
560    * program_info_length bytes + CRC */
561   if (program_info_length && (data + program_info_length + 4 > end)) {
562     GST_WARNING ("PID %d invalid program info length %d left %d",
563         section->pid, program_info_length, (gint) (end - data));
564     goto error;
565   }
566   pmt->descriptors = gst_mpegts_parse_descriptors (data, program_info_length);
567   if (pmt->descriptors == NULL)
568     goto error;
569   data += program_info_length;
570
571   pmt->streams =
572       g_ptr_array_new_full (allocated_streams,
573       (GDestroyNotify) _gst_mpegts_pmt_stream_free);
574
575   /* parse entries, cycle until there's space for another entry (at least 5
576    * bytes) plus the CRC */
577   while (data <= end - 4 - 5) {
578     GstMpegTsPMTStream *stream = g_slice_new0 (GstMpegTsPMTStream);
579
580     g_ptr_array_add (pmt->streams, stream);
581
582     stream->stream_type = *data++;
583     GST_DEBUG ("[%d] Stream type 0x%02x found", i, stream->stream_type);
584
585     stream->pid = GST_READ_UINT16_BE (data) & 0x1FFF;
586     data += 2;
587
588     stream_info_length = GST_READ_UINT16_BE (data) & 0x0FFF;
589     data += 2;
590
591     if (data + stream_info_length + 4 > end) {
592       GST_WARNING ("PID %d invalid stream info length %d left %d", section->pid,
593           stream_info_length, (gint) (end - data));
594       goto error;
595     }
596
597     stream->descriptors =
598         gst_mpegts_parse_descriptors (data, stream_info_length);
599     if (stream->descriptors == NULL)
600       goto error;
601     data += stream_info_length;
602
603     i += 1;
604   }
605
606   g_assert (data == end - 4);
607
608   return (gpointer) pmt;
609
610 error:
611   if (pmt)
612     _gst_mpegts_pmt_free (pmt);
613
614   return NULL;
615 }
616
617 /**
618  * gst_mpegts_section_get_pmt:
619  * @section: a #GstMpegTsSection of type %GST_MPEGTS_SECTION_PMT
620  *
621  * Returns the #GstMpegTsPMT contained in the @section.
622  *
623  * Returns: The #GstMpegTsPMT contained in the section, or %NULL if an error
624  * happened.
625  */
626 const GstMpegTsPMT *
627 gst_mpegts_section_get_pmt (GstMpegTsSection * section)
628 {
629   g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_PMT, NULL);
630   g_return_val_if_fail (section->cached_parsed || section->data, NULL);
631
632   if (!section->cached_parsed)
633     section->cached_parsed =
634         __common_desc_checks (section, 16, _parse_pmt,
635         (GDestroyNotify) _gst_mpegts_pmt_free);
636
637   return (const GstMpegTsPMT *) section->cached_parsed;
638 }
639
640
641 /* Conditional Access Table */
642 static gpointer
643 _parse_cat (GstMpegTsSection * section)
644 {
645   guint8 *data;
646   guint desc_len;
647
648   /* Skip parts already parsed */
649   data = section->data + 8;
650
651   /* descriptors */
652   desc_len = section->section_length - 4 - 8;
653   return (gpointer) gst_mpegts_parse_descriptors (data, desc_len);
654 }
655
656 /**
657  * gst_mpegts_section_get_cat:
658  * @section: a #GstMpegTsSection of type %GST_MPEGTS_SECTION_CAT
659  *
660  * Returns the array of #GstMpegTsDescriptor contained in the Condtional
661  * Access Table.
662  *
663  * Returns: (transfer container) (element-type GstMpegTsDescriptor): The
664  * #GstMpegTsDescriptor contained in the section, or %NULL if an error
665  * happened. Release with #g_array_unref when done.
666  */
667 GPtrArray *
668 gst_mpegts_section_get_cat (GstMpegTsSection * section)
669 {
670   g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_CAT, NULL);
671   g_return_val_if_fail (section->cached_parsed || section->data, NULL);
672
673   if (!section->cached_parsed)
674     section->cached_parsed =
675         __common_desc_checks (section, 12, _parse_cat,
676         (GDestroyNotify) g_ptr_array_unref);
677
678   if (section->cached_parsed)
679     return g_ptr_array_ref ((GPtrArray *) section->cached_parsed);
680   return NULL;
681 }
682
683 /* Transport Stream Description Table (TSDT) */
684 /**
685  * gst_mpegts_section_get_tsdt:
686  * @section: a #GstMpegTsSection of type %GST_MPEGTS_SECTION_TSDT
687  *
688  * Returns the array of #GstMpegTsDescriptor contained in the section
689  *
690  * Returns: (transfer container) (element-type GstMpegTsDescriptor): The
691  * #GstMpegTsDescriptor contained in the section, or %NULL if an error
692  * happened. Release with #g_array_unref when done.
693  */
694 GPtrArray *
695 gst_mpegts_section_get_tsdt (GstMpegTsSection * section)
696 {
697   g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_TSDT, NULL);
698   g_return_val_if_fail (section->cached_parsed || section->data, NULL);
699
700   if (section->cached_parsed)
701     return g_ptr_array_ref ((GPtrArray *) section->cached_parsed);
702
703   /* FIXME : parse TSDT */
704   return NULL;
705 }
706
707
708 /**
709  * gst_mpegts_initialize:
710  *
711  * Initializes the MPEG-TS helper library. Must be called before any
712  * usage.
713  */
714 void
715 gst_mpegts_initialize (void)
716 {
717   if (_gst_mpegts_section_type)
718     return;
719
720   GST_DEBUG_CATEGORY_INIT (gst_mpegts_debug, "mpegts", 0,
721       "MPEG-TS helper library");
722
723   /* FIXME : Temporary hack to initialize section gtype */
724   _gst_mpegts_section_type = gst_mpegts_section_get_type ();
725
726   QUARK_PAT = g_quark_from_string ("pat");
727   QUARK_CAT = g_quark_from_string ("cat");
728   QUARK_PMT = g_quark_from_string ("pmt");
729   QUARK_NIT = g_quark_from_string ("nit");
730   QUARK_BAT = g_quark_from_string ("bat");
731   QUARK_SDT = g_quark_from_string ("sdt");
732   QUARK_EIT = g_quark_from_string ("eit");
733   QUARK_TDT = g_quark_from_string ("tdt");
734   QUARK_TOT = g_quark_from_string ("tot");
735   QUARK_SECTION = g_quark_from_string ("section");
736
737   __initialize_descriptors ();
738 }
739
740 /* FIXME : Later on we might need to use more than just the table_id
741  * to figure out which type of section this is. */
742 static GstMpegTsSectionType
743 _identify_section (guint16 pid, guint8 table_id)
744 {
745   switch (table_id) {
746     case GST_MTS_TABLE_ID_PROGRAM_ASSOCIATION:
747       if (pid == 0x00)
748         return GST_MPEGTS_SECTION_PAT;
749       break;
750     case GST_MTS_TABLE_ID_CONDITIONAL_ACCESS:
751       if (pid == 0x01)
752         return GST_MPEGTS_SECTION_CAT;
753       break;
754     case GST_MTS_TABLE_ID_TS_PROGRAM_MAP:
755       return GST_MPEGTS_SECTION_PMT;
756     case GST_MTS_TABLE_ID_BOUQUET_ASSOCIATION:
757       if (pid == 0x0011)
758         return GST_MPEGTS_SECTION_BAT;
759       break;
760     case GST_MTS_TABLE_ID_NETWORK_INFORMATION_ACTUAL_NETWORK:
761     case GST_MTS_TABLE_ID_NETWORK_INFORMATION_OTHER_NETWORK:
762       if (pid == 0x0010)
763         return GST_MPEGTS_SECTION_NIT;
764       break;
765     case GST_MTS_TABLE_ID_SERVICE_DESCRIPTION_ACTUAL_TS:
766     case GST_MTS_TABLE_ID_SERVICE_DESCRIPTION_OTHER_TS:
767       if (pid == 0x0011)
768         return GST_MPEGTS_SECTION_SDT;
769       break;
770     case GST_MTS_TABLE_ID_TIME_DATE:
771       if (pid == 0x0014)
772         return GST_MPEGTS_SECTION_TDT;
773       break;
774     case GST_MTS_TABLE_ID_TIME_OFFSET:
775       if (pid == 0x0014)
776         return GST_MPEGTS_SECTION_TOT;
777       break;
778       /* FIXME : FILL */
779     default:
780       /* Handle ranges */
781       if (table_id >= GST_MTS_TABLE_ID_EVENT_INFORMATION_ACTUAL_TS_PRESENT &&
782           table_id <= GST_MTS_TABLE_ID_EVENT_INFORMATION_OTHER_TS_SCHEDULE_N) {
783         if (pid == 0x0012)
784           return GST_MPEGTS_SECTION_EIT;
785       }
786       return GST_MPEGTS_SECTION_UNKNOWN;
787   }
788   return GST_MPEGTS_SECTION_UNKNOWN;
789
790 }
791
792 GstMpegTsSection *
793 _gst_mpegts_section_init (guint16 pid, guint8 table_id)
794 {
795   GstMpegTsSection *section;
796
797   section = g_slice_new0 (GstMpegTsSection);
798   gst_mini_object_init (GST_MINI_OBJECT_CAST (section), 0, MPEG_TYPE_TS_SECTION,
799       (GstMiniObjectCopyFunction) _gst_mpegts_section_copy, NULL,
800       (GstMiniObjectFreeFunction) _gst_mpegts_section_free);
801
802   section->pid = pid;
803   section->table_id = table_id;
804   section->current_next_indicator = TRUE;
805   section->section_type = _identify_section (pid, table_id);
806
807   return section;
808 }
809
810 void
811 _packetize_common_section (GstMpegTsSection * section, gsize length)
812 {
813   guint8 *data;
814
815   section->section_length = length;
816   data = section->data = g_malloc (length);
817
818   /* table_id                         - 8 bit uimsbf */
819   *data++ = section->table_id;
820
821   /* section_syntax_indicator         - 1  bit
822      reserved                         - 3  bit
823      section_length                   - 12 bit uimsbf */
824   GST_WRITE_UINT16_BE (data, (section->section_length - 3) | 0x7000);
825
826   if (!section->short_section)
827     *data |= 0x80;
828
829   data += 2;
830
831   /* subtable_extension               - 16 bit uimsbf */
832   GST_WRITE_UINT16_BE (data, section->subtable_extension);
833   data += 2;
834
835   /* reserved                         - 2  bit
836      version_number                   - 5  bit uimsbf
837      current_next_indicator           - 1  bit */
838   *data++ = 0xC0 |
839       ((section->version_number & 0x1F) << 1) |
840       (section->current_next_indicator & 0x01);
841
842   /* section_number                   - 8  bit uimsbf */
843   *data++ = section->section_number;
844   /* last_section_number              - 8  bit uimsbf */
845   *data++ = section->last_section_number;
846 }
847
848 /**
849  * gst_mpegts_section_new:
850  * @pid: the PID to which this section belongs
851  * @data: (transfer full): a pointer to the beginning of the section (i.e. the first byte
852  * should contain the table_id field).
853  * @data_size: size of the @data argument.
854  *
855  * Creates a new #GstMpegTsSection from the provided @data.
856  *
857  * Note: Ensuring @data is big enough to contain the full section is the
858  * responsibility of the caller. If it is not big enough, %NULL will be
859  * returned.
860  *
861  * Note: it is the responsibility of the caller to ensure @data does point
862  * to the beginning of the section.
863  *
864  * Returns: (transfer full): A new #GstMpegTsSection if the data was valid,
865  * else %NULL
866  */
867 GstMpegTsSection *
868 gst_mpegts_section_new (guint16 pid, guint8 * data, gsize data_size)
869 {
870   GstMpegTsSection *res = NULL;
871   guint8 tmp;
872   guint8 table_id;
873   guint16 section_length;
874
875   /* Check for length */
876   section_length = GST_READ_UINT16_BE (data + 1) & 0x0FFF;
877   if (G_UNLIKELY (data_size < section_length + 3))
878     goto short_packet;
879
880   /* Table id is in first byte */
881   table_id = *data;
882
883   res = _gst_mpegts_section_init (pid, table_id);
884
885   res->data = data;
886   /* table_id (already parsed)       : 8  bit */
887   data++;
888   /* section_syntax_indicator        : 1  bit
889    * other_fields (reserved)         : 3  bit*/
890   res->short_section = (*data & 0x80) == 0x00;
891   /* section_length (already parsed) : 12 bit */
892   res->section_length = section_length + 3;
893   if (!res->short_section) {
894     /* CRC is after section_length (-4 for the size of the CRC) */
895     res->crc = GST_READ_UINT32_BE (res->data + res->section_length - 4);
896     /* Skip to after section_length */
897     data += 2;
898     /* subtable extension            : 16 bit */
899     res->subtable_extension = GST_READ_UINT16_BE (data);
900     data += 2;
901     /* reserved                      : 2  bit
902      * version_number                : 5  bit
903      * current_next_indicator        : 1  bit */
904     tmp = *data++;
905     res->version_number = tmp >> 1 & 0x1f;
906     res->current_next_indicator = tmp & 0x01;
907     /* section_number                : 8  bit */
908     res->section_number = *data++;
909     /* last_section_number                : 8  bit */
910     res->last_section_number = *data;
911   }
912
913   return res;
914
915 short_packet:
916   {
917     GST_WARNING
918         ("PID 0x%04x section extends past provided data (got:%" G_GSIZE_FORMAT
919         ", need:%d)", pid, data_size, section_length + 3);
920     return NULL;
921   }
922 }
923
924 /**
925  * gst_mpegts_section_packetize:
926  * @section: (transfer none): the #GstMpegTsSection that holds the data
927  * @output_size: (out): #gsize to hold the size of the data
928  *
929  * If the data in @section has aldready been packetized, the data pointer is returned
930  * immediately. Otherwise, the data field is allocated and populated.
931  *
932  * Returns: (transfer none): pointer to section data, or %NULL on fail
933  */
934 guint8 *
935 gst_mpegts_section_packetize (GstMpegTsSection * section, gsize * output_size)
936 {
937   guint8 *crc;
938   g_return_val_if_fail (section != NULL, NULL);
939   g_return_val_if_fail (output_size != NULL, NULL);
940   g_return_val_if_fail (section->packetizer != NULL, NULL);
941
942   /* Section data has already been packetized */
943   if (section->data)
944     return section->data;
945
946   if (!section->packetizer (section))
947     return NULL;
948
949   if (!section->short_section) {
950     /* Update the CRC in the last 4 bytes of the section */
951     crc = section->data + section->section_length - 4;
952     GST_WRITE_UINT32_BE (crc, _calc_crc32 (section->data, crc - section->data));
953   }
954
955   *output_size = section->section_length;
956
957   return section->data;
958 }