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., 59 Temple Place - Suite 330,
28 * Boston, MA 02111-1307, USA.
40 #include <gst/gst-i18n-plugin.h>
41 #include "mpegtsbase.h"
42 #include "gstmpegdesc.h"
44 /* latency in mseconds */
45 #define TS_LATENCY 700
47 #define TABLE_ID_UNSET 0xFF
48 #define RUNNING_STATUS_RUNNING 4
50 GST_DEBUG_CATEGORY_STATIC (mpegts_base_debug);
51 #define GST_CAT_DEFAULT mpegts_base_debug
53 static GQuark QUARK_PROGRAMS;
54 static GQuark QUARK_PROGRAM_NUMBER;
55 static GQuark QUARK_PID;
56 static GQuark QUARK_PCR_PID;
57 static GQuark QUARK_STREAMS;
58 static GQuark QUARK_STREAM_TYPE;
60 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
63 GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
72 static void mpegts_base_set_property (GObject * object, guint prop_id,
73 const GValue * value, GParamSpec * pspec);
74 static void mpegts_base_get_property (GObject * object, guint prop_id,
75 GValue * value, GParamSpec * pspec);
76 static void mpegts_base_dispose (GObject * object);
77 static void mpegts_base_finalize (GObject * object);
78 static void mpegts_base_free_program (MpegTSBaseProgram * program);
79 static void mpegts_base_free_stream (MpegTSBaseStream * ptream);
80 static gboolean mpegts_base_sink_activate (GstPad * pad);
81 static gboolean mpegts_base_sink_activate_pull (GstPad * pad, gboolean active);
82 static gboolean mpegts_base_sink_activate_push (GstPad * pad, gboolean active);
83 static GstFlowReturn mpegts_base_chain (GstPad * pad, GstBuffer * buf);
84 static gboolean mpegts_base_sink_event (GstPad * pad, GstEvent * event);
85 static GstStateChangeReturn mpegts_base_change_state (GstElement * element,
86 GstStateChange transition);
87 static void _extra_init (GType type);
88 static void mpegts_base_get_tags_from_sdt (MpegTSBase * base,
89 GstStructure * sdt_info);
90 static void mpegts_base_get_tags_from_eit (MpegTSBase * base,
91 GstStructure * eit_info);
93 GST_BOILERPLATE_FULL (MpegTSBase, mpegts_base, GstElement, GST_TYPE_ELEMENT,
97 static const guint32 crc_tab[256] = {
98 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
99 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
100 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
101 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
102 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
103 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
104 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
105 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
106 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
107 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
108 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
109 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
110 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
111 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
112 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
113 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
114 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
115 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
116 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
117 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
118 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
119 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
120 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
121 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
122 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
123 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
124 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
125 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
126 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
127 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
128 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
129 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
130 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
131 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
132 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
133 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
134 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
135 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
136 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
137 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
138 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
139 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
140 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
143 /* relicenced to LGPL from fluendo ts demuxer */
145 mpegts_base_calc_crc32 (guint8 * data, guint datalen)
148 guint32 crc = 0xffffffff;
150 for (i = 0; i < datalen; i++) {
151 crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff];
157 _extra_init (GType type)
159 QUARK_PROGRAMS = g_quark_from_string ("programs");
160 QUARK_PROGRAM_NUMBER = g_quark_from_string ("program-number");
161 QUARK_PID = g_quark_from_string ("pid");
162 QUARK_PCR_PID = g_quark_from_string ("pcr-pid");
163 QUARK_STREAMS = g_quark_from_string ("streams");
164 QUARK_STREAM_TYPE = g_quark_from_string ("stream-type");
168 mpegts_base_base_init (gpointer klass)
170 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
172 gst_element_class_add_static_pad_template (element_class,
177 mpegts_base_class_init (MpegTSBaseClass * klass)
179 GObjectClass *gobject_class;
180 GstElementClass *element_class;
182 element_class = GST_ELEMENT_CLASS (klass);
183 element_class->change_state = mpegts_base_change_state;
185 gobject_class = G_OBJECT_CLASS (klass);
186 gobject_class->set_property = mpegts_base_set_property;
187 gobject_class->get_property = mpegts_base_get_property;
188 gobject_class->dispose = mpegts_base_dispose;
189 gobject_class->finalize = mpegts_base_finalize;
194 mpegts_base_reset (MpegTSBase * base)
196 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
198 mpegts_packetizer_clear (base->packetizer);
199 memset (base->is_pes, 0, 1024);
200 memset (base->known_psi, 0, 1024);
203 MPEGTS_BIT_SET (base->known_psi, 0);
205 /* FIXME : Commenting the Following lines is to be in sync with the following
208 * 61a885613316ce7657c36a6cd215b43f9dc67b79
209 * mpegtsparse: don't free PAT structure which may still be needed later
212 /* if (base->pat != NULL) */
213 /* gst_structure_free (base->pat); */
214 /* base->pat = NULL; */
215 /* pmt pids will be added and removed dynamically */
217 gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED);
219 base->mode = BASE_MODE_STREAMING;
220 base->seen_pat = FALSE;
221 base->first_pat_offset = -1;
223 base->first_buf_ts = GST_CLOCK_TIME_NONE;
230 mpegts_base_init (MpegTSBase * base, MpegTSBaseClass * klass)
232 base->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
233 gst_pad_set_activate_function (base->sinkpad, mpegts_base_sink_activate);
234 gst_pad_set_activatepull_function (base->sinkpad,
235 mpegts_base_sink_activate_pull);
236 gst_pad_set_activatepush_function (base->sinkpad,
237 mpegts_base_sink_activate_push);
238 gst_pad_set_chain_function (base->sinkpad, mpegts_base_chain);
239 gst_pad_set_event_function (base->sinkpad, mpegts_base_sink_event);
240 gst_element_add_pad (GST_ELEMENT (base), base->sinkpad);
242 base->disposed = FALSE;
243 base->packetizer = mpegts_packetizer_new ();
244 base->programs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
245 NULL, (GDestroyNotify) mpegts_base_free_program);
247 base->is_pes = g_new0 (guint8, 1024);
248 base->known_psi = g_new0 (guint8, 1024);
249 base->program_size = sizeof (MpegTSBaseProgram);
250 base->stream_size = sizeof (MpegTSBaseStream);
252 mpegts_base_reset (base);
256 mpegts_base_dispose (GObject * object)
258 MpegTSBase *base = GST_MPEGTS_BASE (object);
260 if (!base->disposed) {
261 g_object_unref (base->packetizer);
262 base->disposed = TRUE;
263 g_free (base->known_psi);
264 g_free (base->is_pes);
267 if (G_OBJECT_CLASS (parent_class)->dispose)
268 G_OBJECT_CLASS (parent_class)->dispose (object);
272 mpegts_base_finalize (GObject * object)
274 MpegTSBase *base = GST_MPEGTS_BASE (object);
277 gst_structure_free (base->pat);
280 g_hash_table_destroy (base->programs);
282 if (G_OBJECT_CLASS (parent_class)->finalize)
283 G_OBJECT_CLASS (parent_class)->finalize (object);
287 mpegts_base_set_property (GObject * object, guint prop_id,
288 const GValue * value, GParamSpec * pspec)
290 /* MpegTSBase *base = GST_MPEGTS_BASE (object); */
294 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
299 mpegts_base_get_property (GObject * object, guint prop_id,
300 GValue * value, GParamSpec * pspec)
302 /* MpegTSBase *base = GST_MPEGTS_BASE (object); */
306 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
310 /* returns NULL if no matching descriptor found *
311 * otherwise returns a descriptor that needs to *
314 mpegts_get_descriptor_from_stream (MpegTSBaseStream * stream, guint8 tag)
316 GValueArray *descriptors = NULL;
317 GstStructure *stream_info = stream->stream_info;
318 guint8 *retval = NULL;
321 gst_structure_get (stream_info, "descriptors", G_TYPE_VALUE_ARRAY,
324 for (i = 0; i < descriptors->n_values; i++) {
325 GValue *value = g_value_array_get_nth (descriptors, i);
326 GString *desc = g_value_dup_boxed (value);
327 if (DESC_TAG (desc->str) == tag) {
328 retval = (guint8 *) desc->str;
329 g_string_free (desc, FALSE);
332 g_string_free (desc, FALSE);
334 g_value_array_free (descriptors);
346 foreach_pid_in_program (gpointer key, MpegTSBaseProgram * program,
349 if (!program->active)
351 if (program->streams[lookup->pid])
356 mpegts_pid_in_active_programs (MpegTSBase * base, guint16 pid)
362 g_hash_table_foreach (base->programs, (GHFunc) foreach_pid_in_program,
368 /* returns NULL if no matching descriptor found *
369 * otherwise returns a descriptor that needs to *
372 mpegts_get_descriptor_from_program (MpegTSBaseProgram * program, guint8 tag)
374 GValueArray *descriptors = NULL;
375 GstStructure *program_info;
376 guint8 *retval = NULL;
379 if (G_UNLIKELY (program == NULL))
381 program_info = program->pmt_info;
382 gst_structure_get (program_info, "descriptors", G_TYPE_VALUE_ARRAY,
385 for (i = 0; i < descriptors->n_values; i++) {
386 GValue *value = g_value_array_get_nth (descriptors, i);
387 GString *desc = g_value_dup_boxed (value);
388 if (DESC_TAG (desc->str) == tag) {
389 retval = (guint8 *) desc->str;
390 g_string_free (desc, FALSE);
393 g_string_free (desc, FALSE);
395 g_value_array_free (descriptors);
400 static MpegTSBaseProgram *
401 mpegts_base_new_program (MpegTSBase * base,
402 gint program_number, guint16 pmt_pid)
404 MpegTSBaseProgram *program;
406 GST_DEBUG_OBJECT (base, "program_number : %d, pmt_pid : %d",
407 program_number, pmt_pid);
409 program = g_malloc0 (base->program_size);
410 program->program_number = program_number;
411 program->pmt_pid = pmt_pid;
412 program->pcr_pid = G_MAXUINT16;
413 program->streams = g_new0 (MpegTSBaseStream *, 0x2000);
414 program->patcount = 0;
420 mpegts_base_add_program (MpegTSBase * base,
421 gint program_number, guint16 pmt_pid)
423 MpegTSBaseProgram *program;
425 GST_DEBUG_OBJECT (base, "program_number : %d, pmt_pid : %d",
426 program_number, pmt_pid);
428 program = mpegts_base_new_program (base, program_number, pmt_pid);
430 /* Mark the PMT PID as being a known PSI PID */
431 MPEGTS_BIT_SET (base->known_psi, pmt_pid);
433 g_hash_table_insert (base->programs,
434 GINT_TO_POINTER (program_number), program);
440 mpegts_base_get_program (MpegTSBase * base, gint program_number)
442 MpegTSBaseProgram *program;
444 program = (MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
445 GINT_TO_POINTER ((gint) program_number));
450 static MpegTSBaseProgram *
451 mpegts_base_steal_program (MpegTSBase * base, gint program_number)
453 MpegTSBaseProgram *program;
455 program = (MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
456 GINT_TO_POINTER ((gint) program_number));
459 g_hash_table_steal (base->programs,
460 GINT_TO_POINTER ((gint) program_number));
466 mpegts_base_free_program (MpegTSBaseProgram * program)
470 if (program->pmt_info)
471 gst_structure_free (program->pmt_info);
473 for (tmp = program->stream_list; tmp; tmp = tmp->next)
474 mpegts_base_free_stream ((MpegTSBaseStream *) tmp->data);
475 if (program->stream_list)
476 g_list_free (program->stream_list);
478 g_free (program->streams);
481 gst_tag_list_free (program->tags);
486 /* FIXME : This is being called by tsdemux::find_timestamps()
487 * We need to avoid re-entrant code like that */
489 mpegts_base_remove_program (MpegTSBase * base, gint program_number)
491 MpegTSBaseProgram *program;
492 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
494 GST_DEBUG_OBJECT (base, "program_number : %d", program_number);
496 if (klass->program_stopped) {
498 (MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
499 GINT_TO_POINTER (program_number));
501 klass->program_stopped (base, program);
503 g_hash_table_remove (base->programs, GINT_TO_POINTER (program_number));
506 static MpegTSBaseStream *
507 mpegts_base_program_add_stream (MpegTSBase * base,
508 MpegTSBaseProgram * program, guint16 pid, guint8 stream_type,
509 GstStructure * stream_info)
511 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
512 MpegTSBaseStream *stream;
514 GST_DEBUG ("pid:0x%04x, stream_type:0x%03x, stream_info:%" GST_PTR_FORMAT,
515 pid, stream_type, stream_info);
517 if (G_UNLIKELY (program->streams[pid])) {
518 GST_WARNING ("Stream already present !");
522 stream = g_malloc0 (base->stream_size);
524 stream->stream_type = stream_type;
525 stream->stream_info = stream_info;
527 program->streams[pid] = stream;
528 program->stream_list = g_list_append (program->stream_list, stream);
530 if (klass->stream_added)
531 klass->stream_added (base, stream, program);
537 mpegts_base_free_stream (MpegTSBaseStream * stream)
543 mpegts_base_program_remove_stream (MpegTSBase * base,
544 MpegTSBaseProgram * program, guint16 pid)
546 MpegTSBaseClass *klass;
547 MpegTSBaseStream *stream = program->streams[pid];
549 GST_DEBUG ("pid:0x%04x", pid);
551 if (G_UNLIKELY (stream == NULL)) {
552 /* Can happen if the PCR PID is the same as a audio/video PID */
553 GST_DEBUG ("Stream already removed");
557 klass = GST_MPEGTS_BASE_GET_CLASS (base);
559 /* If subclass needs it, inform it of the stream we are about to remove */
560 if (klass->stream_removed)
561 klass->stream_removed (base, stream);
563 program->stream_list = g_list_remove_all (program->stream_list, stream);
564 mpegts_base_free_stream (stream);
565 program->streams[pid] = NULL;
569 mpegts_base_deactivate_program (MpegTSBase * base, MpegTSBaseProgram * program)
573 GstStructure *stream;
574 const GValue *streams;
576 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
578 if (G_UNLIKELY (program->active == FALSE))
581 GST_DEBUG_OBJECT (base, "Deactivating PMT");
583 program->active = FALSE;
585 if (program->pmt_info) {
586 /* Inform subclasses we're deactivating this program */
587 if (klass->program_stopped)
588 klass->program_stopped (base, program);
590 streams = gst_structure_id_get_value (program->pmt_info, QUARK_STREAMS);
591 nbstreams = gst_value_list_get_size (streams);
593 for (i = 0; i < nbstreams; ++i) {
594 value = gst_value_list_get_value (streams, i);
595 stream = g_value_get_boxed (value);
597 gst_structure_id_get (stream, QUARK_PID, G_TYPE_UINT, &pid, NULL);
598 mpegts_base_program_remove_stream (base, program, (guint16) pid);
600 /* Only unset the is_pes bit if the PID isn't used in any other active
602 if (!mpegts_pid_in_active_programs (base, pid))
603 MPEGTS_BIT_UNSET (base->is_pes, pid);
606 /* remove pcr stream */
607 /* FIXME : This might actually be shared with another stream ? */
608 mpegts_base_program_remove_stream (base, program, program->pcr_pid);
609 if (!mpegts_pid_in_active_programs (base, program->pcr_pid))
610 MPEGTS_BIT_UNSET (base->is_pes, program->pcr_pid);
612 GST_DEBUG ("program stream_list is now %p", program->stream_list);
617 mpegts_base_activate_program (MpegTSBase * base, MpegTSBaseProgram * program,
618 guint16 pmt_pid, GstStructure * pmt_info)
624 GstStructure *stream;
625 const GValue *new_streams;
627 MpegTSBaseClass *klass;
629 if (G_UNLIKELY (program->active))
632 GST_DEBUG ("Activating program %d", program->program_number);
634 gst_structure_id_get (pmt_info, QUARK_PCR_PID, G_TYPE_UINT, &pcr_pid, NULL);
636 /* activate new pmt */
637 if (program->pmt_info)
638 gst_structure_free (program->pmt_info);
639 program->pmt_info = gst_structure_copy (pmt_info);
640 program->pmt_pid = pmt_pid;
641 program->pcr_pid = pcr_pid;
643 new_streams = gst_structure_id_get_value (pmt_info, QUARK_STREAMS);
644 nbstreams = gst_value_list_get_size (new_streams);
646 for (i = 0; i < nbstreams; ++i) {
647 value = gst_value_list_get_value (new_streams, i);
648 stream = g_value_get_boxed (value);
650 gst_structure_id_get (stream, QUARK_PID, G_TYPE_UINT, &pid,
651 QUARK_STREAM_TYPE, G_TYPE_UINT, &stream_type, NULL);
652 MPEGTS_BIT_SET (base->is_pes, pid);
653 mpegts_base_program_add_stream (base, program,
654 (guint16) pid, (guint8) stream_type, stream);
657 /* We add the PCR pid last. If that PID is already used by one of the media
658 * streams above, no new stream will be created */
659 mpegts_base_program_add_stream (base, program, (guint16) pcr_pid, -1, NULL);
660 MPEGTS_BIT_SET (base->is_pes, pcr_pid);
663 program->active = TRUE;
665 klass = GST_MPEGTS_BASE_GET_CLASS (base);
666 if (klass->program_started != NULL)
667 klass->program_started (base, program);
669 GST_DEBUG_OBJECT (base, "new pmt %" GST_PTR_FORMAT, pmt_info);
673 mpegts_base_is_psi (MpegTSBase * base, MpegTSPacketizerPacket * packet)
675 gboolean retval = FALSE;
678 static const guint8 si_tables[] =
679 { 0x00, 0x01, 0x02, 0x03, 0x40, 0x41, 0x42, 0x46, 0x4A,
680 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
681 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65,
682 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
683 0x72, 0x73, 0x7E, 0x7F, TABLE_ID_UNSET
686 if (MPEGTS_BIT_IS_SET (base->known_psi, packet->pid))
689 /* check is it is a pes pid */
690 if (MPEGTS_BIT_IS_SET (base->is_pes, packet->pid))
694 if (packet->payload_unit_start_indicator) {
695 table_id = *(packet->data);
697 while (si_tables[i] != TABLE_ID_UNSET) {
698 if (G_UNLIKELY (si_tables[i] == table_id)) {
699 GST_DEBUG_OBJECT (base, "Packet has table id 0x%x", table_id);
706 MpegTSPacketizerStream *stream = (MpegTSPacketizerStream *)
707 base->packetizer->streams[packet->pid];
711 GST_DEBUG_OBJECT (base, "section table id: 0x%x",
712 stream->section_table_id);
713 while (si_tables[i] != TABLE_ID_UNSET) {
714 if (G_UNLIKELY (si_tables[i] == stream->section_table_id)) {
724 GST_LOG_OBJECT (base, "Packet of pid 0x%x is psi: %d", packet->pid, retval);
729 mpegts_base_apply_pat (MpegTSBase * base, GstStructure * pat_info)
732 GstStructure *old_pat;
733 GstStructure *program_info;
734 guint program_number;
736 MpegTSBaseProgram *program;
738 const GValue *programs;
740 GST_INFO_OBJECT (base, "PAT %" GST_PTR_FORMAT, pat_info);
742 /* Applying a new PAT does two things:
743 * * It adds the new programs to the list of programs this element handles
744 * and increments at the same time the number of times a program is referenced.
746 * * If there was a previously active PAT, It decrements the reference count
747 * of all program it used. If a program is no longer needed, it is removed.
751 base->pat = gst_structure_copy (pat_info);
753 gst_element_post_message (GST_ELEMENT_CAST (base),
754 gst_message_new_element (GST_OBJECT (base),
755 gst_structure_copy (pat_info)));
758 GST_LOG ("Activating new Program Association Table");
759 /* activate the new table */
760 programs = gst_structure_id_get_value (pat_info, QUARK_PROGRAMS);
761 nbprograms = gst_value_list_get_size (programs);
762 for (i = 0; i < nbprograms; ++i) {
763 value = gst_value_list_get_value (programs, i);
765 program_info = g_value_get_boxed (value);
766 gst_structure_id_get (program_info, QUARK_PROGRAM_NUMBER, G_TYPE_UINT,
767 &program_number, QUARK_PID, G_TYPE_UINT, &pid, NULL);
769 program = mpegts_base_get_program (base, program_number);
771 /* IF the program already existed, just check if the PMT PID changed */
772 if (program->pmt_pid != pid) {
773 if (program->pmt_pid != G_MAXUINT16) {
774 /* pmt pid changed */
775 /* FIXME: when this happens it may still be pmt pid of another
776 * program, so setting to False may make it go through expensive
777 * path in is_psi unnecessarily */
778 MPEGTS_BIT_UNSET (base->known_psi, program->pmt_pid);
781 program->pmt_pid = pid;
782 MPEGTS_BIT_SET (base->known_psi, pid);
785 /* Create a new program */
786 program = mpegts_base_add_program (base, program_number, pid);
788 /* We mark this program as being referenced by one PAT */
789 program->patcount += 1;
793 /* deactivate the old table */
794 GST_LOG ("Deactivating old Program Association Table");
796 programs = gst_structure_id_get_value (old_pat, QUARK_PROGRAMS);
797 nbprograms = gst_value_list_get_size (programs);
798 for (i = 0; i < nbprograms; ++i) {
799 value = gst_value_list_get_value (programs, i);
801 program_info = g_value_get_boxed (value);
802 gst_structure_id_get (program_info,
803 QUARK_PROGRAM_NUMBER, G_TYPE_UINT, &program_number,
804 QUARK_PID, G_TYPE_UINT, &pid, NULL);
806 program = mpegts_base_get_program (base, program_number);
807 if (G_UNLIKELY (program == NULL)) {
808 GST_DEBUG_OBJECT (base, "broken PAT, duplicated entry for program %d",
813 if (--program->patcount > 0)
814 /* the program has been referenced by the new pat, keep it */
817 GST_INFO_OBJECT (base, "PAT removing program %" GST_PTR_FORMAT,
820 mpegts_base_deactivate_program (base, program);
821 mpegts_base_remove_program (base, program_number);
822 /* FIXME: when this happens it may still be pmt pid of another
823 * program, so setting to False may make it go through expensive
824 * path in is_psi unnecessarily */
825 MPEGTS_BIT_SET (base->known_psi, pid);
826 mpegts_packetizer_remove_stream (base->packetizer, pid);
829 gst_structure_free (old_pat);
834 mpegts_base_apply_pmt (MpegTSBase * base,
835 guint16 pmt_pid, GstStructure * pmt_info)
837 MpegTSBaseProgram *program, *old_program;
838 guint program_number;
839 gboolean deactivate_old_program = FALSE;
841 /* FIXME : not so sure this is valid anymore */
842 if (G_UNLIKELY (base->seen_pat == FALSE)) {
843 GST_WARNING ("Got pmt without pat first. Returning");
844 /* remove the stream since we won't get another PMT otherwise */
845 mpegts_packetizer_remove_stream (base->packetizer, pmt_pid);
849 gst_structure_id_get (pmt_info, QUARK_PROGRAM_NUMBER, G_TYPE_UINT,
850 &program_number, NULL);
852 GST_DEBUG ("Applying PMT (program_number:%d, pid:0x%04x)",
853 program_number, pmt_pid);
855 /* In order for stream switching to happen properly in decodebin(2),
856 * we need to first add the new pads (i.e. activate the new program)
857 * before removing the old ones (i.e. deactivating the old program)
860 old_program = mpegts_base_get_program (base, program_number);
861 if (G_UNLIKELY (old_program == NULL))
864 /* If the current program is active, this means we have a new program */
865 if (old_program->active) {
866 old_program = mpegts_base_steal_program (base, program_number);
867 program = mpegts_base_new_program (base, program_number, pmt_pid);
868 g_hash_table_insert (base->programs,
869 GINT_TO_POINTER (program_number), program);
870 deactivate_old_program = TRUE;
872 program = old_program;
874 /* First activate program */
875 mpegts_base_activate_program (base, program, pmt_pid, pmt_info);
877 if (deactivate_old_program) {
878 /* deactivate old pmt */ ;
879 mpegts_base_deactivate_program (base, old_program);
880 mpegts_base_free_program (old_program);
883 /* if (program->pmt_info) */
884 /* gst_structure_free (program->pmt_info); */
885 /* program->pmt_info = NULL; */
887 gst_element_post_message (GST_ELEMENT_CAST (base),
888 gst_message_new_element (GST_OBJECT (base),
889 gst_structure_copy (pmt_info)));
895 GST_ERROR ("Attempted to apply a PMT on a program that wasn't created");
901 mpegts_base_apply_nit (MpegTSBase * base,
902 guint16 pmt_pid, GstStructure * nit_info)
904 GST_DEBUG_OBJECT (base, "NIT %" GST_PTR_FORMAT, nit_info);
906 gst_element_post_message (GST_ELEMENT_CAST (base),
907 gst_message_new_element (GST_OBJECT (base),
908 gst_structure_copy (nit_info)));
912 mpegts_base_apply_sdt (MpegTSBase * base,
913 guint16 pmt_pid, GstStructure * sdt_info)
915 GST_DEBUG_OBJECT (base, "SDT %" GST_PTR_FORMAT, sdt_info);
917 mpegts_base_get_tags_from_sdt (base, sdt_info);
919 gst_element_post_message (GST_ELEMENT_CAST (base),
920 gst_message_new_element (GST_OBJECT (base),
921 gst_structure_copy (sdt_info)));
925 mpegts_base_apply_eit (MpegTSBase * base,
926 guint16 pmt_pid, GstStructure * eit_info)
928 GST_DEBUG_OBJECT (base, "EIT %" GST_PTR_FORMAT, eit_info);
930 mpegts_base_get_tags_from_eit (base, eit_info);
932 gst_element_post_message (GST_ELEMENT_CAST (base),
933 gst_message_new_element (GST_OBJECT (base),
934 gst_structure_copy (eit_info)));
938 mpegts_base_apply_tdt (MpegTSBase * base,
939 guint16 tdt_pid, GstStructure * tdt_info)
941 gst_element_post_message (GST_ELEMENT_CAST (base),
942 gst_message_new_element (GST_OBJECT (base),
943 gst_structure_copy (tdt_info)));
945 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base,
946 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
947 gst_structure_copy (tdt_info)));
952 mpegts_base_handle_psi (MpegTSBase * base, MpegTSPacketizerSection * section)
955 GstStructure *structure = NULL;
957 /* table ids 0x70 - 0x73 do not have a crc */
958 if (G_LIKELY (section->table_id < 0x70 || section->table_id > 0x73)) {
959 if (G_UNLIKELY (mpegts_base_calc_crc32 (GST_BUFFER_DATA (section->buffer),
960 GST_BUFFER_SIZE (section->buffer)) != 0)) {
961 GST_WARNING_OBJECT (base, "bad crc in psi pid 0x%x", section->pid);
966 switch (section->table_id) {
969 structure = mpegts_packetizer_parse_pat (base->packetizer, section);
970 if (G_LIKELY (structure)) {
971 mpegts_base_apply_pat (base, structure);
972 if (base->seen_pat == FALSE) {
973 base->seen_pat = TRUE;
974 base->first_pat_offset = GST_BUFFER_OFFSET (section->buffer);
975 GST_DEBUG ("First PAT offset: %" G_GUINT64_FORMAT,
976 base->first_pat_offset);
984 structure = mpegts_packetizer_parse_pmt (base->packetizer, section);
985 if (G_LIKELY (structure))
986 mpegts_base_apply_pmt (base, section->pid, structure);
992 /* NIT, actual network */
994 /* NIT, other network */
995 structure = mpegts_packetizer_parse_nit (base->packetizer, section);
996 if (G_LIKELY (structure))
997 mpegts_base_apply_nit (base, section->pid, structure);
1004 structure = mpegts_packetizer_parse_sdt (base->packetizer, section);
1005 if (G_LIKELY (structure))
1006 mpegts_base_apply_sdt (base, section->pid, structure);
1012 /* EIT, present/following */
1046 structure = mpegts_packetizer_parse_eit (base->packetizer, section);
1047 if (G_LIKELY (structure))
1048 mpegts_base_apply_eit (base, section->pid, structure);
1053 /* TDT (Time and Date table) */
1054 structure = mpegts_packetizer_parse_tdt (base->packetizer, section);
1055 if (G_LIKELY (structure))
1056 mpegts_base_apply_tdt (base, section->pid, structure);
1065 gst_structure_free (structure);
1071 mpegts_base_get_tags_from_sdt (MpegTSBase * base, GstStructure * sdt_info)
1073 const GValue *services;
1076 services = gst_structure_get_value (sdt_info, "services");
1078 for (i = 0; i < gst_value_list_get_size (services); i++) {
1079 const GstStructure *service;
1080 const gchar *sid_str;
1082 gint program_number;
1083 MpegTSBaseProgram *program;
1085 service = gst_value_get_structure (gst_value_list_get_value (services, i));
1087 /* get program_number from structure name
1088 * which looks like service-%d */
1089 sid_str = gst_structure_get_name (service);
1090 tmp = g_strstr_len (sid_str, -1, "-");
1091 program_number = atoi (++tmp);
1093 program = mpegts_base_get_program (base, program_number);
1094 if (program && !program->tags) {
1095 program->tags = gst_tag_list_new_full (GST_TAG_ARTIST,
1096 gst_structure_get_string (service, "name"), NULL);
1102 mpegts_base_get_tags_from_eit (MpegTSBase * base, GstStructure * eit_info)
1104 const GValue *events;
1106 guint program_number;
1107 MpegTSBaseProgram *program;
1108 gboolean present_following;
1110 gst_structure_get_uint (eit_info, "service-id", &program_number);
1111 program = mpegts_base_get_program (base, program_number);
1113 gst_structure_get_boolean (eit_info, "present-following", &present_following);
1115 if (program && present_following) {
1116 events = gst_structure_get_value (eit_info, "events");
1118 for (i = 0; i < gst_value_list_get_size (events); i++) {
1119 const GstStructure *event;
1125 event = gst_value_get_structure (gst_value_list_get_value (events, i));
1127 title = gst_structure_get_string (event, "name");
1128 gst_structure_get_uint (event, "event-id", &event_id);
1129 gst_structure_get_uint (event, "running-status", &status);
1131 if (title && event_id != program->event_id
1132 && status == RUNNING_STATUS_RUNNING) {
1133 gst_structure_get_uint (event, "duration", &duration);
1135 program->event_id = event_id;
1136 program->tags = gst_tag_list_new_full (GST_TAG_TITLE,
1137 title, GST_TAG_DURATION, duration * GST_SECOND, NULL);
1144 remove_each_program (gpointer key, MpegTSBaseProgram * program,
1147 /* First deactivate it */
1148 mpegts_base_deactivate_program (base, program);
1149 /* Then remove it */
1150 mpegts_base_remove_program (base, program->program_number);
1154 gst_mpegts_base_handle_eos (MpegTSBase * base)
1156 g_hash_table_foreach (base->programs, (GHFunc) remove_each_program, base);
1157 /* finally remove */
1162 mpegts_base_flush (MpegTSBase * base)
1164 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1166 /* Call implementation */
1167 if (G_UNLIKELY (klass->flush == NULL))
1168 GST_WARNING_OBJECT (base, "Class doesn't have a 'flush' implementation !");
1170 klass->flush (base);
1174 mpegts_base_sink_event (GstPad * pad, GstEvent * event)
1176 gboolean res = TRUE;
1177 MpegTSBase *base = GST_MPEGTS_BASE (gst_object_get_parent (GST_OBJECT (pad)));
1179 GST_WARNING_OBJECT (base, "Got event %s",
1180 gst_event_type_get_name (GST_EVENT_TYPE (event)));
1182 switch (GST_EVENT_TYPE (event)) {
1183 case GST_EVENT_NEWSEGMENT:
1186 gdouble rate, applied_rate;
1188 gint64 start, stop, position;
1190 gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
1191 &format, &start, &stop, &position);
1192 GST_DEBUG_OBJECT (base,
1193 "Segment update:%d, rate:%f, applied_rate:%f, format:%s", update,
1194 rate, applied_rate, gst_format_get_name (format));
1195 GST_DEBUG_OBJECT (base,
1196 " start:%" G_GINT64_FORMAT ", stop:%" G_GINT64_FORMAT
1197 ", position:%" G_GINT64_FORMAT, start, stop, position);
1198 gst_segment_set_newsegment_full (&base->segment, update, rate,
1199 applied_rate, format, start, stop, position);
1200 gst_event_unref (event);
1201 base->in_gap = GST_CLOCK_TIME_NONE;
1202 base->first_buf_ts = GST_CLOCK_TIME_NONE;
1206 res = gst_mpegts_base_handle_eos (base);
1208 case GST_EVENT_FLUSH_START:
1209 mpegts_packetizer_flush (base->packetizer);
1210 mpegts_base_flush (base);
1211 res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1212 gst_event_unref (event);
1214 case GST_EVENT_FLUSH_STOP:
1215 gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED);
1216 base->seen_pat = FALSE;
1217 base->first_pat_offset = -1;
1220 res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1221 gst_event_unref (event);
1224 gst_object_unref (base);
1228 static inline GstFlowReturn
1229 mpegts_base_push (MpegTSBase * base, MpegTSPacketizerPacket * packet,
1230 MpegTSPacketizerSection * section)
1232 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1234 /* Call implementation */
1235 if (G_UNLIKELY (klass->push == NULL)) {
1236 GST_ERROR_OBJECT (base, "Class doesn't have a 'push' implementation !");
1237 return GST_FLOW_ERROR;
1240 return klass->push (base, packet, section);
1243 static GstFlowReturn
1244 mpegts_base_chain (GstPad * pad, GstBuffer * buf)
1246 GstFlowReturn res = GST_FLOW_OK;
1249 MpegTSPacketizerPacketReturn pret;
1250 MpegTSPacketizer2 *packetizer;
1251 MpegTSPacketizerPacket packet;
1253 base = GST_MPEGTS_BASE (gst_object_get_parent (GST_OBJECT (pad)));
1254 packetizer = base->packetizer;
1256 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (base->first_buf_ts)) &&
1257 GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1258 base->first_buf_ts = GST_BUFFER_TIMESTAMP (buf);
1259 GST_DEBUG_OBJECT (base, "first buffer timestamp %" GST_TIME_FORMAT,
1260 GST_TIME_ARGS (base->first_buf_ts));
1263 mpegts_packetizer_push (base->packetizer, buf);
1265 mpegts_packetizer_next_packet (base->packetizer,
1266 &packet)) != PACKET_NEED_MORE) && res == GST_FLOW_OK) {
1267 if (G_UNLIKELY (pret == PACKET_BAD))
1268 /* bad header, skip the packet */
1272 if (packet.payload != NULL && mpegts_base_is_psi (base, &packet)) {
1273 MpegTSPacketizerSection section;
1274 based = mpegts_packetizer_push_section (packetizer, &packet, §ion);
1275 if (G_UNLIKELY (!based))
1276 /* bad section data */
1279 if (G_LIKELY (section.complete)) {
1280 /* section complete */
1281 based = mpegts_base_handle_psi (base, §ion);
1282 gst_buffer_unref (section.buffer);
1284 if (G_UNLIKELY (!based))
1288 /* we need to push section packet downstream */
1289 res = mpegts_base_push (base, &packet, §ion);
1291 } else if (MPEGTS_BIT_IS_SET (base->is_pes, packet.pid)) {
1292 /* push the packet downstream */
1293 res = mpegts_base_push (base, &packet, NULL);
1295 gst_buffer_unref (packet.buffer);
1298 mpegts_packetizer_clear_packet (base->packetizer, &packet);
1301 gst_object_unref (base);
1305 static GstFlowReturn
1306 mpegts_base_scan (MpegTSBase * base)
1311 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1313 GST_DEBUG ("Scanning for initial sync point");
1315 /* Find initial sync point */
1316 for (i = 0; i < 10; i++) {
1317 GST_DEBUG ("Grabbing %d => %d",
1318 i * 50 * MPEGTS_MAX_PACKETSIZE, 50 * MPEGTS_MAX_PACKETSIZE);
1319 ret = gst_pad_pull_range (base->sinkpad, i * 50 * MPEGTS_MAX_PACKETSIZE,
1320 50 * MPEGTS_MAX_PACKETSIZE, &buf);
1321 if (G_UNLIKELY (ret != GST_FLOW_OK))
1324 /* Push to packetizer */
1325 mpegts_packetizer_push (base->packetizer, buf);
1327 if (mpegts_packetizer_has_packets (base->packetizer)) {
1328 /* Mark the initial sync point and remember the packetsize */
1329 base->initial_sync_point = base->seek_offset = base->packetizer->offset;
1330 GST_DEBUG ("Sync point is now %" G_GUINT64_FORMAT, base->seek_offset);
1331 base->packetsize = base->packetizer->packet_size;
1333 /* If the subclass can seek for timestamps, do that */
1334 if (klass->find_timestamps) {
1336 mpegts_packetizer_clear (base->packetizer);
1338 ret = klass->find_timestamps (base, 0, &offset);
1340 base->initial_sync_point = base->seek_offset =
1341 base->packetizer->offset = base->first_pat_offset;
1342 GST_DEBUG ("Sync point is now %" G_GUINT64_FORMAT, base->seek_offset);
1348 GST_WARNING ("Didn't find initial sync point");
1349 ret = GST_FLOW_ERROR;
1352 mpegts_packetizer_clear (base->packetizer);
1359 mpegts_base_loop (MpegTSBase * base)
1361 GstFlowReturn ret = GST_FLOW_ERROR;
1362 switch (base->mode) {
1363 case BASE_MODE_SCANNING:
1364 /* Find first sync point */
1365 ret = mpegts_base_scan (base);
1366 if (G_UNLIKELY (ret != GST_FLOW_OK))
1368 base->mode = BASE_MODE_STREAMING;
1369 GST_DEBUG ("Changing to Streaming");
1371 case BASE_MODE_SEEKING:
1372 /* FIXME : yes, we should do something here */
1373 base->mode = BASE_MODE_STREAMING;
1375 case BASE_MODE_STREAMING:
1379 GST_DEBUG ("Pulling data from %" G_GUINT64_FORMAT, base->seek_offset);
1381 ret = gst_pad_pull_range (base->sinkpad, base->seek_offset,
1382 100 * base->packetsize, &buf);
1383 if (G_UNLIKELY (ret != GST_FLOW_OK))
1385 base->seek_offset += GST_BUFFER_SIZE (buf);
1386 ret = mpegts_base_chain (base->sinkpad, buf);
1387 if (G_UNLIKELY (ret != GST_FLOW_OK))
1391 case BASE_MODE_PUSHING:
1392 GST_WARNING ("wrong BASE_MODE_PUSHING mode in pull loop");
1400 const gchar *reason = gst_flow_get_name (ret);
1401 GST_DEBUG_OBJECT (base, "Pausing task, reason %s", reason);
1402 if (ret == GST_FLOW_UNEXPECTED)
1403 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, gst_event_new_eos ());
1404 else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_UNEXPECTED) {
1405 GST_ELEMENT_ERROR (base, STREAM, FAILED,
1406 (_("Internal data stream error.")),
1407 ("stream stopped, reason %s", reason));
1408 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, gst_event_new_eos ());
1410 gst_pad_pause_task (base->sinkpad);
1416 mpegts_base_handle_seek_event (MpegTSBase * base, GstPad * pad,
1419 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1420 GstFlowReturn ret = GST_FLOW_ERROR;
1425 GstSeekType start_type, stop_type;
1430 gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
1433 if (format != GST_FORMAT_TIME)
1436 /* First try if upstream supports seeking in TIME format */
1437 if (gst_pad_push_event (base->sinkpad, gst_event_ref (event))) {
1438 GST_DEBUG ("upstream handled SEEK event");
1439 gst_event_unref (event);
1443 GST_DEBUG ("seek event, rate: %f start: %" GST_TIME_FORMAT
1444 " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start),
1445 GST_TIME_ARGS (stop));
1447 /* extract the pid from the pad name */
1448 pad_name = gst_pad_get_name (pad);
1450 gchar *pidstr = g_strrstr (pad_name, "_");
1453 pid = g_ascii_strtoull (pidstr, NULL, 16);
1458 flush = flags & GST_SEEK_FLAG_FLUSH;
1460 if (base->mode == BASE_MODE_PUSHING) {
1461 GST_ERROR ("seeking in push mode not supported");
1465 /* stop streaming, either by flushing or by pausing the task */
1466 base->mode = BASE_MODE_SEEKING;
1468 GST_DEBUG_OBJECT (base, "sending flush start");
1469 gst_pad_push_event (base->sinkpad, gst_event_new_flush_start ());
1470 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base,
1471 gst_event_new_flush_start ());
1473 gst_pad_pause_task (base->sinkpad);
1474 /* wait for streaming to finish */
1475 GST_PAD_STREAM_LOCK (base->sinkpad);
1478 /* send a FLUSH_STOP for the sinkpad, since we need data for seeking */
1479 GST_DEBUG_OBJECT (base, "sending flush stop");
1480 gst_pad_push_event (base->sinkpad, gst_event_new_flush_stop ());
1483 if (flags & (GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_SKIP)) {
1484 GST_WARNING ("seek flags 0x%x are not supported", (int) flags);
1489 if (format == GST_FORMAT_TIME) {
1490 /* If the subclass can seek, do that */
1492 ret = klass->seek (base, event, pid);
1493 if (G_UNLIKELY (ret != GST_FLOW_OK)) {
1494 GST_WARNING ("seeking failed %s", gst_flow_get_name (ret));
1498 GST_WARNING ("subclass has no seek implementation");
1504 /* if we sent a FLUSH_START, we now send a FLUSH_STOP */
1505 GST_DEBUG_OBJECT (base, "sending flush stop");
1506 //gst_pad_push_event (base->sinkpad, gst_event_new_flush_stop ());
1507 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base,
1508 gst_event_new_flush_stop ());
1512 gst_pad_start_task (base->sinkpad, (GstTaskFunction) mpegts_base_loop, base);
1514 GST_PAD_STREAM_UNLOCK (base->sinkpad);
1515 return ret == GST_FLOW_OK;
1520 mpegts_base_sink_activate (GstPad * pad)
1522 if (gst_pad_check_pull_range (pad)) {
1523 GST_DEBUG_OBJECT (pad, "activating pull");
1524 return gst_pad_activate_pull (pad, TRUE);
1526 GST_DEBUG_OBJECT (pad, "activating push");
1527 return gst_pad_activate_push (pad, TRUE);
1532 mpegts_base_sink_activate_pull (GstPad * pad, gboolean active)
1534 MpegTSBase *base = GST_MPEGTS_BASE (GST_OBJECT_PARENT (pad));
1536 base->mode = BASE_MODE_SCANNING;
1537 return gst_pad_start_task (pad, (GstTaskFunction) mpegts_base_loop, base);
1539 return gst_pad_stop_task (pad);
1543 mpegts_base_sink_activate_push (GstPad * pad, gboolean active)
1545 MpegTSBase *base = GST_MPEGTS_BASE (GST_OBJECT_PARENT (pad));
1546 base->mode = BASE_MODE_PUSHING;
1551 static GstStateChangeReturn
1552 mpegts_base_change_state (GstElement * element, GstStateChange transition)
1555 GstStateChangeReturn ret;
1557 base = GST_MPEGTS_BASE (element);
1558 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1560 switch (transition) {
1561 case GST_STATE_CHANGE_PAUSED_TO_READY:
1562 mpegts_base_reset (base);
1563 if (base->mode != BASE_MODE_PUSHING)
1564 base->mode = BASE_MODE_SCANNING;
1574 gst_mpegtsbase_plugin_init (GstPlugin * plugin)
1576 GST_DEBUG_CATEGORY_INIT (mpegts_base_debug, "mpegtsbase", 0,
1577 "MPEG transport stream base class");
1579 gst_mpegtsdesc_init_debug ();