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., 51 Franklin St, Fifth Floor,
28  * Boston, MA 02110-1301, 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 #define RUNNING_STATUS_RUNNING 4
45 
46 GST_DEBUG_CATEGORY_STATIC (mpegts_base_debug);
47 #define GST_CAT_DEFAULT mpegts_base_debug
48 
49 static GQuark QUARK_PROGRAMS;
50 static GQuark QUARK_PROGRAM_NUMBER;
51 static GQuark QUARK_PID;
52 static GQuark QUARK_PCR_PID;
53 static GQuark QUARK_STREAMS;
54 static GQuark QUARK_STREAM_TYPE;
55 
56 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
57     GST_PAD_SINK,
58     GST_PAD_ALWAYS,
59     GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
60     );
61 
62 enum
63 {
64   PROP_0,
65   PROP_PARSE_PRIVATE_SECTIONS,
66   /* FILL ME */
67 };
68 
69 static void mpegts_base_dispose (GObject * object);
70 static void mpegts_base_finalize (GObject * object);
71 static void mpegts_base_set_property (GObject * object, guint prop_id,
72     const GValue * value, GParamSpec * pspec);
73 static void mpegts_base_get_property (GObject * object, guint prop_id,
74     GValue * value, GParamSpec * pspec);
75 
76 static void mpegts_base_free_program (MpegTSBaseProgram * program);
77 static void mpegts_base_deactivate_program (MpegTSBase * base,
78     MpegTSBaseProgram * program);
79 static gboolean mpegts_base_sink_activate (GstPad * pad, GstObject * parent);
80 static gboolean mpegts_base_sink_activate_mode (GstPad * pad,
81     GstObject * parent, GstPadMode mode, gboolean active);
82 static GstFlowReturn mpegts_base_chain (GstPad * pad, GstObject * parent,
83     GstBuffer * buf);
84 static gboolean mpegts_base_sink_event (GstPad * pad, GstObject * parent,
85     GstEvent * event);
86 static gboolean mpegts_base_sink_query (GstPad * pad, GstObject * parent,
87     GstQuery * query);
88 static gboolean mpegts_base_default_sink_query (MpegTSBase * base,
89     GstQuery * query);
90 static GstStateChangeReturn mpegts_base_change_state (GstElement * element,
91     GstStateChange transition);
92 static gboolean mpegts_base_get_tags_from_eit (MpegTSBase * base,
93     GstMpegtsSection * section);
94 static gboolean mpegts_base_parse_atsc_mgt (MpegTSBase * base,
95     GstMpegtsSection * section);
96 static gboolean remove_each_program (gpointer key, MpegTSBaseProgram * program,
97     MpegTSBase * base);
98 
99 static void
_extra_init(void)100 _extra_init (void)
101 {
102   QUARK_PROGRAMS = g_quark_from_string ("programs");
103   QUARK_PROGRAM_NUMBER = g_quark_from_string ("program-number");
104   QUARK_PID = g_quark_from_string ("pid");
105   QUARK_PCR_PID = g_quark_from_string ("pcr-pid");
106   QUARK_STREAMS = g_quark_from_string ("streams");
107   QUARK_STREAM_TYPE = g_quark_from_string ("stream-type");
108 }
109 
110 #define mpegts_base_parent_class parent_class
111 G_DEFINE_TYPE_WITH_CODE (MpegTSBase, mpegts_base, GST_TYPE_ELEMENT,
112     _extra_init ());
113 
114 /* Default implementation is that mpegtsbase can remove any program */
115 static gboolean
mpegts_base_can_remove_program(MpegTSBase * base,MpegTSBaseProgram * program)116 mpegts_base_can_remove_program (MpegTSBase * base, MpegTSBaseProgram * program)
117 {
118   return TRUE;
119 }
120 
121 static void
mpegts_base_class_init(MpegTSBaseClass * klass)122 mpegts_base_class_init (MpegTSBaseClass * klass)
123 {
124   GObjectClass *gobject_class;
125   GstElementClass *element_class;
126 
127   klass->can_remove_program = mpegts_base_can_remove_program;
128 
129   element_class = GST_ELEMENT_CLASS (klass);
130   element_class->change_state = mpegts_base_change_state;
131 
132   gst_element_class_add_static_pad_template (element_class, &sink_template);
133 
134   gobject_class = G_OBJECT_CLASS (klass);
135   gobject_class->dispose = mpegts_base_dispose;
136   gobject_class->finalize = mpegts_base_finalize;
137   gobject_class->set_property = mpegts_base_set_property;
138   gobject_class->get_property = mpegts_base_get_property;
139 
140   g_object_class_install_property (gobject_class, PROP_PARSE_PRIVATE_SECTIONS,
141       g_param_spec_boolean ("parse-private-sections", "Parse private sections",
142           "Parse private sections", FALSE,
143           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
144 
145   klass->sink_query = GST_DEBUG_FUNCPTR (mpegts_base_default_sink_query);
146 }
147 
148 static void
mpegts_base_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)149 mpegts_base_set_property (GObject * object, guint prop_id,
150     const GValue * value, GParamSpec * pspec)
151 {
152   MpegTSBase *base = GST_MPEGTS_BASE (object);
153 
154   switch (prop_id) {
155     case PROP_PARSE_PRIVATE_SECTIONS:
156       base->parse_private_sections = g_value_get_boolean (value);
157       break;
158     default:
159       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160   }
161 }
162 
163 static void
mpegts_base_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)164 mpegts_base_get_property (GObject * object, guint prop_id,
165     GValue * value, GParamSpec * pspec)
166 {
167   MpegTSBase *base = GST_MPEGTS_BASE (object);
168 
169   switch (prop_id) {
170     case PROP_PARSE_PRIVATE_SECTIONS:
171       g_value_set_boolean (value, base->parse_private_sections);
172       break;
173     default:
174       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
175   }
176 }
177 
178 
179 static void
mpegts_base_reset(MpegTSBase * base)180 mpegts_base_reset (MpegTSBase * base)
181 {
182   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
183 
184   mpegts_packetizer_clear (base->packetizer);
185   memset (base->is_pes, 0, 1024);
186   memset (base->known_psi, 0, 1024);
187 
188   /* FIXME : Actually these are not *always* know SI streams
189    * depending on the variant of mpeg-ts being used. */
190 
191   /* Known PIDs : PAT, TSDT, IPMP CIT */
192   MPEGTS_BIT_SET (base->known_psi, 0);
193   MPEGTS_BIT_SET (base->known_psi, 2);
194   MPEGTS_BIT_SET (base->known_psi, 3);
195   /* TDT, TOT, ST */
196   MPEGTS_BIT_SET (base->known_psi, 0x14);
197   /* network synchronization */
198   MPEGTS_BIT_SET (base->known_psi, 0x15);
199 
200   /* ATSC */
201   MPEGTS_BIT_SET (base->known_psi, 0x1ffb);
202 
203   if (base->pat) {
204     g_ptr_array_unref (base->pat);
205     base->pat = NULL;
206   }
207 
208   gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED);
209   base->last_seek_seqnum = GST_SEQNUM_INVALID;
210 
211   base->mode = BASE_MODE_STREAMING;
212   base->seen_pat = FALSE;
213   base->seek_offset = -1;
214 
215   g_hash_table_foreach_remove (base->programs, (GHRFunc) remove_each_program,
216       base);
217 
218   base->streams_aware = GST_OBJECT_PARENT (base)
219       && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (base),
220       GST_BIN_FLAG_STREAMS_AWARE);
221   GST_DEBUG_OBJECT (base, "Streams aware : %d", base->streams_aware);
222 
223   if (klass->reset)
224     klass->reset (base);
225 }
226 
227 static void
mpegts_base_init(MpegTSBase * base)228 mpegts_base_init (MpegTSBase * base)
229 {
230   base->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
231   gst_pad_set_activate_function (base->sinkpad, mpegts_base_sink_activate);
232   gst_pad_set_activatemode_function (base->sinkpad,
233       mpegts_base_sink_activate_mode);
234   gst_pad_set_chain_function (base->sinkpad, mpegts_base_chain);
235   gst_pad_set_event_function (base->sinkpad, mpegts_base_sink_event);
236   gst_pad_set_query_function (base->sinkpad, mpegts_base_sink_query);
237   gst_element_add_pad (GST_ELEMENT (base), base->sinkpad);
238 
239   base->disposed = FALSE;
240   base->packetizer = mpegts_packetizer_new ();
241   base->programs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
242       NULL, (GDestroyNotify) mpegts_base_free_program);
243 
244   base->parse_private_sections = FALSE;
245   base->is_pes = g_new0 (guint8, 1024);
246   base->known_psi = g_new0 (guint8, 1024);
247   base->program_size = sizeof (MpegTSBaseProgram);
248   base->stream_size = sizeof (MpegTSBaseStream);
249 
250   base->push_data = TRUE;
251   base->push_section = TRUE;
252 
253   mpegts_base_reset (base);
254 }
255 
256 static void
mpegts_base_dispose(GObject * object)257 mpegts_base_dispose (GObject * object)
258 {
259   MpegTSBase *base = GST_MPEGTS_BASE (object);
260 
261   if (!base->disposed) {
262     g_object_unref (base->packetizer);
263     base->disposed = TRUE;
264     g_free (base->known_psi);
265     g_free (base->is_pes);
266   }
267 
268   if (G_OBJECT_CLASS (parent_class)->dispose)
269     G_OBJECT_CLASS (parent_class)->dispose (object);
270 }
271 
272 static void
mpegts_base_finalize(GObject * object)273 mpegts_base_finalize (GObject * object)
274 {
275   MpegTSBase *base = GST_MPEGTS_BASE (object);
276 
277   if (base->pat) {
278     g_ptr_array_unref (base->pat);
279     base->pat = NULL;
280   }
281   g_hash_table_destroy (base->programs);
282 
283   if (G_OBJECT_CLASS (parent_class)->finalize)
284     G_OBJECT_CLASS (parent_class)->finalize (object);
285 }
286 
287 
288 /* returns NULL if no matching descriptor found *
289  * otherwise returns a descriptor that needs to *
290  * be freed */
291 const GstMpegtsDescriptor *
mpegts_get_descriptor_from_stream(MpegTSBaseStream * stream,guint8 tag)292 mpegts_get_descriptor_from_stream (MpegTSBaseStream * stream, guint8 tag)
293 {
294   GstMpegtsPMTStream *pmt = stream->stream;
295 
296   GST_DEBUG ("Searching for tag 0x%02x in stream 0x%04x (stream_type 0x%02x)",
297       tag, stream->pid, stream->stream_type);
298 
299   return gst_mpegts_find_descriptor (pmt->descriptors, tag);
300 }
301 
302 typedef struct
303 {
304   gboolean res;
305   guint16 pid;
306 } PIDLookup;
307 
308 static void
foreach_pid_in_program(gpointer key,MpegTSBaseProgram * program,PIDLookup * lookup)309 foreach_pid_in_program (gpointer key, MpegTSBaseProgram * program,
310     PIDLookup * lookup)
311 {
312   if (!program->active)
313     return;
314   if (program->streams[lookup->pid])
315     lookup->res = TRUE;
316 }
317 
318 static gboolean
mpegts_pid_in_active_programs(MpegTSBase * base,guint16 pid)319 mpegts_pid_in_active_programs (MpegTSBase * base, guint16 pid)
320 {
321   PIDLookup lookup;
322 
323   lookup.res = FALSE;
324   lookup.pid = pid;
325   g_hash_table_foreach (base->programs, (GHFunc) foreach_pid_in_program,
326       &lookup);
327 
328   return lookup.res;
329 }
330 
331 /* returns NULL if no matching descriptor found *
332  * otherwise returns a descriptor that needs to *
333  * be freed */
334 const GstMpegtsDescriptor *
mpegts_get_descriptor_from_program(MpegTSBaseProgram * program,guint8 tag)335 mpegts_get_descriptor_from_program (MpegTSBaseProgram * program, guint8 tag)
336 {
337   const GstMpegtsPMT *pmt = program->pmt;
338 
339   return gst_mpegts_find_descriptor (pmt->descriptors, tag);
340 }
341 
342 static gchar *
_get_upstream_id(GstElement * element,GstPad * sinkpad)343 _get_upstream_id (GstElement * element, GstPad * sinkpad)
344 {
345   gchar *upstream_id = gst_pad_get_stream_id (sinkpad);
346 
347   if (!upstream_id) {
348     /* Try to create one from the upstream URI, else use a randome number */
349     GstQuery *query;
350     gchar *uri = NULL;
351 
352     /* Try to generate one from the URI query and
353      * if it fails take a random number instead */
354     query = gst_query_new_uri ();
355     if (gst_element_query (element, query)) {
356       gst_query_parse_uri (query, &uri);
357     }
358 
359     if (uri) {
360       GChecksum *cs;
361 
362       /* And then generate an SHA256 sum of the URI */
363       cs = g_checksum_new (G_CHECKSUM_SHA256);
364       g_checksum_update (cs, (const guchar *) uri, strlen (uri));
365       g_free (uri);
366       upstream_id = g_strdup (g_checksum_get_string (cs));
367       g_checksum_free (cs);
368     } else {
369       /* Just get some random number if the URI query fails */
370       GST_FIXME_OBJECT (element, "Creating random stream-id, consider "
371           "implementing a deterministic way of creating a stream-id");
372       upstream_id =
373           g_strdup_printf ("%08x%08x%08x%08x", g_random_int (), g_random_int (),
374           g_random_int (), g_random_int ());
375     }
376 
377     gst_query_unref (query);
378   }
379   return upstream_id;
380 }
381 
382 static MpegTSBaseProgram *
mpegts_base_new_program(MpegTSBase * base,gint program_number,guint16 pmt_pid)383 mpegts_base_new_program (MpegTSBase * base,
384     gint program_number, guint16 pmt_pid)
385 {
386   MpegTSBaseProgram *program;
387   gchar *upstream_id, *stream_id;
388 
389   GST_DEBUG_OBJECT (base, "program_number : %d, pmt_pid : %d",
390       program_number, pmt_pid);
391 
392   program = g_malloc0 (base->program_size);
393   program->program_number = program_number;
394   program->pmt_pid = pmt_pid;
395   program->pcr_pid = G_MAXUINT16;
396   program->streams = g_new0 (MpegTSBaseStream *, 0x2000);
397   program->patcount = 0;
398 
399   upstream_id = _get_upstream_id ((GstElement *) base, base->sinkpad);
400   stream_id = g_strdup_printf ("%s:%d", upstream_id, program_number);
401   program->collection = gst_stream_collection_new (stream_id);
402   g_free (stream_id);
403   g_free (upstream_id);
404 
405   return program;
406 }
407 
408 MpegTSBaseProgram *
mpegts_base_add_program(MpegTSBase * base,gint program_number,guint16 pmt_pid)409 mpegts_base_add_program (MpegTSBase * base,
410     gint program_number, guint16 pmt_pid)
411 {
412   MpegTSBaseProgram *program;
413 
414   GST_DEBUG_OBJECT (base, "program_number : %d, pmt_pid : %d",
415       program_number, pmt_pid);
416 
417   program = mpegts_base_new_program (base, program_number, pmt_pid);
418 
419   /* Mark the PMT PID as being a known PSI PID */
420   if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi, pmt_pid))) {
421     GST_FIXME ("Refcounting. Setting twice a PID (0x%04x) as known PSI",
422         pmt_pid);
423   }
424   MPEGTS_BIT_SET (base->known_psi, pmt_pid);
425 
426   g_hash_table_insert (base->programs,
427       GINT_TO_POINTER (program_number), program);
428 
429   return program;
430 }
431 
432 MpegTSBaseProgram *
mpegts_base_get_program(MpegTSBase * base,gint program_number)433 mpegts_base_get_program (MpegTSBase * base, gint program_number)
434 {
435   MpegTSBaseProgram *program;
436 
437   program = (MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
438       GINT_TO_POINTER ((gint) program_number));
439 
440   return program;
441 }
442 
443 static MpegTSBaseProgram *
mpegts_base_steal_program(MpegTSBase * base,gint program_number)444 mpegts_base_steal_program (MpegTSBase * base, gint program_number)
445 {
446   MpegTSBaseProgram *program;
447 
448   program = (MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
449       GINT_TO_POINTER ((gint) program_number));
450 
451   if (program)
452     g_hash_table_steal (base->programs,
453         GINT_TO_POINTER ((gint) program_number));
454 
455   return program;
456 }
457 
458 static void
mpegts_base_free_stream(MpegTSBaseStream * stream)459 mpegts_base_free_stream (MpegTSBaseStream * stream)
460 {
461   if (stream->stream_object)
462     gst_object_unref (stream->stream_object);
463   if (stream->stream_id)
464     g_free (stream->stream_id);
465   g_free (stream);
466 }
467 
468 static void
mpegts_base_free_program(MpegTSBaseProgram * program)469 mpegts_base_free_program (MpegTSBaseProgram * program)
470 {
471   GList *tmp;
472 
473   if (program->pmt) {
474     gst_mpegts_section_unref (program->section);
475     program->pmt = NULL;
476   }
477 
478   /* FIXME FIXME FIXME FREE STREAM OBJECT ! */
479   for (tmp = program->stream_list; tmp; tmp = tmp->next)
480     mpegts_base_free_stream ((MpegTSBaseStream *) tmp->data);
481 
482   if (program->stream_list)
483     g_list_free (program->stream_list);
484 
485   g_free (program->streams);
486 
487   if (program->tags)
488     gst_tag_list_unref (program->tags);
489   if (program->collection)
490     gst_object_unref (program->collection);
491 
492   g_free (program);
493 }
494 
495 void
mpegts_base_deactivate_and_free_program(MpegTSBase * base,MpegTSBaseProgram * program)496 mpegts_base_deactivate_and_free_program (MpegTSBase * base,
497     MpegTSBaseProgram * program)
498 {
499   GST_DEBUG_OBJECT (base, "program_number : %d", program->program_number);
500 
501   mpegts_base_deactivate_program (base, program);
502   mpegts_base_free_program (program);
503 }
504 
505 static void
mpegts_base_remove_program(MpegTSBase * base,gint program_number)506 mpegts_base_remove_program (MpegTSBase * base, gint program_number)
507 {
508   GST_DEBUG_OBJECT (base, "program_number : %d", program_number);
509 
510   g_hash_table_remove (base->programs, GINT_TO_POINTER (program_number));
511 }
512 
513 static guint32
get_registration_from_descriptors(GPtrArray * descriptors)514 get_registration_from_descriptors (GPtrArray * descriptors)
515 {
516   const GstMpegtsDescriptor *desc;
517 
518   if ((desc =
519           gst_mpegts_find_descriptor (descriptors,
520               GST_MTS_DESC_REGISTRATION))) {
521     if (G_UNLIKELY (desc->length < 4)) {
522       GST_WARNING ("Registration descriptor with length < 4. (Corrupted ?)");
523     } else
524       return GST_READ_UINT32_BE (desc->data + 2);
525   }
526 
527   return 0;
528 }
529 
530 static MpegTSBaseStream *
mpegts_base_program_add_stream(MpegTSBase * base,MpegTSBaseProgram * program,guint16 pid,guint8 stream_type,GstMpegtsPMTStream * stream)531 mpegts_base_program_add_stream (MpegTSBase * base,
532     MpegTSBaseProgram * program, guint16 pid, guint8 stream_type,
533     GstMpegtsPMTStream * stream)
534 {
535   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
536   MpegTSBaseStream *bstream;
537 
538   GST_DEBUG ("pid:0x%04x, stream_type:0x%03x", pid, stream_type);
539 
540   /* FIXME : PID information/nature might change through time.
541    * We therefore *do* want to be able to replace an existing stream
542    * with updated information */
543   if (G_UNLIKELY (program->streams[pid])) {
544     if (stream_type != 0xff)
545       GST_WARNING ("Stream already present !");
546     return NULL;
547   }
548 
549   bstream = g_malloc0 (base->stream_size);
550   bstream->stream_id =
551       g_strdup_printf ("%s/%08x",
552       gst_stream_collection_get_upstream_id (program->collection), pid);
553   bstream->pid = pid;
554   bstream->stream_type = stream_type;
555   bstream->stream = stream;
556   /* We don't yet know the stream type, subclasses will fill that */
557   bstream->stream_object = gst_stream_new (bstream->stream_id, NULL,
558       GST_STREAM_TYPE_UNKNOWN, GST_STREAM_FLAG_NONE);
559   if (stream) {
560     bstream->registration_id =
561         get_registration_from_descriptors (stream->descriptors);
562     GST_DEBUG ("PID 0x%04x, registration_id %" SAFE_FOURCC_FORMAT,
563         bstream->pid, SAFE_FOURCC_ARGS (bstream->registration_id));
564   }
565 
566   program->streams[pid] = bstream;
567   program->stream_list = g_list_append (program->stream_list, bstream);
568 
569   if (klass->stream_added)
570     if (klass->stream_added (base, bstream, program))
571       gst_stream_collection_add_stream (program->collection,
572           (GstStream *) gst_object_ref (bstream->stream_object));
573 
574 
575   return bstream;
576 }
577 
578 static void
mpegts_base_program_remove_stream(MpegTSBase * base,MpegTSBaseProgram * program,guint16 pid)579 mpegts_base_program_remove_stream (MpegTSBase * base,
580     MpegTSBaseProgram * program, guint16 pid)
581 {
582   MpegTSBaseClass *klass;
583   MpegTSBaseStream *stream = program->streams[pid];
584 
585   GST_DEBUG ("pid:0x%04x", pid);
586 
587   if (G_UNLIKELY (stream == NULL)) {
588     /* Can happen if the PCR PID is the same as a audio/video PID */
589     GST_DEBUG ("Stream already removed");
590     return;
591   }
592 
593   klass = GST_MPEGTS_BASE_GET_CLASS (base);
594 
595   /* If subclass needs it, inform it of the stream we are about to remove */
596   if (klass->stream_removed)
597     klass->stream_removed (base, stream);
598 
599   program->stream_list = g_list_remove_all (program->stream_list, stream);
600   mpegts_base_free_stream (stream);
601   program->streams[pid] = NULL;
602 }
603 
604 /* Check if pmtstream is already present in the program */
605 static inline gboolean
_stream_in_pmt(const GstMpegtsPMT * pmt,MpegTSBaseStream * stream)606 _stream_in_pmt (const GstMpegtsPMT * pmt, MpegTSBaseStream * stream)
607 {
608   guint i, nbstreams = pmt->streams->len;
609 
610   for (i = 0; i < nbstreams; i++) {
611     GstMpegtsPMTStream *pmt_stream = g_ptr_array_index (pmt->streams, i);
612 
613     if (pmt_stream->pid == stream->pid &&
614         pmt_stream->stream_type == stream->stream_type)
615       return TRUE;
616   }
617 
618   return FALSE;
619 }
620 
621 static inline gboolean
_pmt_stream_in_program(MpegTSBaseProgram * program,GstMpegtsPMTStream * stream)622 _pmt_stream_in_program (MpegTSBaseProgram * program,
623     GstMpegtsPMTStream * stream)
624 {
625   MpegTSBaseStream *old_stream = program->streams[stream->pid];
626   if (!old_stream)
627     return FALSE;
628   return old_stream->stream_type == stream->stream_type;
629 }
630 
631 static gboolean
mpegts_base_update_program(MpegTSBase * base,MpegTSBaseProgram * program,GstMpegtsSection * section,const GstMpegtsPMT * pmt)632 mpegts_base_update_program (MpegTSBase * base, MpegTSBaseProgram * program,
633     GstMpegtsSection * section, const GstMpegtsPMT * pmt)
634 {
635   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
636   const gchar *stream_id =
637       gst_stream_collection_get_upstream_id (program->collection);
638   GstStreamCollection *collection;
639   GList *tmp, *toremove;
640   guint i, nbstreams;
641 
642   /* Create new collection */
643   collection = gst_stream_collection_new (stream_id);
644   gst_object_unref (program->collection);
645   program->collection = collection;
646 
647   /* Replace section and pmt with the new one */
648   gst_mpegts_section_unref (program->section);
649   program->section = gst_mpegts_section_ref (section);
650   program->pmt = pmt;
651 
652   /* Copy over gststream that still exist into the collection */
653   for (tmp = program->stream_list; tmp; tmp = tmp->next) {
654     MpegTSBaseStream *stream = (MpegTSBaseStream *) tmp->data;
655     if (_stream_in_pmt (pmt, stream)) {
656       gst_stream_collection_add_stream (program->collection,
657           gst_object_ref (stream->stream_object));
658     }
659   }
660 
661   /* Add new streams (will also create and add gststream to the collection) */
662   nbstreams = pmt->streams->len;
663   for (i = 0; i < nbstreams; i++) {
664     GstMpegtsPMTStream *stream = g_ptr_array_index (pmt->streams, i);
665     if (!_pmt_stream_in_program (program, stream))
666       mpegts_base_program_add_stream (base, program, stream->pid,
667           stream->stream_type, stream);
668   }
669 
670   /* Call subclass update */
671   if (klass->update_program)
672     klass->update_program (base, program);
673 
674   /* Remove streams no longer present */
675   toremove = NULL;
676   for (tmp = program->stream_list; tmp; tmp = tmp->next) {
677     MpegTSBaseStream *stream = (MpegTSBaseStream *) tmp->data;
678     if (!_stream_in_pmt (pmt, stream))
679       toremove = g_list_prepend (toremove, stream);
680   }
681   for (tmp = toremove; tmp; tmp = tmp->next) {
682     MpegTSBaseStream *stream = (MpegTSBaseStream *) tmp->data;
683     mpegts_base_program_remove_stream (base, program, stream->pid);
684   }
685   return TRUE;
686 }
687 
688 
689 static gboolean
_stream_is_private_section(GstMpegtsPMTStream * stream)690 _stream_is_private_section (GstMpegtsPMTStream * stream)
691 {
692   switch (stream->stream_type) {
693     case GST_MPEGTS_STREAM_TYPE_SCTE_DSMCC_DCB:
694     case GST_MPEGTS_STREAM_TYPE_SCTE_SIGNALING:
695     {
696       guint32 registration_id =
697           get_registration_from_descriptors (stream->descriptors);
698       /* Not a private section stream */
699       if (registration_id != DRF_ID_CUEI && registration_id != DRF_ID_ETV1)
700         return FALSE;
701     }
702     case GST_MPEGTS_STREAM_TYPE_PRIVATE_SECTIONS:
703     case GST_MPEGTS_STREAM_TYPE_MHEG:
704     case GST_MPEGTS_STREAM_TYPE_DSM_CC:
705     case GST_MPEGTS_STREAM_TYPE_DSMCC_A:
706     case GST_MPEGTS_STREAM_TYPE_DSMCC_B:
707     case GST_MPEGTS_STREAM_TYPE_DSMCC_C:
708     case GST_MPEGTS_STREAM_TYPE_DSMCC_D:
709     case GST_MPEGTS_STREAM_TYPE_SL_FLEXMUX_SECTIONS:
710     case GST_MPEGTS_STREAM_TYPE_METADATA_SECTIONS:
711       /* known PSI streams */
712       return TRUE;
713     default:
714       return FALSE;
715   }
716 }
717 
718 /* Return TRUE if programs are equal */
719 static gboolean
mpegts_base_is_same_program(MpegTSBase * base,MpegTSBaseProgram * oldprogram,guint16 new_pmt_pid,const GstMpegtsPMT * new_pmt)720 mpegts_base_is_same_program (MpegTSBase * base, MpegTSBaseProgram * oldprogram,
721     guint16 new_pmt_pid, const GstMpegtsPMT * new_pmt)
722 {
723   guint i, nbstreams;
724   MpegTSBaseStream *oldstream;
725   gboolean sawpcrpid = FALSE;
726 
727   if (oldprogram->pmt_pid != new_pmt_pid) {
728     GST_DEBUG ("Different pmt_pid (new:0x%04x, old:0x%04x)", new_pmt_pid,
729         oldprogram->pmt_pid);
730     return FALSE;
731   }
732 
733   if (oldprogram->pcr_pid != new_pmt->pcr_pid) {
734     GST_DEBUG ("Different pcr_pid (new:0x%04x, old:0x%04x)",
735         new_pmt->pcr_pid, oldprogram->pcr_pid);
736     return FALSE;
737   }
738 
739   /* Check the streams */
740   nbstreams = new_pmt->streams->len;
741   for (i = 0; i < nbstreams; ++i) {
742     GstMpegtsPMTStream *stream = g_ptr_array_index (new_pmt->streams, i);
743 
744     oldstream = oldprogram->streams[stream->pid];
745     if (!oldstream) {
746       GST_DEBUG ("New stream 0x%04x not present in old program", stream->pid);
747       return FALSE;
748     }
749     if (oldstream->stream_type != stream->stream_type) {
750       GST_DEBUG
751           ("New stream 0x%04x has a different stream type (new:%d, old:%d)",
752           stream->pid, stream->stream_type, oldstream->stream_type);
753       return FALSE;
754     }
755     if (stream->pid == oldprogram->pcr_pid)
756       sawpcrpid = TRUE;
757   }
758 
759   /* If the pcr is not shared with an existing stream, we'll have one extra stream */
760   if (!sawpcrpid)
761     nbstreams += 1;
762 
763   if (nbstreams != g_list_length (oldprogram->stream_list)) {
764     GST_DEBUG ("Different number of streams (new:%d, old:%d)",
765         nbstreams, g_list_length (oldprogram->stream_list));
766     return FALSE;
767   }
768 
769   GST_DEBUG ("Programs are equal");
770   return TRUE;
771 }
772 
773 /* Return TRUE if program is an update
774  *
775  * A program is equal if:
776  * * The program number is the same (will be if it enters this function)
777  * * AND The PMT PID is equal to the old one
778  * * AND It contains at least one stream from the previous program
779  *
780  * Changes that are acceptable are therefore:
781  * * New streams appearing
782  * * Old streams going away
783  * * PCR PID changing
784  *
785  * Unclear changes:
786  * * PMT PID being changed ?
787  * * Properties of elementary stream being changed ? (new tags ? metadata ?)
788  */
789 static gboolean
mpegts_base_is_program_update(MpegTSBase * base,MpegTSBaseProgram * oldprogram,guint16 new_pmt_pid,const GstMpegtsPMT * new_pmt)790 mpegts_base_is_program_update (MpegTSBase * base,
791     MpegTSBaseProgram * oldprogram, guint16 new_pmt_pid,
792     const GstMpegtsPMT * new_pmt)
793 {
794   guint i, nbstreams;
795   MpegTSBaseStream *oldstream;
796 
797   if (oldprogram->pmt_pid != new_pmt_pid) {
798     /* FIXME/CHECK: Can a program be updated by just changing its PID
799      * in the PAT ? */
800     GST_DEBUG ("Different pmt_pid (new:0x%04x, old:0x%04x)", new_pmt_pid,
801         oldprogram->pmt_pid);
802     return FALSE;
803   }
804 
805   /* Check if at least one stream from the previous program is still present
806    * in the new program */
807 
808   /* Check the streams */
809   nbstreams = new_pmt->streams->len;
810   for (i = 0; i < nbstreams; ++i) {
811     GstMpegtsPMTStream *stream = g_ptr_array_index (new_pmt->streams, i);
812 
813     oldstream = oldprogram->streams[stream->pid];
814     if (!oldstream) {
815       GST_DEBUG ("New stream 0x%04x not present in old program", stream->pid);
816     } else if (oldstream->stream_type != stream->stream_type) {
817       GST_DEBUG
818           ("New stream 0x%04x has a different stream type (new:%d, old:%d)",
819           stream->pid, stream->stream_type, oldstream->stream_type);
820     } else if (!_stream_is_private_section (stream)) {
821       /* FIXME : We should actually be checking a bit deeper,
822        * especially for private streams (where the differentiation is
823        * done at the registration level) */
824       GST_DEBUG
825           ("Stream 0x%04x is identical (stream_type %d) ! Program is an update",
826           stream->pid, stream->stream_type);
827       return TRUE;
828     }
829   }
830 
831   GST_DEBUG ("Program is not an update of the previous one");
832   return FALSE;
833 }
834 
835 static void
mpegts_base_deactivate_program(MpegTSBase * base,MpegTSBaseProgram * program)836 mpegts_base_deactivate_program (MpegTSBase * base, MpegTSBaseProgram * program)
837 {
838   gint i;
839   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
840 
841   if (G_UNLIKELY (program->active == FALSE))
842     return;
843 
844   GST_DEBUG_OBJECT (base, "Deactivating PMT");
845 
846   program->active = FALSE;
847 
848   if (program->pmt) {
849     for (i = 0; i < program->pmt->streams->len; ++i) {
850       GstMpegtsPMTStream *stream = g_ptr_array_index (program->pmt->streams, i);
851 
852       mpegts_base_program_remove_stream (base, program, stream->pid);
853 
854       /* Only unset the is_pes/known_psi bit if the PID isn't used in any other active
855        * program */
856       if (!mpegts_pid_in_active_programs (base, stream->pid)) {
857         if (_stream_is_private_section (stream)) {
858           if (base->parse_private_sections)
859             MPEGTS_BIT_UNSET (base->known_psi, stream->pid);
860         } else {
861           MPEGTS_BIT_UNSET (base->is_pes, stream->pid);
862         }
863       }
864     }
865 
866     /* remove pcr stream */
867     /* FIXME : This might actually be shared with another stream ? */
868     mpegts_base_program_remove_stream (base, program, program->pcr_pid);
869     if (!mpegts_pid_in_active_programs (base, program->pcr_pid))
870       MPEGTS_BIT_UNSET (base->is_pes, program->pcr_pid);
871 
872     GST_DEBUG ("program stream_list is now %p", program->stream_list);
873   }
874 
875   /* Inform subclasses we're deactivating this program */
876   if (klass->program_stopped)
877     klass->program_stopped (base, program);
878 }
879 
880 static void
mpegts_base_activate_program(MpegTSBase * base,MpegTSBaseProgram * program,guint16 pmt_pid,GstMpegtsSection * section,const GstMpegtsPMT * pmt,gboolean initial_program)881 mpegts_base_activate_program (MpegTSBase * base, MpegTSBaseProgram * program,
882     guint16 pmt_pid, GstMpegtsSection * section, const GstMpegtsPMT * pmt,
883     gboolean initial_program)
884 {
885   guint i;
886   MpegTSBaseClass *klass;
887 
888   if (G_UNLIKELY (program->active))
889     return;
890 
891   GST_DEBUG ("Activating program %d", program->program_number);
892 
893   /* activate new pmt */
894   if (program->section)
895     gst_mpegts_section_unref (program->section);
896   program->section = gst_mpegts_section_ref (section);
897 
898   program->pmt = pmt;
899   program->pmt_pid = pmt_pid;
900   program->pcr_pid = pmt->pcr_pid;
901 
902   /* extract top-level registration_id if present */
903   program->registration_id =
904       get_registration_from_descriptors (pmt->descriptors);
905   GST_DEBUG ("program 0x%04x, registration_id %" SAFE_FOURCC_FORMAT,
906       program->program_number, SAFE_FOURCC_ARGS (program->registration_id));
907 
908   for (i = 0; i < pmt->streams->len; ++i) {
909     GstMpegtsPMTStream *stream = g_ptr_array_index (pmt->streams, i);
910     if (_stream_is_private_section (stream)) {
911       if (base->parse_private_sections)
912         MPEGTS_BIT_SET (base->known_psi, stream->pid);
913     } else {
914       if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->is_pes, stream->pid)))
915         GST_FIXME
916             ("Refcounting issue. Setting twice a PID (0x%04x) as known PES",
917             stream->pid);
918       if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi, stream->pid))) {
919         GST_FIXME
920             ("Refcounting issue. Setting a known PSI PID (0x%04x) as known PES",
921             stream->pid);
922         MPEGTS_BIT_UNSET (base->known_psi, stream->pid);
923       }
924       MPEGTS_BIT_SET (base->is_pes, stream->pid);
925     }
926     mpegts_base_program_add_stream (base, program,
927         stream->pid, stream->stream_type, stream);
928   }
929   /* We add the PCR pid last. If that PID is already used by one of the media
930    * streams above, no new stream will be created */
931   mpegts_base_program_add_stream (base, program, pmt->pcr_pid, -1, NULL);
932   MPEGTS_BIT_SET (base->is_pes, pmt->pcr_pid);
933 
934   program->active = TRUE;
935   program->initial_program = initial_program;
936 
937   klass = GST_MPEGTS_BASE_GET_CLASS (base);
938   if (klass->program_started != NULL)
939     klass->program_started (base, program);
940 
941   GST_DEBUG_OBJECT (base, "new pmt activated");
942 }
943 
944 
945 static gboolean
mpegts_base_apply_pat(MpegTSBase * base,GstMpegtsSection * section)946 mpegts_base_apply_pat (MpegTSBase * base, GstMpegtsSection * section)
947 {
948   GPtrArray *pat = gst_mpegts_section_get_pat (section);
949   GPtrArray *old_pat;
950   MpegTSBaseProgram *program;
951   gint i;
952 
953   if (G_UNLIKELY (pat == NULL))
954     return FALSE;
955 
956   GST_INFO_OBJECT (base, "PAT");
957 
958   /* Applying a new PAT does two things:
959    * * It adds the new programs to the list of programs this element handles
960    *   and increments at the same time the number of times a program is referenced.
961    *
962    * * If there was a previously active PAT, It decrements the reference count
963    *   of all program it used. If a program is no longer needed, it is removed.
964    */
965 
966   old_pat = base->pat;
967   base->pat = pat;
968 
969   GST_LOG ("Activating new Program Association Table");
970   /* activate the new table */
971   for (i = 0; i < pat->len; ++i) {
972     GstMpegtsPatProgram *patp = g_ptr_array_index (pat, i);
973 
974     program = mpegts_base_get_program (base, patp->program_number);
975     if (program) {
976       /* IF the program already existed, just check if the PMT PID changed */
977       if (program->pmt_pid != patp->network_or_program_map_PID) {
978         if (program->pmt_pid != G_MAXUINT16) {
979           /* pmt pid changed */
980           /* FIXME: when this happens it may still be pmt pid of another
981            * program, so setting to False may make it go through expensive
982            * path in is_psi unnecessarily */
983           MPEGTS_BIT_UNSET (base->known_psi, program->pmt_pid);
984         }
985 
986         program->pmt_pid = patp->network_or_program_map_PID;
987         if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi, program->pmt_pid)))
988           GST_FIXME
989               ("Refcounting issue. Setting twice a PMT PID (0x%04x) as know PSI",
990               program->pmt_pid);
991         MPEGTS_BIT_SET (base->known_psi, patp->network_or_program_map_PID);
992       }
993     } else {
994       /* Create a new program */
995       program =
996           mpegts_base_add_program (base, patp->program_number,
997           patp->network_or_program_map_PID);
998     }
999     /* We mark this program as being referenced by one PAT */
1000     program->patcount += 1;
1001   }
1002 
1003   if (old_pat) {
1004     MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1005     /* deactivate the old table */
1006     GST_LOG ("Deactivating old Program Association Table");
1007 
1008     for (i = 0; i < old_pat->len; ++i) {
1009       GstMpegtsPatProgram *patp = g_ptr_array_index (old_pat, i);
1010 
1011       program = mpegts_base_get_program (base, patp->program_number);
1012       if (G_UNLIKELY (program == NULL)) {
1013         GST_DEBUG_OBJECT (base, "broken PAT, duplicated entry for program %d",
1014             patp->program_number);
1015         continue;
1016       }
1017 
1018       if (--program->patcount > 0)
1019         /* the program has been referenced by the new pat, keep it */
1020         continue;
1021 
1022       GST_INFO_OBJECT (base, "PAT removing program 0x%04x 0x%04x",
1023           patp->program_number, patp->network_or_program_map_PID);
1024 
1025       if (klass->can_remove_program (base, program)) {
1026         mpegts_base_deactivate_program (base, program);
1027         mpegts_base_remove_program (base, patp->program_number);
1028       } else {
1029         /* sub-class now owns the program and must call
1030          * mpegts_base_deactivate_and_free_program later */
1031         g_hash_table_steal (base->programs,
1032             GINT_TO_POINTER ((gint) patp->program_number));
1033       }
1034       /* FIXME: when this happens it may still be pmt pid of another
1035        * program, so setting to False may make it go through expensive
1036        * path in is_psi unnecessarily */
1037       if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi,
1038                   patp->network_or_program_map_PID))) {
1039         GST_FIXME
1040             ("Program refcounting : Setting twice a pid (0x%04x) as known PSI",
1041             patp->network_or_program_map_PID);
1042       }
1043       MPEGTS_BIT_SET (base->known_psi, patp->network_or_program_map_PID);
1044       mpegts_packetizer_remove_stream (base->packetizer,
1045           patp->network_or_program_map_PID);
1046     }
1047 
1048     g_ptr_array_unref (old_pat);
1049   }
1050 
1051   return TRUE;
1052 }
1053 
1054 static gboolean
mpegts_base_apply_pmt(MpegTSBase * base,GstMpegtsSection * section)1055 mpegts_base_apply_pmt (MpegTSBase * base, GstMpegtsSection * section)
1056 {
1057   const GstMpegtsPMT *pmt;
1058   MpegTSBaseProgram *program, *old_program;
1059   guint program_number;
1060   gboolean initial_program = TRUE;
1061 
1062   pmt = gst_mpegts_section_get_pmt (section);
1063   if (G_UNLIKELY (pmt == NULL)) {
1064     GST_ERROR ("Could not get PMT (corrupted ?)");
1065     return FALSE;
1066   }
1067 
1068   /* FIXME : not so sure this is valid anymore */
1069   if (G_UNLIKELY (base->seen_pat == FALSE)) {
1070     GST_WARNING ("Got pmt without pat first. Returning");
1071     /* remove the stream since we won't get another PMT otherwise */
1072     mpegts_packetizer_remove_stream (base->packetizer, section->pid);
1073     return TRUE;
1074   }
1075 
1076   program_number = section->subtable_extension;
1077   GST_DEBUG ("Applying PMT (program_number:%d, pid:0x%04x)",
1078       program_number, section->pid);
1079 
1080   /* In order for stream switching to happen properly in decodebin(2),
1081    * we need to first add the new pads (i.e. activate the new program)
1082    * before removing the old ones (i.e. deactivating the old program)
1083    */
1084 
1085   old_program = mpegts_base_get_program (base, program_number);
1086   if (G_UNLIKELY (old_program == NULL))
1087     goto no_program;
1088 
1089   if (base->streams_aware
1090       && mpegts_base_is_program_update (base, old_program, section->pid, pmt)) {
1091     GST_FIXME ("We are streams_aware and new program is an update");
1092     /* The program is an update, and we can add/remove pads dynamically */
1093     mpegts_base_update_program (base, old_program, section, pmt);
1094     goto beach;
1095   }
1096 
1097   if (G_UNLIKELY (mpegts_base_is_same_program (base, old_program, section->pid,
1098               pmt)))
1099     goto same_program;
1100 
1101   /* If the current program is active, this means we have a new program */
1102   if (old_program->active) {
1103     MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1104     old_program = mpegts_base_steal_program (base, program_number);
1105     program = mpegts_base_new_program (base, program_number, section->pid);
1106     program->patcount = old_program->patcount;
1107 
1108     /* Desactivate the old program */
1109     /* FIXME : THIS IS BREAKING THE STREAM SWITCHING LOGIC !
1110      *  */
1111     if (klass->can_remove_program (base, old_program)) {
1112       mpegts_base_deactivate_program (base, old_program);
1113       mpegts_base_free_program (old_program);
1114     } else {
1115       /* sub-class now owns the program and must call
1116        * mpegts_base_deactivate_and_free_program later */
1117       g_hash_table_steal (base->programs,
1118           GINT_TO_POINTER ((gint) old_program->program_number));
1119     }
1120     /* Add new program to the programs we track */
1121     g_hash_table_insert (base->programs,
1122         GINT_TO_POINTER (program_number), program);
1123     initial_program = FALSE;
1124   } else {
1125     GST_DEBUG ("Program update, re-using same program");
1126     program = old_program;
1127   }
1128 
1129   /* activate program */
1130   /* Ownership of pmt_info is given to the program */
1131   mpegts_base_activate_program (base, program, section->pid, section, pmt,
1132       initial_program);
1133 
1134 beach:
1135   GST_DEBUG ("Done activating program");
1136   return TRUE;
1137 
1138 no_program:
1139   {
1140     GST_ERROR ("Attempted to apply a PMT on a program that wasn't created");
1141     return TRUE;
1142   }
1143 
1144 same_program:
1145   {
1146     GST_DEBUG ("Not applying identical program");
1147     return TRUE;
1148   }
1149 }
1150 
1151 static void
mpegts_base_handle_psi(MpegTSBase * base,GstMpegtsSection * section)1152 mpegts_base_handle_psi (MpegTSBase * base, GstMpegtsSection * section)
1153 {
1154   gboolean post_message = TRUE;
1155 
1156   GST_DEBUG ("Handling PSI (pid: 0x%04x , table_id: 0x%02x)",
1157       section->pid, section->table_id);
1158 
1159   switch (section->section_type) {
1160     case GST_MPEGTS_SECTION_PAT:
1161       post_message = mpegts_base_apply_pat (base, section);
1162       if (base->seen_pat == FALSE) {
1163         base->seen_pat = TRUE;
1164         GST_DEBUG ("First PAT offset: %" G_GUINT64_FORMAT, section->offset);
1165         mpegts_packetizer_set_reference_offset (base->packetizer,
1166             section->offset);
1167       }
1168       break;
1169     case GST_MPEGTS_SECTION_PMT:
1170       post_message = mpegts_base_apply_pmt (base, section);
1171       break;
1172     case GST_MPEGTS_SECTION_EIT:
1173       /* some tag xtraction + posting */
1174       post_message = mpegts_base_get_tags_from_eit (base, section);
1175       break;
1176     case GST_MPEGTS_SECTION_ATSC_MGT:
1177       post_message = mpegts_base_parse_atsc_mgt (base, section);
1178       break;
1179     default:
1180       break;
1181   }
1182 
1183   /* Finally post message (if it wasn't corrupted) */
1184   if (post_message)
1185     gst_element_post_message (GST_ELEMENT_CAST (base),
1186         gst_message_new_mpegts_section (GST_OBJECT (base), section));
1187   gst_mpegts_section_unref (section);
1188 }
1189 
1190 static gboolean
mpegts_base_parse_atsc_mgt(MpegTSBase * base,GstMpegtsSection * section)1191 mpegts_base_parse_atsc_mgt (MpegTSBase * base, GstMpegtsSection * section)
1192 {
1193   const GstMpegtsAtscMGT *mgt;
1194   gint i;
1195 
1196   mgt = gst_mpegts_section_get_atsc_mgt (section);
1197   if (G_UNLIKELY (mgt == NULL))
1198     return FALSE;
1199 
1200   for (i = 0; i < mgt->tables->len; ++i) {
1201     GstMpegtsAtscMGTTable *table = g_ptr_array_index (mgt->tables, i);
1202 
1203     if ((table->table_type >= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_EIT0 &&
1204             table->table_type <= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_EIT127) ||
1205         (table->table_type >= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_ETT0 &&
1206             table->table_type <= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_ETT127)) {
1207       MPEGTS_BIT_SET (base->known_psi, table->pid);
1208     }
1209   }
1210 
1211   return TRUE;
1212 }
1213 
1214 static gboolean
mpegts_base_get_tags_from_eit(MpegTSBase * base,GstMpegtsSection * section)1215 mpegts_base_get_tags_from_eit (MpegTSBase * base, GstMpegtsSection * section)
1216 {
1217   const GstMpegtsEIT *eit;
1218   guint i;
1219   MpegTSBaseProgram *program;
1220 
1221   /* Early exit if it's not from the present/following table_id */
1222   if (section->table_id != GST_MTS_TABLE_ID_EVENT_INFORMATION_ACTUAL_TS_PRESENT
1223       && section->table_id !=
1224       GST_MTS_TABLE_ID_EVENT_INFORMATION_OTHER_TS_PRESENT)
1225     return TRUE;
1226 
1227   eit = gst_mpegts_section_get_eit (section);
1228   if (G_UNLIKELY (eit == NULL))
1229     return FALSE;
1230 
1231   program = mpegts_base_get_program (base, section->subtable_extension);
1232 
1233   GST_DEBUG
1234       ("program_id:0x%04x, table_id:0x%02x, actual_stream:%d, present_following:%d, program:%p",
1235       section->subtable_extension, section->table_id, eit->actual_stream,
1236       eit->present_following, program);
1237 
1238   if (program && eit->present_following) {
1239     for (i = 0; i < eit->events->len; i++) {
1240       GstMpegtsEITEvent *event = g_ptr_array_index (eit->events, i);
1241       const GstMpegtsDescriptor *desc;
1242 
1243       if (event->running_status == RUNNING_STATUS_RUNNING) {
1244         program->event_id = event->event_id;
1245         if ((desc =
1246                 gst_mpegts_find_descriptor (event->descriptors,
1247                     GST_MTS_DESC_DVB_SHORT_EVENT))) {
1248           gchar *name = NULL, *text = NULL;
1249 
1250           if (gst_mpegts_descriptor_parse_dvb_short_event (desc, NULL, &name,
1251                   &text)) {
1252             if (!program->tags)
1253               program->tags = gst_tag_list_new_empty ();
1254 
1255             if (name) {
1256               gst_tag_list_add (program->tags, GST_TAG_MERGE_APPEND,
1257                   GST_TAG_TITLE, name, NULL);
1258               g_free (name);
1259             }
1260             if (text) {
1261               gst_tag_list_add (program->tags, GST_TAG_MERGE_APPEND,
1262                   GST_TAG_DESCRIPTION, text, NULL);
1263               g_free (text);
1264             }
1265             /* FIXME : Is it correct to post an event duration as a GST_TAG_DURATION ??? */
1266             gst_tag_list_add (program->tags, GST_TAG_MERGE_APPEND,
1267                 GST_TAG_DURATION, event->duration * GST_SECOND, NULL);
1268             return TRUE;
1269           }
1270         }
1271       }
1272     }
1273   }
1274 
1275   return TRUE;
1276 }
1277 
1278 static gboolean
remove_each_program(gpointer key,MpegTSBaseProgram * program,MpegTSBase * base)1279 remove_each_program (gpointer key, MpegTSBaseProgram * program,
1280     MpegTSBase * base)
1281 {
1282   /* First deactivate it */
1283   mpegts_base_deactivate_program (base, program);
1284 
1285   return TRUE;
1286 }
1287 
1288 static inline GstFlowReturn
mpegts_base_drain(MpegTSBase * base)1289 mpegts_base_drain (MpegTSBase * base)
1290 {
1291   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1292 
1293   /* Call implementation */
1294   if (klass->drain)
1295     return klass->drain (base);
1296 
1297   return GST_FLOW_OK;
1298 }
1299 
1300 static inline void
mpegts_base_flush(MpegTSBase * base,gboolean hard)1301 mpegts_base_flush (MpegTSBase * base, gboolean hard)
1302 {
1303   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1304 
1305   /* Call implementation */
1306   if (klass->flush)
1307     klass->flush (base, hard);
1308 }
1309 
1310 static gboolean
mpegts_base_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)1311 mpegts_base_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
1312 {
1313   gboolean res = TRUE;
1314   gboolean hard;
1315   MpegTSBase *base = GST_MPEGTS_BASE (parent);
1316   gboolean is_sticky = GST_EVENT_IS_STICKY (event);
1317 
1318   GST_DEBUG_OBJECT (base, "Got event %s",
1319       gst_event_type_get_name (GST_EVENT_TYPE (event)));
1320 
1321   switch (GST_EVENT_TYPE (event)) {
1322     case GST_EVENT_SEGMENT:
1323       gst_event_copy_segment (event, &base->segment);
1324       GST_DEBUG_OBJECT (base, "Received segment %" GST_SEGMENT_FORMAT,
1325           &base->segment);
1326       /* Check if we need to switch PCR/PTS handling */
1327       if (base->segment.format == GST_FORMAT_TIME) {
1328         base->packetizer->calculate_offset = FALSE;
1329         base->packetizer->calculate_skew = TRUE;
1330         /* Seek was handled upstream */
1331         base->last_seek_seqnum = gst_event_get_seqnum (event);
1332       } else {
1333         base->packetizer->calculate_offset = TRUE;
1334         base->packetizer->calculate_skew = FALSE;
1335       }
1336 
1337       res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1338       break;
1339     case GST_EVENT_STREAM_START:
1340       gst_event_unref (event);
1341       break;
1342     case GST_EVENT_CAPS:
1343       /* FIXME, do something */
1344       gst_event_unref (event);
1345       break;
1346     case GST_EVENT_FLUSH_STOP:
1347       res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1348       hard = (base->mode != BASE_MODE_SEEKING);
1349       mpegts_packetizer_flush (base->packetizer, hard);
1350       mpegts_base_flush (base, hard);
1351       gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED);
1352       base->seen_pat = FALSE;
1353       break;
1354     default:
1355       res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1356   }
1357 
1358   /* Always return TRUE for sticky events */
1359   if (is_sticky)
1360     res = TRUE;
1361 
1362   return res;
1363 }
1364 
1365 static gboolean
mpegts_base_default_sink_query(MpegTSBase * base,GstQuery * query)1366 mpegts_base_default_sink_query (MpegTSBase * base, GstQuery * query)
1367 {
1368   return gst_pad_query_default (base->sinkpad, GST_OBJECT (base), query);
1369 }
1370 
1371 static gboolean
mpegts_base_sink_query(GstPad * pad,GstObject * parent,GstQuery * query)1372 mpegts_base_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
1373 {
1374   MpegTSBase *base = GST_MPEGTS_BASE (parent);
1375 
1376   GST_DEBUG_OBJECT (base, "Got query %s",
1377       gst_query_type_get_name (GST_QUERY_TYPE (query)));
1378 
1379   return GST_MPEGTS_BASE_GET_CLASS (base)->sink_query (base, query);
1380 }
1381 
1382 static GstFlowReturn
mpegts_base_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)1383 mpegts_base_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
1384 {
1385   GstFlowReturn res = GST_FLOW_OK;
1386   MpegTSBase *base;
1387   MpegTSPacketizerPacketReturn pret;
1388   MpegTSPacketizer2 *packetizer;
1389   MpegTSPacketizerPacket packet;
1390   MpegTSBaseClass *klass;
1391 
1392   base = GST_MPEGTS_BASE (parent);
1393   klass = GST_MPEGTS_BASE_GET_CLASS (base);
1394 
1395   packetizer = base->packetizer;
1396 
1397   if (klass->input_done)
1398     gst_buffer_ref (buf);
1399 
1400   if (GST_BUFFER_IS_DISCONT (buf)) {
1401     GST_DEBUG_OBJECT (base, "Got DISCONT buffer, flushing");
1402     res = mpegts_base_drain (base);
1403     if (G_UNLIKELY (res != GST_FLOW_OK))
1404       return res;
1405 
1406     mpegts_base_flush (base, FALSE);
1407     /* In the case of discontinuities in push-mode with TIME segment
1408      * we want to drop all previous observations (hard:TRUE) from
1409      * the packetizer */
1410     if (base->mode == BASE_MODE_PUSHING
1411         && base->segment.format == GST_FORMAT_TIME) {
1412       mpegts_packetizer_flush (base->packetizer, TRUE);
1413       mpegts_packetizer_clear (base->packetizer);
1414     } else
1415       mpegts_packetizer_flush (base->packetizer, FALSE);
1416   }
1417 
1418   mpegts_packetizer_push (base->packetizer, buf);
1419 
1420   while (res == GST_FLOW_OK) {
1421     pret = mpegts_packetizer_next_packet (base->packetizer, &packet);
1422 
1423     /* If we don't have enough data, return */
1424     if (G_UNLIKELY (pret == PACKET_NEED_MORE))
1425       break;
1426 
1427     if (G_UNLIKELY (pret == PACKET_BAD)) {
1428       /* bad header, skip the packet */
1429       GST_DEBUG_OBJECT (base, "bad packet, skipping");
1430       goto next;
1431     }
1432 
1433     if (klass->inspect_packet)
1434       klass->inspect_packet (base, &packet);
1435 
1436     /* If it's a known PES, push it */
1437     if (MPEGTS_BIT_IS_SET (base->is_pes, packet.pid)) {
1438       /* push the packet downstream */
1439       if (base->push_data)
1440         res = klass->push (base, &packet, NULL);
1441     } else if (packet.payload
1442         && MPEGTS_BIT_IS_SET (base->known_psi, packet.pid)) {
1443       /* base PSI data */
1444       GList *others, *tmp;
1445       GstMpegtsSection *section;
1446 
1447       section = mpegts_packetizer_push_section (packetizer, &packet, &others);
1448       if (section)
1449         mpegts_base_handle_psi (base, section);
1450       if (G_UNLIKELY (others)) {
1451         for (tmp = others; tmp; tmp = tmp->next)
1452           mpegts_base_handle_psi (base, (GstMpegtsSection *) tmp->data);
1453         g_list_free (others);
1454       }
1455 
1456       /* we need to push section packet downstream */
1457       if (base->push_section)
1458         res = klass->push (base, &packet, section);
1459 
1460     } else if (packet.payload && packet.pid != 0x1fff)
1461       GST_LOG ("PID 0x%04x Saw packet on a pid we don't handle", packet.pid);
1462 
1463   next:
1464     mpegts_packetizer_clear_packet (base->packetizer, &packet);
1465   }
1466 
1467   if (klass->input_done) {
1468     if (res == GST_FLOW_OK)
1469       res = klass->input_done (base, buf);
1470     else
1471       gst_buffer_unref (buf);
1472   }
1473 
1474   return res;
1475 }
1476 
1477 static GstFlowReturn
mpegts_base_scan(MpegTSBase * base)1478 mpegts_base_scan (MpegTSBase * base)
1479 {
1480   GstFlowReturn ret = GST_FLOW_OK;
1481   GstBuffer *buf = NULL;
1482   guint i;
1483   gboolean done = FALSE;
1484   MpegTSPacketizerPacketReturn pret;
1485   gint64 tmpval;
1486   gint64 upstream_size, seek_pos, reverse_limit;
1487   GstFormat format;
1488   guint initial_pcr_seen;
1489 
1490   GST_DEBUG ("Scanning for initial sync point");
1491 
1492   /* Find initial sync point and at least 5 PCR values */
1493   for (i = 0; i < 20 && !done; i++) {
1494     GST_DEBUG ("Grabbing %d => %d", i * 65536, (i + 1) * 65536);
1495 
1496     ret = gst_pad_pull_range (base->sinkpad, i * 65536, 65536, &buf);
1497     if (G_UNLIKELY (ret == GST_FLOW_EOS))
1498       break;
1499     if (G_UNLIKELY (ret != GST_FLOW_OK))
1500       goto beach;
1501 
1502     /* Push to packetizer */
1503     mpegts_packetizer_push (base->packetizer, buf);
1504     buf = NULL;
1505 
1506     if (mpegts_packetizer_has_packets (base->packetizer)) {
1507       if (base->seek_offset == -1) {
1508         /* Mark the initial sync point and remember the packetsize */
1509         base->seek_offset = base->packetizer->offset;
1510         GST_DEBUG ("Sync point is now %" G_GUINT64_FORMAT, base->seek_offset);
1511         base->packetsize = base->packetizer->packet_size;
1512       }
1513       while (1) {
1514         /* Eat up all packets */
1515         pret = mpegts_packetizer_process_next_packet (base->packetizer);
1516         if (pret == PACKET_NEED_MORE)
1517           break;
1518         if (pret != PACKET_BAD && base->packetizer->nb_seen_offsets >= 5) {
1519           GST_DEBUG ("Got enough initial PCR");
1520           done = TRUE;
1521           break;
1522         }
1523       }
1524     }
1525   }
1526 
1527   initial_pcr_seen = base->packetizer->nb_seen_offsets;
1528   if (G_UNLIKELY (initial_pcr_seen == 0))
1529     goto no_initial_pcr;
1530   GST_DEBUG ("Seen %d initial PCR", initial_pcr_seen);
1531 
1532   /* Now send data from the end */
1533 
1534   /* Get the size of upstream */
1535   format = GST_FORMAT_BYTES;
1536   if (!gst_pad_peer_query_duration (base->sinkpad, format, &tmpval))
1537     goto beach;
1538   upstream_size = tmpval;
1539 
1540   /* The scanning takes place on the last 2048kB. Considering PCR should
1541    * be present at least every 100ms, this should cope with streams
1542    * up to 160Mbit/s */
1543   reverse_limit = MAX (0, upstream_size - 2097152);
1544 
1545   /* Find last PCR value, searching backwards by chunks of 300 MPEG-ts packets */
1546   for (seek_pos = MAX (0, upstream_size - 56400);
1547       seek_pos >= reverse_limit; seek_pos -= 56400) {
1548     mpegts_packetizer_clear (base->packetizer);
1549     GST_DEBUG ("Grabbing %" G_GUINT64_FORMAT " => %" G_GUINT64_FORMAT, seek_pos,
1550         seek_pos + 56400);
1551 
1552     ret = gst_pad_pull_range (base->sinkpad, seek_pos, 56400, &buf);
1553     if (G_UNLIKELY (ret == GST_FLOW_EOS))
1554       break;
1555     if (G_UNLIKELY (ret != GST_FLOW_OK))
1556       goto beach;
1557 
1558     /* Push to packetizer */
1559     mpegts_packetizer_push (base->packetizer, buf);
1560     buf = NULL;
1561 
1562     if (mpegts_packetizer_has_packets (base->packetizer)) {
1563       pret = PACKET_OK;
1564       /* Eat up all packets, really try to get last PCR(s) */
1565       while (pret != PACKET_NEED_MORE)
1566         pret = mpegts_packetizer_process_next_packet (base->packetizer);
1567 
1568       if (base->packetizer->nb_seen_offsets > initial_pcr_seen) {
1569         GST_DEBUG ("Got last PCR(s) (total seen:%d)",
1570             base->packetizer->nb_seen_offsets);
1571         break;
1572       }
1573     }
1574   }
1575 
1576 beach:
1577   mpegts_packetizer_clear (base->packetizer);
1578   return ret;
1579 
1580 no_initial_pcr:
1581   mpegts_packetizer_clear (base->packetizer);
1582   GST_WARNING_OBJECT (base, "Couldn't find any PCR within the first %d bytes",
1583       10 * 65536);
1584   return GST_FLOW_OK;
1585 }
1586 
1587 
1588 static void
mpegts_base_loop(MpegTSBase * base)1589 mpegts_base_loop (MpegTSBase * base)
1590 {
1591   GstFlowReturn ret = GST_FLOW_ERROR;
1592 
1593   switch (base->mode) {
1594     case BASE_MODE_SCANNING:
1595       /* Find first sync point */
1596       ret = mpegts_base_scan (base);
1597       if (G_UNLIKELY (ret != GST_FLOW_OK))
1598         goto error;
1599       base->mode = BASE_MODE_STREAMING;
1600       GST_DEBUG ("Changing to Streaming");
1601       break;
1602     case BASE_MODE_SEEKING:
1603       /* FIXME : unclear if we still need mode_seeking... */
1604       base->mode = BASE_MODE_STREAMING;
1605       break;
1606     case BASE_MODE_STREAMING:
1607     {
1608       GstBuffer *buf = NULL;
1609 
1610       GST_DEBUG ("Pulling data from %" G_GUINT64_FORMAT, base->seek_offset);
1611 
1612       if (G_UNLIKELY (base->last_seek_seqnum == GST_SEQNUM_INVALID)) {
1613         /* No configured seek, set a valid seqnum */
1614         base->last_seek_seqnum = gst_util_seqnum_next ();
1615       }
1616       ret = gst_pad_pull_range (base->sinkpad, base->seek_offset,
1617           100 * base->packetsize, &buf);
1618       if (G_UNLIKELY (ret != GST_FLOW_OK))
1619         goto error;
1620       base->seek_offset += gst_buffer_get_size (buf);
1621       ret = mpegts_base_chain (base->sinkpad, GST_OBJECT_CAST (base), buf);
1622       if (G_UNLIKELY (ret != GST_FLOW_OK))
1623         goto error;
1624     }
1625       break;
1626     case BASE_MODE_PUSHING:
1627       GST_WARNING ("wrong BASE_MODE_PUSHING mode in pull loop");
1628       break;
1629   }
1630 
1631   return;
1632 
1633 error:
1634   {
1635     GST_DEBUG_OBJECT (base, "Pausing task, reason %s", gst_flow_get_name (ret));
1636     if (ret == GST_FLOW_EOS) {
1637       if (!GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base,
1638               gst_event_new_eos ()))
1639         GST_ELEMENT_ERROR (base, STREAM, FAILED,
1640             (_("Internal data stream error.")),
1641             ("No program activated before EOS"));
1642     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
1643       GST_ELEMENT_FLOW_ERROR (base, ret);
1644       GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, gst_event_new_eos ());
1645     }
1646     gst_pad_pause_task (base->sinkpad);
1647   }
1648 }
1649 
1650 
1651 gboolean
mpegts_base_handle_seek_event(MpegTSBase * base,GstPad * pad,GstEvent * event)1652 mpegts_base_handle_seek_event (MpegTSBase * base, GstPad * pad,
1653     GstEvent * event)
1654 {
1655   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1656   GstFlowReturn ret = GST_FLOW_ERROR;
1657   gdouble rate;
1658   gboolean flush;
1659   GstFormat format;
1660   GstSeekFlags flags;
1661   GstSeekType start_type, stop_type;
1662   gint64 start, stop;
1663   GstEvent *flush_event = NULL;
1664 
1665   gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
1666       &stop_type, &stop);
1667 
1668   if (format != GST_FORMAT_TIME)
1669     return FALSE;
1670 
1671   if (GST_EVENT_SEQNUM (event) == base->last_seek_seqnum) {
1672     GST_DEBUG_OBJECT (base, "Skipping already handled seek");
1673     return TRUE;
1674   }
1675 
1676   if (base->mode == BASE_MODE_PUSHING) {
1677     /* First try if upstream supports seeking in TIME format */
1678     if (gst_pad_push_event (base->sinkpad, gst_event_ref (event))) {
1679       GST_DEBUG ("upstream handled SEEK event");
1680       return TRUE;
1681     }
1682 
1683     /* If the subclass can seek, do that */
1684     if (klass->seek) {
1685       ret = klass->seek (base, event);
1686       if (G_UNLIKELY (ret != GST_FLOW_OK))
1687         GST_WARNING ("seeking failed %s", gst_flow_get_name (ret));
1688       else {
1689         GstEvent *new_seek;
1690 
1691         if (GST_CLOCK_TIME_IS_VALID (base->seek_offset)) {
1692           base->mode = BASE_MODE_SEEKING;
1693           new_seek = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
1694               GST_SEEK_TYPE_SET, base->seek_offset, GST_SEEK_TYPE_NONE, -1);
1695           gst_event_set_seqnum (new_seek, GST_EVENT_SEQNUM (event));
1696           if (!gst_pad_push_event (base->sinkpad, new_seek))
1697             ret = GST_FLOW_ERROR;
1698           else
1699             base->last_seek_seqnum = GST_EVENT_SEQNUM (event);
1700         }
1701         base->mode = BASE_MODE_PUSHING;
1702       }
1703     } else {
1704       GST_WARNING ("subclass has no seek implementation");
1705     }
1706 
1707     return ret == GST_FLOW_OK;
1708   }
1709 
1710   if (!klass->seek) {
1711     GST_WARNING ("subclass has no seek implementation");
1712     return FALSE;
1713   }
1714 
1715   if (rate <= 0.0) {
1716     GST_WARNING ("Negative rate not supported");
1717     return FALSE;
1718   }
1719 
1720   GST_DEBUG ("seek event, rate: %f start: %" GST_TIME_FORMAT
1721       " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start),
1722       GST_TIME_ARGS (stop));
1723 
1724   flush = flags & GST_SEEK_FLAG_FLUSH;
1725 
1726   /* stop streaming, either by flushing or by pausing the task */
1727   base->mode = BASE_MODE_SEEKING;
1728   if (flush) {
1729     GST_DEBUG_OBJECT (base, "sending flush start");
1730     flush_event = gst_event_new_flush_start ();
1731     gst_event_set_seqnum (flush_event, GST_EVENT_SEQNUM (event));
1732     gst_pad_push_event (base->sinkpad, gst_event_ref (flush_event));
1733     GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, flush_event);
1734   } else
1735     gst_pad_pause_task (base->sinkpad);
1736 
1737   /* wait for streaming to finish */
1738   GST_PAD_STREAM_LOCK (base->sinkpad);
1739 
1740   if (flush) {
1741     /* send a FLUSH_STOP for the sinkpad, since we need data for seeking */
1742     GST_DEBUG_OBJECT (base, "sending flush stop");
1743     flush_event = gst_event_new_flush_stop (TRUE);
1744     gst_event_set_seqnum (flush_event, GST_EVENT_SEQNUM (event));
1745 
1746     /* ref for it to be reused later */
1747     gst_pad_push_event (base->sinkpad, gst_event_ref (flush_event));
1748     /* And actually flush our pending data but allow to preserve some info
1749      * to perform the seek */
1750     mpegts_base_flush (base, FALSE);
1751     mpegts_packetizer_flush (base->packetizer, FALSE);
1752   }
1753 
1754   if (flags & (GST_SEEK_FLAG_SEGMENT)) {
1755     GST_WARNING ("seek flags 0x%x are not supported", (int) flags);
1756     goto done;
1757   }
1758 
1759 
1760   /* If the subclass can seek, do that */
1761   ret = klass->seek (base, event);
1762   if (G_UNLIKELY (ret != GST_FLOW_OK))
1763     GST_WARNING ("seeking failed %s", gst_flow_get_name (ret));
1764   else
1765     base->last_seek_seqnum = GST_EVENT_SEQNUM (event);
1766 
1767   if (flush_event) {
1768     /* if we sent a FLUSH_START, we now send a FLUSH_STOP */
1769     GST_DEBUG_OBJECT (base, "sending flush stop");
1770     GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, flush_event);
1771     flush_event = NULL;
1772   }
1773 done:
1774   if (flush_event)
1775     gst_event_unref (flush_event);
1776   gst_pad_start_task (base->sinkpad, (GstTaskFunction) mpegts_base_loop, base,
1777       NULL);
1778 
1779   GST_PAD_STREAM_UNLOCK (base->sinkpad);
1780   return ret == GST_FLOW_OK;
1781 }
1782 
1783 
1784 static gboolean
mpegts_base_sink_activate(GstPad * sinkpad,GstObject * parent)1785 mpegts_base_sink_activate (GstPad * sinkpad, GstObject * parent)
1786 {
1787   GstQuery *query;
1788   gboolean pull_mode;
1789 
1790   query = gst_query_new_scheduling ();
1791 
1792   if (!gst_pad_peer_query (sinkpad, query)) {
1793     gst_query_unref (query);
1794     goto activate_push;
1795   }
1796 
1797   pull_mode = gst_query_has_scheduling_mode_with_flags (query,
1798       GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
1799   gst_query_unref (query);
1800 
1801   if (!pull_mode)
1802     goto activate_push;
1803 
1804   GST_DEBUG_OBJECT (sinkpad, "activating pull");
1805   return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
1806 
1807 activate_push:
1808   {
1809     GST_DEBUG_OBJECT (sinkpad, "activating push");
1810     return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
1811   }
1812 }
1813 
1814 static gboolean
mpegts_base_sink_activate_mode(GstPad * pad,GstObject * parent,GstPadMode mode,gboolean active)1815 mpegts_base_sink_activate_mode (GstPad * pad, GstObject * parent,
1816     GstPadMode mode, gboolean active)
1817 {
1818   gboolean res;
1819   MpegTSBase *base = GST_MPEGTS_BASE (parent);
1820 
1821   switch (mode) {
1822     case GST_PAD_MODE_PUSH:
1823       base->mode = BASE_MODE_PUSHING;
1824       res = TRUE;
1825       break;
1826     case GST_PAD_MODE_PULL:
1827       if (active) {
1828         base->mode = BASE_MODE_SCANNING;
1829         /* When working pull-based, we always use offsets for estimation */
1830         base->packetizer->calculate_offset = TRUE;
1831         base->packetizer->calculate_skew = FALSE;
1832         gst_segment_init (&base->segment, GST_FORMAT_BYTES);
1833         res =
1834             gst_pad_start_task (pad, (GstTaskFunction) mpegts_base_loop, base,
1835             NULL);
1836       } else
1837         res = gst_pad_stop_task (pad);
1838       break;
1839     default:
1840       res = FALSE;
1841       break;
1842   }
1843   return res;
1844 }
1845 
1846 static GstStateChangeReturn
mpegts_base_change_state(GstElement * element,GstStateChange transition)1847 mpegts_base_change_state (GstElement * element, GstStateChange transition)
1848 {
1849   MpegTSBase *base;
1850   GstStateChangeReturn ret;
1851 
1852   base = GST_MPEGTS_BASE (element);
1853 
1854   switch (transition) {
1855     case GST_STATE_CHANGE_READY_TO_PAUSED:
1856       mpegts_base_reset (base);
1857       break;
1858     default:
1859       break;
1860   }
1861 
1862   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1863 
1864   switch (transition) {
1865     case GST_STATE_CHANGE_PAUSED_TO_READY:
1866       mpegts_base_reset (base);
1867       if (base->mode != BASE_MODE_PUSHING)
1868         base->mode = BASE_MODE_SCANNING;
1869       break;
1870     default:
1871       break;
1872   }
1873 
1874   return ret;
1875 }
1876 
1877 gboolean
gst_mpegtsbase_plugin_init(GstPlugin * plugin)1878 gst_mpegtsbase_plugin_init (GstPlugin * plugin)
1879 {
1880   GST_DEBUG_CATEGORY_INIT (mpegts_base_debug, "mpegtsbase", 0,
1881       "MPEG transport stream base class");
1882 
1883   return TRUE;
1884 }
1885