blob: d46a47e94ee2fd98561e5d76e66f0b78dc2a6032 [file] [log] [blame]
Olivier Naudanb28313f2012-04-16 08:10:18 -04001/*
2 * mpegtsbase.c -
3 * Copyright (C) 2007 Alessandro Decina
4 * 2010 Edward Hervey
5 * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
6 * Author: Youness Alaoui <youness.alaoui@collabora.co.uk>, Collabora Ltd.
7 * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
8 * Author: Edward Hervey <bilboed@bilboed.com>, Collabora Ltd.
9 *
10 * Authors:
11 * Alessandro Decina <alessandro@nnva.org>
12 * Zaheer Abbas Merali <zaheerabbas at merali dot org>
13 * Edward Hervey <edward.hervey@collabora.co.uk>
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Library General Public
17 * License as published by the Free Software Foundation; either
18 * version 2 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Library General Public License for more details.
24 *
25 * You should have received a copy of the GNU Library General Public
26 * License along with this library; if not, write to the
Sebastian Drögee6513c12013-07-14 12:12:42 +020027 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
28 * Boston, MA 02110-1301, USA.
Olivier Naudanb28313f2012-04-16 08:10:18 -040029 */
30
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
Olivier Naudanb28313f2012-04-16 08:10:18 -040035#include <stdlib.h>
36#include <string.h>
37
38#include <glib.h>
39
40#include <gst/gst-i18n-plugin.h>
41#include "mpegtsbase.h"
42#include "gstmpegdesc.h"
43
Olivier Naudanb28313f2012-04-16 08:10:18 -040044#define RUNNING_STATUS_RUNNING 4
45
46GST_DEBUG_CATEGORY_STATIC (mpegts_base_debug);
47#define GST_CAT_DEFAULT mpegts_base_debug
48
49static GQuark QUARK_PROGRAMS;
50static GQuark QUARK_PROGRAM_NUMBER;
51static GQuark QUARK_PID;
52static GQuark QUARK_PCR_PID;
53static GQuark QUARK_STREAMS;
54static GQuark QUARK_STREAM_TYPE;
55
56static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
57 GST_PAD_SINK,
58 GST_PAD_ALWAYS,
59 GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
60 );
61
62enum
63{
Sebastian Drögee6513c12013-07-14 12:12:42 +020064 PROP_0,
65 PROP_PARSE_PRIVATE_SECTIONS,
Olivier Naudanb28313f2012-04-16 08:10:18 -040066 /* FILL ME */
67};
68
Sebastian Drögee6513c12013-07-14 12:12:42 +020069static void mpegts_base_dispose (GObject * object);
70static void mpegts_base_finalize (GObject * object);
Olivier Naudanb28313f2012-04-16 08:10:18 -040071static void mpegts_base_set_property (GObject * object, guint prop_id,
72 const GValue * value, GParamSpec * pspec);
73static void mpegts_base_get_property (GObject * object, guint prop_id,
74 GValue * value, GParamSpec * pspec);
Sebastian Drögee6513c12013-07-14 12:12:42 +020075
Olivier Naudanb28313f2012-04-16 08:10:18 -040076static void mpegts_base_free_program (MpegTSBaseProgram * program);
Sebastian Dröge7e661862012-05-21 15:50:23 +020077static gboolean mpegts_base_sink_activate (GstPad * pad, GstObject * parent);
78static gboolean mpegts_base_sink_activate_mode (GstPad * pad,
79 GstObject * parent, GstPadMode mode, gboolean active);
80static GstFlowReturn mpegts_base_chain (GstPad * pad, GstObject * parent,
81 GstBuffer * buf);
82static gboolean mpegts_base_sink_event (GstPad * pad, GstObject * parent,
83 GstEvent * event);
Olivier Naudanb28313f2012-04-16 08:10:18 -040084static GstStateChangeReturn mpegts_base_change_state (GstElement * element,
85 GstStateChange transition);
Sebastian Drögee6513c12013-07-14 12:12:42 +020086static gboolean mpegts_base_get_tags_from_eit (MpegTSBase * base,
Sebastian Dröge4139fce2015-03-17 09:38:41 +010087 GstMpegtsSection * section);
Sebastian Dröge1491e032014-06-22 19:09:53 +020088static gboolean mpegts_base_parse_atsc_mgt (MpegTSBase * base,
Sebastian Dröge4139fce2015-03-17 09:38:41 +010089 GstMpegtsSection * section);
Sebastian Drögee6513c12013-07-14 12:12:42 +020090static gboolean remove_each_program (gpointer key, MpegTSBaseProgram * program,
Olivier Naudanb28313f2012-04-16 08:10:18 -040091 MpegTSBase * base);
92
Sebastian Dröge7e661862012-05-21 15:50:23 +020093static void
94_extra_init (void)
95{
96 QUARK_PROGRAMS = g_quark_from_string ("programs");
97 QUARK_PROGRAM_NUMBER = g_quark_from_string ("program-number");
98 QUARK_PID = g_quark_from_string ("pid");
99 QUARK_PCR_PID = g_quark_from_string ("pcr-pid");
100 QUARK_STREAMS = g_quark_from_string ("streams");
101 QUARK_STREAM_TYPE = g_quark_from_string ("stream-type");
102}
Olivier Naudanb28313f2012-04-16 08:10:18 -0400103
Sebastian Dröge7e661862012-05-21 15:50:23 +0200104#define mpegts_base_parent_class parent_class
105G_DEFINE_TYPE_WITH_CODE (MpegTSBase, mpegts_base, GST_TYPE_ELEMENT,
106 _extra_init ());
Olivier Naudanb28313f2012-04-16 08:10:18 -0400107
Olivier Naudanb28313f2012-04-16 08:10:18 -0400108static void
Olivier Naudanb28313f2012-04-16 08:10:18 -0400109mpegts_base_class_init (MpegTSBaseClass * klass)
110{
111 GObjectClass *gobject_class;
112 GstElementClass *element_class;
113
114 element_class = GST_ELEMENT_CLASS (klass);
115 element_class->change_state = mpegts_base_change_state;
116
Sebastian Dröge7e661862012-05-21 15:50:23 +0200117 gst_element_class_add_pad_template (element_class,
118 gst_static_pad_template_get (&sink_template));
119
Olivier Naudanb28313f2012-04-16 08:10:18 -0400120 gobject_class = G_OBJECT_CLASS (klass);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400121 gobject_class->dispose = mpegts_base_dispose;
122 gobject_class->finalize = mpegts_base_finalize;
Sebastian Drögee6513c12013-07-14 12:12:42 +0200123 gobject_class->set_property = mpegts_base_set_property;
124 gobject_class->get_property = mpegts_base_get_property;
125
126 g_object_class_install_property (gobject_class, PROP_PARSE_PRIVATE_SECTIONS,
127 g_param_spec_boolean ("parse-private-sections", "Parse private sections",
128 "Parse private sections", FALSE,
129 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Olivier Naudanb28313f2012-04-16 08:10:18 -0400130
131}
132
133static void
Sebastian Drögee6513c12013-07-14 12:12:42 +0200134mpegts_base_set_property (GObject * object, guint prop_id,
135 const GValue * value, GParamSpec * pspec)
136{
137 MpegTSBase *base = GST_MPEGTS_BASE (object);
138
139 switch (prop_id) {
140 case PROP_PARSE_PRIVATE_SECTIONS:
141 base->parse_private_sections = g_value_get_boolean (value);
142 break;
143 default:
144 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
145 }
146}
147
148static void
149mpegts_base_get_property (GObject * object, guint prop_id,
150 GValue * value, GParamSpec * pspec)
151{
152 MpegTSBase *base = GST_MPEGTS_BASE (object);
153
154 switch (prop_id) {
155 case PROP_PARSE_PRIVATE_SECTIONS:
156 g_value_set_boolean (value, base->parse_private_sections);
157 break;
158 default:
159 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160 }
161}
162
163
164static void
Olivier Naudanb28313f2012-04-16 08:10:18 -0400165mpegts_base_reset (MpegTSBase * base)
166{
167 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
168
169 mpegts_packetizer_clear (base->packetizer);
170 memset (base->is_pes, 0, 1024);
171 memset (base->known_psi, 0, 1024);
172
Sebastian Drögee6513c12013-07-14 12:12:42 +0200173 /* FIXME : Actually these are not *always* know SI streams
174 * depending on the variant of mpeg-ts being used. */
175
Sebastian Dröge63e92012012-08-09 11:32:59 +0200176 /* Known PIDs : PAT, TSDT, IPMP CIT */
Olivier Naudanb28313f2012-04-16 08:10:18 -0400177 MPEGTS_BIT_SET (base->known_psi, 0);
Sebastian Dröge63e92012012-08-09 11:32:59 +0200178 MPEGTS_BIT_SET (base->known_psi, 2);
179 MPEGTS_BIT_SET (base->known_psi, 3);
180 /* TDT, TOT, ST */
181 MPEGTS_BIT_SET (base->known_psi, 0x14);
182 /* network synchronization */
183 MPEGTS_BIT_SET (base->known_psi, 0x15);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400184
Sebastian Drögee6513c12013-07-14 12:12:42 +0200185 /* ATSC */
186 MPEGTS_BIT_SET (base->known_psi, 0x1ffb);
187
Sebastian Dröge12682a62014-05-03 19:58:45 +0200188 if (base->pat) {
189 g_ptr_array_unref (base->pat);
190 base->pat = NULL;
191 }
Olivier Naudanb28313f2012-04-16 08:10:18 -0400192
193 gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED);
Sebastian Dröge1064a452013-07-30 08:48:14 +0200194 base->last_seek_seqnum = (guint32) - 1;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400195
196 base->mode = BASE_MODE_STREAMING;
197 base->seen_pat = FALSE;
198 base->seek_offset = -1;
199
Olivier Naudanb28313f2012-04-16 08:10:18 -0400200 g_hash_table_foreach_remove (base->programs, (GHRFunc) remove_each_program,
201 base);
202
203 if (klass->reset)
204 klass->reset (base);
205}
206
207static void
Sebastian Dröge7e661862012-05-21 15:50:23 +0200208mpegts_base_init (MpegTSBase * base)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400209{
210 base->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
211 gst_pad_set_activate_function (base->sinkpad, mpegts_base_sink_activate);
Sebastian Dröge7e661862012-05-21 15:50:23 +0200212 gst_pad_set_activatemode_function (base->sinkpad,
213 mpegts_base_sink_activate_mode);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400214 gst_pad_set_chain_function (base->sinkpad, mpegts_base_chain);
215 gst_pad_set_event_function (base->sinkpad, mpegts_base_sink_event);
216 gst_element_add_pad (GST_ELEMENT (base), base->sinkpad);
217
218 base->disposed = FALSE;
219 base->packetizer = mpegts_packetizer_new ();
220 base->programs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
221 NULL, (GDestroyNotify) mpegts_base_free_program);
222
Sebastian Drögee6513c12013-07-14 12:12:42 +0200223 base->parse_private_sections = FALSE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400224 base->is_pes = g_new0 (guint8, 1024);
225 base->known_psi = g_new0 (guint8, 1024);
226 base->program_size = sizeof (MpegTSBaseProgram);
227 base->stream_size = sizeof (MpegTSBaseStream);
228
Sebastian Drögee6513c12013-07-14 12:12:42 +0200229 base->push_data = TRUE;
230 base->push_section = TRUE;
231
Olivier Naudanb28313f2012-04-16 08:10:18 -0400232 mpegts_base_reset (base);
233}
234
235static void
236mpegts_base_dispose (GObject * object)
237{
238 MpegTSBase *base = GST_MPEGTS_BASE (object);
239
240 if (!base->disposed) {
241 g_object_unref (base->packetizer);
242 base->disposed = TRUE;
243 g_free (base->known_psi);
244 g_free (base->is_pes);
245 }
246
247 if (G_OBJECT_CLASS (parent_class)->dispose)
248 G_OBJECT_CLASS (parent_class)->dispose (object);
249}
250
251static void
252mpegts_base_finalize (GObject * object)
253{
254 MpegTSBase *base = GST_MPEGTS_BASE (object);
255
256 if (base->pat) {
Sebastian Drögedd8e1152013-08-30 12:58:53 +0200257 g_ptr_array_unref (base->pat);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400258 base->pat = NULL;
259 }
260 g_hash_table_destroy (base->programs);
261
262 if (G_OBJECT_CLASS (parent_class)->finalize)
263 G_OBJECT_CLASS (parent_class)->finalize (object);
264}
265
Olivier Naudanb28313f2012-04-16 08:10:18 -0400266
267/* returns NULL if no matching descriptor found *
268 * otherwise returns a descriptor that needs to *
269 * be freed */
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100270const GstMpegtsDescriptor *
Olivier Naudanb28313f2012-04-16 08:10:18 -0400271mpegts_get_descriptor_from_stream (MpegTSBaseStream * stream, guint8 tag)
272{
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100273 GstMpegtsPMTStream *pmt = stream->stream;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400274
Sebastian Drögee6513c12013-07-14 12:12:42 +0200275 GST_DEBUG ("Searching for tag 0x%02x in stream 0x%04x (stream_type 0x%02x)",
276 tag, stream->pid, stream->stream_type);
Sebastian Dröge7eb51492012-06-08 13:11:50 +0200277
Sebastian Dröge12682a62014-05-03 19:58:45 +0200278 return gst_mpegts_find_descriptor (pmt->descriptors, tag);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400279}
280
281typedef struct
282{
283 gboolean res;
284 guint16 pid;
285} PIDLookup;
286
287static void
288foreach_pid_in_program (gpointer key, MpegTSBaseProgram * program,
289 PIDLookup * lookup)
290{
291 if (!program->active)
292 return;
293 if (program->streams[lookup->pid])
294 lookup->res = TRUE;
295}
296
297static gboolean
298mpegts_pid_in_active_programs (MpegTSBase * base, guint16 pid)
299{
300 PIDLookup lookup;
301
302 lookup.res = FALSE;
303 lookup.pid = pid;
304 g_hash_table_foreach (base->programs, (GHFunc) foreach_pid_in_program,
305 &lookup);
306
307 return lookup.res;
308}
309
310/* returns NULL if no matching descriptor found *
311 * otherwise returns a descriptor that needs to *
312 * be freed */
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100313const GstMpegtsDescriptor *
Olivier Naudanb28313f2012-04-16 08:10:18 -0400314mpegts_get_descriptor_from_program (MpegTSBaseProgram * program, guint8 tag)
315{
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100316 const GstMpegtsPMT *pmt = program->pmt;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400317
Sebastian Dröge12682a62014-05-03 19:58:45 +0200318 return gst_mpegts_find_descriptor (pmt->descriptors, tag);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400319}
320
321static MpegTSBaseProgram *
322mpegts_base_new_program (MpegTSBase * base,
323 gint program_number, guint16 pmt_pid)
324{
325 MpegTSBaseProgram *program;
326
327 GST_DEBUG_OBJECT (base, "program_number : %d, pmt_pid : %d",
328 program_number, pmt_pid);
329
330 program = g_malloc0 (base->program_size);
331 program->program_number = program_number;
332 program->pmt_pid = pmt_pid;
333 program->pcr_pid = G_MAXUINT16;
334 program->streams = g_new0 (MpegTSBaseStream *, 0x2000);
335 program->patcount = 0;
336
337 return program;
338}
339
340MpegTSBaseProgram *
341mpegts_base_add_program (MpegTSBase * base,
342 gint program_number, guint16 pmt_pid)
343{
344 MpegTSBaseProgram *program;
345
346 GST_DEBUG_OBJECT (base, "program_number : %d, pmt_pid : %d",
347 program_number, pmt_pid);
348
349 program = mpegts_base_new_program (base, program_number, pmt_pid);
350
351 /* Mark the PMT PID as being a known PSI PID */
Sebastian Drögee6513c12013-07-14 12:12:42 +0200352 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi, pmt_pid))) {
353 GST_FIXME ("Refcounting. Setting twice a PID (0x%04x) as known PSI",
354 pmt_pid);
355 }
Olivier Naudanb28313f2012-04-16 08:10:18 -0400356 MPEGTS_BIT_SET (base->known_psi, pmt_pid);
357
358 g_hash_table_insert (base->programs,
359 GINT_TO_POINTER (program_number), program);
360
361 return program;
362}
363
364MpegTSBaseProgram *
365mpegts_base_get_program (MpegTSBase * base, gint program_number)
366{
367 MpegTSBaseProgram *program;
368
369 program = (MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
370 GINT_TO_POINTER ((gint) program_number));
371
372 return program;
373}
374
375static MpegTSBaseProgram *
376mpegts_base_steal_program (MpegTSBase * base, gint program_number)
377{
378 MpegTSBaseProgram *program;
379
380 program = (MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
381 GINT_TO_POINTER ((gint) program_number));
382
383 if (program)
384 g_hash_table_steal (base->programs,
385 GINT_TO_POINTER ((gint) program_number));
386
387 return program;
388}
389
390static void
391mpegts_base_free_program (MpegTSBaseProgram * program)
392{
393 GList *tmp;
394
Sebastian Drögee6513c12013-07-14 12:12:42 +0200395 if (program->pmt) {
396 gst_mpegts_section_unref (program->section);
397 program->pmt = NULL;
398 }
Olivier Naudanb28313f2012-04-16 08:10:18 -0400399
400 for (tmp = program->stream_list; tmp; tmp = tmp->next)
Sebastian Drögee6513c12013-07-14 12:12:42 +0200401 g_free (tmp->data);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400402 if (program->stream_list)
403 g_list_free (program->stream_list);
404
405 g_free (program->streams);
406
407 if (program->tags)
Sebastian Dröge63e92012012-08-09 11:32:59 +0200408 gst_tag_list_unref (program->tags);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400409
410 g_free (program);
411}
412
Olivier Naudanb28313f2012-04-16 08:10:18 -0400413void
414mpegts_base_remove_program (MpegTSBase * base, gint program_number)
415{
Olivier Naudanb28313f2012-04-16 08:10:18 -0400416 GST_DEBUG_OBJECT (base, "program_number : %d", program_number);
417
Olivier Naudanb28313f2012-04-16 08:10:18 -0400418 g_hash_table_remove (base->programs, GINT_TO_POINTER (program_number));
419}
420
Sebastian Drögee6513c12013-07-14 12:12:42 +0200421static guint32
Sebastian Drögedd8e1152013-08-30 12:58:53 +0200422get_registration_from_descriptors (GPtrArray * descriptors)
Sebastian Drögee6513c12013-07-14 12:12:42 +0200423{
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100424 const GstMpegtsDescriptor *desc;
Sebastian Drögee6513c12013-07-14 12:12:42 +0200425
426 if ((desc =
427 gst_mpegts_find_descriptor (descriptors,
428 GST_MTS_DESC_REGISTRATION))) {
Sebastian Drögedd8e1152013-08-30 12:58:53 +0200429 if (G_UNLIKELY (desc->length < 4)) {
Sebastian Drögee6513c12013-07-14 12:12:42 +0200430 GST_WARNING ("Registration descriptor with length < 4. (Corrupted ?)");
431 } else
Sebastian Drögedd8e1152013-08-30 12:58:53 +0200432 return GST_READ_UINT32_BE (desc->data + 2);
Sebastian Drögee6513c12013-07-14 12:12:42 +0200433 }
434
435 return 0;
436}
437
Olivier Naudanb28313f2012-04-16 08:10:18 -0400438static MpegTSBaseStream *
439mpegts_base_program_add_stream (MpegTSBase * base,
440 MpegTSBaseProgram * program, guint16 pid, guint8 stream_type,
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100441 GstMpegtsPMTStream * stream)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400442{
443 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
Sebastian Drögee6513c12013-07-14 12:12:42 +0200444 MpegTSBaseStream *bstream;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400445
Sebastian Drögee6513c12013-07-14 12:12:42 +0200446 GST_DEBUG ("pid:0x%04x, stream_type:0x%03x", pid, stream_type);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400447
448 if (G_UNLIKELY (program->streams[pid])) {
Sebastian Drögee6513c12013-07-14 12:12:42 +0200449 if (stream_type != 0xff)
450 GST_WARNING ("Stream already present !");
Olivier Naudanb28313f2012-04-16 08:10:18 -0400451 return NULL;
452 }
453
Sebastian Drögee6513c12013-07-14 12:12:42 +0200454 bstream = g_malloc0 (base->stream_size);
455 bstream->pid = pid;
456 bstream->stream_type = stream_type;
457 bstream->stream = stream;
458 if (stream) {
459 bstream->registration_id =
460 get_registration_from_descriptors (stream->descriptors);
461 GST_DEBUG ("PID 0x%04x, registration_id %" SAFE_FOURCC_FORMAT,
462 bstream->pid, SAFE_FOURCC_ARGS (bstream->registration_id));
463 }
Olivier Naudanb28313f2012-04-16 08:10:18 -0400464
Sebastian Drögee6513c12013-07-14 12:12:42 +0200465
466 program->streams[pid] = bstream;
467 program->stream_list = g_list_append (program->stream_list, bstream);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400468
469 if (klass->stream_added)
Sebastian Drögee6513c12013-07-14 12:12:42 +0200470 klass->stream_added (base, bstream, program);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400471
Sebastian Drögee6513c12013-07-14 12:12:42 +0200472 return bstream;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400473}
474
475void
476mpegts_base_program_remove_stream (MpegTSBase * base,
477 MpegTSBaseProgram * program, guint16 pid)
478{
479 MpegTSBaseClass *klass;
480 MpegTSBaseStream *stream = program->streams[pid];
481
482 GST_DEBUG ("pid:0x%04x", pid);
483
484 if (G_UNLIKELY (stream == NULL)) {
485 /* Can happen if the PCR PID is the same as a audio/video PID */
486 GST_DEBUG ("Stream already removed");
487 return;
488 }
489
490 klass = GST_MPEGTS_BASE_GET_CLASS (base);
491
492 /* If subclass needs it, inform it of the stream we are about to remove */
493 if (klass->stream_removed)
494 klass->stream_removed (base, stream);
495
496 program->stream_list = g_list_remove_all (program->stream_list, stream);
Sebastian Drögee6513c12013-07-14 12:12:42 +0200497 g_free (stream);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400498 program->streams[pid] = NULL;
499}
500
501/* Return TRUE if programs are equal */
502static gboolean
503mpegts_base_is_same_program (MpegTSBase * base, MpegTSBaseProgram * oldprogram,
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100504 guint16 new_pmt_pid, const GstMpegtsPMT * new_pmt)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400505{
506 guint i, nbstreams;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400507 MpegTSBaseStream *oldstream;
508 gboolean sawpcrpid = FALSE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400509
510 if (oldprogram->pmt_pid != new_pmt_pid) {
511 GST_DEBUG ("Different pmt_pid (new:0x%04x, old:0x%04x)", new_pmt_pid,
512 oldprogram->pmt_pid);
513 return FALSE;
514 }
515
Sebastian Drögee6513c12013-07-14 12:12:42 +0200516 if (oldprogram->pcr_pid != new_pmt->pcr_pid) {
Olivier Naudanb28313f2012-04-16 08:10:18 -0400517 GST_DEBUG ("Different pcr_pid (new:0x%04x, old:0x%04x)",
Sebastian Drögee6513c12013-07-14 12:12:42 +0200518 new_pmt->pcr_pid, oldprogram->pcr_pid);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400519 return FALSE;
520 }
521
522 /* Check the streams */
Sebastian Drögee6513c12013-07-14 12:12:42 +0200523 nbstreams = new_pmt->streams->len;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400524 for (i = 0; i < nbstreams; ++i) {
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100525 GstMpegtsPMTStream *stream = g_ptr_array_index (new_pmt->streams, i);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400526
Sebastian Drögee6513c12013-07-14 12:12:42 +0200527 oldstream = oldprogram->streams[stream->pid];
Olivier Naudanb28313f2012-04-16 08:10:18 -0400528 if (!oldstream) {
Sebastian Drögee6513c12013-07-14 12:12:42 +0200529 GST_DEBUG ("New stream 0x%04x not present in old program", stream->pid);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400530 return FALSE;
531 }
Sebastian Drögee6513c12013-07-14 12:12:42 +0200532 if (oldstream->stream_type != stream->stream_type) {
Olivier Naudanb28313f2012-04-16 08:10:18 -0400533 GST_DEBUG
534 ("New stream 0x%04x has a different stream type (new:%d, old:%d)",
Sebastian Drögee6513c12013-07-14 12:12:42 +0200535 stream->pid, stream->stream_type, oldstream->stream_type);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400536 return FALSE;
537 }
Sebastian Drögee6513c12013-07-14 12:12:42 +0200538 if (stream->pid == oldprogram->pcr_pid)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400539 sawpcrpid = TRUE;
540 }
541
542 /* If the pcr is not shared with an existing stream, we'll have one extra stream */
543 if (!sawpcrpid)
544 nbstreams += 1;
545
546 if (nbstreams != g_list_length (oldprogram->stream_list)) {
547 GST_DEBUG ("Different number of streams (new:%d, old:%d)",
548 nbstreams, g_list_length (oldprogram->stream_list));
549 return FALSE;
550 }
551
552 GST_DEBUG ("Programs are equal");
553 return TRUE;
554}
555
556static void
557mpegts_base_deactivate_program (MpegTSBase * base, MpegTSBaseProgram * program)
558{
Sebastian Drögee6513c12013-07-14 12:12:42 +0200559 gint i;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400560 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
561
562 if (G_UNLIKELY (program->active == FALSE))
563 return;
564
565 GST_DEBUG_OBJECT (base, "Deactivating PMT");
566
567 program->active = FALSE;
568
Sebastian Drögee6513c12013-07-14 12:12:42 +0200569 if (program->pmt) {
570 for (i = 0; i < program->pmt->streams->len; ++i) {
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100571 GstMpegtsPMTStream *stream = g_ptr_array_index (program->pmt->streams, i);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400572
Sebastian Drögee6513c12013-07-14 12:12:42 +0200573 mpegts_base_program_remove_stream (base, program, stream->pid);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400574
Sebastian Drögee6513c12013-07-14 12:12:42 +0200575 /* Only unset the is_pes/known_psi bit if the PID isn't used in any other active
Olivier Naudanb28313f2012-04-16 08:10:18 -0400576 * program */
Sebastian Drögee6513c12013-07-14 12:12:42 +0200577 if (!mpegts_pid_in_active_programs (base, stream->pid)) {
578 switch (stream->stream_type) {
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100579 case GST_MPEGTS_STREAM_TYPE_SCTE_DSMCC_DCB:
580 case GST_MPEGTS_STREAM_TYPE_SCTE_SIGNALING:
Sebastian Dröge12682a62014-05-03 19:58:45 +0200581 {
582 guint32 registration_id =
583 get_registration_from_descriptors (stream->descriptors);
584
585 /* Not a private section stream */
586 if (registration_id != DRF_ID_CUEI
587 && registration_id != DRF_ID_ETV1)
588 break;
589 /* Fall through on purpose - remove this PID from known_psi */
590 }
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100591 case GST_MPEGTS_STREAM_TYPE_PRIVATE_SECTIONS:
592 case GST_MPEGTS_STREAM_TYPE_MHEG:
593 case GST_MPEGTS_STREAM_TYPE_DSM_CC:
594 case GST_MPEGTS_STREAM_TYPE_DSMCC_A:
595 case GST_MPEGTS_STREAM_TYPE_DSMCC_B:
596 case GST_MPEGTS_STREAM_TYPE_DSMCC_C:
597 case GST_MPEGTS_STREAM_TYPE_DSMCC_D:
598 case GST_MPEGTS_STREAM_TYPE_SL_FLEXMUX_SECTIONS:
599 case GST_MPEGTS_STREAM_TYPE_METADATA_SECTIONS:
Sebastian Drögee6513c12013-07-14 12:12:42 +0200600 /* Set known PSI streams */
601 if (base->parse_private_sections)
602 MPEGTS_BIT_UNSET (base->known_psi, stream->pid);
603 break;
604 default:
605 MPEGTS_BIT_UNSET (base->is_pes, stream->pid);
606 break;
607 }
608 }
Olivier Naudanb28313f2012-04-16 08:10:18 -0400609 }
610
611 /* remove pcr stream */
612 /* FIXME : This might actually be shared with another stream ? */
613 mpegts_base_program_remove_stream (base, program, program->pcr_pid);
614 if (!mpegts_pid_in_active_programs (base, program->pcr_pid))
615 MPEGTS_BIT_UNSET (base->is_pes, program->pcr_pid);
616
617 GST_DEBUG ("program stream_list is now %p", program->stream_list);
618 }
Sebastian Dröge12491072013-07-13 11:31:28 +0200619
620 /* Inform subclasses we're deactivating this program */
621 if (klass->program_stopped)
622 klass->program_stopped (base, program);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400623}
624
625static void
626mpegts_base_activate_program (MpegTSBase * base, MpegTSBaseProgram * program,
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100627 guint16 pmt_pid, GstMpegtsSection * section, const GstMpegtsPMT * pmt,
Sebastian Drögee6513c12013-07-14 12:12:42 +0200628 gboolean initial_program)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400629{
Sebastian Drögee6513c12013-07-14 12:12:42 +0200630 guint i;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400631 MpegTSBaseClass *klass;
632
633 if (G_UNLIKELY (program->active))
634 return;
635
636 GST_DEBUG ("Activating program %d", program->program_number);
637
Olivier Naudanb28313f2012-04-16 08:10:18 -0400638 /* activate new pmt */
Sebastian Drögee6513c12013-07-14 12:12:42 +0200639 if (program->section)
640 gst_mpegts_section_unref (program->section);
641 program->section = gst_mpegts_section_ref (section);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400642
Sebastian Drögee6513c12013-07-14 12:12:42 +0200643 program->pmt = pmt;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400644 program->pmt_pid = pmt_pid;
Sebastian Drögee6513c12013-07-14 12:12:42 +0200645 program->pcr_pid = pmt->pcr_pid;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400646
Sebastian Drögee6513c12013-07-14 12:12:42 +0200647 /* extract top-level registration_id if present */
648 program->registration_id =
649 get_registration_from_descriptors (pmt->descriptors);
650 GST_DEBUG ("program 0x%04x, registration_id %" SAFE_FOURCC_FORMAT,
651 program->program_number, SAFE_FOURCC_ARGS (program->registration_id));
Olivier Naudanb28313f2012-04-16 08:10:18 -0400652
Sebastian Drögee6513c12013-07-14 12:12:42 +0200653 for (i = 0; i < pmt->streams->len; ++i) {
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100654 GstMpegtsPMTStream *stream = g_ptr_array_index (pmt->streams, i);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400655
Sebastian Drögee6513c12013-07-14 12:12:42 +0200656 switch (stream->stream_type) {
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100657 case GST_MPEGTS_STREAM_TYPE_SCTE_DSMCC_DCB:
658 case GST_MPEGTS_STREAM_TYPE_SCTE_SIGNALING:
Sebastian Dröge12682a62014-05-03 19:58:45 +0200659 {
660 guint32 registration_id =
661 get_registration_from_descriptors (stream->descriptors);
662 /* Not a private section stream */
663 if (registration_id != DRF_ID_CUEI && registration_id != DRF_ID_ETV1)
664 break;
665 /* Fall through on purpose - remove this PID from known_psi */
666 }
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100667 case GST_MPEGTS_STREAM_TYPE_PRIVATE_SECTIONS:
668 case GST_MPEGTS_STREAM_TYPE_MHEG:
669 case GST_MPEGTS_STREAM_TYPE_DSM_CC:
670 case GST_MPEGTS_STREAM_TYPE_DSMCC_A:
671 case GST_MPEGTS_STREAM_TYPE_DSMCC_B:
672 case GST_MPEGTS_STREAM_TYPE_DSMCC_C:
673 case GST_MPEGTS_STREAM_TYPE_DSMCC_D:
674 case GST_MPEGTS_STREAM_TYPE_SL_FLEXMUX_SECTIONS:
675 case GST_MPEGTS_STREAM_TYPE_METADATA_SECTIONS:
Sebastian Drögee6513c12013-07-14 12:12:42 +0200676 /* Set known PSI streams */
677 if (base->parse_private_sections)
678 MPEGTS_BIT_SET (base->known_psi, stream->pid);
679 break;
680 default:
681 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->is_pes, stream->pid)))
682 GST_FIXME
683 ("Refcounting issue. Setting twice a PID (0x%04x) as known PES",
684 stream->pid);
685 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi, stream->pid))) {
686 GST_FIXME
687 ("Refcounting issue. Setting a known PSI PID (0x%04x) as known PES",
688 stream->pid);
689 MPEGTS_BIT_UNSET (base->known_psi, stream->pid);
690 }
691
692 MPEGTS_BIT_SET (base->is_pes, stream->pid);
693 break;
694 }
Olivier Naudanb28313f2012-04-16 08:10:18 -0400695 mpegts_base_program_add_stream (base, program,
Sebastian Drögee6513c12013-07-14 12:12:42 +0200696 stream->pid, stream->stream_type, stream);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400697
698 }
699 /* We add the PCR pid last. If that PID is already used by one of the media
700 * streams above, no new stream will be created */
Sebastian Drögee6513c12013-07-14 12:12:42 +0200701 mpegts_base_program_add_stream (base, program, pmt->pcr_pid, -1, NULL);
702 MPEGTS_BIT_SET (base->is_pes, pmt->pcr_pid);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400703
704 program->active = TRUE;
705 program->initial_program = initial_program;
706
707 klass = GST_MPEGTS_BASE_GET_CLASS (base);
708 if (klass->program_started != NULL)
709 klass->program_started (base, program);
710
Sebastian Drögee6513c12013-07-14 12:12:42 +0200711 GST_DEBUG_OBJECT (base, "new pmt activated");
Olivier Naudanb28313f2012-04-16 08:10:18 -0400712}
713
Sebastian Drögee6513c12013-07-14 12:12:42 +0200714
715static gboolean
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100716mpegts_base_apply_pat (MpegTSBase * base, GstMpegtsSection * section)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400717{
Sebastian Drögedd8e1152013-08-30 12:58:53 +0200718 GPtrArray *pat = gst_mpegts_section_get_pat (section);
719 GPtrArray *old_pat;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400720 MpegTSBaseProgram *program;
Sebastian Drögee6513c12013-07-14 12:12:42 +0200721 gint i;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400722
Sebastian Drögee6513c12013-07-14 12:12:42 +0200723 if (G_UNLIKELY (pat == NULL))
724 return FALSE;
725
726 GST_INFO_OBJECT (base, "PAT");
Olivier Naudanb28313f2012-04-16 08:10:18 -0400727
728 /* Applying a new PAT does two things:
729 * * It adds the new programs to the list of programs this element handles
730 * and increments at the same time the number of times a program is referenced.
731 *
732 * * If there was a previously active PAT, It decrements the reference count
733 * of all program it used. If a program is no longer needed, it is removed.
734 */
735
736 old_pat = base->pat;
Sebastian Drögee6513c12013-07-14 12:12:42 +0200737 base->pat = pat;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400738
739 GST_LOG ("Activating new Program Association Table");
740 /* activate the new table */
Sebastian Drögee6513c12013-07-14 12:12:42 +0200741 for (i = 0; i < pat->len; ++i) {
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100742 GstMpegtsPatProgram *patp = g_ptr_array_index (pat, i);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400743
Sebastian Drögee6513c12013-07-14 12:12:42 +0200744 program = mpegts_base_get_program (base, patp->program_number);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400745 if (program) {
746 /* IF the program already existed, just check if the PMT PID changed */
Sebastian Drögee6513c12013-07-14 12:12:42 +0200747 if (program->pmt_pid != patp->network_or_program_map_PID) {
Olivier Naudanb28313f2012-04-16 08:10:18 -0400748 if (program->pmt_pid != G_MAXUINT16) {
749 /* pmt pid changed */
750 /* FIXME: when this happens it may still be pmt pid of another
751 * program, so setting to False may make it go through expensive
752 * path in is_psi unnecessarily */
753 MPEGTS_BIT_UNSET (base->known_psi, program->pmt_pid);
754 }
755
Sebastian Drögee6513c12013-07-14 12:12:42 +0200756 program->pmt_pid = patp->network_or_program_map_PID;
757 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi, program->pmt_pid)))
758 GST_FIXME
759 ("Refcounting issue. Setting twice a PMT PID (0x%04x) as know PSI",
760 program->pmt_pid);
761 MPEGTS_BIT_SET (base->known_psi, patp->network_or_program_map_PID);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400762 }
763 } else {
764 /* Create a new program */
Sebastian Drögee6513c12013-07-14 12:12:42 +0200765 program =
766 mpegts_base_add_program (base, patp->program_number,
767 patp->network_or_program_map_PID);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400768 }
769 /* We mark this program as being referenced by one PAT */
770 program->patcount += 1;
771 }
772
773 if (old_pat) {
774 /* deactivate the old table */
775 GST_LOG ("Deactivating old Program Association Table");
776
Sebastian Drögee6513c12013-07-14 12:12:42 +0200777 for (i = 0; i < old_pat->len; ++i) {
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100778 GstMpegtsPatProgram *patp = g_ptr_array_index (old_pat, i);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400779
Sebastian Drögee6513c12013-07-14 12:12:42 +0200780 program = mpegts_base_get_program (base, patp->program_number);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400781 if (G_UNLIKELY (program == NULL)) {
782 GST_DEBUG_OBJECT (base, "broken PAT, duplicated entry for program %d",
Sebastian Drögee6513c12013-07-14 12:12:42 +0200783 patp->program_number);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400784 continue;
785 }
786
787 if (--program->patcount > 0)
788 /* the program has been referenced by the new pat, keep it */
789 continue;
790
Sebastian Drögee6513c12013-07-14 12:12:42 +0200791 GST_INFO_OBJECT (base, "PAT removing program 0x%04x 0x%04x",
792 patp->program_number, patp->network_or_program_map_PID);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400793
794 mpegts_base_deactivate_program (base, program);
Sebastian Drögee6513c12013-07-14 12:12:42 +0200795 mpegts_base_remove_program (base, patp->program_number);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400796 /* FIXME: when this happens it may still be pmt pid of another
797 * program, so setting to False may make it go through expensive
798 * path in is_psi unnecessarily */
Sebastian Drögee6513c12013-07-14 12:12:42 +0200799 if (G_UNLIKELY (MPEGTS_BIT_IS_SET (base->known_psi,
800 patp->network_or_program_map_PID))) {
801 GST_FIXME
802 ("Program refcounting : Setting twice a pid (0x%04x) as known PSI",
803 patp->network_or_program_map_PID);
804 }
805 MPEGTS_BIT_SET (base->known_psi, patp->network_or_program_map_PID);
806 mpegts_packetizer_remove_stream (base->packetizer,
807 patp->network_or_program_map_PID);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400808 }
809
Sebastian Drögedd8e1152013-08-30 12:58:53 +0200810 g_ptr_array_unref (old_pat);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400811 }
Sebastian Drögee6513c12013-07-14 12:12:42 +0200812
813 return TRUE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400814}
815
Sebastian Drögee6513c12013-07-14 12:12:42 +0200816static gboolean
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100817mpegts_base_apply_pmt (MpegTSBase * base, GstMpegtsSection * section)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400818{
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100819 const GstMpegtsPMT *pmt;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400820 MpegTSBaseProgram *program, *old_program;
821 guint program_number;
822 gboolean initial_program = TRUE;
823
Sebastian Drögee6513c12013-07-14 12:12:42 +0200824 pmt = gst_mpegts_section_get_pmt (section);
825 if (G_UNLIKELY (pmt == NULL)) {
826 GST_ERROR ("Could not get PMT (corrupted ?)");
827 return FALSE;
828 }
829
Olivier Naudanb28313f2012-04-16 08:10:18 -0400830 /* FIXME : not so sure this is valid anymore */
831 if (G_UNLIKELY (base->seen_pat == FALSE)) {
832 GST_WARNING ("Got pmt without pat first. Returning");
833 /* remove the stream since we won't get another PMT otherwise */
Sebastian Drögee6513c12013-07-14 12:12:42 +0200834 mpegts_packetizer_remove_stream (base->packetizer, section->pid);
835 return TRUE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400836 }
837
Sebastian Drögee6513c12013-07-14 12:12:42 +0200838 program_number = section->subtable_extension;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400839 GST_DEBUG ("Applying PMT (program_number:%d, pid:0x%04x)",
Sebastian Drögee6513c12013-07-14 12:12:42 +0200840 program_number, section->pid);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400841
842 /* In order for stream switching to happen properly in decodebin(2),
843 * we need to first add the new pads (i.e. activate the new program)
844 * before removing the old ones (i.e. deactivating the old program)
845 */
846
847 old_program = mpegts_base_get_program (base, program_number);
848 if (G_UNLIKELY (old_program == NULL))
849 goto no_program;
850
Sebastian Drögee6513c12013-07-14 12:12:42 +0200851 if (G_UNLIKELY (mpegts_base_is_same_program (base, old_program, section->pid,
852 pmt)))
Olivier Naudanb28313f2012-04-16 08:10:18 -0400853 goto same_program;
854
855 /* If the current program is active, this means we have a new program */
856 if (old_program->active) {
857 old_program = mpegts_base_steal_program (base, program_number);
Sebastian Drögee6513c12013-07-14 12:12:42 +0200858 program = mpegts_base_new_program (base, program_number, section->pid);
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200859 program->patcount = old_program->patcount;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400860 g_hash_table_insert (base->programs,
861 GINT_TO_POINTER (program_number), program);
862
863 /* Desactivate the old program */
864 mpegts_base_deactivate_program (base, old_program);
865 mpegts_base_free_program (old_program);
866 initial_program = FALSE;
867 } else
868 program = old_program;
869
Sebastian Dröge63e92012012-08-09 11:32:59 +0200870 /* activate program */
871 /* Ownership of pmt_info is given to the program */
Sebastian Drögee6513c12013-07-14 12:12:42 +0200872 mpegts_base_activate_program (base, program, section->pid, section, pmt,
Olivier Naudanb28313f2012-04-16 08:10:18 -0400873 initial_program);
874
Sebastian Drögee6513c12013-07-14 12:12:42 +0200875 return TRUE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400876
877no_program:
878 {
879 GST_ERROR ("Attempted to apply a PMT on a program that wasn't created");
Sebastian Drögee6513c12013-07-14 12:12:42 +0200880 return TRUE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400881 }
882
883same_program:
884 {
885 GST_DEBUG ("Not applying identical program");
Sebastian Drögee6513c12013-07-14 12:12:42 +0200886 return TRUE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400887 }
888}
889
890static void
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100891mpegts_base_handle_psi (MpegTSBase * base, GstMpegtsSection * section)
Sebastian Dröge7eb51492012-06-08 13:11:50 +0200892{
Sebastian Drögee6513c12013-07-14 12:12:42 +0200893 gboolean post_message = TRUE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400894
Sebastian Dröge7eb51492012-06-08 13:11:50 +0200895 GST_DEBUG ("Handling PSI (pid: 0x%04x , table_id: 0x%02x)",
896 section->pid, section->table_id);
897
Sebastian Drögee6513c12013-07-14 12:12:42 +0200898 switch (section->section_type) {
899 case GST_MPEGTS_SECTION_PAT:
900 post_message = mpegts_base_apply_pat (base, section);
901 if (base->seen_pat == FALSE) {
902 base->seen_pat = TRUE;
903 GST_DEBUG ("First PAT offset: %" G_GUINT64_FORMAT, section->offset);
904 mpegts_packetizer_set_reference_offset (base->packetizer,
905 section->offset);
906 }
Olivier Naudanb28313f2012-04-16 08:10:18 -0400907 break;
Sebastian Drögee6513c12013-07-14 12:12:42 +0200908 case GST_MPEGTS_SECTION_PMT:
909 post_message = mpegts_base_apply_pmt (base, section);
Sebastian Dröge7eb51492012-06-08 13:11:50 +0200910 break;
Sebastian Drögee6513c12013-07-14 12:12:42 +0200911 case GST_MPEGTS_SECTION_EIT:
912 /* some tag xtraction + posting */
913 post_message = mpegts_base_get_tags_from_eit (base, section);
Sebastian Dröge7eb51492012-06-08 13:11:50 +0200914 break;
Sebastian Dröge1491e032014-06-22 19:09:53 +0200915 case GST_MPEGTS_SECTION_ATSC_MGT:
916 post_message = mpegts_base_parse_atsc_mgt (base, section);
917 break;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400918 default:
919 break;
920 }
921
Sebastian Drögee6513c12013-07-14 12:12:42 +0200922 /* Finally post message (if it wasn't corrupted) */
923 if (post_message)
924 gst_element_post_message (GST_ELEMENT_CAST (base),
925 gst_message_new_mpegts_section (GST_OBJECT (base), section));
926 gst_mpegts_section_unref (section);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400927}
928
Sebastian Dröge1491e032014-06-22 19:09:53 +0200929static gboolean
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100930mpegts_base_parse_atsc_mgt (MpegTSBase * base, GstMpegtsSection * section)
Sebastian Dröge1491e032014-06-22 19:09:53 +0200931{
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100932 const GstMpegtsAtscMGT *mgt;
Sebastian Dröge1491e032014-06-22 19:09:53 +0200933 gint i;
934
935 mgt = gst_mpegts_section_get_atsc_mgt (section);
936 if (G_UNLIKELY (mgt == NULL))
937 return FALSE;
938
939 for (i = 0; i < mgt->tables->len; ++i) {
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100940 GstMpegtsAtscMGTTable *table = g_ptr_array_index (mgt->tables, i);
Sebastian Dröge1491e032014-06-22 19:09:53 +0200941
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100942 if ((table->table_type >= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_EIT0 &&
943 table->table_type <= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_EIT127) ||
944 (table->table_type >= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_ETT0 &&
945 table->table_type <= GST_MPEGTS_ATSC_MGT_TABLE_TYPE_ETT127)) {
Sebastian Dröge1491e032014-06-22 19:09:53 +0200946 MPEGTS_BIT_SET (base->known_psi, table->pid);
947 }
948 }
949
950 return TRUE;
951}
Sebastian Drögee6513c12013-07-14 12:12:42 +0200952
953static gboolean
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100954mpegts_base_get_tags_from_eit (MpegTSBase * base, GstMpegtsSection * section)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400955{
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100956 const GstMpegtsEIT *eit;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400957 guint i;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400958 MpegTSBaseProgram *program;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400959
Sebastian Drögee6513c12013-07-14 12:12:42 +0200960 /* Early exit if it's not from the present/following table_id */
961 if (section->table_id != GST_MTS_TABLE_ID_EVENT_INFORMATION_ACTUAL_TS_PRESENT
Sebastian Drögedbfbfdc2015-08-19 13:33:51 +0300962 && section->table_id !=
Sebastian Drögee6513c12013-07-14 12:12:42 +0200963 GST_MTS_TABLE_ID_EVENT_INFORMATION_OTHER_TS_PRESENT)
964 return TRUE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400965
Sebastian Drögee6513c12013-07-14 12:12:42 +0200966 eit = gst_mpegts_section_get_eit (section);
967 if (G_UNLIKELY (eit == NULL))
968 return FALSE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400969
Sebastian Drögee6513c12013-07-14 12:12:42 +0200970 program = mpegts_base_get_program (base, section->subtable_extension);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400971
Sebastian Drögee6513c12013-07-14 12:12:42 +0200972 GST_DEBUG
973 ("program_id:0x%04x, table_id:0x%02x, actual_stream:%d, present_following:%d, program:%p",
974 section->subtable_extension, section->table_id, eit->actual_stream,
975 eit->present_following, program);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400976
Sebastian Drögee6513c12013-07-14 12:12:42 +0200977 if (program && eit->present_following) {
978 for (i = 0; i < eit->events->len; i++) {
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100979 GstMpegtsEITEvent *event = g_ptr_array_index (eit->events, i);
980 const GstMpegtsDescriptor *desc;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400981
Sebastian Drögee6513c12013-07-14 12:12:42 +0200982 if (event->running_status == RUNNING_STATUS_RUNNING) {
983 program->event_id = event->event_id;
984 if ((desc =
985 gst_mpegts_find_descriptor (event->descriptors,
986 GST_MTS_DESC_DVB_SHORT_EVENT))) {
987 gchar *name;
988 if (gst_mpegts_descriptor_parse_dvb_short_event (desc, NULL, &name,
989 NULL)) {
990 /* FIXME : Is it correct to post an event duration as a GST_TAG_DURATION ??? */
991 program->tags =
992 gst_tag_list_new (GST_TAG_TITLE, name, GST_TAG_DURATION,
993 event->duration * GST_SECOND, NULL);
994 return TRUE;
995 }
996 }
Olivier Naudanb28313f2012-04-16 08:10:18 -0400997 }
998 }
999 }
Sebastian Drögee6513c12013-07-14 12:12:42 +02001000
1001 return TRUE;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001002}
1003
1004static gboolean
1005remove_each_program (gpointer key, MpegTSBaseProgram * program,
1006 MpegTSBase * base)
1007{
Olivier Naudanb28313f2012-04-16 08:10:18 -04001008 /* First deactivate it */
1009 mpegts_base_deactivate_program (base, program);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001010
1011 return TRUE;
1012}
1013
Sebastian Dröge12682a62014-05-03 19:58:45 +02001014static inline GstFlowReturn
1015mpegts_base_drain (MpegTSBase * base)
1016{
1017 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1018
1019 /* Call implementation */
1020 if (klass->drain)
1021 return klass->drain (base);
1022
1023 return GST_FLOW_OK;
1024}
1025
Olivier Naudanb28313f2012-04-16 08:10:18 -04001026static inline void
Sebastian Dröge12491072013-07-13 11:31:28 +02001027mpegts_base_flush (MpegTSBase * base, gboolean hard)
Olivier Naudanb28313f2012-04-16 08:10:18 -04001028{
1029 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1030
1031 /* Call implementation */
Sebastian Drögee6513c12013-07-14 12:12:42 +02001032 if (klass->flush)
Sebastian Dröge12491072013-07-13 11:31:28 +02001033 klass->flush (base, hard);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001034}
1035
1036static gboolean
Sebastian Dröge7e661862012-05-21 15:50:23 +02001037mpegts_base_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
Olivier Naudanb28313f2012-04-16 08:10:18 -04001038{
1039 gboolean res = TRUE;
Sebastian Dröge1064a452013-07-30 08:48:14 +02001040 gboolean hard;
Sebastian Dröge7e661862012-05-21 15:50:23 +02001041 MpegTSBase *base = GST_MPEGTS_BASE (parent);
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001042 gboolean is_sticky = GST_EVENT_IS_STICKY (event);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001043
Sebastian Dröge63e92012012-08-09 11:32:59 +02001044 GST_DEBUG_OBJECT (base, "Got event %s",
Olivier Naudanb28313f2012-04-16 08:10:18 -04001045 gst_event_type_get_name (GST_EVENT_TYPE (event)));
1046
1047 switch (GST_EVENT_TYPE (event)) {
Sebastian Dröge7e661862012-05-21 15:50:23 +02001048 case GST_EVENT_SEGMENT:
1049 gst_event_copy_segment (event, &base->segment);
Sebastian Drögee6513c12013-07-14 12:12:42 +02001050 GST_DEBUG_OBJECT (base, "Received segment %" GST_SEGMENT_FORMAT,
1051 &base->segment);
Sebastian Dröge9cada452012-11-21 14:56:48 +01001052 /* Check if we need to switch PCR/PTS handling */
1053 if (base->segment.format == GST_FORMAT_TIME) {
1054 base->packetizer->calculate_offset = FALSE;
1055 base->packetizer->calculate_skew = TRUE;
1056 } else {
1057 base->packetizer->calculate_offset = TRUE;
1058 base->packetizer->calculate_skew = FALSE;
1059 }
Sebastian Drögec495fb92013-04-26 14:09:59 +02001060 res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
Sebastian Dröge7e661862012-05-21 15:50:23 +02001061 break;
1062 case GST_EVENT_STREAM_START:
1063 gst_event_unref (event);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001064 break;
Sebastian Dröge7e661862012-05-21 15:50:23 +02001065 case GST_EVENT_CAPS:
1066 /* FIXME, do something */
1067 gst_event_unref (event);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001068 break;
Sebastian Dröge9cada452012-11-21 14:56:48 +01001069 case GST_EVENT_FLUSH_STOP:
1070 res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
Sebastian Dröge1064a452013-07-30 08:48:14 +02001071 hard = (base->mode != BASE_MODE_SEEKING);
1072 mpegts_packetizer_flush (base->packetizer, hard);
1073 mpegts_base_flush (base, hard);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001074 gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED);
1075 base->seen_pat = FALSE;
Sebastian Dröge9cada452012-11-21 14:56:48 +01001076 break;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001077 default:
1078 res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001079 }
1080
Sebastian Dröge1491e032014-06-22 19:09:53 +02001081 /* Always return TRUE for sticky events */
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001082 if (is_sticky)
Sebastian Dröge1491e032014-06-22 19:09:53 +02001083 res = TRUE;
1084
Olivier Naudanb28313f2012-04-16 08:10:18 -04001085 return res;
1086}
1087
Olivier Naudanb28313f2012-04-16 08:10:18 -04001088static GstFlowReturn
Sebastian Dröge7e661862012-05-21 15:50:23 +02001089mpegts_base_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
Olivier Naudanb28313f2012-04-16 08:10:18 -04001090{
1091 GstFlowReturn res = GST_FLOW_OK;
1092 MpegTSBase *base;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001093 MpegTSPacketizerPacketReturn pret;
1094 MpegTSPacketizer2 *packetizer;
1095 MpegTSPacketizerPacket packet;
Sebastian Dröge5a155a02012-09-14 10:49:31 +02001096 MpegTSBaseClass *klass;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001097
Sebastian Dröge7e661862012-05-21 15:50:23 +02001098 base = GST_MPEGTS_BASE (parent);
Sebastian Dröge5a155a02012-09-14 10:49:31 +02001099 klass = GST_MPEGTS_BASE_GET_CLASS (base);
1100
Olivier Naudanb28313f2012-04-16 08:10:18 -04001101 packetizer = base->packetizer;
1102
Sebastian Dröge5a155a02012-09-14 10:49:31 +02001103 if (klass->input_done)
1104 gst_buffer_ref (buf);
1105
Sebastian Dröge12682a62014-05-03 19:58:45 +02001106 if (GST_BUFFER_IS_DISCONT (buf)) {
1107 GST_DEBUG_OBJECT (base, "Got DISCONT buffer, flushing");
1108 res = mpegts_base_drain (base);
1109 if (G_UNLIKELY (res != GST_FLOW_OK))
1110 return res;
1111
1112 mpegts_base_flush (base, FALSE);
Sebastian Drögedbfbfdc2015-08-19 13:33:51 +03001113 /* In the case of discontinuities in push-mode with TIME segment
1114 * we want to drop all previous observations (hard:TRUE) from
1115 * the packetizer */
1116 if (base->mode == BASE_MODE_PUSHING
1117 && base->segment.format == GST_FORMAT_TIME)
1118 mpegts_packetizer_flush (base->packetizer, TRUE);
1119 else
1120 mpegts_packetizer_flush (base->packetizer, FALSE);
Sebastian Dröge12682a62014-05-03 19:58:45 +02001121 }
1122
Olivier Naudanb28313f2012-04-16 08:10:18 -04001123 mpegts_packetizer_push (base->packetizer, buf);
Sebastian Dröge7e661862012-05-21 15:50:23 +02001124
1125 while (res == GST_FLOW_OK) {
1126 pret = mpegts_packetizer_next_packet (base->packetizer, &packet);
1127
1128 /* If we don't have enough data, return */
1129 if (G_UNLIKELY (pret == PACKET_NEED_MORE))
1130 break;
1131
1132 if (G_UNLIKELY (pret == PACKET_BAD)) {
Olivier Naudanb28313f2012-04-16 08:10:18 -04001133 /* bad header, skip the packet */
Sebastian Dröge7e661862012-05-21 15:50:23 +02001134 GST_DEBUG_OBJECT (base, "bad packet, skipping");
Olivier Naudanb28313f2012-04-16 08:10:18 -04001135 goto next;
Sebastian Dröge7e661862012-05-21 15:50:23 +02001136 }
Olivier Naudanb28313f2012-04-16 08:10:18 -04001137
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001138 if (klass->inspect_packet)
1139 klass->inspect_packet (base, &packet);
1140
Sebastian Drögee6513c12013-07-14 12:12:42 +02001141 /* If it's a known PES, push it */
1142 if (MPEGTS_BIT_IS_SET (base->is_pes, packet.pid)) {
Olivier Naudanb28313f2012-04-16 08:10:18 -04001143 /* push the packet downstream */
Sebastian Drögee6513c12013-07-14 12:12:42 +02001144 if (base->push_data)
1145 res = klass->push (base, &packet, NULL);
1146 } else if (packet.payload
1147 && MPEGTS_BIT_IS_SET (base->known_psi, packet.pid)) {
1148 /* base PSI data */
1149 GList *others, *tmp;
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001150 GstMpegtsSection *section;
Sebastian Drögee6513c12013-07-14 12:12:42 +02001151
1152 section = mpegts_packetizer_push_section (packetizer, &packet, &others);
1153 if (section)
1154 mpegts_base_handle_psi (base, section);
1155 if (G_UNLIKELY (others)) {
1156 for (tmp = others; tmp; tmp = tmp->next)
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001157 mpegts_base_handle_psi (base, (GstMpegtsSection *) tmp->data);
Sebastian Drögee6513c12013-07-14 12:12:42 +02001158 g_list_free (others);
1159 }
1160
1161 /* we need to push section packet downstream */
1162 if (base->push_section)
1163 res = klass->push (base, &packet, section);
1164
1165 } else if (packet.payload && packet.pid != 0x1fff)
1166 GST_LOG ("PID 0x%04x Saw packet on a pid we don't handle", packet.pid);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001167
1168 next:
1169 mpegts_packetizer_clear_packet (base->packetizer, &packet);
1170 }
1171
Sebastian Dröge5a155a02012-09-14 10:49:31 +02001172 if (klass->input_done) {
1173 if (res == GST_FLOW_OK)
1174 res = klass->input_done (base, buf);
1175 else
1176 gst_buffer_unref (buf);
1177 }
1178
Olivier Naudanb28313f2012-04-16 08:10:18 -04001179 return res;
1180}
1181
1182static GstFlowReturn
1183mpegts_base_scan (MpegTSBase * base)
1184{
Sebastian Dröge12491072013-07-13 11:31:28 +02001185 GstFlowReturn ret = GST_FLOW_OK;
Sebastian Dröge7e661862012-05-21 15:50:23 +02001186 GstBuffer *buf = NULL;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001187 guint i;
1188 gboolean done = FALSE;
1189 MpegTSPacketizerPacketReturn pret;
1190 gint64 tmpval;
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001191 gint64 upstream_size, seek_pos, reverse_limit;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001192 GstFormat format;
1193 guint initial_pcr_seen;
1194
1195 GST_DEBUG ("Scanning for initial sync point");
1196
1197 /* Find initial sync point and at least 5 PCR values */
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001198 for (i = 0; i < 20 && !done; i++) {
1199 GST_DEBUG ("Grabbing %d => %d", i * 65536, (i + 1) * 65536);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001200
1201 ret = gst_pad_pull_range (base->sinkpad, i * 65536, 65536, &buf);
Sebastian Dröge543e3a32014-05-21 12:50:00 +02001202 if (G_UNLIKELY (ret == GST_FLOW_EOS))
Sebastian Dröge12682a62014-05-03 19:58:45 +02001203 break;
Sebastian Dröge543e3a32014-05-21 12:50:00 +02001204 if (G_UNLIKELY (ret != GST_FLOW_OK))
Olivier Naudanb28313f2012-04-16 08:10:18 -04001205 goto beach;
1206
1207 /* Push to packetizer */
1208 mpegts_packetizer_push (base->packetizer, buf);
Sebastian Dröge7e661862012-05-21 15:50:23 +02001209 buf = NULL;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001210
1211 if (mpegts_packetizer_has_packets (base->packetizer)) {
1212 if (base->seek_offset == -1) {
1213 /* Mark the initial sync point and remember the packetsize */
1214 base->seek_offset = base->packetizer->offset;
1215 GST_DEBUG ("Sync point is now %" G_GUINT64_FORMAT, base->seek_offset);
1216 base->packetsize = base->packetizer->packet_size;
1217 }
1218 while (1) {
1219 /* Eat up all packets */
1220 pret = mpegts_packetizer_process_next_packet (base->packetizer);
1221 if (pret == PACKET_NEED_MORE)
1222 break;
Sebastian Dröge12682a62014-05-03 19:58:45 +02001223 if (pret != PACKET_BAD && base->packetizer->nb_seen_offsets >= 5) {
Olivier Naudanb28313f2012-04-16 08:10:18 -04001224 GST_DEBUG ("Got enough initial PCR");
1225 done = TRUE;
1226 break;
1227 }
1228 }
1229 }
1230 }
1231
Sebastian Dröge12682a62014-05-03 19:58:45 +02001232 initial_pcr_seen = base->packetizer->nb_seen_offsets;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001233 if (G_UNLIKELY (initial_pcr_seen == 0))
1234 goto no_initial_pcr;
1235 GST_DEBUG ("Seen %d initial PCR", initial_pcr_seen);
1236
1237 /* Now send data from the end */
Olivier Naudanb28313f2012-04-16 08:10:18 -04001238
1239 /* Get the size of upstream */
1240 format = GST_FORMAT_BYTES;
Sebastian Dröge7e661862012-05-21 15:50:23 +02001241 if (!gst_pad_peer_query_duration (base->sinkpad, format, &tmpval))
Olivier Naudanb28313f2012-04-16 08:10:18 -04001242 goto beach;
1243 upstream_size = tmpval;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001244
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001245 /* The scanning takes place on the last 2048kB. Considering PCR should
1246 * be present at least every 100ms, this should cope with streams
1247 * up to 160Mbit/s */
1248 reverse_limit = MAX (0, upstream_size - 2097152);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001249
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001250 /* Find last PCR value, searching backwards by chunks of 300 MPEG-ts packets */
1251 for (seek_pos = MAX (0, upstream_size - 56400);
Sebastian Drögedbfbfdc2015-08-19 13:33:51 +03001252 seek_pos >= reverse_limit; seek_pos -= 56400) {
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001253 mpegts_packetizer_clear (base->packetizer);
1254 GST_DEBUG ("Grabbing %" G_GUINT64_FORMAT " => %" G_GUINT64_FORMAT, seek_pos,
1255 seek_pos + 56400);
1256
1257 ret = gst_pad_pull_range (base->sinkpad, seek_pos, 56400, &buf);
Sebastian Dröge543e3a32014-05-21 12:50:00 +02001258 if (G_UNLIKELY (ret == GST_FLOW_EOS))
Sebastian Dröge12682a62014-05-03 19:58:45 +02001259 break;
Sebastian Dröge543e3a32014-05-21 12:50:00 +02001260 if (G_UNLIKELY (ret != GST_FLOW_OK))
Olivier Naudanb28313f2012-04-16 08:10:18 -04001261 goto beach;
1262
1263 /* Push to packetizer */
1264 mpegts_packetizer_push (base->packetizer, buf);
Sebastian Dröge7e661862012-05-21 15:50:23 +02001265 buf = NULL;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001266
1267 if (mpegts_packetizer_has_packets (base->packetizer)) {
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001268 pret = PACKET_OK;
1269 /* Eat up all packets, really try to get last PCR(s) */
1270 while (pret != PACKET_NEED_MORE)
Olivier Naudanb28313f2012-04-16 08:10:18 -04001271 pret = mpegts_packetizer_process_next_packet (base->packetizer);
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001272
1273 if (base->packetizer->nb_seen_offsets > initial_pcr_seen) {
1274 GST_DEBUG ("Got last PCR(s) (total seen:%d)",
1275 base->packetizer->nb_seen_offsets);
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001276 break;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001277 }
1278 }
1279 }
1280
1281beach:
1282 mpegts_packetizer_clear (base->packetizer);
1283 return ret;
1284
1285no_initial_pcr:
1286 mpegts_packetizer_clear (base->packetizer);
1287 GST_WARNING_OBJECT (base, "Couldn't find any PCR within the first %d bytes",
1288 10 * 65536);
1289 return GST_FLOW_ERROR;
1290}
1291
1292
1293static void
1294mpegts_base_loop (MpegTSBase * base)
1295{
1296 GstFlowReturn ret = GST_FLOW_ERROR;
Sebastian Dröge12491072013-07-13 11:31:28 +02001297
Olivier Naudanb28313f2012-04-16 08:10:18 -04001298 switch (base->mode) {
1299 case BASE_MODE_SCANNING:
1300 /* Find first sync point */
1301 ret = mpegts_base_scan (base);
1302 if (G_UNLIKELY (ret != GST_FLOW_OK))
1303 goto error;
1304 base->mode = BASE_MODE_STREAMING;
1305 GST_DEBUG ("Changing to Streaming");
1306 break;
1307 case BASE_MODE_SEEKING:
1308 /* FIXME : unclear if we still need mode_seeking... */
1309 base->mode = BASE_MODE_STREAMING;
1310 break;
1311 case BASE_MODE_STREAMING:
1312 {
Sebastian Dröge7e661862012-05-21 15:50:23 +02001313 GstBuffer *buf = NULL;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001314
1315 GST_DEBUG ("Pulling data from %" G_GUINT64_FORMAT, base->seek_offset);
1316
1317 ret = gst_pad_pull_range (base->sinkpad, base->seek_offset,
1318 100 * base->packetsize, &buf);
1319 if (G_UNLIKELY (ret != GST_FLOW_OK))
1320 goto error;
Sebastian Dröge7e661862012-05-21 15:50:23 +02001321 base->seek_offset += gst_buffer_get_size (buf);
1322 ret = mpegts_base_chain (base->sinkpad, GST_OBJECT_CAST (base), buf);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001323 if (G_UNLIKELY (ret != GST_FLOW_OK))
1324 goto error;
1325 }
1326 break;
1327 case BASE_MODE_PUSHING:
1328 GST_WARNING ("wrong BASE_MODE_PUSHING mode in pull loop");
1329 break;
1330 }
1331
1332 return;
1333
1334error:
1335 {
1336 const gchar *reason = gst_flow_get_name (ret);
1337 GST_DEBUG_OBJECT (base, "Pausing task, reason %s", reason);
Sebastian Dröge7e661862012-05-21 15:50:23 +02001338 if (ret == GST_FLOW_EOS) {
Sebastian Drögee6513c12013-07-14 12:12:42 +02001339 if (!GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base,
1340 gst_event_new_eos ()))
1341 GST_ELEMENT_ERROR (base, STREAM, FAILED,
1342 (_("Internal data stream error.")),
1343 ("No program activated before EOS"));
Sebastian Dröge7e661862012-05-21 15:50:23 +02001344 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
Olivier Naudanb28313f2012-04-16 08:10:18 -04001345 GST_ELEMENT_ERROR (base, STREAM, FAILED,
1346 (_("Internal data stream error.")),
1347 ("stream stopped, reason %s", reason));
1348 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, gst_event_new_eos ());
1349 }
1350 gst_pad_pause_task (base->sinkpad);
1351 }
1352}
1353
1354
1355gboolean
1356mpegts_base_handle_seek_event (MpegTSBase * base, GstPad * pad,
1357 GstEvent * event)
1358{
1359 MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1360 GstFlowReturn ret = GST_FLOW_ERROR;
1361 gdouble rate;
1362 gboolean flush;
1363 GstFormat format;
1364 GstSeekFlags flags;
1365 GstSeekType start_type, stop_type;
1366 gint64 start, stop;
Sebastian Dröge30887412013-09-19 12:51:06 +02001367 GstEvent *flush_event = NULL;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001368
1369 gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
1370 &stop_type, &stop);
1371
1372 if (format != GST_FORMAT_TIME)
1373 return FALSE;
1374
Sebastian Dröge1064a452013-07-30 08:48:14 +02001375 if (GST_EVENT_SEQNUM (event) == base->last_seek_seqnum) {
1376 GST_DEBUG_OBJECT (base, "Skipping already handled seek");
1377 return TRUE;
1378 }
1379
Sebastian Dröge7e661862012-05-21 15:50:23 +02001380 if (base->mode == BASE_MODE_PUSHING) {
1381 /* First try if upstream supports seeking in TIME format */
1382 if (gst_pad_push_event (base->sinkpad, gst_event_ref (event))) {
1383 GST_DEBUG ("upstream handled SEEK event");
Sebastian Dröge7e661862012-05-21 15:50:23 +02001384 return TRUE;
1385 }
Sebastian Dröge1064a452013-07-30 08:48:14 +02001386
1387 /* If the subclass can seek, do that */
1388 if (klass->seek) {
1389 ret = klass->seek (base, event);
1390 if (G_UNLIKELY (ret != GST_FLOW_OK))
1391 GST_WARNING ("seeking failed %s", gst_flow_get_name (ret));
1392 else {
Sebastian Dröge30887412013-09-19 12:51:06 +02001393 GstEvent *new_seek;
Sebastian Dröge30887412013-09-19 12:51:06 +02001394
Sebastian Dröge441328f2015-05-13 15:47:04 +03001395 if (GST_CLOCK_TIME_IS_VALID (base->seek_offset)) {
1396 base->mode = BASE_MODE_SEEKING;
1397 new_seek = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
1398 GST_SEEK_TYPE_SET, base->seek_offset, GST_SEEK_TYPE_NONE, -1);
1399 gst_event_set_seqnum (new_seek, GST_EVENT_SEQNUM (event));
1400 if (!gst_pad_push_event (base->sinkpad, new_seek))
1401 ret = GST_FLOW_ERROR;
1402 else
1403 base->last_seek_seqnum = GST_EVENT_SEQNUM (event);
1404 }
Sebastian Dröge1064a452013-07-30 08:48:14 +02001405 base->mode = BASE_MODE_PUSHING;
1406 }
1407 }
1408
1409 return ret == GST_FLOW_OK;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001410 }
1411
1412 GST_DEBUG ("seek event, rate: %f start: %" GST_TIME_FORMAT
1413 " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start),
1414 GST_TIME_ARGS (stop));
1415
1416 flush = flags & GST_SEEK_FLAG_FLUSH;
1417
Olivier Naudanb28313f2012-04-16 08:10:18 -04001418 /* stop streaming, either by flushing or by pausing the task */
1419 base->mode = BASE_MODE_SEEKING;
1420 if (flush) {
1421 GST_DEBUG_OBJECT (base, "sending flush start");
Sebastian Dröge30887412013-09-19 12:51:06 +02001422 flush_event = gst_event_new_flush_start ();
1423 gst_event_set_seqnum (flush_event, GST_EVENT_SEQNUM (event));
1424 gst_pad_push_event (base->sinkpad, gst_event_ref (flush_event));
1425 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, flush_event);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001426 } else
1427 gst_pad_pause_task (base->sinkpad);
1428
1429 /* wait for streaming to finish */
1430 GST_PAD_STREAM_LOCK (base->sinkpad);
1431
1432 if (flush) {
1433 /* send a FLUSH_STOP for the sinkpad, since we need data for seeking */
1434 GST_DEBUG_OBJECT (base, "sending flush stop");
Sebastian Dröge30887412013-09-19 12:51:06 +02001435 flush_event = gst_event_new_flush_stop (TRUE);
1436 gst_event_set_seqnum (flush_event, GST_EVENT_SEQNUM (event));
1437
1438 /* ref for it to be reused later */
1439 gst_pad_push_event (base->sinkpad, gst_event_ref (flush_event));
Sebastian Dröge12491072013-07-13 11:31:28 +02001440 /* And actually flush our pending data but allow to preserve some info
1441 * to perform the seek */
1442 mpegts_base_flush (base, FALSE);
1443 mpegts_packetizer_flush (base->packetizer, FALSE);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001444 }
1445
Sebastian Dröged24b3252014-02-09 11:32:00 +01001446 if (flags & (GST_SEEK_FLAG_SEGMENT)) {
Olivier Naudanb28313f2012-04-16 08:10:18 -04001447 GST_WARNING ("seek flags 0x%x are not supported", (int) flags);
1448 goto done;
1449 }
1450
1451
Sebastian Dröge1064a452013-07-30 08:48:14 +02001452 /* If the subclass can seek, do that */
1453 if (klass->seek) {
1454 ret = klass->seek (base, event);
1455 if (G_UNLIKELY (ret != GST_FLOW_OK))
1456 GST_WARNING ("seeking failed %s", gst_flow_get_name (ret));
1457 else
1458 base->last_seek_seqnum = GST_EVENT_SEQNUM (event);
1459 } else {
1460 /* FIXME : Check this before so we don't do seeks we can't handle ? */
1461 GST_WARNING ("subclass has no seek implementation");
Olivier Naudanb28313f2012-04-16 08:10:18 -04001462 }
1463
Sebastian Dröge30887412013-09-19 12:51:06 +02001464 if (flush_event) {
Olivier Naudanb28313f2012-04-16 08:10:18 -04001465 /* if we sent a FLUSH_START, we now send a FLUSH_STOP */
1466 GST_DEBUG_OBJECT (base, "sending flush stop");
Sebastian Dröge30887412013-09-19 12:51:06 +02001467 GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, flush_event);
1468 flush_event = NULL;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001469 }
Olivier Naudanb28313f2012-04-16 08:10:18 -04001470done:
Sebastian Dröge30887412013-09-19 12:51:06 +02001471 if (flush_event)
1472 gst_event_unref (flush_event);
Sebastian Dröge63e92012012-08-09 11:32:59 +02001473 gst_pad_start_task (base->sinkpad, (GstTaskFunction) mpegts_base_loop, base,
1474 NULL);
Sebastian Dröge1064a452013-07-30 08:48:14 +02001475
Olivier Naudanb28313f2012-04-16 08:10:18 -04001476 GST_PAD_STREAM_UNLOCK (base->sinkpad);
1477 return ret == GST_FLOW_OK;
1478}
1479
1480
1481static gboolean
Sebastian Dröge7e661862012-05-21 15:50:23 +02001482mpegts_base_sink_activate (GstPad * sinkpad, GstObject * parent)
Olivier Naudanb28313f2012-04-16 08:10:18 -04001483{
Sebastian Dröge7e661862012-05-21 15:50:23 +02001484 GstQuery *query;
1485 gboolean pull_mode;
1486
1487 query = gst_query_new_scheduling ();
1488
1489 if (!gst_pad_peer_query (sinkpad, query)) {
1490 gst_query_unref (query);
1491 goto activate_push;
1492 }
1493
Sebastian Dröge5a155a02012-09-14 10:49:31 +02001494 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
1495 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
Sebastian Dröge7e661862012-05-21 15:50:23 +02001496 gst_query_unref (query);
1497
1498 if (!pull_mode)
1499 goto activate_push;
1500
1501 GST_DEBUG_OBJECT (sinkpad, "activating pull");
1502 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
1503
1504activate_push:
1505 {
1506 GST_DEBUG_OBJECT (sinkpad, "activating push");
1507 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001508 }
1509}
1510
1511static gboolean
Sebastian Dröge7e661862012-05-21 15:50:23 +02001512mpegts_base_sink_activate_mode (GstPad * pad, GstObject * parent,
1513 GstPadMode mode, gboolean active)
Olivier Naudanb28313f2012-04-16 08:10:18 -04001514{
Sebastian Dröge7e661862012-05-21 15:50:23 +02001515 gboolean res;
1516 MpegTSBase *base = GST_MPEGTS_BASE (parent);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001517
Sebastian Dröge7e661862012-05-21 15:50:23 +02001518 switch (mode) {
1519 case GST_PAD_MODE_PUSH:
1520 base->mode = BASE_MODE_PUSHING;
Sebastian Dröge7e661862012-05-21 15:50:23 +02001521 res = TRUE;
1522 break;
1523 case GST_PAD_MODE_PULL:
1524 if (active) {
1525 base->mode = BASE_MODE_SCANNING;
Sebastian Dröge9cada452012-11-21 14:56:48 +01001526 /* When working pull-based, we always use offsets for estimation */
Sebastian Dröge7e661862012-05-21 15:50:23 +02001527 base->packetizer->calculate_offset = TRUE;
Sebastian Dröge9cada452012-11-21 14:56:48 +01001528 base->packetizer->calculate_skew = FALSE;
Sebastian Drögee6513c12013-07-14 12:12:42 +02001529 gst_segment_init (&base->segment, GST_FORMAT_BYTES);
Sebastian Dröge7e661862012-05-21 15:50:23 +02001530 res =
Sebastian Dröge63e92012012-08-09 11:32:59 +02001531 gst_pad_start_task (pad, (GstTaskFunction) mpegts_base_loop, base,
1532 NULL);
Sebastian Dröge7e661862012-05-21 15:50:23 +02001533 } else
1534 res = gst_pad_stop_task (pad);
1535 break;
1536 default:
1537 res = FALSE;
1538 break;
1539 }
1540 return res;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001541}
1542
Olivier Naudanb28313f2012-04-16 08:10:18 -04001543static GstStateChangeReturn
1544mpegts_base_change_state (GstElement * element, GstStateChange transition)
1545{
1546 MpegTSBase *base;
1547 GstStateChangeReturn ret;
1548
1549 base = GST_MPEGTS_BASE (element);
Sebastian Dröge5a155a02012-09-14 10:49:31 +02001550
1551 switch (transition) {
1552 case GST_STATE_CHANGE_READY_TO_PAUSED:
1553 mpegts_base_reset (base);
1554 break;
1555 default:
1556 break;
1557 }
1558
Olivier Naudanb28313f2012-04-16 08:10:18 -04001559 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1560
1561 switch (transition) {
1562 case GST_STATE_CHANGE_PAUSED_TO_READY:
1563 mpegts_base_reset (base);
1564 if (base->mode != BASE_MODE_PUSHING)
1565 base->mode = BASE_MODE_SCANNING;
1566 break;
1567 default:
1568 break;
1569 }
1570
1571 return ret;
1572}
1573
1574gboolean
1575gst_mpegtsbase_plugin_init (GstPlugin * plugin)
1576{
1577 GST_DEBUG_CATEGORY_INIT (mpegts_base_debug, "mpegtsbase", 0,
1578 "MPEG transport stream base class");
1579
Olivier Naudanb28313f2012-04-16 08:10:18 -04001580 return TRUE;
1581}