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 <gst/gst-i18n-plugin.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 ")
65 PROP_PARSE_PRIVATE_SECTIONS,
69 static void mpegts_base_dispose (GObject * object);
70 static void mpegts_base_finalize (GObject * object);
71 static void mpegts_base_set_property (GObject * object, guint prop_id,
72 const GValue * value, GParamSpec * pspec);
73 static void mpegts_base_get_property (GObject * object, guint prop_id,
74 GValue * value, GParamSpec * pspec);
76 static void mpegts_base_free_program (MpegTSBaseProgram * program);
77 static void mpegts_base_deactivate_program (MpegTSBase * base,
78 MpegTSBaseProgram * program);
79 static gboolean mpegts_base_sink_activate (GstPad * pad, GstObject * parent);
80 static gboolean mpegts_base_sink_activate_mode (GstPad * pad,
81 GstObject * parent, GstPadMode mode, gboolean active);
82 static GstFlowReturn mpegts_base_chain (GstPad * pad, GstObject * parent,
84 static gboolean mpegts_base_sink_event (GstPad * pad, GstObject * parent,
86 static gboolean mpegts_base_sink_query (GstPad * pad, GstObject * parent,
88 static gboolean mpegts_base_default_sink_query (MpegTSBase * base,
90 static GstStateChangeReturn mpegts_base_change_state (GstElement * element,
91 GstStateChange transition);
92 static gboolean mpegts_base_get_tags_from_eit (MpegTSBase * base,
93 GstMpegtsSection * section);
94 static gboolean mpegts_base_parse_atsc_mgt (MpegTSBase * base,
95 GstMpegtsSection * section);
96 static gboolean remove_each_program (gpointer key, MpegTSBaseProgram * program,
102 QUARK_PROGRAMS = g_quark_from_string ("programs");
103 QUARK_PROGRAM_NUMBER = g_quark_from_string ("program-number");
104 QUARK_PID = g_quark_from_string ("pid");
105 QUARK_PCR_PID = g_quark_from_string ("pcr-pid");
106 QUARK_STREAMS = g_quark_from_string ("streams");
107 QUARK_STREAM_TYPE = g_quark_from_string ("stream-type");
110 #define mpegts_base_parent_class parent_class
111 G_DEFINE_TYPE_WITH_CODE (MpegTSBase, mpegts_base, GST_TYPE_ELEMENT,
114 /* Default implementation is that mpegtsbase can remove any program */
116 mpegts_base_can_remove_program (MpegTSBase * base, MpegTSBaseProgram * program)
122 mpegts_base_class_init (MpegTSBaseClass * klass)
124 GObjectClass *gobject_class;
125 GstElementClass *element_class;
127 klass->can_remove_program = mpegts_base_can_remove_program;
129 element_class = GST_ELEMENT_CLASS (klass);
130 element_class->change_state = mpegts_base_change_state;
132 gst_element_class_add_static_pad_template (element_class, &sink_template);
134 gobject_class = G_OBJECT_CLASS (klass);
135 gobject_class->dispose = mpegts_base_dispose;
136 gobject_class->finalize = mpegts_base_finalize;
137 gobject_class->set_property = mpegts_base_set_property;
138 gobject_class->get_property = mpegts_base_get_property;
140 g_object_class_install_property (gobject_class, PROP_PARSE_PRIVATE_SECTIONS,
141 g_param_spec_boolean ("parse-private-sections", "Parse private sections",
142 "Parse private sections", FALSE,
143 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
145 klass->sink_query = GST_DEBUG_FUNCPTR (mpegts_base_default_sink_query);
149 mpegts_base_set_property (GObject * object, guint prop_id,
150 const GValue * value, GParamSpec * pspec)
152 MpegTSBase *base = GST_MPEGTS_BASE (object);
155 case PROP_PARSE_PRIVATE_SECTIONS:
156 base->parse_private_sections = g_value_get_boolean (value);
159 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
164 mpegts_base_get_property (GObject * object, guint prop_id,
165 GValue * value, GParamSpec * pspec)
167 MpegTSBase *base = GST_MPEGTS_BASE (object);
170 case PROP_PARSE_PRIVATE_SECTIONS:
171 g_value_set_boolean (value, base->parse_private_sections);
174 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
180 mpegts_base_reset (MpegTSBase * base)
182 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
184 mpegts_packetizer_clear (base->packetizer);
185 memset (base->is_pes, 0, 1024);
186 memset (base->known_psi, 0, 1024);
188 /* FIXME : Actually these are not *always* know SI streams
189 * depending on the variant of mpeg-ts being used. */
191 /* Known PIDs : PAT, TSDT, IPMP CIT */
192 MPEGTS_BIT_SET (base->known_psi, 0);
193 MPEGTS_BIT_SET (base->known_psi, 2);
194 MPEGTS_BIT_SET (base->known_psi, 3);
196 MPEGTS_BIT_SET (base->known_psi, 0x14);
197 /* network synchronization */
198 MPEGTS_BIT_SET (base->known_psi, 0x15);
201 MPEGTS_BIT_SET (base->known_psi, 0x1ffb);
204 g_ptr_array_unref (base->pat);
208 gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED);
209 gst_segment_init (&base->out_segment, GST_FORMAT_UNDEFINED);
210 base->last_seek_seqnum = GST_SEQNUM_INVALID;
212 base->mode = BASE_MODE_STREAMING;
213 base->seen_pat = FALSE;
214 base->seek_offset = -1;
216 g_hash_table_foreach_remove (base->programs, (GHRFunc) remove_each_program,
219 base->streams_aware = GST_OBJECT_PARENT (base)
220 && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (base),
221 GST_BIN_FLAG_STREAMS_AWARE);
222 GST_DEBUG_OBJECT (base, "Streams aware : %d", base->streams_aware);
229 mpegts_base_init (MpegTSBase * base)
231 base->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
232 gst_pad_set_activate_function (base->sinkpad, mpegts_base_sink_activate);
233 gst_pad_set_activatemode_function (base->sinkpad,
234 mpegts_base_sink_activate_mode);
235 gst_pad_set_chain_function (base->sinkpad, mpegts_base_chain);
236 gst_pad_set_event_function (base->sinkpad, mpegts_base_sink_event);
237 gst_pad_set_query_function (base->sinkpad, mpegts_base_sink_query);
238 gst_element_add_pad (GST_ELEMENT (base), base->sinkpad);
240 base->disposed = FALSE;
241 base->packetizer = mpegts_packetizer_new ();
242 base->programs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
243 NULL, (GDestroyNotify) mpegts_base_free_program);
245 base->parse_private_sections = FALSE;
246 base->is_pes = g_new0 (guint8, 1024);
247 base->known_psi = g_new0 (guint8, 1024);
248 base->program_size = sizeof (MpegTSBaseProgram);
249 base->stream_size = sizeof (MpegTSBaseStream);
251 base->push_data = TRUE;
252 base->push_section = TRUE;
254 mpegts_base_reset (base);
258 mpegts_base_dispose (GObject * object)
260 MpegTSBase *base = GST_MPEGTS_BASE (object);
262 if (!base->disposed) {
263 g_object_unref (base->packetizer);
264 base->disposed = TRUE;
265 g_free (base->known_psi);
266 g_free (base->is_pes);
269 if (G_OBJECT_CLASS (parent_class)->dispose)
270 G_OBJECT_CLASS (parent_class)->dispose (object);
274 mpegts_base_finalize (GObject * object)
276 MpegTSBase *base = GST_MPEGTS_BASE (object);
279 g_ptr_array_unref (base->pat);
282 g_hash_table_destroy (base->programs);
284 if (G_OBJECT_CLASS (parent_class)->finalize)
285 G_OBJECT_CLASS (parent_class)->finalize (object);
289 /* returns NULL if no matching descriptor found *
290 * otherwise returns a descriptor that needs to *
292 const GstMpegtsDescriptor *
293 mpegts_get_descriptor_from_stream (MpegTSBaseStream * stream, guint8 tag)
295 GstMpegtsPMTStream *pmt = stream->stream;
297 GST_DEBUG ("Searching for tag 0x%02x in stream 0x%04x (stream_type 0x%02x)",
298 tag, stream->pid, stream->stream_type);
300 return gst_mpegts_find_descriptor (pmt->descriptors, tag);
310 foreach_pid_in_program (gpointer key, MpegTSBaseProgram * program,
313 if (!program->active)
315 if (program->streams[lookup->pid])
320 mpegts_pid_in_active_programs (MpegTSBase * base, guint16 pid)
326 g_hash_table_foreach (base->programs, (GHFunc) foreach_pid_in_program,
332 /* returns NULL if no matching descriptor found *
333 * otherwise returns a descriptor that needs to *
335 const GstMpegtsDescriptor *
336 mpegts_get_descriptor_from_program (MpegTSBaseProgram * program, guint8 tag)
338 const GstMpegtsPMT *pmt = program->pmt;
340 return gst_mpegts_find_descriptor (pmt->descriptors, tag);
344 _get_upstream_id (GstElement * element, GstPad * sinkpad)
346 gchar *upstream_id = gst_pad_get_stream_id (sinkpad);
349 /* Try to create one from the upstream URI, else use a randome number */
353 /* Try to generate one from the URI query and
354 * if it fails take a random number instead */
355 query = gst_query_new_uri ();
356 if (gst_element_query (element, query)) {
357 gst_query_parse_uri (query, &uri);
363 /* And then generate an SHA256 sum of the URI */
364 cs = g_checksum_new (G_CHECKSUM_SHA256);
365 g_checksum_update (cs, (const guchar *) uri, strlen (uri));
367 upstream_id = g_strdup (g_checksum_get_string (cs));
368 g_checksum_free (cs);
370 /* Just get some random number if the URI query fails */
371 GST_FIXME_OBJECT (element, "Creating random stream-id, consider "
372 "implementing a deterministic way of creating a stream-id");
374 g_strdup_printf ("%08x%08x%08x%08x", g_random_int (), g_random_int (),
375 g_random_int (), g_random_int ());
378 gst_query_unref (query);
383 static MpegTSBaseProgram *
384 mpegts_base_new_program (MpegTSBase * base,
385 gint program_number, guint16 pmt_pid)
387 MpegTSBaseProgram *program;
388 gchar *upstream_id, *stream_id;
390 GST_DEBUG_OBJECT (base, "program_number : %d, pmt_pid : %d",
391 program_number, pmt_pid);
393 program = g_malloc0 (base->program_size);
394 program->program_number = program_number;
395 program->pmt_pid = pmt_pid;
396 program->pcr_pid = G_MAXUINT16;
397 program->streams = g_new0 (MpegTSBaseStream *, 0x2000);
398 program->patcount = 0;
400 upstream_id = _get_upstream_id ((GstElement *) base, base->sinkpad);
401 stream_id = g_strdup_printf ("%s:%d", upstream_id, program_number);
402 program->collection = gst_stream_collection_new (stream_id);
404 g_free (upstream_id);
410 mpegts_base_add_program (MpegTSBase * base,
411 gint program_number, guint16 pmt_pid)
413 MpegTSBaseProgram *program;
415 GST_DEBUG_OBJECT (base, "program_number : %d, pmt_pid : %d",
416 program_number, pmt_pid);
418 program = mpegts_base_new_program (base, program_number, pmt_pid);
420 /* Mark the PMT PID as being a known PSI PID */
421 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi, pmt_pid))) {
422 GST_FIXME ("Refcounting. Setting twice a PID (0x%04x) as known PSI",
425 MPEGTS_BIT_SET (base->known_psi, pmt_pid);
427 g_hash_table_insert (base->programs,
428 GINT_TO_POINTER (program_number), program);
434 mpegts_base_get_program (MpegTSBase * base, gint program_number)
436 MpegTSBaseProgram *program;
438 program = (MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
439 GINT_TO_POINTER ((gint) program_number));
444 static MpegTSBaseProgram *
445 mpegts_base_steal_program (MpegTSBase * base, gint program_number)
447 MpegTSBaseProgram *program;
449 program = (MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
450 GINT_TO_POINTER ((gint) program_number));
453 g_hash_table_steal (base->programs,
454 GINT_TO_POINTER ((gint) program_number));
460 mpegts_base_free_stream (MpegTSBaseStream * stream)
462 if (stream->stream_object)
463 gst_object_unref (stream->stream_object);
464 if (stream->stream_id)
465 g_free (stream->stream_id);
470 mpegts_base_free_program (MpegTSBaseProgram * program)
475 gst_mpegts_section_unref (program->section);
479 /* FIXME FIXME FIXME FREE STREAM OBJECT ! */
480 for (tmp = program->stream_list; tmp; tmp = tmp->next)
481 mpegts_base_free_stream ((MpegTSBaseStream *) tmp->data);
483 if (program->stream_list)
484 g_list_free (program->stream_list);
486 g_free (program->streams);
489 gst_tag_list_unref (program->tags);
490 if (program->collection)
491 gst_object_unref (program->collection);
497 mpegts_base_deactivate_and_free_program (MpegTSBase * base,
498 MpegTSBaseProgram * program)
500 GST_DEBUG_OBJECT (base, "program_number : %d", program->program_number);
502 mpegts_base_deactivate_program (base, program);
503 mpegts_base_free_program (program);
507 mpegts_base_remove_program (MpegTSBase * base, gint program_number)
509 GST_DEBUG_OBJECT (base, "program_number : %d", program_number);
511 g_hash_table_remove (base->programs, GINT_TO_POINTER (program_number));
515 get_registration_from_descriptors (GPtrArray * descriptors)
517 const GstMpegtsDescriptor *desc;
520 gst_mpegts_find_descriptor (descriptors,
521 GST_MTS_DESC_REGISTRATION))) {
522 if (G_UNLIKELY (desc->length < 4)) {
523 GST_WARNING ("Registration descriptor with length < 4. (Corrupted ?)");
525 return GST_READ_UINT32_BE (desc->data + 2);
531 static MpegTSBaseStream *
532 mpegts_base_program_add_stream (MpegTSBase * base,
533 MpegTSBaseProgram * program, guint16 pid, guint8 stream_type,
534 GstMpegtsPMTStream * stream)
536 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
537 MpegTSBaseStream *bstream;
539 GST_DEBUG ("pid:0x%04x, stream_type:0x%03x", pid, stream_type);
541 /* FIXME : PID information/nature might change through time.
542 * We therefore *do* want to be able to replace an existing stream
543 * with updated information */
544 if (G_UNLIKELY (program->streams[pid])) {
545 if (stream_type != 0xff)
546 GST_WARNING ("Stream already present !");
550 bstream = g_malloc0 (base->stream_size);
552 g_strdup_printf ("%s/%08x",
553 gst_stream_collection_get_upstream_id (program->collection), pid);
555 bstream->stream_type = stream_type;
556 bstream->stream = stream;
557 /* We don't yet know the stream type, subclasses will fill that */
558 bstream->stream_object = gst_stream_new (bstream->stream_id, NULL,
559 GST_STREAM_TYPE_UNKNOWN, GST_STREAM_FLAG_NONE);
561 bstream->registration_id =
562 get_registration_from_descriptors (stream->descriptors);
563 GST_DEBUG ("PID 0x%04x, registration_id %" SAFE_FOURCC_FORMAT,
564 bstream->pid, SAFE_FOURCC_ARGS (bstream->registration_id));
567 program->streams[pid] = bstream;
568 program->stream_list = g_list_append (program->stream_list, bstream);
570 if (klass->stream_added)
571 if (klass->stream_added (base, bstream, program))
572 gst_stream_collection_add_stream (program->collection,
573 (GstStream *) gst_object_ref (bstream->stream_object));
580 mpegts_base_program_remove_stream (MpegTSBase * base,
581 MpegTSBaseProgram * program, guint16 pid)
583 MpegTSBaseClass *klass;
584 MpegTSBaseStream *stream = program->streams[pid];
586 GST_DEBUG ("pid:0x%04x", pid);
588 if (G_UNLIKELY (stream == NULL)) {
589 /* Can happen if the PCR PID is the same as a audio/video PID */
590 GST_DEBUG ("Stream already removed");
594 klass = GST_MPEGTS_BASE_GET_CLASS (base);
596 /* If subclass needs it, inform it of the stream we are about to remove */
597 if (klass->stream_removed)
598 klass->stream_removed (base, stream);
600 program->stream_list = g_list_remove_all (program->stream_list, stream);
601 mpegts_base_free_stream (stream);
602 program->streams[pid] = NULL;
605 /* Check if pmtstream is already present in the program */
606 static inline gboolean
607 _stream_in_pmt (const GstMpegtsPMT * pmt, MpegTSBaseStream * stream)
609 guint i, nbstreams = pmt->streams->len;
611 for (i = 0; i < nbstreams; i++) {
612 GstMpegtsPMTStream *pmt_stream = g_ptr_array_index (pmt->streams, i);
614 if (pmt_stream->pid == stream->pid &&
615 pmt_stream->stream_type == stream->stream_type)
622 static inline gboolean
623 _pmt_stream_in_program (MpegTSBaseProgram * program,
624 GstMpegtsPMTStream * stream)
626 MpegTSBaseStream *old_stream = program->streams[stream->pid];
629 return old_stream->stream_type == stream->stream_type;
633 mpegts_base_update_program (MpegTSBase * base, MpegTSBaseProgram * program,
634 GstMpegtsSection * section, const GstMpegtsPMT * pmt)
636 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
637 const gchar *stream_id =
638 gst_stream_collection_get_upstream_id (program->collection);
639 GstStreamCollection *collection;
640 GList *tmp, *toremove;
643 /* Create new collection */
644 collection = gst_stream_collection_new (stream_id);
645 gst_object_unref (program->collection);
646 program->collection = collection;
648 /* Replace section and pmt with the new one */
649 gst_mpegts_section_unref (program->section);
650 program->section = gst_mpegts_section_ref (section);
653 /* Copy over gststream that still exist into the collection */
654 for (tmp = program->stream_list; tmp; tmp = tmp->next) {
655 MpegTSBaseStream *stream = (MpegTSBaseStream *) tmp->data;
656 if (_stream_in_pmt (pmt, stream)) {
657 gst_stream_collection_add_stream (program->collection,
658 gst_object_ref (stream->stream_object));
662 /* Add new streams (will also create and add gststream to the collection) */
663 nbstreams = pmt->streams->len;
664 for (i = 0; i < nbstreams; i++) {
665 GstMpegtsPMTStream *stream = g_ptr_array_index (pmt->streams, i);
666 if (!_pmt_stream_in_program (program, stream))
667 mpegts_base_program_add_stream (base, program, stream->pid,
668 stream->stream_type, stream);
671 /* Call subclass update */
672 if (klass->update_program)
673 klass->update_program (base, program);
675 /* Remove streams no longer present */
677 for (tmp = program->stream_list; tmp; tmp = tmp->next) {
678 MpegTSBaseStream *stream = (MpegTSBaseStream *) tmp->data;
679 if (!_stream_in_pmt (pmt, stream))
680 toremove = g_list_prepend (toremove, stream);
682 for (tmp = toremove; tmp; tmp = tmp->next) {
683 MpegTSBaseStream *stream = (MpegTSBaseStream *) tmp->data;
684 mpegts_base_program_remove_stream (base, program, stream->pid);
691 _stream_is_private_section (const GstMpegtsPMT * pmt,
692 GstMpegtsPMTStream * stream)
694 switch (stream->stream_type) {
695 case GST_MPEGTS_STREAM_TYPE_SCTE_DSMCC_DCB:
696 case GST_MPEGTS_STREAM_TYPE_SCTE_SIGNALING:
698 guint32 registration_id =
699 get_registration_from_descriptors (stream->descriptors);
700 /* Not a private section stream */
701 if (registration_id != DRF_ID_CUEI && registration_id != DRF_ID_ETV1)
704 case GST_MPEGTS_STREAM_TYPE_PRIVATE_SECTIONS:
705 case GST_MPEGTS_STREAM_TYPE_MHEG:
706 case GST_MPEGTS_STREAM_TYPE_DSM_CC:
707 case GST_MPEGTS_STREAM_TYPE_DSMCC_A:
708 case GST_MPEGTS_STREAM_TYPE_DSMCC_B:
709 case GST_MPEGTS_STREAM_TYPE_DSMCC_C:
710 case GST_MPEGTS_STREAM_TYPE_DSMCC_D:
711 case GST_MPEGTS_STREAM_TYPE_SL_FLEXMUX_SECTIONS:
712 case GST_MPEGTS_STREAM_TYPE_METADATA_SECTIONS:
713 /* known PSI streams */
715 case GST_MPEGTS_STREAM_TYPE_SCTE_SIT:
717 guint32 registration_id =
718 get_registration_from_descriptors (pmt->descriptors);
719 /* Not a private section stream */
720 if (registration_id != DRF_ID_CUEI)
729 /* Return TRUE if programs are equal */
731 mpegts_base_is_same_program (MpegTSBase * base, MpegTSBaseProgram * oldprogram,
732 guint16 new_pmt_pid, const GstMpegtsPMT * new_pmt)
735 MpegTSBaseStream *oldstream;
736 gboolean sawpcrpid = FALSE;
738 if (oldprogram->pmt_pid != new_pmt_pid) {
739 GST_DEBUG ("Different pmt_pid (new:0x%04x, old:0x%04x)", new_pmt_pid,
740 oldprogram->pmt_pid);
744 if (oldprogram->pcr_pid != new_pmt->pcr_pid) {
745 GST_DEBUG ("Different pcr_pid (new:0x%04x, old:0x%04x)",
746 new_pmt->pcr_pid, oldprogram->pcr_pid);
750 /* Check the streams */
751 nbstreams = new_pmt->streams->len;
752 for (i = 0; i < nbstreams; ++i) {
753 GstMpegtsPMTStream *stream = g_ptr_array_index (new_pmt->streams, i);
755 oldstream = oldprogram->streams[stream->pid];
757 GST_DEBUG ("New stream 0x%04x not present in old program", stream->pid);
760 if (oldstream->stream_type != stream->stream_type) {
762 ("New stream 0x%04x has a different stream type (new:%d, old:%d)",
763 stream->pid, stream->stream_type, oldstream->stream_type);
766 if (stream->pid == oldprogram->pcr_pid)
770 /* If the pcr is not shared with an existing stream, we'll have one extra stream */
774 if (nbstreams != g_list_length (oldprogram->stream_list)) {
775 GST_DEBUG ("Different number of streams (new:%d, old:%d)",
776 nbstreams, g_list_length (oldprogram->stream_list));
780 GST_DEBUG ("Programs are equal");
784 /* Return TRUE if program is an update
786 * A program is equal if:
787 * * The program number is the same (will be if it enters this function)
788 * * AND The PMT PID is equal to the old one
789 * * AND It contains at least one stream from the previous program
791 * Changes that are acceptable are therefore:
792 * * New streams appearing
793 * * Old streams going away
797 * * PMT PID being changed ?
798 * * Properties of elementary stream being changed ? (new tags ? metadata ?)
801 mpegts_base_is_program_update (MpegTSBase * base,
802 MpegTSBaseProgram * oldprogram, guint16 new_pmt_pid,
803 const GstMpegtsPMT * new_pmt)
806 MpegTSBaseStream *oldstream;
808 if (oldprogram->pmt_pid != new_pmt_pid) {
809 /* FIXME/CHECK: Can a program be updated by just changing its PID
811 GST_DEBUG ("Different pmt_pid (new:0x%04x, old:0x%04x)", new_pmt_pid,
812 oldprogram->pmt_pid);
816 /* Check if at least one stream from the previous program is still present
817 * in the new program */
819 /* Check the streams */
820 nbstreams = new_pmt->streams->len;
821 for (i = 0; i < nbstreams; ++i) {
822 GstMpegtsPMTStream *stream = g_ptr_array_index (new_pmt->streams, i);
824 oldstream = oldprogram->streams[stream->pid];
826 GST_DEBUG ("New stream 0x%04x not present in old program", stream->pid);
827 } else if (oldstream->stream_type != stream->stream_type) {
829 ("New stream 0x%04x has a different stream type (new:%d, old:%d)",
830 stream->pid, stream->stream_type, oldstream->stream_type);
831 } else if (!_stream_is_private_section (new_pmt, stream)) {
832 /* FIXME : We should actually be checking a bit deeper,
833 * especially for private streams (where the differentiation is
834 * done at the registration level) */
836 ("Stream 0x%04x is identical (stream_type %d) ! Program is an update",
837 stream->pid, stream->stream_type);
842 GST_DEBUG ("Program is not an update of the previous one");
847 mpegts_base_deactivate_program (MpegTSBase * base, MpegTSBaseProgram * program)
850 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
852 if (G_UNLIKELY (program->active == FALSE))
855 GST_DEBUG_OBJECT (base, "Deactivating PMT");
857 program->active = FALSE;
860 for (i = 0; i < program->pmt->streams->len; ++i) {
861 GstMpegtsPMTStream *stream = g_ptr_array_index (program->pmt->streams, i);
863 mpegts_base_program_remove_stream (base, program, stream->pid);
865 /* Only unset the is_pes/known_psi bit if the PID isn't used in any other active
867 if (!mpegts_pid_in_active_programs (base, stream->pid)) {
868 if (_stream_is_private_section (program->pmt, stream)) {
869 if (base->parse_private_sections)
870 MPEGTS_BIT_UNSET (base->known_psi, stream->pid);
872 MPEGTS_BIT_UNSET (base->is_pes, stream->pid);
877 /* remove pcr stream */
878 /* FIXME : This might actually be shared with another stream ? */
879 mpegts_base_program_remove_stream (base, program, program->pcr_pid);
880 if (!mpegts_pid_in_active_programs (base, program->pcr_pid))
881 MPEGTS_BIT_UNSET (base->is_pes, program->pcr_pid);
883 GST_DEBUG ("program stream_list is now %p", program->stream_list);
886 /* Inform subclasses we're deactivating this program */
887 if (klass->program_stopped)
888 klass->program_stopped (base, program);
892 mpegts_base_activate_program (MpegTSBase * base, MpegTSBaseProgram * program,
893 guint16 pmt_pid, GstMpegtsSection * section, const GstMpegtsPMT * pmt,
894 gboolean initial_program)
897 MpegTSBaseClass *klass;
899 if (G_UNLIKELY (program->active))
902 GST_DEBUG ("Activating program %d", program->program_number);
904 /* activate new pmt */
905 if (program->section)
906 gst_mpegts_section_unref (program->section);
907 program->section = gst_mpegts_section_ref (section);
910 program->pmt_pid = pmt_pid;
911 program->pcr_pid = pmt->pcr_pid;
913 /* extract top-level registration_id if present */
914 program->registration_id =
915 get_registration_from_descriptors (pmt->descriptors);
916 GST_DEBUG ("program 0x%04x, registration_id %" SAFE_FOURCC_FORMAT,
917 program->program_number, SAFE_FOURCC_ARGS (program->registration_id));
919 for (i = 0; i < pmt->streams->len; ++i) {
920 GstMpegtsPMTStream *stream = g_ptr_array_index (pmt->streams, i);
921 if (_stream_is_private_section (pmt, stream)) {
922 if (base->parse_private_sections)
923 MPEGTS_BIT_SET (base->known_psi, stream->pid);
925 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->is_pes, stream->pid)))
927 ("Refcounting issue. Setting twice a PID (0x%04x) as known PES",
929 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi, stream->pid))) {
931 ("Refcounting issue. Setting a known PSI PID (0x%04x) as known PES",
933 MPEGTS_BIT_UNSET (base->known_psi, stream->pid);
935 MPEGTS_BIT_SET (base->is_pes, stream->pid);
937 mpegts_base_program_add_stream (base, program,
938 stream->pid, stream->stream_type, stream);
940 /* We add the PCR pid last. If that PID is already used by one of the media
941 * streams above, no new stream will be created */
942 mpegts_base_program_add_stream (base, program, pmt->pcr_pid, -1, NULL);
943 MPEGTS_BIT_SET (base->is_pes, pmt->pcr_pid);
945 program->active = TRUE;
946 program->initial_program = initial_program;
948 klass = GST_MPEGTS_BASE_GET_CLASS (base);
949 if (klass->program_started != NULL)
950 klass->program_started (base, program);
952 GST_DEBUG_OBJECT (base, "new pmt activated");
957 mpegts_base_apply_pat (MpegTSBase * base, GstMpegtsSection * section)
959 GPtrArray *pat = gst_mpegts_section_get_pat (section);
961 MpegTSBaseProgram *program;
964 if (G_UNLIKELY (pat == NULL))
967 GST_INFO_OBJECT (base, "PAT");
969 /* Applying a new PAT does two things:
970 * * It adds the new programs to the list of programs this element handles
971 * and increments at the same time the number of times a program is referenced.
973 * * If there was a previously active PAT, It decrements the reference count
974 * of all program it used. If a program is no longer needed, it is removed.
980 GST_LOG ("Activating new Program Association Table");
981 /* activate the new table */
982 for (i = 0; i < pat->len; ++i) {
983 GstMpegtsPatProgram *patp = g_ptr_array_index (pat, i);
985 program = mpegts_base_get_program (base, patp->program_number);
987 /* IF the program already existed, just check if the PMT PID changed */
988 if (program->pmt_pid != patp->network_or_program_map_PID) {
989 if (program->pmt_pid != G_MAXUINT16) {
990 /* pmt pid changed */
991 /* FIXME: when this happens it may still be pmt pid of another
992 * program, so setting to False may make it go through expensive
993 * path in is_psi unnecessarily */
994 MPEGTS_BIT_UNSET (base->known_psi, program->pmt_pid);
997 program->pmt_pid = patp->network_or_program_map_PID;
998 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi, program->pmt_pid)))
1000 ("Refcounting issue. Setting twice a PMT PID (0x%04x) as know PSI",
1002 MPEGTS_BIT_SET (base->known_psi, patp->network_or_program_map_PID);
1005 /* Create a new program */
1007 mpegts_base_add_program (base, patp->program_number,
1008 patp->network_or_program_map_PID);
1010 /* We mark this program as being referenced by one PAT */
1011 program->patcount += 1;
1015 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1016 /* deactivate the old table */
1017 GST_LOG ("Deactivating old Program Association Table");
1019 for (i = 0; i < old_pat->len; ++i) {
1020 GstMpegtsPatProgram *patp = g_ptr_array_index (old_pat, i);
1022 program = mpegts_base_get_program (base, patp->program_number);
1023 if (G_UNLIKELY (program == NULL)) {
1024 GST_DEBUG_OBJECT (base, "broken PAT, duplicated entry for program %d",
1025 patp->program_number);
1029 if (--program->patcount > 0)
1030 /* the program has been referenced by the new pat, keep it */
1033 GST_INFO_OBJECT (base, "PAT removing program 0x%04x 0x%04x",
1034 patp->program_number, patp->network_or_program_map_PID);
1036 if (klass->can_remove_program (base, program)) {
1037 mpegts_base_deactivate_program (base, program);
1038 mpegts_base_remove_program (base, patp->program_number);
1040 /* sub-class now owns the program and must call
1041 * mpegts_base_deactivate_and_free_program later */
1042 g_hash_table_steal (base->programs,
1043 GINT_TO_POINTER ((gint) patp->program_number));
1045 /* FIXME: when this happens it may still be pmt pid of another
1046 * program, so setting to False may make it go through expensive
1047 * path in is_psi unnecessarily */
1048 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi,
1049 patp->network_or_program_map_PID))) {
1051 ("Program refcounting : Setting twice a pid (0x%04x) as known PSI",
1052 patp->network_or_program_map_PID);
1054 MPEGTS_BIT_SET (base->known_psi, patp->network_or_program_map_PID);
1055 mpegts_packetizer_remove_stream (base->packetizer,
1056 patp->network_or_program_map_PID);
1059 g_ptr_array_unref (old_pat);
1066 mpegts_base_apply_pmt (MpegTSBase * base, GstMpegtsSection * section)
1068 const GstMpegtsPMT *pmt;
1069 MpegTSBaseProgram *program, *old_program;
1070 guint program_number;
1071 gboolean initial_program = TRUE;
1073 pmt = gst_mpegts_section_get_pmt (section);
1074 if (G_UNLIKELY (pmt == NULL)) {
1075 GST_ERROR ("Could not get PMT (corrupted ?)");
1079 /* FIXME : not so sure this is valid anymore */
1080 if (G_UNLIKELY (base->seen_pat == FALSE)) {
1081 GST_WARNING ("Got pmt without pat first. Returning");
1082 /* remove the stream since we won't get another PMT otherwise */
1083 mpegts_packetizer_remove_stream (base->packetizer, section->pid);
1087 program_number = section->subtable_extension;
1088 GST_DEBUG ("Applying PMT (program_number:%d, pid:0x%04x)",
1089 program_number, section->pid);
1091 /* In order for stream switching to happen properly in decodebin(2),
1092 * we need to first add the new pads (i.e. activate the new program)
1093 * before removing the old ones (i.e. deactivating the old program)
1096 old_program = mpegts_base_get_program (base, program_number);
1097 if (G_UNLIKELY (old_program == NULL))
1100 if (base->streams_aware
1101 && mpegts_base_is_program_update (base, old_program, section->pid, pmt)) {
1102 GST_FIXME ("We are streams_aware and new program is an update");
1103 /* The program is an update, and we can add/remove pads dynamically */
1104 mpegts_base_update_program (base, old_program, section, pmt);
1108 if (G_UNLIKELY (mpegts_base_is_same_program (base, old_program, section->pid,
1112 /* If the current program is active, this means we have a new program */
1113 if (old_program->active) {
1114 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1115 old_program = mpegts_base_steal_program (base, program_number);
1116 program = mpegts_base_new_program (base, program_number, section->pid);
1117 program->patcount = old_program->patcount;
1119 /* Deactivate the old program */
1120 /* FIXME : THIS IS BREAKING THE STREAM SWITCHING LOGIC !
1122 if (klass->can_remove_program (base, old_program)) {
1123 mpegts_base_deactivate_program (base, old_program);
1124 mpegts_base_free_program (old_program);
1126 /* sub-class now owns the program and must call
1127 * mpegts_base_deactivate_and_free_program later */
1128 g_hash_table_steal (base->programs,
1129 GINT_TO_POINTER ((gint) old_program->program_number));
1131 /* Add new program to the programs we track */
1132 g_hash_table_insert (base->programs,
1133 GINT_TO_POINTER (program_number), program);
1134 initial_program = FALSE;
1136 GST_DEBUG ("Program update, re-using same program");
1137 program = old_program;
1140 /* activate program */
1141 /* Ownership of pmt_info is given to the program */
1142 mpegts_base_activate_program (base, program, section->pid, section, pmt,
1146 GST_DEBUG ("Done activating program");
1151 GST_ERROR ("Attempted to apply a PMT on a program that wasn't created");
1157 GST_DEBUG ("Not applying identical program");
1163 mpegts_base_handle_psi (MpegTSBase * base, GstMpegtsSection * section)
1165 gboolean post_message = TRUE;
1167 GST_DEBUG ("Handling PSI (pid: 0x%04x , table_id: 0x%02x)",
1168 section->pid, section->table_id);
1170 switch (section->section_type) {
1171 case GST_MPEGTS_SECTION_PAT:
1172 post_message = mpegts_base_apply_pat (base, section);
1173 if (base->seen_pat == FALSE) {
1174 base->seen_pat = TRUE;
1175 GST_DEBUG ("First PAT offset: %" G_GUINT64_FORMAT, section->offset);
1176 mpegts_packetizer_set_reference_offset (base->packetizer,
1180 case GST_MPEGTS_SECTION_PMT:
1181 post_message = mpegts_base_apply_pmt (base, section);
1183 case GST_MPEGTS_SECTION_EIT:
1184 /* some tag xtraction + posting */
1185 post_message = mpegts_base_get_tags_from_eit (base, section);
1187 case GST_MPEGTS_SECTION_ATSC_MGT:
1188 post_message = mpegts_base_parse_atsc_mgt (base, section);
1194 /* Finally post message (if it wasn't corrupted) */
1196 gst_element_post_message (GST_ELEMENT_CAST (base),
1197 gst_message_new_mpegts_section (GST_OBJECT (base), section));
1198 gst_mpegts_section_unref (section);
1202 mpegts_base_parse_atsc_mgt (MpegTSBase * base, GstMpegtsSection * section)
1204 const GstMpegtsAtscMGT *mgt;
1207 mgt = gst_mpegts_section_get_atsc_mgt (section);
1208 if (G_UNLIKELY (mgt == NULL))
1211 for (i = 0; i < mgt->tables->len; ++i) {
1212 GstMpegtsAtscMGTTable *table = g_ptr_array_index (mgt->tables, i);
1214 if ((table->table_type >= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_EIT0 &&
1215 table->table_type <= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_EIT127) ||
1216 (table->table_type >= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_ETT0 &&
1217 table->table_type <= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_ETT127)) {
1218 MPEGTS_BIT_SET (base->known_psi, table->pid);
1226 mpegts_base_get_tags_from_eit (MpegTSBase * base, GstMpegtsSection * section)
1228 const GstMpegtsEIT *eit;
1230 MpegTSBaseProgram *program;
1232 /* Early exit if it's not from the present/following table_id */
1233 if (section->table_id != GST_MTS_TABLE_ID_EVENT_INFORMATION_ACTUAL_TS_PRESENT
1234 && section->table_id !=
1235 GST_MTS_TABLE_ID_EVENT_INFORMATION_OTHER_TS_PRESENT)
1238 eit = gst_mpegts_section_get_eit (section);
1239 if (G_UNLIKELY (eit == NULL))
1242 program = mpegts_base_get_program (base, section->subtable_extension);
1245 ("program_id:0x%04x, table_id:0x%02x, actual_stream:%d, present_following:%d, program:%p",
1246 section->subtable_extension, section->table_id, eit->actual_stream,
1247 eit->present_following, program);
1249 if (program && eit->present_following) {
1250 for (i = 0; i < eit->events->len; i++) {
1251 GstMpegtsEITEvent *event = g_ptr_array_index (eit->events, i);
1252 const GstMpegtsDescriptor *desc;
1254 if (event->running_status == RUNNING_STATUS_RUNNING) {
1255 program->event_id = event->event_id;
1257 gst_mpegts_find_descriptor (event->descriptors,
1258 GST_MTS_DESC_DVB_SHORT_EVENT))) {
1259 gchar *name = NULL, *text = NULL;
1261 if (gst_mpegts_descriptor_parse_dvb_short_event (desc, NULL, &name,
1264 program->tags = gst_tag_list_new_empty ();
1267 gst_tag_list_add (program->tags, GST_TAG_MERGE_APPEND,
1268 GST_TAG_TITLE, name, NULL);
1272 gst_tag_list_add (program->tags, GST_TAG_MERGE_APPEND,
1273 GST_TAG_DESCRIPTION, text, NULL);
1276 /* FIXME : Is it correct to post an event duration as a GST_TAG_DURATION ??? */
1277 gst_tag_list_add (program->tags, GST_TAG_MERGE_APPEND,
1278 GST_TAG_DURATION, event->duration * GST_SECOND, NULL);
1290 remove_each_program (gpointer key, MpegTSBaseProgram * program,
1293 /* First deactivate it */
1294 mpegts_base_deactivate_program (base, program);
1299 static inline GstFlowReturn
1300 mpegts_base_drain (MpegTSBase * base)
1302 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1304 /* Call implementation */
1306 return klass->drain (base);
1312 mpegts_base_flush (MpegTSBase * base, gboolean hard)
1314 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1316 /* Call implementation */
1318 klass->flush (base, hard);
1322 mpegts_base_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
1324 gboolean res = TRUE;
1326 MpegTSBase *base = GST_MPEGTS_BASE (parent);
1327 gboolean is_sticky = GST_EVENT_IS_STICKY (event);
1329 GST_DEBUG_OBJECT (base, "Got event %s",
1330 gst_event_type_get_name (GST_EVENT_TYPE (event)));
1332 switch (GST_EVENT_TYPE (event)) {
1333 case GST_EVENT_SEGMENT:
1334 gst_event_copy_segment (event, &base->segment);
1335 GST_DEBUG_OBJECT (base, "Received segment %" GST_SEGMENT_FORMAT,
1337 /* Check if we need to switch PCR/PTS handling */
1338 if (base->segment.format == GST_FORMAT_TIME) {
1339 base->packetizer->calculate_offset = FALSE;
1340 base->packetizer->calculate_skew = TRUE;
1341 /* Seek was handled upstream */
1342 base->last_seek_seqnum = gst_event_get_seqnum (event);
1344 base->packetizer->calculate_offset = TRUE;
1345 base->packetizer->calculate_skew = FALSE;
1348 res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1350 case GST_EVENT_STREAM_START:
1351 gst_event_unref (event);
1353 case GST_EVENT_CAPS:
1354 /* FIXME, do something */
1355 gst_event_unref (event);
1357 case GST_EVENT_FLUSH_STOP:
1358 res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1359 hard = (base->mode != BASE_MODE_SEEKING);
1360 mpegts_packetizer_flush (base->packetizer, hard);
1361 mpegts_base_flush (base, hard);
1362 gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED);
1363 base->seen_pat = FALSE;
1366 res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1369 /* Always return TRUE for sticky events */
1377 mpegts_base_default_sink_query (MpegTSBase * base, GstQuery * query)
1379 return gst_pad_query_default (base->sinkpad, GST_OBJECT (base), query);
1383 mpegts_base_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
1385 MpegTSBase *base = GST_MPEGTS_BASE (parent);
1387 GST_DEBUG_OBJECT (base, "Got query %s",
1388 gst_query_type_get_name (GST_QUERY_TYPE (query)));
1390 return GST_MPEGTS_BASE_GET_CLASS (base)->sink_query (base, query);
1393 static GstFlowReturn
1394 mpegts_base_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
1396 GstFlowReturn res = GST_FLOW_OK;
1398 MpegTSPacketizerPacketReturn pret;
1399 MpegTSPacketizer2 *packetizer;
1400 MpegTSPacketizerPacket packet;
1401 MpegTSBaseClass *klass;
1403 base = GST_MPEGTS_BASE (parent);
1404 klass = GST_MPEGTS_BASE_GET_CLASS (base);
1406 packetizer = base->packetizer;
1408 if (klass->input_done)
1409 gst_buffer_ref (buf);
1411 if (GST_BUFFER_IS_DISCONT (buf)) {
1412 GST_DEBUG_OBJECT (base, "Got DISCONT buffer, flushing");
1413 res = mpegts_base_drain (base);
1414 if (G_UNLIKELY (res != GST_FLOW_OK))
1417 mpegts_base_flush (base, FALSE);
1418 /* In the case of discontinuities in push-mode with TIME segment
1419 * we want to drop all previous observations (hard:TRUE) from
1421 if (base->mode == BASE_MODE_PUSHING
1422 && base->segment.format == GST_FORMAT_TIME) {
1423 mpegts_packetizer_flush (base->packetizer, TRUE);
1424 mpegts_packetizer_clear (base->packetizer);
1426 mpegts_packetizer_flush (base->packetizer, FALSE);
1429 mpegts_packetizer_push (base->packetizer, buf);
1431 while (res == GST_FLOW_OK) {
1432 pret = mpegts_packetizer_next_packet (base->packetizer, &packet);
1434 /* If we don't have enough data, return */
1435 if (G_UNLIKELY (pret == PACKET_NEED_MORE))
1438 if (G_UNLIKELY (pret == PACKET_BAD)) {
1439 /* bad header, skip the packet */
1440 GST_DEBUG_OBJECT (base, "bad packet, skipping");
1444 if (klass->inspect_packet)
1445 klass->inspect_packet (base, &packet);
1447 /* If it's a known PES, push it */
1448 if (MPEGTS_BIT_IS_SET (base->is_pes, packet.pid)) {
1449 /* push the packet downstream */
1450 if (base->push_data)
1451 res = klass->push (base, &packet, NULL);
1452 } else if (packet.payload
1453 && MPEGTS_BIT_IS_SET (base->known_psi, packet.pid)) {
1455 GList *others, *tmp;
1456 GstMpegtsSection *section;
1458 section = mpegts_packetizer_push_section (packetizer, &packet, &others);
1460 mpegts_base_handle_psi (base, section);
1461 if (G_UNLIKELY (others)) {
1462 for (tmp = others; tmp; tmp = tmp->next)
1463 mpegts_base_handle_psi (base, (GstMpegtsSection *) tmp->data);
1464 g_list_free (others);
1467 /* we need to push section packet downstream */
1468 if (base->push_section)
1469 res = klass->push (base, &packet, section);
1471 } else if (packet.payload && packet.pid != 0x1fff)
1472 GST_LOG ("PID 0x%04x Saw packet on a pid we don't handle", packet.pid);
1475 mpegts_packetizer_clear_packet (base->packetizer, &packet);
1478 if (klass->input_done) {
1479 if (res == GST_FLOW_OK)
1480 res = klass->input_done (base, buf);
1482 gst_buffer_unref (buf);
1488 static GstFlowReturn
1489 mpegts_base_scan (MpegTSBase * base)
1491 GstFlowReturn ret = GST_FLOW_OK;
1492 GstBuffer *buf = NULL;
1494 gboolean done = FALSE;
1495 MpegTSPacketizerPacketReturn pret;
1497 gint64 upstream_size, seek_pos, reverse_limit;
1499 guint initial_pcr_seen;
1501 GST_DEBUG ("Scanning for initial sync point");
1503 /* Find initial sync point and at least 5 PCR values */
1504 for (i = 0; i < 20 && !done; i++) {
1505 GST_DEBUG ("Grabbing %d => %d", i * 65536, (i + 1) * 65536);
1507 ret = gst_pad_pull_range (base->sinkpad, i * 65536, 65536, &buf);
1508 if (G_UNLIKELY (ret == GST_FLOW_EOS))
1510 if (G_UNLIKELY (ret != GST_FLOW_OK))
1513 /* Push to packetizer */
1514 mpegts_packetizer_push (base->packetizer, buf);
1517 if (mpegts_packetizer_has_packets (base->packetizer)) {
1518 if (base->seek_offset == -1) {
1519 /* Mark the initial sync point and remember the packetsize */
1520 base->seek_offset = base->packetizer->offset;
1521 GST_DEBUG ("Sync point is now %" G_GUINT64_FORMAT, base->seek_offset);
1522 base->packetsize = base->packetizer->packet_size;
1525 /* Eat up all packets */
1526 pret = mpegts_packetizer_process_next_packet (base->packetizer);
1527 if (pret == PACKET_NEED_MORE)
1529 if (pret != PACKET_BAD && base->packetizer->nb_seen_offsets >= 5) {
1530 GST_DEBUG ("Got enough initial PCR");
1538 initial_pcr_seen = base->packetizer->nb_seen_offsets;
1539 if (G_UNLIKELY (initial_pcr_seen == 0))
1540 goto no_initial_pcr;
1541 GST_DEBUG ("Seen %d initial PCR", initial_pcr_seen);
1543 /* Now send data from the end */
1545 /* Get the size of upstream */
1546 format = GST_FORMAT_BYTES;
1547 if (!gst_pad_peer_query_duration (base->sinkpad, format, &tmpval))
1549 upstream_size = tmpval;
1551 /* The scanning takes place on the last 2048kB. Considering PCR should
1552 * be present at least every 100ms, this should cope with streams
1553 * up to 160Mbit/s */
1554 reverse_limit = MAX (0, upstream_size - 2097152);
1556 /* Find last PCR value, searching backwards by chunks of 300 MPEG-ts packets */
1557 for (seek_pos = MAX (0, upstream_size - 56400);
1558 seek_pos >= reverse_limit; seek_pos -= 56400) {
1559 mpegts_packetizer_clear (base->packetizer);
1560 GST_DEBUG ("Grabbing %" G_GUINT64_FORMAT " => %" G_GUINT64_FORMAT, seek_pos,
1563 ret = gst_pad_pull_range (base->sinkpad, seek_pos, 56400, &buf);
1564 if (G_UNLIKELY (ret == GST_FLOW_EOS))
1566 if (G_UNLIKELY (ret != GST_FLOW_OK))
1569 /* Push to packetizer */
1570 mpegts_packetizer_push (base->packetizer, buf);
1573 if (mpegts_packetizer_has_packets (base->packetizer)) {
1575 /* Eat up all packets, really try to get last PCR(s) */
1576 while (pret != PACKET_NEED_MORE)
1577 pret = mpegts_packetizer_process_next_packet (base->packetizer);
1579 if (base->packetizer->nb_seen_offsets > initial_pcr_seen) {
1580 GST_DEBUG ("Got last PCR(s) (total seen:%d)",
1581 base->packetizer->nb_seen_offsets);
1588 mpegts_packetizer_clear (base->packetizer);
1592 mpegts_packetizer_clear (base->packetizer);
1593 GST_WARNING_OBJECT (base, "Couldn't find any PCR within the first %d bytes",
1600 mpegts_base_loop (MpegTSBase * base)
1602 GstFlowReturn ret = GST_FLOW_ERROR;
1604 switch (base->mode) {
1605 case BASE_MODE_SCANNING:
1606 /* Find first sync point */
1607 ret = mpegts_base_scan (base);
1608 if (G_UNLIKELY (ret != GST_FLOW_OK))
1610 base->mode = BASE_MODE_STREAMING;
1611 GST_DEBUG ("Changing to Streaming");
1613 case BASE_MODE_SEEKING:
1614 /* FIXME : unclear if we still need mode_seeking... */
1615 base->mode = BASE_MODE_STREAMING;
1617 case BASE_MODE_STREAMING:
1619 GstBuffer *buf = NULL;
1621 GST_DEBUG ("Pulling data from %" G_GUINT64_FORMAT, base->seek_offset);
1623 if (G_UNLIKELY (base->last_seek_seqnum == GST_SEQNUM_INVALID)) {
1624 /* No configured seek, set a valid seqnum */
1625 base->last_seek_seqnum = gst_util_seqnum_next ();
1627 ret = gst_pad_pull_range (base->sinkpad, base->seek_offset,
1628 100 * base->packetsize, &buf);
1629 if (G_UNLIKELY (ret != GST_FLOW_OK))
1631 base->seek_offset += gst_buffer_get_size (buf);
1632 ret = mpegts_base_chain (base->sinkpad, GST_OBJECT_CAST (base), buf);
1633 if (G_UNLIKELY (ret != GST_FLOW_OK))
1637 case BASE_MODE_PUSHING:
1638 GST_WARNING ("wrong BASE_MODE_PUSHING mode in pull loop");
1646 GST_DEBUG_OBJECT (base, "Pausing task, reason %s", gst_flow_get_name (ret));
1647 if (ret == GST_FLOW_EOS) {
1648 if (!GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base,
1649 gst_event_new_eos ()))
1650 GST_ELEMENT_ERROR (base, STREAM, FAILED,
1651 (_("Internal data stream error.")),
1652 ("No program activated before EOS"));
1653 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
1654 GST_ELEMENT_FLOW_ERROR (base, ret);
1655 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, gst_event_new_eos ());
1657 gst_pad_pause_task (base->sinkpad);
1663 mpegts_base_handle_seek_event (MpegTSBase * base, GstPad * pad,
1666 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1667 GstFlowReturn ret = GST_FLOW_ERROR;
1672 GstSeekType start_type, stop_type;
1674 GstEvent *flush_event = NULL;
1676 gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
1679 if (format != GST_FORMAT_TIME)
1682 if (GST_EVENT_SEQNUM (event) == base->last_seek_seqnum) {
1683 GST_DEBUG_OBJECT (base, "Skipping already handled seek");
1687 if (base->mode == BASE_MODE_PUSHING) {
1688 /* First try if upstream supports seeking in TIME format */
1689 if (gst_pad_push_event (base->sinkpad, gst_event_ref (event))) {
1690 GST_DEBUG ("upstream handled SEEK event");
1694 /* If the subclass can seek, do that */
1696 ret = klass->seek (base, event);
1697 if (G_UNLIKELY (ret != GST_FLOW_OK))
1698 GST_WARNING ("seeking failed %s", gst_flow_get_name (ret));
1702 if (GST_CLOCK_TIME_IS_VALID (base->seek_offset)) {
1703 base->mode = BASE_MODE_SEEKING;
1704 new_seek = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
1705 GST_SEEK_TYPE_SET, base->seek_offset, GST_SEEK_TYPE_NONE, -1);
1706 gst_event_set_seqnum (new_seek, GST_EVENT_SEQNUM (event));
1707 if (!gst_pad_push_event (base->sinkpad, new_seek))
1708 ret = GST_FLOW_ERROR;
1710 base->last_seek_seqnum = GST_EVENT_SEQNUM (event);
1712 base->mode = BASE_MODE_PUSHING;
1715 GST_WARNING ("subclass has no seek implementation");
1718 return ret == GST_FLOW_OK;
1722 GST_WARNING ("subclass has no seek implementation");
1727 GST_WARNING ("Negative rate not supported");
1731 GST_DEBUG ("seek event, rate: %f start: %" GST_TIME_FORMAT
1732 " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start),
1733 GST_TIME_ARGS (stop));
1735 flush = flags & GST_SEEK_FLAG_FLUSH;
1737 /* stop streaming, either by flushing or by pausing the task */
1738 base->mode = BASE_MODE_SEEKING;
1740 GST_DEBUG_OBJECT (base, "sending flush start");
1741 flush_event = gst_event_new_flush_start ();
1742 gst_event_set_seqnum (flush_event, GST_EVENT_SEQNUM (event));
1743 gst_pad_push_event (base->sinkpad, gst_event_ref (flush_event));
1744 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, flush_event);
1746 gst_pad_pause_task (base->sinkpad);
1748 /* wait for streaming to finish */
1749 GST_PAD_STREAM_LOCK (base->sinkpad);
1752 /* send a FLUSH_STOP for the sinkpad, since we need data for seeking */
1753 GST_DEBUG_OBJECT (base, "sending flush stop");
1754 flush_event = gst_event_new_flush_stop (TRUE);
1755 gst_event_set_seqnum (flush_event, GST_EVENT_SEQNUM (event));
1757 /* ref for it to be reused later */
1758 gst_pad_push_event (base->sinkpad, gst_event_ref (flush_event));
1759 /* And actually flush our pending data but allow to preserve some info
1760 * to perform the seek */
1761 mpegts_base_flush (base, FALSE);
1762 mpegts_packetizer_flush (base->packetizer, FALSE);
1765 if (flags & (GST_SEEK_FLAG_SEGMENT)) {
1766 GST_WARNING ("seek flags 0x%x are not supported", (int) flags);
1771 /* If the subclass can seek, do that */
1772 ret = klass->seek (base, event);
1773 if (G_UNLIKELY (ret != GST_FLOW_OK))
1774 GST_WARNING ("seeking failed %s", gst_flow_get_name (ret));
1776 base->last_seek_seqnum = GST_EVENT_SEQNUM (event);
1779 /* if we sent a FLUSH_START, we now send a FLUSH_STOP */
1780 GST_DEBUG_OBJECT (base, "sending flush stop");
1781 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, flush_event);
1786 gst_event_unref (flush_event);
1787 gst_pad_start_task (base->sinkpad, (GstTaskFunction) mpegts_base_loop, base,
1790 GST_PAD_STREAM_UNLOCK (base->sinkpad);
1791 return ret == GST_FLOW_OK;
1796 mpegts_base_sink_activate (GstPad * sinkpad, GstObject * parent)
1801 query = gst_query_new_scheduling ();
1803 if (!gst_pad_peer_query (sinkpad, query)) {
1804 gst_query_unref (query);
1808 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
1809 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
1810 gst_query_unref (query);
1815 GST_DEBUG_OBJECT (sinkpad, "activating pull");
1816 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
1820 GST_DEBUG_OBJECT (sinkpad, "activating push");
1821 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
1826 mpegts_base_sink_activate_mode (GstPad * pad, GstObject * parent,
1827 GstPadMode mode, gboolean active)
1830 MpegTSBase *base = GST_MPEGTS_BASE (parent);
1833 case GST_PAD_MODE_PUSH:
1834 base->mode = BASE_MODE_PUSHING;
1837 case GST_PAD_MODE_PULL:
1839 base->mode = BASE_MODE_SCANNING;
1840 /* When working pull-based, we always use offsets for estimation */
1841 base->packetizer->calculate_offset = TRUE;
1842 base->packetizer->calculate_skew = FALSE;
1843 gst_segment_init (&base->segment, GST_FORMAT_BYTES);
1845 gst_pad_start_task (pad, (GstTaskFunction) mpegts_base_loop, base,
1848 res = gst_pad_stop_task (pad);
1857 static GstStateChangeReturn
1858 mpegts_base_change_state (GstElement * element, GstStateChange transition)
1861 GstStateChangeReturn ret;
1863 base = GST_MPEGTS_BASE (element);
1865 switch (transition) {
1866 case GST_STATE_CHANGE_READY_TO_PAUSED:
1867 mpegts_base_reset (base);
1873 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1875 switch (transition) {
1876 case GST_STATE_CHANGE_PAUSED_TO_READY:
1877 mpegts_base_reset (base);
1878 if (base->mode != BASE_MODE_PUSHING)
1879 base->mode = BASE_MODE_SCANNING;
1889 gst_mpegtsbase_plugin_init (GstPlugin * plugin)
1891 GST_DEBUG_CATEGORY_INIT (mpegts_base_debug, "mpegtsbase", 0,
1892 "MPEG transport stream base class");