d8c7073e5bfa9ae4852b4fbd9d498d6d38d871da
[platform/upstream/gstreamer.git] / gst / mpegtsdemux / mpegtsbase.c
1 /*
2  * mpegtsbase.c -
3  * Copyright (C) 2007 Alessandro Decina
4  *               2010 Edward Hervey
5  * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
6  *  Author: Youness Alaoui <youness.alaoui@collabora.co.uk>, Collabora Ltd.
7  *  Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
8  *  Author: Edward Hervey <bilboed@bilboed.com>, Collabora Ltd.
9  *
10  * Authors:
11  *   Alessandro Decina <alessandro@nnva.org>
12  *   Zaheer Abbas Merali <zaheerabbas at merali dot org>
13  *   Edward Hervey <edward.hervey@collabora.co.uk>
14  *
15  * This library is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU Library General Public
17  * License as published by the Free Software Foundation; either
18  * version 2 of the License, or (at your option) any later version.
19  *
20  * This library is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  * Library General Public License for more details.
24  *
25  * You should have received a copy of the GNU Library General Public
26  * License along with this library; if not, write to the
27  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
28  * Boston, MA 02111-1307, USA.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include <glib.h>
39
40 #include <gst/gst-i18n-plugin.h>
41 #include "mpegtsbase.h"
42 #include "gstmpegdesc.h"
43
44 /* latency in mseconds */
45 #define TS_LATENCY 700
46
47 #define TABLE_ID_UNSET 0xFF
48 #define RUNNING_STATUS_RUNNING 4
49
50 GST_DEBUG_CATEGORY_STATIC (mpegts_base_debug);
51 #define GST_CAT_DEFAULT mpegts_base_debug
52
53 static GQuark QUARK_PROGRAMS;
54 static GQuark QUARK_PROGRAM_NUMBER;
55 static GQuark QUARK_PID;
56 static GQuark QUARK_PCR_PID;
57 static GQuark QUARK_STREAMS;
58 static GQuark QUARK_STREAM_TYPE;
59
60 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
61     GST_PAD_SINK,
62     GST_PAD_ALWAYS,
63     GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
64     );
65
66 enum
67 {
68   ARG_0,
69   /* FILL ME */
70 };
71
72 static void mpegts_base_set_property (GObject * object, guint prop_id,
73     const GValue * value, GParamSpec * pspec);
74 static void mpegts_base_get_property (GObject * object, guint prop_id,
75     GValue * value, GParamSpec * pspec);
76 static void mpegts_base_dispose (GObject * object);
77 static void mpegts_base_finalize (GObject * object);
78 static void mpegts_base_free_program (MpegTSBaseProgram * program);
79 static void mpegts_base_free_stream (MpegTSBaseStream * ptream);
80 static gboolean mpegts_base_sink_activate (GstPad * pad);
81 static gboolean mpegts_base_sink_activate_pull (GstPad * pad, gboolean active);
82 static gboolean mpegts_base_sink_activate_push (GstPad * pad, gboolean active);
83 static GstFlowReturn mpegts_base_chain (GstPad * pad, GstBuffer * buf);
84 static gboolean mpegts_base_sink_event (GstPad * pad, GstEvent * event);
85 static GstStateChangeReturn mpegts_base_change_state (GstElement * element,
86     GstStateChange transition);
87 static void _extra_init (GType type);
88 static void mpegts_base_get_tags_from_sdt (MpegTSBase * base,
89     GstStructure * sdt_info);
90 static void mpegts_base_get_tags_from_eit (MpegTSBase * base,
91     GstStructure * eit_info);
92
93 GST_BOILERPLATE_FULL (MpegTSBase, mpegts_base, GstElement, GST_TYPE_ELEMENT,
94     _extra_init);
95
96
97 static const guint32 crc_tab[256] = {
98   0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
99   0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
100   0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
101   0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
102   0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
103   0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
104   0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
105   0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
106   0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
107   0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
108   0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
109   0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
110   0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
111   0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
112   0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
113   0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
114   0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
115   0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
116   0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
117   0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
118   0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
119   0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
120   0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
121   0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
122   0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
123   0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
124   0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
125   0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
126   0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
127   0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
128   0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
129   0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
130   0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
131   0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
132   0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
133   0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
134   0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
135   0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
136   0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
137   0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
138   0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
139   0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
140   0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
141 };
142
143 /* relicenced to LGPL from fluendo ts demuxer */
144 static guint32
145 mpegts_base_calc_crc32 (guint8 * data, guint datalen)
146 {
147   gint i;
148   guint32 crc = 0xffffffff;
149
150   for (i = 0; i < datalen; i++) {
151     crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff];
152   }
153   return crc;
154 }
155
156 static void
157 _extra_init (GType type)
158 {
159   QUARK_PROGRAMS = g_quark_from_string ("programs");
160   QUARK_PROGRAM_NUMBER = g_quark_from_string ("program-number");
161   QUARK_PID = g_quark_from_string ("pid");
162   QUARK_PCR_PID = g_quark_from_string ("pcr-pid");
163   QUARK_STREAMS = g_quark_from_string ("streams");
164   QUARK_STREAM_TYPE = g_quark_from_string ("stream-type");
165 }
166
167 static void
168 mpegts_base_base_init (gpointer klass)
169 {
170   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
171
172   gst_element_class_add_static_pad_template (element_class,
173       &sink_template);
174 }
175
176 static void
177 mpegts_base_class_init (MpegTSBaseClass * klass)
178 {
179   GObjectClass *gobject_class;
180   GstElementClass *element_class;
181
182   element_class = GST_ELEMENT_CLASS (klass);
183   element_class->change_state = mpegts_base_change_state;
184
185   gobject_class = G_OBJECT_CLASS (klass);
186   gobject_class->set_property = mpegts_base_set_property;
187   gobject_class->get_property = mpegts_base_get_property;
188   gobject_class->dispose = mpegts_base_dispose;
189   gobject_class->finalize = mpegts_base_finalize;
190
191 }
192
193 static void
194 mpegts_base_reset (MpegTSBase * base)
195 {
196   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
197
198   mpegts_packetizer_clear (base->packetizer);
199   memset (base->is_pes, 0, 1024);
200   memset (base->known_psi, 0, 1024);
201
202   /* PAT */
203   MPEGTS_BIT_SET (base->known_psi, 0);
204
205   /* FIXME : Commenting the Following lines is to be in sync with the following
206    * commit
207    *
208    * 61a885613316ce7657c36a6cd215b43f9dc67b79
209    *     mpegtsparse: don't free PAT structure which may still be needed later
210    */
211
212   /* if (base->pat != NULL) */
213   /*   gst_structure_free (base->pat); */
214   /* base->pat = NULL; */
215   /* pmt pids will be added and removed dynamically */
216
217   gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED);
218
219   base->mode = BASE_MODE_STREAMING;
220   base->seen_pat = FALSE;
221   base->first_pat_offset = -1;
222   base->in_gap = 0;
223   base->first_buf_ts = GST_CLOCK_TIME_NONE;
224
225   if (klass->reset)
226     klass->reset (base);
227 }
228
229 static void
230 mpegts_base_init (MpegTSBase * base, MpegTSBaseClass * klass)
231 {
232   base->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
233   gst_pad_set_activate_function (base->sinkpad, mpegts_base_sink_activate);
234   gst_pad_set_activatepull_function (base->sinkpad,
235       mpegts_base_sink_activate_pull);
236   gst_pad_set_activatepush_function (base->sinkpad,
237       mpegts_base_sink_activate_push);
238   gst_pad_set_chain_function (base->sinkpad, mpegts_base_chain);
239   gst_pad_set_event_function (base->sinkpad, mpegts_base_sink_event);
240   gst_element_add_pad (GST_ELEMENT (base), base->sinkpad);
241
242   base->disposed = FALSE;
243   base->packetizer = mpegts_packetizer_new ();
244   base->programs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
245       NULL, (GDestroyNotify) mpegts_base_free_program);
246
247   base->is_pes = g_new0 (guint8, 1024);
248   base->known_psi = g_new0 (guint8, 1024);
249   base->program_size = sizeof (MpegTSBaseProgram);
250   base->stream_size = sizeof (MpegTSBaseStream);
251
252   mpegts_base_reset (base);
253 }
254
255 static void
256 mpegts_base_dispose (GObject * object)
257 {
258   MpegTSBase *base = GST_MPEGTS_BASE (object);
259
260   if (!base->disposed) {
261     g_object_unref (base->packetizer);
262     base->disposed = TRUE;
263     g_free (base->known_psi);
264     g_free (base->is_pes);
265   }
266
267   if (G_OBJECT_CLASS (parent_class)->dispose)
268     G_OBJECT_CLASS (parent_class)->dispose (object);
269 }
270
271 static void
272 mpegts_base_finalize (GObject * object)
273 {
274   MpegTSBase *base = GST_MPEGTS_BASE (object);
275
276   if (base->pat) {
277     gst_structure_free (base->pat);
278     base->pat = NULL;
279   }
280   g_hash_table_destroy (base->programs);
281
282   if (G_OBJECT_CLASS (parent_class)->finalize)
283     G_OBJECT_CLASS (parent_class)->finalize (object);
284 }
285
286 static void
287 mpegts_base_set_property (GObject * object, guint prop_id,
288     const GValue * value, GParamSpec * pspec)
289 {
290   /* MpegTSBase *base = GST_MPEGTS_BASE (object); */
291
292   switch (prop_id) {
293     default:
294       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
295   }
296 }
297
298 static void
299 mpegts_base_get_property (GObject * object, guint prop_id,
300     GValue * value, GParamSpec * pspec)
301 {
302   /* MpegTSBase *base = GST_MPEGTS_BASE (object); */
303
304   switch (prop_id) {
305     default:
306       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
307   }
308 }
309
310 /* returns NULL if no matching descriptor found *
311  * otherwise returns a descriptor that needs to *
312  * be freed */
313 guint8 *
314 mpegts_get_descriptor_from_stream (MpegTSBaseStream * stream, guint8 tag)
315 {
316   GValueArray *descriptors = NULL;
317   GstStructure *stream_info = stream->stream_info;
318   guint8 *retval = NULL;
319   int i;
320
321   gst_structure_get (stream_info, "descriptors", G_TYPE_VALUE_ARRAY,
322       &descriptors, NULL);
323   if (descriptors) {
324     for (i = 0; i < descriptors->n_values; i++) {
325       GValue *value = g_value_array_get_nth (descriptors, i);
326       GString *desc = g_value_dup_boxed (value);
327       if (DESC_TAG (desc->str) == tag) {
328         retval = (guint8 *) desc->str;
329         g_string_free (desc, FALSE);
330         break;
331       } else
332         g_string_free (desc, FALSE);
333     }
334     g_value_array_free (descriptors);
335   }
336   return retval;
337 }
338
339 typedef struct
340 {
341   gboolean res;
342   guint16 pid;
343 } PIDLookup;
344
345 static void
346 foreach_pid_in_program (gpointer key, MpegTSBaseProgram * program,
347     PIDLookup * lookup)
348 {
349   if (!program->active)
350     return;
351   if (program->streams[lookup->pid])
352     lookup->res = TRUE;
353 }
354
355 static gboolean
356 mpegts_pid_in_active_programs (MpegTSBase * base, guint16 pid)
357 {
358   PIDLookup lookup;
359
360   lookup.res = FALSE;
361   lookup.pid = pid;
362   g_hash_table_foreach (base->programs, (GHFunc) foreach_pid_in_program,
363       &lookup);
364
365   return lookup.res;
366 }
367
368 /* returns NULL if no matching descriptor found *
369  * otherwise returns a descriptor that needs to *
370  * be freed */
371 guint8 *
372 mpegts_get_descriptor_from_program (MpegTSBaseProgram * program, guint8 tag)
373 {
374   GValueArray *descriptors = NULL;
375   GstStructure *program_info;
376   guint8 *retval = NULL;
377   int i;
378
379   if (G_UNLIKELY (program == NULL))
380     return NULL;
381   program_info = program->pmt_info;
382   gst_structure_get (program_info, "descriptors", G_TYPE_VALUE_ARRAY,
383       &descriptors, NULL);
384   if (descriptors) {
385     for (i = 0; i < descriptors->n_values; i++) {
386       GValue *value = g_value_array_get_nth (descriptors, i);
387       GString *desc = g_value_dup_boxed (value);
388       if (DESC_TAG (desc->str) == tag) {
389         retval = (guint8 *) desc->str;
390         g_string_free (desc, FALSE);
391         break;
392       } else
393         g_string_free (desc, FALSE);
394     }
395     g_value_array_free (descriptors);
396   }
397   return retval;
398 }
399
400 static MpegTSBaseProgram *
401 mpegts_base_new_program (MpegTSBase * base,
402     gint program_number, guint16 pmt_pid)
403 {
404   MpegTSBaseProgram *program;
405
406   GST_DEBUG_OBJECT (base, "program_number : %d, pmt_pid : %d",
407       program_number, pmt_pid);
408
409   program = g_malloc0 (base->program_size);
410   program->program_number = program_number;
411   program->pmt_pid = pmt_pid;
412   program->pcr_pid = G_MAXUINT16;
413   program->streams = g_new0 (MpegTSBaseStream *, 0x2000);
414   program->patcount = 0;
415
416   return program;
417 }
418
419 MpegTSBaseProgram *
420 mpegts_base_add_program (MpegTSBase * base,
421     gint program_number, guint16 pmt_pid)
422 {
423   MpegTSBaseProgram *program;
424
425   GST_DEBUG_OBJECT (base, "program_number : %d, pmt_pid : %d",
426       program_number, pmt_pid);
427
428   program = mpegts_base_new_program (base, program_number, pmt_pid);
429
430   /* Mark the PMT PID as being a known PSI PID */
431   MPEGTS_BIT_SET (base->known_psi, pmt_pid);
432
433   g_hash_table_insert (base->programs,
434       GINT_TO_POINTER (program_number), program);
435
436   return program;
437 }
438
439 MpegTSBaseProgram *
440 mpegts_base_get_program (MpegTSBase * base, gint program_number)
441 {
442   MpegTSBaseProgram *program;
443
444   program = (MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
445       GINT_TO_POINTER ((gint) program_number));
446
447   return program;
448 }
449
450 static MpegTSBaseProgram *
451 mpegts_base_steal_program (MpegTSBase * base, gint program_number)
452 {
453   MpegTSBaseProgram *program;
454
455   program = (MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
456       GINT_TO_POINTER ((gint) program_number));
457
458   if (program)
459     g_hash_table_steal (base->programs,
460         GINT_TO_POINTER ((gint) program_number));
461
462   return program;
463 }
464
465 static void
466 mpegts_base_free_program (MpegTSBaseProgram * program)
467 {
468   GList *tmp;
469
470   if (program->pmt_info)
471     gst_structure_free (program->pmt_info);
472
473   for (tmp = program->stream_list; tmp; tmp = tmp->next)
474     mpegts_base_free_stream ((MpegTSBaseStream *) tmp->data);
475   if (program->stream_list)
476     g_list_free (program->stream_list);
477
478   g_free (program->streams);
479
480   if (program->tags)
481     gst_tag_list_free (program->tags);
482
483   g_free (program);
484 }
485
486 /* FIXME : This is being called by tsdemux::find_timestamps()
487  * We need to avoid re-entrant code like that */
488 void
489 mpegts_base_remove_program (MpegTSBase * base, gint program_number)
490 {
491   MpegTSBaseProgram *program;
492   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
493
494   GST_DEBUG_OBJECT (base, "program_number : %d", program_number);
495
496   if (klass->program_stopped) {
497     program =
498         (MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
499         GINT_TO_POINTER (program_number));
500     if (program)
501       klass->program_stopped (base, program);
502   }
503   g_hash_table_remove (base->programs, GINT_TO_POINTER (program_number));
504 }
505
506 static MpegTSBaseStream *
507 mpegts_base_program_add_stream (MpegTSBase * base,
508     MpegTSBaseProgram * program, guint16 pid, guint8 stream_type,
509     GstStructure * stream_info)
510 {
511   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
512   MpegTSBaseStream *stream;
513
514   GST_DEBUG ("pid:0x%04x, stream_type:0x%03x, stream_info:%" GST_PTR_FORMAT,
515       pid, stream_type, stream_info);
516
517   if (G_UNLIKELY (program->streams[pid])) {
518     GST_WARNING ("Stream already present !");
519     return NULL;
520   }
521
522   stream = g_malloc0 (base->stream_size);
523   stream->pid = pid;
524   stream->stream_type = stream_type;
525   stream->stream_info = stream_info;
526
527   program->streams[pid] = stream;
528   program->stream_list = g_list_append (program->stream_list, stream);
529
530   if (klass->stream_added)
531     klass->stream_added (base, stream, program);
532
533   return stream;
534 }
535
536 static void
537 mpegts_base_free_stream (MpegTSBaseStream * stream)
538 {
539   g_free (stream);
540 }
541
542 void
543 mpegts_base_program_remove_stream (MpegTSBase * base,
544     MpegTSBaseProgram * program, guint16 pid)
545 {
546   MpegTSBaseClass *klass;
547   MpegTSBaseStream *stream = program->streams[pid];
548
549   GST_DEBUG ("pid:0x%04x", pid);
550
551   if (G_UNLIKELY (stream == NULL)) {
552     /* Can happen if the PCR PID is the same as a audio/video PID */
553     GST_DEBUG ("Stream already removed");
554     return;
555   }
556
557   klass = GST_MPEGTS_BASE_GET_CLASS (base);
558
559   /* If subclass needs it, inform it of the stream we are about to remove */
560   if (klass->stream_removed)
561     klass->stream_removed (base, stream);
562
563   program->stream_list = g_list_remove_all (program->stream_list, stream);
564   mpegts_base_free_stream (stream);
565   program->streams[pid] = NULL;
566 }
567
568 static void
569 mpegts_base_deactivate_program (MpegTSBase * base, MpegTSBaseProgram * program)
570 {
571   gint i, nbstreams;
572   guint pid;
573   GstStructure *stream;
574   const GValue *streams;
575   const GValue *value;
576   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
577
578   if (G_UNLIKELY (program->active == FALSE))
579     return;
580
581   GST_DEBUG_OBJECT (base, "Deactivating PMT");
582
583   program->active = FALSE;
584
585   if (program->pmt_info) {
586     /* Inform subclasses we're deactivating this program */
587     if (klass->program_stopped)
588       klass->program_stopped (base, program);
589
590     streams = gst_structure_id_get_value (program->pmt_info, QUARK_STREAMS);
591     nbstreams = gst_value_list_get_size (streams);
592
593     for (i = 0; i < nbstreams; ++i) {
594       value = gst_value_list_get_value (streams, i);
595       stream = g_value_get_boxed (value);
596
597       gst_structure_id_get (stream, QUARK_PID, G_TYPE_UINT, &pid, NULL);
598       mpegts_base_program_remove_stream (base, program, (guint16) pid);
599
600       /* Only unset the is_pes bit if the PID isn't used in any other active
601        * program */
602       if (!mpegts_pid_in_active_programs (base, pid))
603         MPEGTS_BIT_UNSET (base->is_pes, pid);
604     }
605
606     /* remove pcr stream */
607     /* FIXME : This might actually be shared with another stream ? */
608     mpegts_base_program_remove_stream (base, program, program->pcr_pid);
609     if (!mpegts_pid_in_active_programs (base, program->pcr_pid))
610       MPEGTS_BIT_UNSET (base->is_pes, program->pcr_pid);
611
612     GST_DEBUG ("program stream_list is now %p", program->stream_list);
613   }
614 }
615
616 static void
617 mpegts_base_activate_program (MpegTSBase * base, MpegTSBaseProgram * program,
618     guint16 pmt_pid, GstStructure * pmt_info)
619 {
620   guint i, nbstreams;
621   guint pcr_pid;
622   guint pid;
623   guint stream_type;
624   GstStructure *stream;
625   const GValue *new_streams;
626   const GValue *value;
627   MpegTSBaseClass *klass;
628
629   if (G_UNLIKELY (program->active))
630     return;
631
632   GST_DEBUG ("Activating program %d", program->program_number);
633
634   gst_structure_id_get (pmt_info, QUARK_PCR_PID, G_TYPE_UINT, &pcr_pid, NULL);
635
636   /* activate new pmt */
637   if (program->pmt_info)
638     gst_structure_free (program->pmt_info);
639   program->pmt_info = gst_structure_copy (pmt_info);
640   program->pmt_pid = pmt_pid;
641   program->pcr_pid = pcr_pid;
642
643   new_streams = gst_structure_id_get_value (pmt_info, QUARK_STREAMS);
644   nbstreams = gst_value_list_get_size (new_streams);
645
646   for (i = 0; i < nbstreams; ++i) {
647     value = gst_value_list_get_value (new_streams, i);
648     stream = g_value_get_boxed (value);
649
650     gst_structure_id_get (stream, QUARK_PID, G_TYPE_UINT, &pid,
651         QUARK_STREAM_TYPE, G_TYPE_UINT, &stream_type, NULL);
652     MPEGTS_BIT_SET (base->is_pes, pid);
653     mpegts_base_program_add_stream (base, program,
654         (guint16) pid, (guint8) stream_type, stream);
655
656   }
657   /* We add the PCR pid last. If that PID is already used by one of the media
658    * streams above, no new stream will be created */
659   mpegts_base_program_add_stream (base, program, (guint16) pcr_pid, -1, NULL);
660   MPEGTS_BIT_SET (base->is_pes, pcr_pid);
661
662
663   program->active = TRUE;
664
665   klass = GST_MPEGTS_BASE_GET_CLASS (base);
666   if (klass->program_started != NULL)
667     klass->program_started (base, program);
668
669   GST_DEBUG_OBJECT (base, "new pmt %" GST_PTR_FORMAT, pmt_info);
670 }
671
672 gboolean
673 mpegts_base_is_psi (MpegTSBase * base, MpegTSPacketizerPacket * packet)
674 {
675   gboolean retval = FALSE;
676   guint8 table_id;
677   int i;
678   static const guint8 si_tables[] =
679       { 0x00, 0x01, 0x02, 0x03, 0x40, 0x41, 0x42, 0x46, 0x4A,
680     0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
681     0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65,
682     0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
683     0x72, 0x73, 0x7E, 0x7F, TABLE_ID_UNSET
684   };
685
686   if (MPEGTS_BIT_IS_SET (base->known_psi, packet->pid))
687     retval = TRUE;
688
689   /* check is it is a pes pid */
690   if (MPEGTS_BIT_IS_SET (base->is_pes, packet->pid))
691     return FALSE;
692
693   if (!retval) {
694     if (packet->payload_unit_start_indicator) {
695       table_id = *(packet->data);
696       i = 0;
697       while (si_tables[i] != TABLE_ID_UNSET) {
698         if (G_UNLIKELY (si_tables[i] == table_id)) {
699           GST_DEBUG_OBJECT (base, "Packet has table id 0x%x", table_id);
700           retval = TRUE;
701           break;
702         }
703         i++;
704       }
705     } else {
706       MpegTSPacketizerStream *stream = (MpegTSPacketizerStream *)
707           base->packetizer->streams[packet->pid];
708
709       if (stream) {
710         i = 0;
711         GST_DEBUG_OBJECT (base, "section table id: 0x%x",
712             stream->section_table_id);
713         while (si_tables[i] != TABLE_ID_UNSET) {
714           if (G_UNLIKELY (si_tables[i] == stream->section_table_id)) {
715             retval = TRUE;
716             break;
717           }
718           i++;
719         }
720       }
721     }
722   }
723
724   GST_LOG_OBJECT (base, "Packet of pid 0x%x is psi: %d", packet->pid, retval);
725   return retval;
726 }
727
728 static void
729 mpegts_base_apply_pat (MpegTSBase * base, GstStructure * pat_info)
730 {
731   const GValue *value;
732   GstStructure *old_pat;
733   GstStructure *program_info;
734   guint program_number;
735   guint pid;
736   MpegTSBaseProgram *program;
737   gint i, nbprograms;
738   const GValue *programs;
739
740   GST_INFO_OBJECT (base, "PAT %" GST_PTR_FORMAT, pat_info);
741
742   /* Applying a new PAT does two things:
743    * * It adds the new programs to the list of programs this element handles
744    *   and increments at the same time the number of times a program is referenced.
745    *
746    * * If there was a previously active PAT, It decrements the reference count
747    *   of all program it used. If a program is no longer needed, it is removed.
748    */
749
750   old_pat = base->pat;
751   base->pat = gst_structure_copy (pat_info);
752
753   gst_element_post_message (GST_ELEMENT_CAST (base),
754       gst_message_new_element (GST_OBJECT (base),
755           gst_structure_copy (pat_info)));
756
757
758   GST_LOG ("Activating new Program Association Table");
759   /* activate the new table */
760   programs = gst_structure_id_get_value (pat_info, QUARK_PROGRAMS);
761   nbprograms = gst_value_list_get_size (programs);
762   for (i = 0; i < nbprograms; ++i) {
763     value = gst_value_list_get_value (programs, i);
764
765     program_info = g_value_get_boxed (value);
766     gst_structure_id_get (program_info, QUARK_PROGRAM_NUMBER, G_TYPE_UINT,
767         &program_number, QUARK_PID, G_TYPE_UINT, &pid, NULL);
768
769     program = mpegts_base_get_program (base, program_number);
770     if (program) {
771       /* IF the program already existed, just check if the PMT PID changed */
772       if (program->pmt_pid != pid) {
773         if (program->pmt_pid != G_MAXUINT16) {
774           /* pmt pid changed */
775           /* FIXME: when this happens it may still be pmt pid of another
776            * program, so setting to False may make it go through expensive
777            * path in is_psi unnecessarily */
778           MPEGTS_BIT_UNSET (base->known_psi, program->pmt_pid);
779         }
780
781         program->pmt_pid = pid;
782         MPEGTS_BIT_SET (base->known_psi, pid);
783       }
784     } else {
785       /* Create a new program */
786       program = mpegts_base_add_program (base, program_number, pid);
787     }
788     /* We mark this program as being referenced by one PAT */
789     program->patcount += 1;
790   }
791
792   if (old_pat) {
793     /* deactivate the old table */
794     GST_LOG ("Deactivating old Program Association Table");
795
796     programs = gst_structure_id_get_value (old_pat, QUARK_PROGRAMS);
797     nbprograms = gst_value_list_get_size (programs);
798     for (i = 0; i < nbprograms; ++i) {
799       value = gst_value_list_get_value (programs, i);
800
801       program_info = g_value_get_boxed (value);
802       gst_structure_id_get (program_info,
803           QUARK_PROGRAM_NUMBER, G_TYPE_UINT, &program_number,
804           QUARK_PID, G_TYPE_UINT, &pid, NULL);
805
806       program = mpegts_base_get_program (base, program_number);
807       if (G_UNLIKELY (program == NULL)) {
808         GST_DEBUG_OBJECT (base, "broken PAT, duplicated entry for program %d",
809             program_number);
810         continue;
811       }
812
813       if (--program->patcount > 0)
814         /* the program has been referenced by the new pat, keep it */
815         continue;
816
817       GST_INFO_OBJECT (base, "PAT removing program %" GST_PTR_FORMAT,
818           program_info);
819
820       mpegts_base_deactivate_program (base, program);
821       mpegts_base_remove_program (base, program_number);
822       /* FIXME: when this happens it may still be pmt pid of another
823        * program, so setting to False may make it go through expensive
824        * path in is_psi unnecessarily */
825       MPEGTS_BIT_SET (base->known_psi, pid);
826       mpegts_packetizer_remove_stream (base->packetizer, pid);
827     }
828
829     gst_structure_free (old_pat);
830   }
831 }
832
833 static void
834 mpegts_base_apply_pmt (MpegTSBase * base,
835     guint16 pmt_pid, GstStructure * pmt_info)
836 {
837   MpegTSBaseProgram *program, *old_program;
838   guint program_number;
839   gboolean deactivate_old_program = FALSE;
840
841   /* FIXME : not so sure this is valid anymore */
842   if (G_UNLIKELY (base->seen_pat == FALSE)) {
843     GST_WARNING ("Got pmt without pat first. Returning");
844     /* remove the stream since we won't get another PMT otherwise */
845     mpegts_packetizer_remove_stream (base->packetizer, pmt_pid);
846     return;
847   }
848
849   gst_structure_id_get (pmt_info, QUARK_PROGRAM_NUMBER, G_TYPE_UINT,
850       &program_number, NULL);
851
852   GST_DEBUG ("Applying PMT (program_number:%d, pid:0x%04x)",
853       program_number, pmt_pid);
854
855   /* In order for stream switching to happen properly in decodebin(2),
856    * we need to first add the new pads (i.e. activate the new program)
857    * before removing the old ones (i.e. deactivating the old program)
858    */
859
860   old_program = mpegts_base_get_program (base, program_number);
861   if (G_UNLIKELY (old_program == NULL))
862     goto no_program;
863
864   /* If the current program is active, this means we have a new program */
865   if (old_program->active) {
866     old_program = mpegts_base_steal_program (base, program_number);
867     program = mpegts_base_new_program (base, program_number, pmt_pid);
868     g_hash_table_insert (base->programs,
869         GINT_TO_POINTER (program_number), program);
870     deactivate_old_program = TRUE;
871   } else
872     program = old_program;
873
874   /* First activate program */
875   mpegts_base_activate_program (base, program, pmt_pid, pmt_info);
876
877   if (deactivate_old_program) {
878     /* deactivate old pmt */ ;
879     mpegts_base_deactivate_program (base, old_program);
880     mpegts_base_free_program (old_program);
881   }
882
883   /* if (program->pmt_info) */
884   /*   gst_structure_free (program->pmt_info); */
885   /* program->pmt_info = NULL; */
886
887   gst_element_post_message (GST_ELEMENT_CAST (base),
888       gst_message_new_element (GST_OBJECT (base),
889           gst_structure_copy (pmt_info)));
890
891   return;
892
893 no_program:
894   {
895     GST_ERROR ("Attempted to apply a PMT on a program that wasn't created");
896     return;
897   }
898 }
899
900 static void
901 mpegts_base_apply_nit (MpegTSBase * base,
902     guint16 pmt_pid, GstStructure * nit_info)
903 {
904   GST_DEBUG_OBJECT (base, "NIT %" GST_PTR_FORMAT, nit_info);
905
906   gst_element_post_message (GST_ELEMENT_CAST (base),
907       gst_message_new_element (GST_OBJECT (base),
908           gst_structure_copy (nit_info)));
909 }
910
911 static void
912 mpegts_base_apply_sdt (MpegTSBase * base,
913     guint16 pmt_pid, GstStructure * sdt_info)
914 {
915   GST_DEBUG_OBJECT (base, "SDT %" GST_PTR_FORMAT, sdt_info);
916
917   mpegts_base_get_tags_from_sdt (base, sdt_info);
918
919   gst_element_post_message (GST_ELEMENT_CAST (base),
920       gst_message_new_element (GST_OBJECT (base),
921           gst_structure_copy (sdt_info)));
922 }
923
924 static void
925 mpegts_base_apply_eit (MpegTSBase * base,
926     guint16 pmt_pid, GstStructure * eit_info)
927 {
928   GST_DEBUG_OBJECT (base, "EIT %" GST_PTR_FORMAT, eit_info);
929
930   mpegts_base_get_tags_from_eit (base, eit_info);
931
932   gst_element_post_message (GST_ELEMENT_CAST (base),
933       gst_message_new_element (GST_OBJECT (base),
934           gst_structure_copy (eit_info)));
935 }
936
937 static void
938 mpegts_base_apply_tdt (MpegTSBase * base,
939     guint16 tdt_pid, GstStructure * tdt_info)
940 {
941   gst_element_post_message (GST_ELEMENT_CAST (base),
942       gst_message_new_element (GST_OBJECT (base),
943           gst_structure_copy (tdt_info)));
944
945   GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base,
946       gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
947           gst_structure_copy (tdt_info)));
948 }
949
950
951 gboolean
952 mpegts_base_handle_psi (MpegTSBase * base, MpegTSPacketizerSection * section)
953 {
954   gboolean res = TRUE;
955   GstStructure *structure = NULL;
956
957   /* table ids 0x70 - 0x73 do not have a crc */
958   if (G_LIKELY (section->table_id < 0x70 || section->table_id > 0x73)) {
959     if (G_UNLIKELY (mpegts_base_calc_crc32 (GST_BUFFER_DATA (section->buffer),
960                 GST_BUFFER_SIZE (section->buffer)) != 0)) {
961       GST_WARNING_OBJECT (base, "bad crc in psi pid 0x%x", section->pid);
962       return FALSE;
963     }
964   }
965
966   switch (section->table_id) {
967     case 0x00:
968       /* PAT */
969       structure = mpegts_packetizer_parse_pat (base->packetizer, section);
970       if (G_LIKELY (structure)) {
971         mpegts_base_apply_pat (base, structure);
972         if (base->seen_pat == FALSE) {
973           base->seen_pat = TRUE;
974           base->first_pat_offset = GST_BUFFER_OFFSET (section->buffer);
975           GST_DEBUG ("First PAT offset: %" G_GUINT64_FORMAT,
976               base->first_pat_offset);
977         }
978
979       } else
980         res = FALSE;
981
982       break;
983     case 0x02:
984       structure = mpegts_packetizer_parse_pmt (base->packetizer, section);
985       if (G_LIKELY (structure))
986         mpegts_base_apply_pmt (base, section->pid, structure);
987       else
988         res = FALSE;
989
990       break;
991     case 0x40:
992       /* NIT, actual network */
993     case 0x41:
994       /* NIT, other network */
995       structure = mpegts_packetizer_parse_nit (base->packetizer, section);
996       if (G_LIKELY (structure))
997         mpegts_base_apply_nit (base, section->pid, structure);
998       else
999         res = FALSE;
1000
1001       break;
1002     case 0x42:
1003     case 0x46:
1004       structure = mpegts_packetizer_parse_sdt (base->packetizer, section);
1005       if (G_LIKELY (structure))
1006         mpegts_base_apply_sdt (base, section->pid, structure);
1007       else
1008         res = FALSE;
1009       break;
1010     case 0x4E:
1011     case 0x4F:
1012       /* EIT, present/following */
1013     case 0x50:
1014     case 0x51:
1015     case 0x52:
1016     case 0x53:
1017     case 0x54:
1018     case 0x55:
1019     case 0x56:
1020     case 0x57:
1021     case 0x58:
1022     case 0x59:
1023     case 0x5A:
1024     case 0x5B:
1025     case 0x5C:
1026     case 0x5D:
1027     case 0x5E:
1028     case 0x5F:
1029     case 0x60:
1030     case 0x61:
1031     case 0x62:
1032     case 0x63:
1033     case 0x64:
1034     case 0x65:
1035     case 0x66:
1036     case 0x67:
1037     case 0x68:
1038     case 0x69:
1039     case 0x6A:
1040     case 0x6B:
1041     case 0x6C:
1042     case 0x6D:
1043     case 0x6E:
1044     case 0x6F:
1045       /* EIT, schedule */
1046       structure = mpegts_packetizer_parse_eit (base->packetizer, section);
1047       if (G_LIKELY (structure))
1048         mpegts_base_apply_eit (base, section->pid, structure);
1049       else
1050         res = FALSE;
1051       break;
1052     case 0x70:
1053       /* TDT (Time and Date table) */
1054       structure = mpegts_packetizer_parse_tdt (base->packetizer, section);
1055       if (G_LIKELY (structure))
1056         mpegts_base_apply_tdt (base, section->pid, structure);
1057       else
1058         res = FALSE;
1059       break;
1060     default:
1061       break;
1062   }
1063
1064   if (structure)
1065     gst_structure_free (structure);
1066
1067   return res;
1068 }
1069
1070 static void
1071 mpegts_base_get_tags_from_sdt (MpegTSBase * base, GstStructure * sdt_info)
1072 {
1073   const GValue *services;
1074   guint i;
1075
1076   services = gst_structure_get_value (sdt_info, "services");
1077
1078   for (i = 0; i < gst_value_list_get_size (services); i++) {
1079     const GstStructure *service;
1080     const gchar *sid_str;
1081     gchar *tmp;
1082     gint program_number;
1083     MpegTSBaseProgram *program;
1084
1085     service = gst_value_get_structure (gst_value_list_get_value (services, i));
1086
1087     /* get program_number from structure name
1088      * which looks like service-%d */
1089     sid_str = gst_structure_get_name (service);
1090     tmp = g_strstr_len (sid_str, -1, "-");
1091     program_number = atoi (++tmp);
1092
1093     program = mpegts_base_get_program (base, program_number);
1094     if (program && !program->tags) {
1095       program->tags = gst_tag_list_new_full (GST_TAG_ARTIST,
1096           gst_structure_get_string (service, "name"), NULL);
1097     }
1098   }
1099 }
1100
1101 static void
1102 mpegts_base_get_tags_from_eit (MpegTSBase * base, GstStructure * eit_info)
1103 {
1104   const GValue *events;
1105   guint i;
1106   guint program_number;
1107   MpegTSBaseProgram *program;
1108   gboolean present_following;
1109
1110   gst_structure_get_uint (eit_info, "service-id", &program_number);
1111   program = mpegts_base_get_program (base, program_number);
1112
1113   gst_structure_get_boolean (eit_info, "present-following", &present_following);
1114
1115   if (program && present_following) {
1116     events = gst_structure_get_value (eit_info, "events");
1117
1118     for (i = 0; i < gst_value_list_get_size (events); i++) {
1119       const GstStructure *event;
1120       const gchar *title;
1121       guint status;
1122       guint event_id;
1123       guint duration;
1124
1125       event = gst_value_get_structure (gst_value_list_get_value (events, i));
1126
1127       title = gst_structure_get_string (event, "name");
1128       gst_structure_get_uint (event, "event-id", &event_id);
1129       gst_structure_get_uint (event, "running-status", &status);
1130
1131       if (title && event_id != program->event_id
1132           && status == RUNNING_STATUS_RUNNING) {
1133         gst_structure_get_uint (event, "duration", &duration);
1134
1135         program->event_id = event_id;
1136         program->tags = gst_tag_list_new_full (GST_TAG_TITLE,
1137             title, GST_TAG_DURATION, duration * GST_SECOND, NULL);
1138       }
1139     }
1140   }
1141 }
1142
1143 static void
1144 remove_each_program (gpointer key, MpegTSBaseProgram * program,
1145     MpegTSBase * base)
1146 {
1147   /* First deactivate it */
1148   mpegts_base_deactivate_program (base, program);
1149   /* Then remove it */
1150   mpegts_base_remove_program (base, program->program_number);
1151 }
1152
1153 static gboolean
1154 gst_mpegts_base_handle_eos (MpegTSBase * base)
1155 {
1156   g_hash_table_foreach (base->programs, (GHFunc) remove_each_program, base);
1157   /* finally remove  */
1158   return TRUE;
1159 }
1160
1161 static inline void
1162 mpegts_base_flush (MpegTSBase * base)
1163 {
1164   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1165
1166   /* Call implementation */
1167   if (G_UNLIKELY (klass->flush == NULL))
1168     GST_WARNING_OBJECT (base, "Class doesn't have a 'flush' implementation !");
1169   else
1170     klass->flush (base);
1171 }
1172
1173 static gboolean
1174 mpegts_base_sink_event (GstPad * pad, GstEvent * event)
1175 {
1176   gboolean res = TRUE;
1177   MpegTSBase *base = GST_MPEGTS_BASE (gst_object_get_parent (GST_OBJECT (pad)));
1178
1179   GST_WARNING_OBJECT (base, "Got event %s",
1180       gst_event_type_get_name (GST_EVENT_TYPE (event)));
1181
1182   switch (GST_EVENT_TYPE (event)) {
1183     case GST_EVENT_NEWSEGMENT:
1184     {
1185       gboolean update;
1186       gdouble rate, applied_rate;
1187       GstFormat format;
1188       gint64 start, stop, position;
1189
1190       gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
1191           &format, &start, &stop, &position);
1192       GST_DEBUG_OBJECT (base,
1193           "Segment update:%d, rate:%f, applied_rate:%f, format:%s", update,
1194           rate, applied_rate, gst_format_get_name (format));
1195       GST_DEBUG_OBJECT (base,
1196           "        start:%" G_GINT64_FORMAT ", stop:%" G_GINT64_FORMAT
1197           ", position:%" G_GINT64_FORMAT, start, stop, position);
1198       gst_segment_set_newsegment_full (&base->segment, update, rate,
1199           applied_rate, format, start, stop, position);
1200       gst_event_unref (event);
1201       base->in_gap = GST_CLOCK_TIME_NONE;
1202       base->first_buf_ts = GST_CLOCK_TIME_NONE;
1203     }
1204       break;
1205     case GST_EVENT_EOS:
1206       res = gst_mpegts_base_handle_eos (base);
1207       break;
1208     case GST_EVENT_FLUSH_START:
1209       mpegts_packetizer_flush (base->packetizer);
1210       mpegts_base_flush (base);
1211       res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1212       gst_event_unref (event);
1213       break;
1214     case GST_EVENT_FLUSH_STOP:
1215       gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED);
1216       base->seen_pat = FALSE;
1217       base->first_pat_offset = -1;
1218       /* Passthrough */
1219     default:
1220       res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1221       gst_event_unref (event);
1222   }
1223
1224   gst_object_unref (base);
1225   return res;
1226 }
1227
1228 static inline GstFlowReturn
1229 mpegts_base_push (MpegTSBase * base, MpegTSPacketizerPacket * packet,
1230     MpegTSPacketizerSection * section)
1231 {
1232   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1233
1234   /* Call implementation */
1235   if (G_UNLIKELY (klass->push == NULL)) {
1236     GST_ERROR_OBJECT (base, "Class doesn't have a 'push' implementation !");
1237     return GST_FLOW_ERROR;
1238   }
1239
1240   return klass->push (base, packet, section);
1241 }
1242
1243 static GstFlowReturn
1244 mpegts_base_chain (GstPad * pad, GstBuffer * buf)
1245 {
1246   GstFlowReturn res = GST_FLOW_OK;
1247   MpegTSBase *base;
1248   gboolean based;
1249   MpegTSPacketizerPacketReturn pret;
1250   MpegTSPacketizer2 *packetizer;
1251   MpegTSPacketizerPacket packet;
1252
1253   base = GST_MPEGTS_BASE (gst_object_get_parent (GST_OBJECT (pad)));
1254   packetizer = base->packetizer;
1255
1256   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (base->first_buf_ts)) &&
1257       GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1258     base->first_buf_ts = GST_BUFFER_TIMESTAMP (buf);
1259     GST_DEBUG_OBJECT (base, "first buffer timestamp %" GST_TIME_FORMAT,
1260         GST_TIME_ARGS (base->first_buf_ts));
1261   }
1262
1263   mpegts_packetizer_push (base->packetizer, buf);
1264   while (((pret =
1265               mpegts_packetizer_next_packet (base->packetizer,
1266                   &packet)) != PACKET_NEED_MORE) && res == GST_FLOW_OK) {
1267     if (G_UNLIKELY (pret == PACKET_BAD))
1268       /* bad header, skip the packet */
1269       goto next;
1270
1271     /* base PSI data */
1272     if (packet.payload != NULL && mpegts_base_is_psi (base, &packet)) {
1273       MpegTSPacketizerSection section;
1274       based = mpegts_packetizer_push_section (packetizer, &packet, &section);
1275       if (G_UNLIKELY (!based))
1276         /* bad section data */
1277         goto next;
1278
1279       if (G_LIKELY (section.complete)) {
1280         /* section complete */
1281         based = mpegts_base_handle_psi (base, &section);
1282         gst_buffer_unref (section.buffer);
1283
1284         if (G_UNLIKELY (!based))
1285           /* bad PSI table */
1286           goto next;
1287       }
1288       /* we need to push section packet downstream */
1289       res = mpegts_base_push (base, &packet, &section);
1290
1291     } else if (MPEGTS_BIT_IS_SET (base->is_pes, packet.pid)) {
1292       /* push the packet downstream */
1293       res = mpegts_base_push (base, &packet, NULL);
1294     } else
1295       gst_buffer_unref (packet.buffer);
1296
1297   next:
1298     mpegts_packetizer_clear_packet (base->packetizer, &packet);
1299   }
1300
1301   gst_object_unref (base);
1302   return res;
1303 }
1304
1305 static GstFlowReturn
1306 mpegts_base_scan (MpegTSBase * base)
1307 {
1308   GstFlowReturn ret;
1309   GstBuffer *buf;
1310   guint i;
1311   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1312
1313   GST_DEBUG ("Scanning for initial sync point");
1314
1315   /* Find initial sync point */
1316   for (i = 0; i < 10; i++) {
1317     GST_DEBUG ("Grabbing %d => %d",
1318         i * 50 * MPEGTS_MAX_PACKETSIZE, 50 * MPEGTS_MAX_PACKETSIZE);
1319     ret = gst_pad_pull_range (base->sinkpad, i * 50 * MPEGTS_MAX_PACKETSIZE,
1320         50 * MPEGTS_MAX_PACKETSIZE, &buf);
1321     if (G_UNLIKELY (ret != GST_FLOW_OK))
1322       goto beach;
1323
1324     /* Push to packetizer */
1325     mpegts_packetizer_push (base->packetizer, buf);
1326
1327     if (mpegts_packetizer_has_packets (base->packetizer)) {
1328       /* Mark the initial sync point and remember the packetsize */
1329       base->initial_sync_point = base->seek_offset = base->packetizer->offset;
1330       GST_DEBUG ("Sync point is now %" G_GUINT64_FORMAT, base->seek_offset);
1331       base->packetsize = base->packetizer->packet_size;
1332
1333       /* If the subclass can seek for timestamps, do that */
1334       if (klass->find_timestamps) {
1335         guint64 offset;
1336         mpegts_packetizer_clear (base->packetizer);
1337
1338         ret = klass->find_timestamps (base, 0, &offset);
1339
1340         base->initial_sync_point = base->seek_offset =
1341             base->packetizer->offset = base->first_pat_offset;
1342         GST_DEBUG ("Sync point is now %" G_GUINT64_FORMAT, base->seek_offset);
1343       }
1344       goto beach;
1345     }
1346   }
1347
1348   GST_WARNING ("Didn't find initial sync point");
1349   ret = GST_FLOW_ERROR;
1350
1351 beach:
1352   mpegts_packetizer_clear (base->packetizer);
1353   return ret;
1354
1355 }
1356
1357
1358 static void
1359 mpegts_base_loop (MpegTSBase * base)
1360 {
1361   GstFlowReturn ret = GST_FLOW_ERROR;
1362   switch (base->mode) {
1363     case BASE_MODE_SCANNING:
1364       /* Find first sync point */
1365       ret = mpegts_base_scan (base);
1366       if (G_UNLIKELY (ret != GST_FLOW_OK))
1367         goto error;
1368       base->mode = BASE_MODE_STREAMING;
1369       GST_DEBUG ("Changing to Streaming");
1370       break;
1371     case BASE_MODE_SEEKING:
1372       /* FIXME : yes, we should do something here */
1373       base->mode = BASE_MODE_STREAMING;
1374       break;
1375     case BASE_MODE_STREAMING:
1376     {
1377       GstBuffer *buf;
1378
1379       GST_DEBUG ("Pulling data from %" G_GUINT64_FORMAT, base->seek_offset);
1380
1381       ret = gst_pad_pull_range (base->sinkpad, base->seek_offset,
1382           100 * base->packetsize, &buf);
1383       if (G_UNLIKELY (ret != GST_FLOW_OK))
1384         goto error;
1385       base->seek_offset += GST_BUFFER_SIZE (buf);
1386       ret = mpegts_base_chain (base->sinkpad, buf);
1387       if (G_UNLIKELY (ret != GST_FLOW_OK))
1388         goto error;
1389     }
1390       break;
1391     case BASE_MODE_PUSHING:
1392       GST_WARNING ("wrong BASE_MODE_PUSHING mode in pull loop");
1393       break;
1394   }
1395
1396   return;
1397
1398 error:
1399   {
1400     const gchar *reason = gst_flow_get_name (ret);
1401     GST_DEBUG_OBJECT (base, "Pausing task, reason %s", reason);
1402     if (ret == GST_FLOW_UNEXPECTED)
1403       GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, gst_event_new_eos ());
1404     else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_UNEXPECTED) {
1405       GST_ELEMENT_ERROR (base, STREAM, FAILED,
1406           (_("Internal data stream error.")),
1407           ("stream stopped, reason %s", reason));
1408       GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, gst_event_new_eos ());
1409     }
1410     gst_pad_pause_task (base->sinkpad);
1411   }
1412 }
1413
1414
1415 gboolean
1416 mpegts_base_handle_seek_event (MpegTSBase * base, GstPad * pad,
1417     GstEvent * event)
1418 {
1419   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1420   GstFlowReturn ret = GST_FLOW_ERROR;
1421   gdouble rate;
1422   gboolean flush;
1423   GstFormat format;
1424   GstSeekFlags flags;
1425   GstSeekType start_type, stop_type;
1426   gint64 start, stop;
1427   gchar *pad_name;
1428   guint16 pid = 0;
1429
1430   gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
1431       &stop_type, &stop);
1432
1433   if (format != GST_FORMAT_TIME)
1434     return FALSE;
1435
1436   /* First try if upstream supports seeking in TIME format */
1437   if (gst_pad_push_event (base->sinkpad, gst_event_ref (event))) {
1438     GST_DEBUG ("upstream handled SEEK event");
1439     gst_event_unref (event);
1440     return TRUE;
1441   }
1442
1443   GST_DEBUG ("seek event, rate: %f start: %" GST_TIME_FORMAT
1444       " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start),
1445       GST_TIME_ARGS (stop));
1446
1447   /* extract the pid from the pad name */
1448   pad_name = gst_pad_get_name (pad);
1449   if (pad_name) {
1450     gchar *pidstr = g_strrstr (pad_name, "_");
1451     if (pidstr) {
1452       pidstr++;
1453       pid = g_ascii_strtoull (pidstr, NULL, 16);
1454     }
1455     g_free (pad_name);
1456   }
1457
1458   flush = flags & GST_SEEK_FLAG_FLUSH;
1459
1460   if (base->mode == BASE_MODE_PUSHING) {
1461     GST_ERROR ("seeking in push mode not supported");
1462     goto push_mode;
1463   }
1464
1465   /* stop streaming, either by flushing or by pausing the task */
1466   base->mode = BASE_MODE_SEEKING;
1467   if (flush) {
1468     GST_DEBUG_OBJECT (base, "sending flush start");
1469     gst_pad_push_event (base->sinkpad, gst_event_new_flush_start ());
1470     GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base,
1471         gst_event_new_flush_start ());
1472   } else
1473     gst_pad_pause_task (base->sinkpad);
1474   /* wait for streaming to finish */
1475   GST_PAD_STREAM_LOCK (base->sinkpad);
1476
1477   if (flush) {
1478     /* send a FLUSH_STOP for the sinkpad, since we need data for seeking */
1479     GST_DEBUG_OBJECT (base, "sending flush stop");
1480     gst_pad_push_event (base->sinkpad, gst_event_new_flush_stop ());
1481   }
1482
1483   if (flags & (GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_SKIP)) {
1484     GST_WARNING ("seek flags 0x%x are not supported", (int) flags);
1485     goto done;
1486   }
1487
1488
1489   if (format == GST_FORMAT_TIME) {
1490     /* If the subclass can seek, do that */
1491     if (klass->seek) {
1492       ret = klass->seek (base, event, pid);
1493       if (G_UNLIKELY (ret != GST_FLOW_OK)) {
1494         GST_WARNING ("seeking failed %s", gst_flow_get_name (ret));
1495         goto done;
1496       }
1497     } else {
1498       GST_WARNING ("subclass has no seek implementation");
1499       goto done;
1500     }
1501   }
1502
1503   if (flush) {
1504     /* if we sent a FLUSH_START, we now send a FLUSH_STOP */
1505     GST_DEBUG_OBJECT (base, "sending flush stop");
1506     //gst_pad_push_event (base->sinkpad, gst_event_new_flush_stop ());
1507     GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base,
1508         gst_event_new_flush_stop ());
1509   }
1510   //else
1511 done:
1512   gst_pad_start_task (base->sinkpad, (GstTaskFunction) mpegts_base_loop, base);
1513 push_mode:
1514   GST_PAD_STREAM_UNLOCK (base->sinkpad);
1515   return ret == GST_FLOW_OK;
1516 }
1517
1518
1519 static gboolean
1520 mpegts_base_sink_activate (GstPad * pad)
1521 {
1522   if (gst_pad_check_pull_range (pad)) {
1523     GST_DEBUG_OBJECT (pad, "activating pull");
1524     return gst_pad_activate_pull (pad, TRUE);
1525   } else {
1526     GST_DEBUG_OBJECT (pad, "activating push");
1527     return gst_pad_activate_push (pad, TRUE);
1528   }
1529 }
1530
1531 static gboolean
1532 mpegts_base_sink_activate_pull (GstPad * pad, gboolean active)
1533 {
1534   MpegTSBase *base = GST_MPEGTS_BASE (GST_OBJECT_PARENT (pad));
1535   if (active) {
1536     base->mode = BASE_MODE_SCANNING;
1537     return gst_pad_start_task (pad, (GstTaskFunction) mpegts_base_loop, base);
1538   } else
1539     return gst_pad_stop_task (pad);
1540 }
1541
1542 static gboolean
1543 mpegts_base_sink_activate_push (GstPad * pad, gboolean active)
1544 {
1545   MpegTSBase *base = GST_MPEGTS_BASE (GST_OBJECT_PARENT (pad));
1546   base->mode = BASE_MODE_PUSHING;
1547   return TRUE;
1548 }
1549
1550
1551 static GstStateChangeReturn
1552 mpegts_base_change_state (GstElement * element, GstStateChange transition)
1553 {
1554   MpegTSBase *base;
1555   GstStateChangeReturn ret;
1556
1557   base = GST_MPEGTS_BASE (element);
1558   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1559
1560   switch (transition) {
1561     case GST_STATE_CHANGE_PAUSED_TO_READY:
1562       mpegts_base_reset (base);
1563       if (base->mode != BASE_MODE_PUSHING)
1564         base->mode = BASE_MODE_SCANNING;
1565       break;
1566     default:
1567       break;
1568   }
1569
1570   return ret;
1571 }
1572
1573 gboolean
1574 gst_mpegtsbase_plugin_init (GstPlugin * plugin)
1575 {
1576   GST_DEBUG_CATEGORY_INIT (mpegts_base_debug, "mpegtsbase", 0,
1577       "MPEG transport stream base class");
1578
1579   gst_mpegtsdesc_init_debug ();
1580
1581   return TRUE;
1582 }