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