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