3 * Copyright (C) 2007 Alessandro Decina
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.
11 * Alessandro Decina <alessandro@nnva.org>
12 * Zaheer Abbas Merali <zaheerabbas at merali dot org>
13 * Edward Hervey <edward.hervey@collabora.co.uk>
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.
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.
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.
40 #include <glib/gi18n-lib.h>
41 #include "mpegtsbase.h"
42 #include "gstmpegdesc.h"
44 #define RUNNING_STATUS_RUNNING 4
46 GST_DEBUG_CATEGORY_STATIC (mpegts_base_debug);
47 #define GST_CAT_DEFAULT mpegts_base_debug
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;
56 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
59 GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
62 #define DEFAULT_IGNORE_PCR FALSE
67 PROP_PARSE_PRIVATE_SECTIONS,
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);
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,
87 static gboolean mpegts_base_sink_event (GstPad * pad, GstObject * parent,
89 static gboolean mpegts_base_sink_query (GstPad * pad, GstObject * parent,
91 static gboolean mpegts_base_default_sink_query (MpegTSBase * base,
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,
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 ();
116 #define mpegts_base_parent_class parent_class
117 G_DEFINE_TYPE_WITH_CODE (MpegTSBase, mpegts_base, GST_TYPE_ELEMENT,
120 /* Default implementation is that mpegtsbase can remove any program */
122 mpegts_base_can_remove_program (MpegTSBase * base, MpegTSBaseProgram * program)
128 mpegts_base_class_init (MpegTSBaseClass * klass)
130 GObjectClass *gobject_class;
131 GstElementClass *element_class;
133 klass->can_remove_program = mpegts_base_can_remove_program;
135 element_class = GST_ELEMENT_CLASS (klass);
136 element_class->change_state = mpegts_base_change_state;
138 gst_element_class_add_static_pad_template (element_class, &sink_template);
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;
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));
152 * GstMpegtsBase:ignore-pcr:
154 * Ignore PCR (Program Clock Reference) data from MPEG-TS PSI.
155 * This can help with playback of some broken files.
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));
164 klass->sink_query = GST_DEBUG_FUNCPTR (mpegts_base_default_sink_query);
165 klass->handle_psi = NULL;
167 gst_type_mark_as_plugin_api (GST_TYPE_MPEGTS_BASE, 0);
171 mpegts_base_set_property (GObject * object, guint prop_id,
172 const GValue * value, GParamSpec * pspec)
174 MpegTSBase *base = GST_MPEGTS_BASE (object);
177 case PROP_PARSE_PRIVATE_SECTIONS:
178 base->parse_private_sections = g_value_get_boolean (value);
180 case PROP_IGNORE_PCR:
181 base->ignore_pcr = g_value_get_boolean (value);
184 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
189 mpegts_base_get_property (GObject * object, guint prop_id,
190 GValue * value, GParamSpec * pspec)
192 MpegTSBase *base = GST_MPEGTS_BASE (object);
195 case PROP_PARSE_PRIVATE_SECTIONS:
196 g_value_set_boolean (value, base->parse_private_sections);
198 case PROP_IGNORE_PCR:
199 g_value_set_boolean (value, base->ignore_pcr);
202 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
208 mpegts_base_reset (MpegTSBase * base)
210 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
212 mpegts_packetizer_clear (base->packetizer);
213 memset (base->is_pes, 0, 1024);
214 memset (base->known_psi, 0, 1024);
216 /* FIXME : Actually these are not *always* know SI streams
217 * depending on the variant of mpeg-ts being used. */
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);
224 MPEGTS_BIT_SET (base->known_psi, 0x14);
225 /* network synchronization */
226 MPEGTS_BIT_SET (base->known_psi, 0x15);
229 MPEGTS_BIT_SET (base->known_psi, 0x1ffb);
232 g_ptr_array_unref (base->pat);
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;
240 base->mode = BASE_MODE_STREAMING;
241 base->seen_pat = FALSE;
242 base->seek_offset = -1;
244 g_ptr_array_foreach (base->programs, (GFunc) remove_each_program, base);
245 g_ptr_array_remove_range (base->programs, 0, base->programs->len);
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);
252 gst_event_replace (&base->seek_event, NULL);
259 mpegts_base_init (MpegTSBase * base)
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);
270 base->disposed = FALSE;
271 base->packetizer = mpegts_packetizer_new ();
273 g_ptr_array_new_full (16, (GDestroyNotify) mpegts_base_free_program);
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);
281 base->push_data = TRUE;
282 base->push_section = TRUE;
283 base->ignore_pcr = DEFAULT_IGNORE_PCR;
285 mpegts_base_reset (base);
289 mpegts_base_dispose (GObject * object)
291 MpegTSBase *base = GST_MPEGTS_BASE (object);
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);
300 if (G_OBJECT_CLASS (parent_class)->dispose)
301 G_OBJECT_CLASS (parent_class)->dispose (object);
305 mpegts_base_finalize (GObject * object)
307 MpegTSBase *base = GST_MPEGTS_BASE (object);
310 g_ptr_array_unref (base->pat);
313 g_ptr_array_free (base->programs, TRUE);
315 gst_event_replace (&base->seek_event, NULL);
317 if (G_OBJECT_CLASS (parent_class)->finalize)
318 G_OBJECT_CLASS (parent_class)->finalize (object);
322 /* returns NULL if no matching descriptor found *
323 * otherwise returns a descriptor that needs to *
325 const GstMpegtsDescriptor *
326 mpegts_get_descriptor_from_stream (MpegTSBaseStream * stream, guint8 tag)
328 GstMpegtsPMTStream *pmt = stream->stream;
330 GST_DEBUG ("Searching for tag 0x%02x in stream 0x%04x (stream_type 0x%02x)",
331 tag, stream->pid, stream->stream_type);
333 return gst_mpegts_find_descriptor (pmt->descriptors, tag);
336 const GstMpegtsDescriptor *
337 mpegts_get_descriptor_from_stream_with_extension (MpegTSBaseStream * stream,
338 guint8 tag, guint8 tag_extension)
340 GstMpegtsPMTStream *pmt = stream->stream;
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);
346 return gst_mpegts_find_descriptor_with_extension (pmt->descriptors, tag,
357 foreach_pid_in_program (MpegTSBaseProgram * program, PIDLookup * lookup)
359 if (!program->active)
361 if (program->streams[lookup->pid])
366 mpegts_pid_in_active_programs (MpegTSBase * base, guint16 pid)
372 g_ptr_array_foreach (base->programs, (GFunc) foreach_pid_in_program, &lookup);
377 /* returns NULL if no matching descriptor found *
378 * otherwise returns a descriptor that needs to *
380 const GstMpegtsDescriptor *
381 mpegts_get_descriptor_from_program (MpegTSBaseProgram * program, guint8 tag)
383 const GstMpegtsPMT *pmt = program->pmt;
385 return gst_mpegts_find_descriptor (pmt->descriptors, tag);
389 _get_upstream_id (GstElement * element, GstPad * sinkpad)
391 gchar *upstream_id = gst_pad_get_stream_id (sinkpad);
394 /* Try to create one from the upstream URI, else use a randome number */
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);
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));
412 upstream_id = g_strdup (g_checksum_get_string (cs));
413 g_checksum_free (cs);
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");
419 g_strdup_printf ("%08x%08x%08x%08x", g_random_int (), g_random_int (),
420 g_random_int (), g_random_int ());
423 gst_query_unref (query);
428 static MpegTSBaseProgram *
429 mpegts_base_new_program (MpegTSBase * base,
430 gint program_number, guint16 pmt_pid)
432 MpegTSBaseProgram *program;
433 gchar *upstream_id, *stream_id;
435 GST_DEBUG_OBJECT (base, "program_number : %d, pmt_pid : 0x%04x",
436 program_number, pmt_pid);
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;
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);
449 g_free (upstream_id);
455 mpegts_base_add_program (MpegTSBase * base,
456 gint program_number, guint16 pmt_pid)
458 MpegTSBaseProgram *program;
460 GST_DEBUG_OBJECT (base, "program_number : %d, pmt_pid : 0x%04x",
461 program_number, pmt_pid);
463 program = mpegts_base_new_program (base, program_number, pmt_pid);
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",
470 MPEGTS_BIT_SET (base->known_psi, pmt_pid);
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);
479 g_ptr_array_add (base->programs, program);
485 mpegts_base_get_program (MpegTSBase * base, gint program_number)
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)
497 static MpegTSBaseProgram *
498 mpegts_base_steal_program (MpegTSBase * base, gint program_number)
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 return g_ptr_array_steal_index (base->programs, i);
513 mpegts_base_free_stream (MpegTSBaseStream * stream)
515 if (stream->stream_object)
516 gst_object_unref (stream->stream_object);
517 if (stream->stream_id)
518 g_free (stream->stream_id);
523 mpegts_base_free_program (MpegTSBaseProgram * program)
527 if (program->recycle) {
528 program->recycle = FALSE;
533 gst_mpegts_section_unref (program->section);
537 /* FIXME FIXME FIXME FREE STREAM OBJECT ! */
538 for (tmp = program->stream_list; tmp; tmp = tmp->next)
539 mpegts_base_free_stream ((MpegTSBaseStream *) tmp->data);
541 if (program->stream_list)
542 g_list_free (program->stream_list);
544 g_free (program->streams);
547 gst_tag_list_unref (program->tags);
548 if (program->collection)
549 gst_object_unref (program->collection);
555 mpegts_base_deactivate_and_free_program (MpegTSBase * base,
556 MpegTSBaseProgram * program)
558 GST_DEBUG_OBJECT (base, "program_number : %d", program->program_number);
560 mpegts_base_deactivate_program (base, program);
561 mpegts_base_free_program (program);
565 mpegts_base_remove_program (MpegTSBase * base, MpegTSBaseProgram * program)
567 GST_DEBUG_OBJECT (base, "program_number : %d", program->program_number);
569 g_ptr_array_remove (base->programs, program);
573 get_registration_from_descriptors (GPtrArray * descriptors)
575 const GstMpegtsDescriptor *desc;
578 gst_mpegts_find_descriptor (descriptors,
579 GST_MTS_DESC_REGISTRATION))) {
580 if (G_UNLIKELY (desc->length < 4)) {
581 GST_WARNING ("Registration descriptor with length < 4. (Corrupted ?)");
583 return GST_READ_UINT32_BE (desc->data + 2);
590 find_registration_in_descriptors (GPtrArray * descriptors,
591 guint32 registration_id)
599 nb_desc = descriptors->len;
600 for (i = 0; i < nb_desc; i++) {
601 GstMpegtsDescriptor *desc = g_ptr_array_index (descriptors, i);
602 if (desc->tag == GST_MTS_DESC_REGISTRATION) {
603 guint32 reg_desc = GST_READ_UINT32_BE (desc->data + 2);
604 if (reg_desc == registration_id)
611 static MpegTSBaseStream *
612 mpegts_base_program_add_stream (MpegTSBase * base,
613 MpegTSBaseProgram * program, guint16 pid, guint8 stream_type,
614 GstMpegtsPMTStream * stream)
616 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
617 MpegTSBaseStream *bstream;
619 GST_DEBUG ("pid:0x%04x, stream_type:0x%03x", pid, stream_type);
621 /* FIXME : PID information/nature might change through time.
622 * We therefore *do* want to be able to replace an existing stream
623 * with updated information */
624 if (G_UNLIKELY (program->streams[pid])) {
625 if (stream_type != 0xff)
626 GST_WARNING ("Stream already present !");
630 bstream = g_malloc0 (base->stream_size);
632 g_strdup_printf ("%s/%08x",
633 gst_stream_collection_get_upstream_id (program->collection), pid);
635 bstream->stream_type = stream_type;
636 bstream->stream = stream;
637 /* We don't yet know the stream type, subclasses will fill that */
638 bstream->stream_object = gst_stream_new (bstream->stream_id, NULL,
639 GST_STREAM_TYPE_UNKNOWN, GST_STREAM_FLAG_NONE);
641 bstream->registration_id =
642 get_registration_from_descriptors (stream->descriptors);
643 GST_DEBUG ("PID 0x%04x, registration_id %" SAFE_FOURCC_FORMAT,
644 bstream->pid, SAFE_FOURCC_ARGS (bstream->registration_id));
647 program->streams[pid] = bstream;
648 program->stream_list = g_list_append (program->stream_list, bstream);
650 if (klass->stream_added)
651 if (klass->stream_added (base, bstream, program)) {
652 gst_stream_collection_add_stream (program->collection,
653 (GstStream *) gst_object_ref (bstream->stream_object));
654 bstream->in_collection = TRUE;
662 mpegts_base_program_remove_stream (MpegTSBase * base,
663 MpegTSBaseProgram * program, guint16 pid)
665 MpegTSBaseClass *klass;
666 MpegTSBaseStream *stream = program->streams[pid];
668 GST_DEBUG ("pid:0x%04x", pid);
670 if (G_UNLIKELY (stream == NULL)) {
671 /* Can happen if the PCR PID is the same as a audio/video PID */
672 GST_DEBUG ("Stream already removed");
676 klass = GST_MPEGTS_BASE_GET_CLASS (base);
678 /* If subclass needs it, inform it of the stream we are about to remove */
679 if (klass->stream_removed)
680 klass->stream_removed (base, stream);
682 program->stream_list = g_list_remove_all (program->stream_list, stream);
683 mpegts_base_free_stream (stream);
684 program->streams[pid] = NULL;
687 /* Check if pmtstream is already present in the program */
688 static inline gboolean
689 _stream_in_pmt (const GstMpegtsPMT * pmt, MpegTSBaseStream * stream)
691 guint i, nbstreams = pmt->streams->len;
693 for (i = 0; i < nbstreams; i++) {
694 GstMpegtsPMTStream *pmt_stream = g_ptr_array_index (pmt->streams, i);
696 if (pmt_stream->pid == stream->pid &&
697 pmt_stream->stream_type == stream->stream_type)
704 static inline gboolean
705 _pmt_stream_in_program (MpegTSBaseProgram * program,
706 GstMpegtsPMTStream * stream)
708 MpegTSBaseStream *old_stream = program->streams[stream->pid];
711 return old_stream->stream_type == stream->stream_type;
715 mpegts_base_update_program (MpegTSBase * base, MpegTSBaseProgram * program,
716 GstMpegtsSection * section, const GstMpegtsPMT * pmt)
718 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
719 const gchar *stream_id =
720 gst_stream_collection_get_upstream_id (program->collection);
721 GstStreamCollection *collection;
722 GList *tmp, *toremove;
725 /* Create new collection */
726 collection = gst_stream_collection_new (stream_id);
727 gst_object_unref (program->collection);
728 program->collection = collection;
730 /* Replace section and pmt with the new one */
731 gst_mpegts_section_unref (program->section);
732 program->section = gst_mpegts_section_ref (section);
735 /* Copy over gststream that still exist into the collection */
736 for (tmp = program->stream_list; tmp; tmp = tmp->next) {
737 MpegTSBaseStream *stream = (MpegTSBaseStream *) tmp->data;
738 if (_stream_in_pmt (pmt, stream) && stream->in_collection) {
739 gst_stream_collection_add_stream (program->collection,
740 gst_object_ref (stream->stream_object));
744 /* Add new streams (will also create and add gststream to the collection) */
745 nbstreams = pmt->streams->len;
746 for (i = 0; i < nbstreams; i++) {
747 GstMpegtsPMTStream *stream = g_ptr_array_index (pmt->streams, i);
748 if (!_pmt_stream_in_program (program, stream)) {
749 #ifdef TIZEN_FEATURE_TSDEMUX_UPDATE_STREAM
750 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->is_pes, stream->pid)))
752 ("Refcounting issue. Setting twice a PID (0x%04x) as known PES",
754 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi, stream->pid))) {
756 ("Refcounting issue. Setting a known PSI PID (0x%04x) as known PES",
758 MPEGTS_BIT_UNSET (base->known_psi, stream->pid);
760 MPEGTS_BIT_SET (base->is_pes, stream->pid);
762 mpegts_base_program_add_stream (base, program, stream->pid,
763 stream->stream_type, stream);
767 /* Call subclass update */
768 if (klass->update_program)
769 klass->update_program (base, program);
771 /* Remove streams no longer present */
773 for (tmp = program->stream_list; tmp; tmp = tmp->next) {
774 MpegTSBaseStream *stream = (MpegTSBaseStream *) tmp->data;
775 if (!_stream_in_pmt (pmt, stream))
776 toremove = g_list_prepend (toremove, stream);
778 for (tmp = toremove; tmp; tmp = tmp->next) {
779 MpegTSBaseStream *stream = (MpegTSBaseStream *) tmp->data;
780 mpegts_base_program_remove_stream (base, program, stream->pid);
781 #ifdef TIZEN_FEATURE_TSDEMUX_UPDATE_STREAM
782 MPEGTS_BIT_UNSET (base->is_pes, stream->pid);
785 g_list_free (toremove);
791 _stream_is_private_section (const GstMpegtsPMT * pmt,
792 GstMpegtsPMTStream * stream)
794 switch (stream->stream_type) {
795 case GST_MPEGTS_STREAM_TYPE_SCTE_DSMCC_DCB:
796 case GST_MPEGTS_STREAM_TYPE_SCTE_SIGNALING:
798 guint32 registration_id =
799 get_registration_from_descriptors (stream->descriptors);
800 /* Not a private section stream */
801 if (registration_id != DRF_ID_CUEI && registration_id != DRF_ID_ETV1)
804 case GST_MPEGTS_STREAM_TYPE_PRIVATE_SECTIONS:
805 case GST_MPEGTS_STREAM_TYPE_MHEG:
806 case GST_MPEGTS_STREAM_TYPE_DSM_CC:
807 case GST_MPEGTS_STREAM_TYPE_DSMCC_A:
808 case GST_MPEGTS_STREAM_TYPE_DSMCC_B:
809 case GST_MPEGTS_STREAM_TYPE_DSMCC_C:
810 case GST_MPEGTS_STREAM_TYPE_DSMCC_D:
811 case GST_MPEGTS_STREAM_TYPE_SL_FLEXMUX_SECTIONS:
812 case GST_MPEGTS_STREAM_TYPE_METADATA_SECTIONS:
813 /* known PSI streams */
815 case GST_MPEGTS_STREAM_TYPE_SCTE_SIT:
817 /* Not a private section stream */
818 if (!find_registration_in_descriptors (pmt->descriptors, DRF_ID_CUEI))
827 /* Return TRUE if programs are equal */
829 mpegts_base_is_same_program (MpegTSBase * base, MpegTSBaseProgram * oldprogram,
830 guint16 new_pmt_pid, const GstMpegtsPMT * new_pmt)
833 MpegTSBaseStream *oldstream;
834 gboolean sawpcrpid = FALSE;
836 if (oldprogram->pmt_pid != new_pmt_pid) {
837 GST_DEBUG ("Different pmt_pid (new:0x%04x, old:0x%04x)", new_pmt_pid,
838 oldprogram->pmt_pid);
842 if (!base->ignore_pcr && oldprogram->pcr_pid != new_pmt->pcr_pid) {
843 GST_DEBUG ("Different pcr_pid (new:0x%04x, old:0x%04x)",
844 new_pmt->pcr_pid, oldprogram->pcr_pid);
848 /* Check the streams */
849 nbstreams = new_pmt->streams->len;
850 for (i = 0; i < nbstreams; ++i) {
851 GstMpegtsPMTStream *stream = g_ptr_array_index (new_pmt->streams, i);
853 oldstream = oldprogram->streams[stream->pid];
855 GST_DEBUG ("New stream 0x%04x not present in old program", stream->pid);
858 if (oldstream->stream_type != stream->stream_type) {
860 ("New stream 0x%04x has a different stream type (new:%d, old:%d)",
861 stream->pid, stream->stream_type, oldstream->stream_type);
864 if (stream->pid == oldprogram->pcr_pid)
868 /* If we have a PCR PID and the pcr is not shared with an existing stream, we'll have one extra stream */
869 if (!sawpcrpid && !base->ignore_pcr)
872 if (nbstreams != g_list_length (oldprogram->stream_list)) {
873 GST_DEBUG ("Different number of streams (new:%d, old:%d)",
874 nbstreams, g_list_length (oldprogram->stream_list));
878 GST_DEBUG ("Programs are equal");
882 /* Return TRUE if program is an update
884 * A program is equal if:
885 * * The program number is the same (will be if it enters this function)
886 * * AND The PMT PID is equal to the old one
887 * * AND It contains at least one stream from the previous program
889 * Changes that are acceptable are therefore:
890 * * New streams appearing
891 * * Old streams going away
895 * * PMT PID being changed ?
896 * * Properties of elementary stream being changed ? (new tags ? metadata ?)
899 mpegts_base_is_program_update (MpegTSBase * base,
900 MpegTSBaseProgram * oldprogram, guint16 new_pmt_pid,
901 const GstMpegtsPMT * new_pmt)
904 MpegTSBaseStream *oldstream;
906 if (oldprogram->pmt_pid != new_pmt_pid) {
907 /* FIXME/CHECK: Can a program be updated by just changing its PID
909 GST_DEBUG ("Different pmt_pid (new:0x%04x, old:0x%04x)", new_pmt_pid,
910 oldprogram->pmt_pid);
914 /* Check if at least one stream from the previous program is still present
915 * in the new program */
917 /* Check the streams */
918 nbstreams = new_pmt->streams->len;
919 for (i = 0; i < nbstreams; ++i) {
920 GstMpegtsPMTStream *stream = g_ptr_array_index (new_pmt->streams, i);
922 oldstream = oldprogram->streams[stream->pid];
924 GST_DEBUG ("New stream 0x%04x not present in old program", stream->pid);
925 } else if (oldstream->stream_type != stream->stream_type) {
927 ("New stream 0x%04x has a different stream type (new:%d, old:%d)",
928 stream->pid, stream->stream_type, oldstream->stream_type);
929 } else if (!_stream_is_private_section (new_pmt, stream)) {
930 /* FIXME : We should actually be checking a bit deeper,
931 * especially for private streams (where the differentiation is
932 * done at the registration level) */
934 ("Stream 0x%04x is identical (stream_type %d) ! Program is an update",
935 stream->pid, stream->stream_type);
940 GST_DEBUG ("Program is not an update of the previous one");
945 mpegts_base_deactivate_program (MpegTSBase * base, MpegTSBaseProgram * program)
948 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
950 if (G_UNLIKELY (program->active == FALSE))
953 GST_DEBUG_OBJECT (base, "Deactivating PMT");
955 program->active = FALSE;
958 for (i = 0; i < program->pmt->streams->len; ++i) {
959 GstMpegtsPMTStream *stream = g_ptr_array_index (program->pmt->streams, i);
961 mpegts_base_program_remove_stream (base, program, stream->pid);
963 /* Only unset the is_pes/known_psi bit if the PID isn't used in any other active
965 if (!mpegts_pid_in_active_programs (base, stream->pid)) {
966 if (_stream_is_private_section (program->pmt, stream)) {
967 if (base->parse_private_sections)
968 MPEGTS_BIT_UNSET (base->known_psi, stream->pid);
970 MPEGTS_BIT_UNSET (base->is_pes, stream->pid);
975 /* remove pcr stream */
976 /* FIXME : This might actually be shared with another stream ? */
977 mpegts_base_program_remove_stream (base, program, program->pcr_pid);
978 if (!mpegts_pid_in_active_programs (base, program->pcr_pid))
979 MPEGTS_BIT_UNSET (base->is_pes, program->pcr_pid);
981 GST_DEBUG ("program stream_list is now %p", program->stream_list);
984 /* Inform subclasses we're deactivating this program */
985 if (klass->program_stopped)
986 klass->program_stopped (base, program);
990 mpegts_base_activate_program (MpegTSBase * base, MpegTSBaseProgram * program,
991 guint16 pmt_pid, GstMpegtsSection * section, const GstMpegtsPMT * pmt,
992 gboolean initial_program)
995 MpegTSBaseClass *klass;
997 if (G_UNLIKELY (program->active))
1000 GST_DEBUG ("Activating program %d", program->program_number);
1002 /* activate new pmt */
1003 if (program->section)
1004 gst_mpegts_section_unref (program->section);
1005 program->section = gst_mpegts_section_ref (section);
1008 program->pmt_pid = pmt_pid;
1009 if (!base->ignore_pcr)
1010 program->pcr_pid = pmt->pcr_pid;
1012 program->pcr_pid = 0x1fff;
1014 /* extract top-level registration_id if present */
1015 program->registration_id =
1016 get_registration_from_descriptors (pmt->descriptors);
1017 GST_DEBUG ("program 0x%04x, registration_id %" SAFE_FOURCC_FORMAT,
1018 program->program_number, SAFE_FOURCC_ARGS (program->registration_id));
1020 for (i = 0; i < pmt->streams->len; ++i) {
1021 GstMpegtsPMTStream *stream = g_ptr_array_index (pmt->streams, i);
1022 if (_stream_is_private_section (pmt, stream)) {
1023 if (base->parse_private_sections)
1024 MPEGTS_BIT_SET (base->known_psi, stream->pid);
1026 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->is_pes, stream->pid)))
1028 ("Refcounting issue. Setting twice a PID (0x%04x) as known PES",
1030 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi, stream->pid))) {
1032 ("Refcounting issue. Setting a known PSI PID (0x%04x) as known PES",
1034 MPEGTS_BIT_UNSET (base->known_psi, stream->pid);
1036 MPEGTS_BIT_SET (base->is_pes, stream->pid);
1038 mpegts_base_program_add_stream (base, program,
1039 stream->pid, stream->stream_type, stream);
1041 /* We add the PCR pid last. If that PID is already used by one of the media
1042 * streams above, no new stream will be created */
1043 mpegts_base_program_add_stream (base, program, program->pcr_pid, -1, NULL);
1044 MPEGTS_BIT_SET (base->is_pes, program->pcr_pid);
1046 program->active = TRUE;
1047 program->initial_program = initial_program;
1049 klass = GST_MPEGTS_BASE_GET_CLASS (base);
1050 if (klass->program_started != NULL)
1051 klass->program_started (base, program);
1053 GST_DEBUG_OBJECT (base, "new pmt activated");
1058 mpegts_base_apply_pat (MpegTSBase * base, GstMpegtsSection * section)
1060 GPtrArray *pat = gst_mpegts_section_get_pat (section);
1062 MpegTSBaseProgram *program;
1065 if (G_UNLIKELY (pat == NULL))
1068 GST_INFO_OBJECT (base, "PAT");
1070 /* Applying a new PAT does two things:
1071 * * It adds the new programs to the list of programs this element handles
1072 * and increments at the same time the number of times a program is referenced.
1074 * * If there was a previously active PAT, It decrements the reference count
1075 * of all program it used. If a program is no longer needed, it is removed.
1078 old_pat = base->pat;
1081 GST_LOG ("Activating new Program Association Table");
1082 /* activate the new table */
1083 for (i = 0; i < pat->len; ++i) {
1084 GstMpegtsPatProgram *patp = g_ptr_array_index (pat, i);
1086 GST_LOG ("Looking for program %d / 0x%04x", patp->program_number,
1087 patp->network_or_program_map_PID);
1088 program = mpegts_base_get_program (base, patp->program_number);
1090 GST_LOG ("Program exists on pid 0x%04x", program->pmt_pid);
1091 /* If the new PMT PID clashes with an existing known PES stream, we know
1092 * it is not an update */
1093 if (MPEGTS_BIT_IS_SET (base->is_pes, patp->network_or_program_map_PID)) {
1094 GST_LOG ("Program is not an update");
1096 mpegts_base_add_program (base, patp->program_number,
1097 patp->network_or_program_map_PID);
1098 } else if (program->pmt_pid != patp->network_or_program_map_PID) {
1099 /* IF the program already existed, just check if the PMT PID changed */
1100 GST_LOG ("PMT is on a different PID");
1101 if (program->pmt_pid != G_MAXUINT16) {
1102 /* pmt pid changed */
1103 /* FIXME: when this happens it may still be pmt pid of another
1104 * program, so setting to False may make it go through expensive
1105 * path in is_psi unnecessarily */
1106 MPEGTS_BIT_UNSET (base->known_psi, program->pmt_pid);
1109 program->pmt_pid = patp->network_or_program_map_PID;
1110 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi, program->pmt_pid)))
1112 ("Refcounting issue. Setting twice a PMT PID (0x%04x) as know PSI",
1114 MPEGTS_BIT_SET (base->known_psi, patp->network_or_program_map_PID);
1115 #ifdef TIZEN_FEATURE_TSDEMUX_UPDATE_PMT
1116 MPEGTS_BIT_UNSET (base->is_pes, patp->network_or_program_map_PID);
1119 GST_LOG ("Regular program update");
1122 /* Create a new program */
1124 mpegts_base_add_program (base, patp->program_number,
1125 patp->network_or_program_map_PID);
1127 /* We mark this program as being referenced by one PAT */
1128 program->patcount += 1;
1132 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1133 /* deactivate the old table */
1134 GST_LOG ("Deactivating old Program Association Table");
1136 for (i = 0; i < old_pat->len; ++i) {
1137 GstMpegtsPatProgram *patp = g_ptr_array_index (old_pat, i);
1139 program = mpegts_base_get_program (base, patp->program_number);
1140 if (G_UNLIKELY (program == NULL)) {
1141 GST_DEBUG_OBJECT (base, "broken PAT, duplicated entry for program %d",
1142 patp->program_number);
1146 GST_LOG ("Deactivating program %d / 0x%04x", patp->program_number,
1147 patp->network_or_program_map_PID);
1149 if (--program->patcount > 0) {
1150 GST_LOG ("Referenced by new program, keeping");
1151 /* the program has been referenced by the new pat, keep it */
1155 GST_INFO_OBJECT (base, "PAT removing program 0x%04x 0x%04x",
1156 patp->program_number, patp->network_or_program_map_PID);
1158 if (klass->can_remove_program (base, program)) {
1159 mpegts_base_deactivate_program (base, program);
1160 mpegts_base_remove_program (base, program);
1162 /* sub-class now owns the program and must call
1163 * mpegts_base_deactivate_and_free_program later */
1164 mpegts_base_steal_program (base, patp->program_number);
1166 /* FIXME: when this happens it may still be pmt pid of another
1167 * program, so setting to False may make it go through expensive
1168 * path in is_psi unnecessarily */
1169 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi,
1170 patp->network_or_program_map_PID))) {
1172 ("Program refcounting : Setting twice a pid (0x%04x) as known PSI",
1173 patp->network_or_program_map_PID);
1175 MPEGTS_BIT_SET (base->known_psi, patp->network_or_program_map_PID);
1176 mpegts_packetizer_remove_stream (base->packetizer,
1177 patp->network_or_program_map_PID);
1180 g_ptr_array_unref (old_pat);
1187 mpegts_base_apply_pmt (MpegTSBase * base, GstMpegtsSection * section)
1189 const GstMpegtsPMT *pmt;
1190 MpegTSBaseProgram *program, *old_program;
1191 guint program_number;
1192 gboolean initial_program = TRUE;
1194 pmt = gst_mpegts_section_get_pmt (section);
1195 if (G_UNLIKELY (pmt == NULL)) {
1196 GST_ERROR ("Could not get PMT (corrupted ?)");
1200 /* FIXME : not so sure this is valid anymore */
1201 if (G_UNLIKELY (base->seen_pat == FALSE)) {
1202 GST_WARNING ("Got pmt without pat first. Returning");
1203 /* remove the stream since we won't get another PMT otherwise */
1204 mpegts_packetizer_remove_stream (base->packetizer, section->pid);
1208 /* Don't attempt to handle pmt without any streams */
1209 if (G_UNLIKELY (pmt->streams->len == 0)) {
1210 GST_WARNING ("Skipping PMT without any entries");
1214 program_number = section->subtable_extension;
1215 GST_DEBUG ("Applying PMT (program_number:%d, pid:0x%04x)",
1216 program_number, section->pid);
1218 /* In order for stream switching to happen properly in decodebin(2),
1219 * we need to first add the new pads (i.e. activate the new program)
1220 * before removing the old ones (i.e. deactivating the old program)
1223 old_program = mpegts_base_get_program (base, program_number);
1224 if (G_UNLIKELY (old_program == NULL))
1227 if (G_UNLIKELY (mpegts_base_is_same_program (base, old_program, section->pid,
1231 if (base->streams_aware
1232 && mpegts_base_is_program_update (base, old_program, section->pid, pmt)) {
1233 GST_FIXME ("We are streams_aware and new program is an update");
1234 /* The program is an update, and we can add/remove pads dynamically */
1235 mpegts_base_update_program (base, old_program, section, pmt);
1239 /* If the current program is active, this means we have a new program */
1240 if (old_program->active) {
1241 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1242 old_program = mpegts_base_steal_program (base, program_number);
1243 program = mpegts_base_new_program (base, program_number, section->pid);
1244 program->patcount = old_program->patcount;
1246 /* Deactivate the old program */
1247 /* FIXME : THIS IS BREAKING THE STREAM SWITCHING LOGIC !
1249 if (klass->can_remove_program (base, old_program)) {
1250 mpegts_base_deactivate_program (base, old_program);
1251 mpegts_base_free_program (old_program);
1253 /* sub-class now owns the program and must call
1254 * mpegts_base_deactivate_and_free_program later */
1255 mpegts_base_steal_program (base, old_program->program_number);
1257 /* Add new program to the programs we track */
1258 g_ptr_array_add (base->programs, program);
1259 initial_program = FALSE;
1261 GST_DEBUG ("Program update, re-using same program");
1262 program = old_program;
1265 /* activate program */
1266 /* Ownership of pmt_info is given to the program */
1267 mpegts_base_activate_program (base, program, section->pid, section, pmt,
1271 GST_DEBUG ("Done activating program");
1276 GST_ERROR ("Attempted to apply a PMT on a program that wasn't created");
1282 GST_DEBUG ("Not applying identical program");
1288 mpegts_base_handle_psi (MpegTSBase * base, GstMpegtsSection * section)
1290 gboolean post_message = TRUE;
1292 GST_DEBUG ("Handling PSI (pid: 0x%04x , table_id: 0x%02x)",
1293 section->pid, section->table_id);
1295 switch (section->section_type) {
1296 case GST_MPEGTS_SECTION_PAT:
1297 post_message = mpegts_base_apply_pat (base, section);
1298 if (base->seen_pat == FALSE) {
1299 base->seen_pat = TRUE;
1300 GST_DEBUG ("First PAT offset: %" G_GUINT64_FORMAT, section->offset);
1301 mpegts_packetizer_set_reference_offset (base->packetizer,
1305 case GST_MPEGTS_SECTION_PMT:
1306 post_message = mpegts_base_apply_pmt (base, section);
1308 case GST_MPEGTS_SECTION_EIT:
1309 /* some tag xtraction + posting */
1310 post_message = mpegts_base_get_tags_from_eit (base, section);
1312 case GST_MPEGTS_SECTION_ATSC_MGT:
1313 post_message = mpegts_base_parse_atsc_mgt (base, section);
1319 /* Give the subclass a chance to look at the section */
1320 if (GST_MPEGTS_BASE_GET_CLASS (base)->handle_psi)
1321 GST_MPEGTS_BASE_GET_CLASS (base)->handle_psi (base, section);
1323 /* Finally post message (if it wasn't corrupted) */
1325 gst_element_post_message (GST_ELEMENT_CAST (base),
1326 gst_message_new_mpegts_section (GST_OBJECT (base), section));
1327 gst_mpegts_section_unref (section);
1331 mpegts_base_parse_atsc_mgt (MpegTSBase * base, GstMpegtsSection * section)
1333 const GstMpegtsAtscMGT *mgt;
1336 mgt = gst_mpegts_section_get_atsc_mgt (section);
1337 if (G_UNLIKELY (mgt == NULL))
1340 for (i = 0; i < mgt->tables->len; ++i) {
1341 GstMpegtsAtscMGTTable *table = g_ptr_array_index (mgt->tables, i);
1343 if ((table->table_type >= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_EIT0 &&
1344 table->table_type <= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_EIT127) ||
1345 (table->table_type >= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_ETT0 &&
1346 table->table_type <= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_ETT127)) {
1347 MPEGTS_BIT_SET (base->known_psi, table->pid);
1355 mpegts_base_get_tags_from_eit (MpegTSBase * base, GstMpegtsSection * section)
1357 const GstMpegtsEIT *eit;
1359 MpegTSBaseProgram *program;
1361 /* Early exit if it's not from the present/following table_id */
1362 if (section->table_id != GST_MTS_TABLE_ID_EVENT_INFORMATION_ACTUAL_TS_PRESENT
1363 && section->table_id !=
1364 GST_MTS_TABLE_ID_EVENT_INFORMATION_OTHER_TS_PRESENT)
1367 eit = gst_mpegts_section_get_eit (section);
1368 if (G_UNLIKELY (eit == NULL))
1371 program = mpegts_base_get_program (base, section->subtable_extension);
1374 ("program_id:0x%04x, table_id:0x%02x, actual_stream:%d, present_following:%d, program:%p",
1375 section->subtable_extension, section->table_id, eit->actual_stream,
1376 eit->present_following, program);
1378 if (program && eit->present_following) {
1379 for (i = 0; i < eit->events->len; i++) {
1380 GstMpegtsEITEvent *event = g_ptr_array_index (eit->events, i);
1381 const GstMpegtsDescriptor *desc;
1383 if (event->running_status == RUNNING_STATUS_RUNNING) {
1384 program->event_id = event->event_id;
1386 gst_mpegts_find_descriptor (event->descriptors,
1387 GST_MTS_DESC_DVB_SHORT_EVENT))) {
1388 gchar *name = NULL, *text = NULL;
1390 if (gst_mpegts_descriptor_parse_dvb_short_event (desc, NULL, &name,
1393 program->tags = gst_tag_list_new_empty ();
1396 gst_tag_list_add (program->tags, GST_TAG_MERGE_APPEND,
1397 GST_TAG_TITLE, name, NULL);
1401 gst_tag_list_add (program->tags, GST_TAG_MERGE_APPEND,
1402 GST_TAG_DESCRIPTION, text, NULL);
1405 /* FIXME : Is it correct to post an event duration as a GST_TAG_DURATION ??? */
1406 gst_tag_list_add (program->tags, GST_TAG_MERGE_APPEND,
1407 GST_TAG_DURATION, event->duration * GST_SECOND, NULL);
1419 remove_each_program (MpegTSBaseProgram * program, MpegTSBase * base)
1421 /* First deactivate it */
1422 mpegts_base_deactivate_program (base, program);
1425 static inline GstFlowReturn
1426 mpegts_base_drain (MpegTSBase * base)
1428 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1430 /* Call implementation */
1432 return klass->drain (base);
1438 mpegts_base_flush (MpegTSBase * base, gboolean hard)
1440 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1442 /* Call implementation */
1444 klass->flush (base, hard);
1448 mpegts_base_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
1450 gboolean res = TRUE;
1452 MpegTSBase *base = GST_MPEGTS_BASE (parent);
1453 gboolean is_sticky = GST_EVENT_IS_STICKY (event);
1454 #ifdef TIZEN_FEATURE_TSDEMUX_MODIFICATION
1455 GstStructure *structure = NULL;
1456 GstCaps *caps = NULL;
1457 gboolean caps_ret = FALSE;
1460 GST_DEBUG_OBJECT (base, "Got event %s",
1461 gst_event_type_get_name (GST_EVENT_TYPE (event)));
1463 switch (GST_EVENT_TYPE (event)) {
1464 case GST_EVENT_SEGMENT:
1465 gst_event_copy_segment (event, &base->segment);
1466 GST_DEBUG_OBJECT (base, "Received segment %" GST_SEGMENT_FORMAT,
1468 /* Check if we need to switch PCR/PTS handling */
1469 if (base->segment.format == GST_FORMAT_TIME) {
1470 base->packetizer->calculate_offset = FALSE;
1471 base->packetizer->calculate_skew = TRUE;
1472 /* Seek was handled upstream */
1473 base->last_seek_seqnum = gst_event_get_seqnum (event);
1475 base->packetizer->calculate_offset = TRUE;
1476 base->packetizer->calculate_skew = FALSE;
1479 res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1481 case GST_EVENT_STREAM_START:
1482 gst_event_unref (event);
1484 case GST_EVENT_CAPS:
1485 /* FIXME, do something */
1486 #ifdef TIZEN_FEATURE_TSDEMUX_MODIFICATION
1487 if (base->packetizer) {
1488 gst_event_parse_caps (event, &caps);
1489 GST_DEBUG_OBJECT (base, "got caps: %" GST_PTR_FORMAT, caps);
1491 structure = gst_caps_get_structure (caps, 0);
1492 caps_ret = gst_structure_get_boolean (structure, "is_live", &base->packetizer->is_live_stream);
1493 if (caps_ret == FALSE)
1494 base->packetizer->is_live_stream = TRUE;
1496 GST_DEBUG_OBJECT (base, "base->packetizer pointer is NULL !!!");
1499 gst_event_unref (event);
1501 case GST_EVENT_FLUSH_STOP:
1502 res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1503 hard = (base->mode != BASE_MODE_SEEKING);
1504 mpegts_packetizer_flush (base->packetizer, hard);
1505 mpegts_base_flush (base, hard);
1506 gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED);
1507 base->seen_pat = FALSE;
1510 res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1513 /* Always return TRUE for sticky events */
1521 mpegts_base_default_sink_query (MpegTSBase * base, GstQuery * query)
1523 return gst_pad_query_default (base->sinkpad, GST_OBJECT (base), query);
1527 mpegts_base_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
1529 MpegTSBase *base = GST_MPEGTS_BASE (parent);
1531 GST_DEBUG_OBJECT (base, "Got query %s",
1532 gst_query_type_get_name (GST_QUERY_TYPE (query)));
1534 return GST_MPEGTS_BASE_GET_CLASS (base)->sink_query (base, query);
1537 static GstFlowReturn
1538 mpegts_base_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
1540 GstFlowReturn res = GST_FLOW_OK;
1542 MpegTSPacketizerPacketReturn pret;
1543 MpegTSPacketizer2 *packetizer;
1544 MpegTSPacketizerPacket packet;
1545 MpegTSBaseClass *klass;
1547 base = GST_MPEGTS_BASE (parent);
1548 klass = GST_MPEGTS_BASE_GET_CLASS (base);
1550 packetizer = base->packetizer;
1552 if (GST_BUFFER_IS_DISCONT (buf)) {
1553 GST_DEBUG_OBJECT (base, "Got DISCONT buffer, flushing");
1554 res = mpegts_base_drain (base);
1555 if (G_UNLIKELY (res != GST_FLOW_OK))
1558 mpegts_base_flush (base, FALSE);
1559 if (base->mode == BASE_MODE_PUSHING) {
1560 if (base->segment.format == GST_FORMAT_TIME) {
1561 /* In the case of discontinuities in push-mode with TIME segment
1562 * we want to drop all previous observations (hard:TRUE) from
1564 mpegts_packetizer_flush (base->packetizer, TRUE);
1566 /* In all cases, we clear observations when we get a discontinuity in
1567 * push-mode to re-check if the sections (PAT/PMT) changed or not */
1568 mpegts_packetizer_clear (base->packetizer);
1570 mpegts_packetizer_flush (base->packetizer, FALSE);
1574 mpegts_packetizer_push (base->packetizer, buf);
1576 while (res == GST_FLOW_OK) {
1577 pret = mpegts_packetizer_next_packet (base->packetizer, &packet);
1579 /* If we don't have enough data, return */
1580 if (G_UNLIKELY (pret == PACKET_NEED_MORE))
1583 if (G_UNLIKELY (pret == PACKET_BAD)) {
1584 /* bad header, skip the packet */
1585 GST_DEBUG_OBJECT (base, "bad packet, skipping");
1589 if (klass->inspect_packet)
1590 klass->inspect_packet (base, &packet);
1592 /* If it's a known PES, push it */
1593 if (MPEGTS_BIT_IS_SET (base->is_pes, packet.pid)) {
1594 /* push the packet downstream */
1595 if (base->push_data)
1596 res = klass->push (base, &packet, NULL);
1597 } else if (packet.payload
1598 && MPEGTS_BIT_IS_SET (base->known_psi, packet.pid)) {
1600 GList *others, *tmp;
1601 GstMpegtsSection *section;
1603 section = mpegts_packetizer_push_section (packetizer, &packet, &others);
1605 mpegts_base_handle_psi (base, section);
1606 if (G_UNLIKELY (others)) {
1607 for (tmp = others; tmp; tmp = tmp->next)
1608 mpegts_base_handle_psi (base, (GstMpegtsSection *) tmp->data);
1609 g_list_free (others);
1612 /* we need to push section packet downstream */
1613 if (base->push_section)
1614 res = klass->push (base, &packet, section);
1616 } else if (base->push_unknown) {
1617 res = klass->push (base, &packet, NULL);
1618 } else if (packet.payload && packet.pid != 0x1fff)
1619 GST_LOG ("PID 0x%04x Saw packet on a pid we don't handle", packet.pid);
1622 mpegts_packetizer_clear_packet (base->packetizer, &packet);
1625 if (res == GST_FLOW_OK && klass->input_done)
1626 res = klass->input_done (base);
1631 static GstFlowReturn
1632 mpegts_base_scan (MpegTSBase * base)
1634 GstFlowReturn ret = GST_FLOW_OK;
1635 GstBuffer *buf = NULL;
1637 gboolean done = FALSE;
1638 MpegTSPacketizerPacketReturn pret;
1640 gint64 upstream_size, seek_pos, reverse_limit;
1642 guint initial_pcr_seen;
1644 GST_DEBUG ("Scanning for initial sync point");
1646 /* Find initial sync point and at least 5 PCR values */
1647 for (i = 0; i < 20 && !done; i++) {
1648 GST_DEBUG ("Grabbing %d => %d", i * 65536, (i + 1) * 65536);
1650 ret = gst_pad_pull_range (base->sinkpad, i * 65536, 65536, &buf);
1651 if (G_UNLIKELY (ret == GST_FLOW_EOS))
1653 if (G_UNLIKELY (ret != GST_FLOW_OK))
1656 /* Push to packetizer */
1657 mpegts_packetizer_push (base->packetizer, buf);
1660 if (mpegts_packetizer_has_packets (base->packetizer)) {
1661 if (base->seek_offset == -1) {
1662 /* Mark the initial sync point and remember the packetsize */
1663 base->seek_offset = base->packetizer->offset;
1664 GST_DEBUG ("Sync point is now %" G_GUINT64_FORMAT, base->seek_offset);
1665 base->packetsize = base->packetizer->packet_size;
1668 /* Eat up all packets */
1669 pret = mpegts_packetizer_process_next_packet (base->packetizer);
1670 if (pret == PACKET_NEED_MORE)
1672 if (pret != PACKET_BAD && base->packetizer->nb_seen_offsets >= 5) {
1673 GST_DEBUG ("Got enough initial PCR");
1681 initial_pcr_seen = base->packetizer->nb_seen_offsets;
1682 if (G_UNLIKELY (initial_pcr_seen == 0))
1683 goto no_initial_pcr;
1684 GST_DEBUG ("Seen %d initial PCR", initial_pcr_seen);
1686 /* Now send data from the end */
1688 /* Get the size of upstream */
1689 format = GST_FORMAT_BYTES;
1690 if (!gst_pad_peer_query_duration (base->sinkpad, format, &tmpval))
1692 upstream_size = tmpval;
1694 /* The scanning takes place on the last 2048kB. Considering PCR should
1695 * be present at least every 100ms, this should cope with streams
1696 * up to 160Mbit/s */
1697 reverse_limit = MAX (0, upstream_size - 2097152);
1699 /* Find last PCR value, searching backwards by chunks of 300 MPEG-ts packets */
1700 for (seek_pos = MAX (0, upstream_size - 56400);
1701 seek_pos >= reverse_limit; seek_pos -= 56400) {
1702 mpegts_packetizer_clear (base->packetizer);
1703 GST_DEBUG ("Grabbing %" G_GUINT64_FORMAT " => %" G_GUINT64_FORMAT, seek_pos,
1706 ret = gst_pad_pull_range (base->sinkpad, seek_pos, 56400, &buf);
1707 if (G_UNLIKELY (ret == GST_FLOW_EOS))
1709 if (G_UNLIKELY (ret != GST_FLOW_OK))
1712 /* Push to packetizer */
1713 mpegts_packetizer_push (base->packetizer, buf);
1716 if (mpegts_packetizer_has_packets (base->packetizer)) {
1718 /* Eat up all packets, really try to get last PCR(s) */
1719 while (pret != PACKET_NEED_MORE)
1720 pret = mpegts_packetizer_process_next_packet (base->packetizer);
1722 if (base->packetizer->nb_seen_offsets > initial_pcr_seen) {
1723 GST_DEBUG ("Got last PCR(s) (total seen:%d)",
1724 base->packetizer->nb_seen_offsets);
1731 mpegts_packetizer_clear (base->packetizer);
1735 mpegts_packetizer_clear (base->packetizer);
1736 GST_WARNING_OBJECT (base, "Couldn't find any PCR within the first %d bytes",
1743 mpegts_base_loop (MpegTSBase * base)
1745 GstFlowReturn ret = GST_FLOW_ERROR;
1747 switch (base->mode) {
1748 case BASE_MODE_SCANNING:
1749 /* Find first sync point */
1750 ret = mpegts_base_scan (base);
1751 if (G_UNLIKELY (ret != GST_FLOW_OK))
1753 base->mode = BASE_MODE_STREAMING;
1754 GST_DEBUG ("Changing to Streaming");
1756 case BASE_MODE_SEEKING:
1757 /* FIXME : unclear if we still need mode_seeking... */
1758 base->mode = BASE_MODE_STREAMING;
1760 case BASE_MODE_STREAMING:
1762 GstBuffer *buf = NULL;
1764 GST_DEBUG ("Pulling data from %" G_GUINT64_FORMAT, base->seek_offset);
1766 if (G_UNLIKELY (base->last_seek_seqnum == GST_SEQNUM_INVALID)) {
1767 /* No configured seek, set a valid seqnum */
1768 base->last_seek_seqnum = gst_util_seqnum_next ();
1770 ret = gst_pad_pull_range (base->sinkpad, base->seek_offset,
1771 100 * base->packetsize, &buf);
1772 if (G_UNLIKELY (ret != GST_FLOW_OK))
1774 base->seek_offset += gst_buffer_get_size (buf);
1775 ret = mpegts_base_chain (base->sinkpad, GST_OBJECT_CAST (base), buf);
1776 if (G_UNLIKELY (ret != GST_FLOW_OK))
1780 case BASE_MODE_PUSHING:
1781 GST_WARNING ("wrong BASE_MODE_PUSHING mode in pull loop");
1789 GST_DEBUG_OBJECT (base, "Pausing task, reason %s", gst_flow_get_name (ret));
1790 if (ret == GST_FLOW_EOS) {
1791 if (!GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base,
1792 gst_event_new_eos ()))
1793 GST_ELEMENT_ERROR (base, STREAM, FAILED,
1794 (_("Internal data stream error.")),
1795 ("No program activated before EOS"));
1796 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
1797 GST_ELEMENT_FLOW_ERROR (base, ret);
1798 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, gst_event_new_eos ());
1800 gst_pad_pause_task (base->sinkpad);
1806 mpegts_base_handle_seek_event (MpegTSBase * base, GstPad * pad,
1809 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1810 GstFlowReturn ret = GST_FLOW_ERROR;
1812 gboolean flush, instant_rate_change;
1815 GstSeekType start_type, stop_type;
1817 GstEvent *flush_event = NULL;
1819 gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
1822 if (format != GST_FORMAT_TIME)
1825 if (GST_EVENT_SEQNUM (event) == base->last_seek_seqnum) {
1826 GST_DEBUG_OBJECT (base, "Skipping already handled seek");
1830 if (base->mode == BASE_MODE_PUSHING) {
1831 /* First try if upstream supports seeking in TIME format */
1832 if (gst_pad_push_event (base->sinkpad, gst_event_ref (event))) {
1833 GST_DEBUG ("upstream handled SEEK event");
1837 /* If the subclass can seek, do that */
1839 ret = klass->seek (base, event);
1840 if (G_UNLIKELY (ret != GST_FLOW_OK))
1841 GST_WARNING ("seeking failed %s", gst_flow_get_name (ret));
1845 if (GST_CLOCK_TIME_IS_VALID (base->seek_offset)) {
1846 base->mode = BASE_MODE_SEEKING;
1847 new_seek = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
1848 GST_SEEK_TYPE_SET, base->seek_offset, GST_SEEK_TYPE_NONE, -1);
1849 gst_event_set_seqnum (new_seek, GST_EVENT_SEQNUM (event));
1850 if (!gst_pad_push_event (base->sinkpad, new_seek))
1851 ret = GST_FLOW_ERROR;
1853 base->last_seek_seqnum = GST_EVENT_SEQNUM (event);
1855 base->mode = BASE_MODE_PUSHING;
1858 GST_WARNING ("subclass has no seek implementation");
1861 return ret == GST_FLOW_OK;
1865 GST_WARNING ("subclass has no seek implementation");
1870 GST_WARNING ("Negative rate not supported");
1874 GST_DEBUG ("seek event, rate: %f start: %" GST_TIME_FORMAT
1875 " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start),
1876 GST_TIME_ARGS (stop));
1878 flush = ! !(flags & GST_SEEK_FLAG_FLUSH);
1879 instant_rate_change = ! !(flags & GST_SEEK_FLAG_INSTANT_RATE_CHANGE);
1881 /* Directly send the instant-rate-change event here before taking the
1882 * stream-lock so that it can be applied as soon as possible */
1883 if (base->mode != BASE_MODE_PUSHING && instant_rate_change) {
1886 /* instant rate change only supported if direction does not change. All
1887 * other requirements are already checked before creating the seek event
1888 * but let's double-check here to be sure */
1889 if ((rate > 0 && base->out_segment.rate < 0) ||
1890 (rate < 0 && base->out_segment.rate > 0) ||
1891 start_type != GST_SEEK_TYPE_NONE ||
1892 stop_type != GST_SEEK_TYPE_NONE || flush) {
1893 GST_ERROR_OBJECT (base,
1894 "Instant rate change seeks only supported in the "
1895 "same direction, without flushing and position change");
1899 ev = gst_event_new_instant_rate_change (rate / base->out_segment.rate,
1900 (GstSegmentFlags) (flags));
1901 gst_event_set_seqnum (ev, GST_EVENT_SEQNUM (event));
1902 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, ev);
1906 /* stop streaming, either by flushing or by pausing the task */
1907 base->mode = BASE_MODE_SEEKING;
1909 GST_DEBUG_OBJECT (base, "sending flush start");
1910 flush_event = gst_event_new_flush_start ();
1911 gst_event_set_seqnum (flush_event, GST_EVENT_SEQNUM (event));
1912 gst_pad_push_event (base->sinkpad, gst_event_ref (flush_event));
1913 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, flush_event);
1915 gst_pad_pause_task (base->sinkpad);
1917 /* wait for streaming to finish */
1918 GST_PAD_STREAM_LOCK (base->sinkpad);
1921 /* send a FLUSH_STOP for the sinkpad, since we need data for seeking */
1922 GST_DEBUG_OBJECT (base, "sending flush stop");
1923 flush_event = gst_event_new_flush_stop (TRUE);
1924 gst_event_set_seqnum (flush_event, GST_EVENT_SEQNUM (event));
1926 /* ref for it to be reused later */
1927 gst_pad_push_event (base->sinkpad, gst_event_ref (flush_event));
1928 /* And actually flush our pending data but allow to preserve some info
1929 * to perform the seek */
1930 mpegts_base_flush (base, FALSE);
1931 mpegts_packetizer_flush (base->packetizer, FALSE);
1934 if (flags & (GST_SEEK_FLAG_SEGMENT)) {
1935 GST_WARNING ("seek flags 0x%x are not supported", (int) flags);
1940 /* If the subclass can seek, do that */
1941 ret = klass->seek (base, event);
1942 if (G_UNLIKELY (ret != GST_FLOW_OK))
1943 GST_WARNING ("seeking failed %s", gst_flow_get_name (ret));
1945 base->last_seek_seqnum = GST_EVENT_SEQNUM (event);
1948 /* if we sent a FLUSH_START, we now send a FLUSH_STOP */
1949 GST_DEBUG_OBJECT (base, "sending flush stop");
1950 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, flush_event);
1955 gst_event_unref (flush_event);
1956 gst_pad_start_task (base->sinkpad, (GstTaskFunction) mpegts_base_loop, base,
1959 GST_PAD_STREAM_UNLOCK (base->sinkpad);
1960 return ret == GST_FLOW_OK;
1965 mpegts_base_sink_activate (GstPad * sinkpad, GstObject * parent)
1970 query = gst_query_new_scheduling ();
1972 if (!gst_pad_peer_query (sinkpad, query)) {
1973 gst_query_unref (query);
1977 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
1978 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
1979 gst_query_unref (query);
1984 GST_DEBUG_OBJECT (sinkpad, "activating pull");
1985 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
1989 GST_DEBUG_OBJECT (sinkpad, "activating push");
1990 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
1995 mpegts_base_sink_activate_mode (GstPad * pad, GstObject * parent,
1996 GstPadMode mode, gboolean active)
1999 MpegTSBase *base = GST_MPEGTS_BASE (parent);
2002 case GST_PAD_MODE_PUSH:
2003 base->mode = BASE_MODE_PUSHING;
2006 case GST_PAD_MODE_PULL:
2008 base->mode = BASE_MODE_SCANNING;
2009 /* When working pull-based, we always use offsets for estimation */
2010 base->packetizer->calculate_offset = TRUE;
2011 base->packetizer->calculate_skew = FALSE;
2012 gst_segment_init (&base->segment, GST_FORMAT_BYTES);
2014 gst_pad_start_task (pad, (GstTaskFunction) mpegts_base_loop, base,
2017 res = gst_pad_stop_task (pad);
2026 static GstStateChangeReturn
2027 mpegts_base_change_state (GstElement * element, GstStateChange transition)
2030 GstStateChangeReturn ret;
2032 base = GST_MPEGTS_BASE (element);
2034 switch (transition) {
2035 case GST_STATE_CHANGE_READY_TO_PAUSED:
2036 mpegts_base_reset (base);
2042 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2044 switch (transition) {
2045 case GST_STATE_CHANGE_PAUSED_TO_READY:
2046 mpegts_base_reset (base);
2047 if (base->mode != BASE_MODE_PUSHING)
2048 base->mode = BASE_MODE_SCANNING;
2058 gst_mpegtsbase_plugin_init (GstPlugin * plugin)
2060 GST_DEBUG_CATEGORY_INIT (mpegts_base_debug, "mpegtsbase", 0,
2061 "MPEG transport stream base class");