3
* Copyright (C) 2007 Alessandro Decina
7
* Alessandro Decina <alessandro@nnva.org>
8
* Zaheer Abbas Merali <zaheerabbas at merali dot org>
9
* Edward Hervey <edward.hervey@collabora.co.uk>
11
* This library is free software; you can redistribute it and/or
12
* modify it under the terms of the GNU Library General Public
13
* License as published by the Free Software Foundation; either
14
* version 2 of the License, or (at your option) any later version.
16
* This library is distributed in the hope that it will be useful,
17
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
* Library General Public License for more details.
21
* You should have received a copy of the GNU Library General Public
22
* License along with this library; if not, write to the
23
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24
* Boston, MA 02111-1307, USA.
34
#include <gst/gst-i18n-plugin.h>
35
#include "mpegtsbase.h"
36
#include "gstmpegdesc.h"
38
/* latency in mseconds */
39
#define TS_LATENCY 700
41
#define TABLE_ID_UNSET 0xFF
42
#define RUNNING_STATUS_RUNNING 4
44
GST_DEBUG_CATEGORY_STATIC (mpegts_base_debug);
45
#define GST_CAT_DEFAULT mpegts_base_debug
47
static GQuark QUARK_PROGRAMS;
48
static GQuark QUARK_PROGRAM_NUMBER;
49
static GQuark QUARK_PID;
50
static GQuark QUARK_PCR_PID;
51
static GQuark QUARK_STREAMS;
52
static GQuark QUARK_STREAM_TYPE;
54
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
57
GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
66
static void mpegts_base_set_property (GObject * object, guint prop_id,
67
const GValue * value, GParamSpec * pspec);
68
static void mpegts_base_get_property (GObject * object, guint prop_id,
69
GValue * value, GParamSpec * pspec);
70
static void mpegts_base_dispose (GObject * object);
71
static void mpegts_base_finalize (GObject * object);
72
static void mpegts_base_free_program (MpegTSBaseProgram * program);
73
static void mpegts_base_free_stream (MpegTSBaseStream * ptream);
74
static gboolean mpegts_base_sink_activate (GstPad * pad);
75
static gboolean mpegts_base_sink_activate_pull (GstPad * pad, gboolean active);
76
static gboolean mpegts_base_sink_activate_push (GstPad * pad, gboolean active);
77
static GstFlowReturn mpegts_base_chain (GstPad * pad, GstBuffer * buf);
78
static gboolean mpegts_base_sink_event (GstPad * pad, GstEvent * event);
79
static GstStateChangeReturn mpegts_base_change_state (GstElement * element,
80
GstStateChange transition);
81
static void _extra_init (GType type);
82
static void mpegts_base_get_tags_from_sdt (MpegTSBase * base,
83
GstStructure * sdt_info);
84
static void mpegts_base_get_tags_from_eit (MpegTSBase * base,
85
GstStructure * eit_info);
87
GST_BOILERPLATE_FULL (MpegTSBase, mpegts_base, GstElement, GST_TYPE_ELEMENT,
91
static const guint32 crc_tab[256] = {
92
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
93
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
94
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
95
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
96
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
97
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
98
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
99
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
100
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
101
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
102
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
103
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
104
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
105
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
106
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
107
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
108
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
109
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
110
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
111
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
112
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
113
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
114
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
115
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
116
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
117
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
118
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
119
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
120
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
121
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
122
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
123
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
124
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
125
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
126
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
127
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
128
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
129
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
130
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
131
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
132
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
133
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
134
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
137
/* relicenced to LGPL from fluendo ts demuxer */
139
mpegts_base_calc_crc32 (guint8 * data, guint datalen)
142
guint32 crc = 0xffffffff;
144
for (i = 0; i < datalen; i++) {
145
crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff];
151
_extra_init (GType type)
153
QUARK_PROGRAMS = g_quark_from_string ("programs");
154
QUARK_PROGRAM_NUMBER = g_quark_from_string ("program-number");
155
QUARK_PID = g_quark_from_string ("pid");
156
QUARK_PCR_PID = g_quark_from_string ("pcr-pid");
157
QUARK_STREAMS = g_quark_from_string ("streams");
158
QUARK_STREAM_TYPE = g_quark_from_string ("stream-type");
162
mpegts_base_base_init (gpointer klass)
164
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
166
gst_element_class_add_pad_template (element_class,
167
gst_static_pad_template_get (&sink_template));
171
mpegts_base_class_init (MpegTSBaseClass * klass)
173
GObjectClass *gobject_class;
174
GstElementClass *element_class;
176
element_class = GST_ELEMENT_CLASS (klass);
177
element_class->change_state = mpegts_base_change_state;
179
gobject_class = G_OBJECT_CLASS (klass);
180
gobject_class->set_property = mpegts_base_set_property;
181
gobject_class->get_property = mpegts_base_get_property;
182
gobject_class->dispose = mpegts_base_dispose;
183
gobject_class->finalize = mpegts_base_finalize;
188
mpegts_base_reset (MpegTSBase * base)
190
mpegts_packetizer_clear (base->packetizer);
191
memset (base->is_pes, 0, 8192);
192
memset (base->known_psi, 0, 8192);
195
base->known_psi[0] = TRUE;
197
/* FIXME : Commenting the Following lines is to be in sync with the following
200
* 61a885613316ce7657c36a6cd215b43f9dc67b79
201
* mpegtsparse: don't free PAT structure which may still be needed later
204
/* if (base->pat != NULL) */
205
/* gst_structure_free (base->pat); */
206
/* base->pat = NULL; */
207
/* pmt pids will be added and removed dynamically */
212
mpegts_base_init (MpegTSBase * base, MpegTSBaseClass * klass)
214
base->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
215
gst_pad_set_activate_function (base->sinkpad, mpegts_base_sink_activate);
216
gst_pad_set_activatepull_function (base->sinkpad,
217
mpegts_base_sink_activate_pull);
218
gst_pad_set_activatepush_function (base->sinkpad,
219
mpegts_base_sink_activate_push);
220
gst_pad_set_chain_function (base->sinkpad, mpegts_base_chain);
221
gst_pad_set_event_function (base->sinkpad, mpegts_base_sink_event);
222
gst_element_add_pad (GST_ELEMENT (base), base->sinkpad);
224
base->disposed = FALSE;
225
base->packetizer = mpegts_packetizer_new ();
226
base->programs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
227
NULL, (GDestroyNotify) mpegts_base_free_program);
229
base->is_pes = g_new0 (gboolean, 8192);
230
base->known_psi = g_new0 (gboolean, 8192);
231
mpegts_base_reset (base);
232
base->program_size = sizeof (MpegTSBaseProgram);
233
base->stream_size = sizeof (MpegTSBaseStream);
235
base->mode = BASE_MODE_STREAMING;
236
base->first_pat_offset = -1;
240
mpegts_base_dispose (GObject * object)
242
MpegTSBase *base = GST_MPEGTS_BASE (object);
244
if (!base->disposed) {
245
g_object_unref (base->packetizer);
246
base->disposed = TRUE;
247
g_free (base->known_psi);
248
g_free (base->is_pes);
251
if (G_OBJECT_CLASS (parent_class)->dispose)
252
G_OBJECT_CLASS (parent_class)->dispose (object);
256
mpegts_base_finalize (GObject * object)
258
MpegTSBase *base = GST_MPEGTS_BASE (object);
261
gst_structure_free (base->pat);
264
g_hash_table_destroy (base->programs);
266
if (G_OBJECT_CLASS (parent_class)->finalize)
267
G_OBJECT_CLASS (parent_class)->finalize (object);
271
mpegts_base_set_property (GObject * object, guint prop_id,
272
const GValue * value, GParamSpec * pspec)
274
/* MpegTSBase *base = GST_MPEGTS_BASE (object); */
278
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
283
mpegts_base_get_property (GObject * object, guint prop_id,
284
GValue * value, GParamSpec * pspec)
286
/* MpegTSBase *base = GST_MPEGTS_BASE (object); */
290
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
294
/* returns NULL if no matching descriptor found *
295
* otherwise returns a descriptor that needs to *
298
mpegts_get_descriptor_from_stream (MpegTSBaseStream * stream, guint8 tag)
300
GValueArray *descriptors = NULL;
301
GstStructure *stream_info = stream->stream_info;
302
guint8 *retval = NULL;
305
gst_structure_get (stream_info, "descriptors", G_TYPE_VALUE_ARRAY,
308
for (i = 0; i < descriptors->n_values; i++) {
309
GValue *value = g_value_array_get_nth (descriptors, i);
310
GString *desc = g_value_dup_boxed (value);
311
if (DESC_TAG (desc->str) == tag) {
312
retval = (guint8 *) desc->str;
313
g_string_free (desc, FALSE);
316
g_string_free (desc, FALSE);
318
g_value_array_free (descriptors);
323
/* returns NULL if no matching descriptor found *
324
* otherwise returns a descriptor that needs to *
327
mpegts_get_descriptor_from_program (MpegTSBaseProgram * program, guint8 tag)
329
GValueArray *descriptors = NULL;
330
GstStructure *program_info;
331
guint8 *retval = NULL;
334
if (G_UNLIKELY (program == NULL))
336
program_info = program->pmt_info;
337
gst_structure_get (program_info, "descriptors", G_TYPE_VALUE_ARRAY,
340
for (i = 0; i < descriptors->n_values; i++) {
341
GValue *value = g_value_array_get_nth (descriptors, i);
342
GString *desc = g_value_dup_boxed (value);
343
if (DESC_TAG (desc->str) == tag) {
344
retval = (guint8 *) desc->str;
345
g_string_free (desc, FALSE);
348
g_string_free (desc, FALSE);
350
g_value_array_free (descriptors);
356
mpegts_base_add_program (MpegTSBase * base,
357
gint program_number, guint16 pmt_pid)
359
MpegTSBaseProgram *program;
361
program = g_malloc0 (base->program_size);
362
program->program_number = program_number;
363
program->pmt_pid = pmt_pid;
364
program->pcr_pid = G_MAXUINT16;
365
program->streams = g_new0 (MpegTSBaseStream *, 0x2000);
366
program->patcount = 0;
368
g_hash_table_insert (base->programs,
369
GINT_TO_POINTER (program_number), program);
375
mpegts_base_get_program (MpegTSBase * base, gint program_number)
377
MpegTSBaseProgram *program;
379
program = (MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
380
GINT_TO_POINTER ((gint) program_number));
387
mpegts_base_activate_program (MpegTSBase * base, MpegTSBaseProgram * program)
389
MpegTSBasePad *tspad;
392
pad_name = g_strdup_printf ("program_%d", program->program_number);
394
tspad = mpegts_base_create_tspad (base, pad_name);
395
tspad->program_number = program->program_number;
396
tspad->program = program;
397
program->tspad = tspad;
399
gst_pad_set_active (tspad->pad, TRUE);
400
program->active = TRUE;
406
mpegts_base_deactivate_program (MpegTSBase * base, MpegTSBaseProgram * program)
408
MpegTSBasePad *tspad;
410
tspad = program->tspad;
411
gst_pad_set_active (tspad->pad, FALSE);
412
program->active = FALSE;
414
/* tspad will be destroyed in GstElementClass::pad_removed */
422
mpegts_base_free_program (MpegTSBaseProgram * program)
426
if (program->pmt_info)
427
gst_structure_free (program->pmt_info);
429
for (i = 0; i < 0x2000; i++)
430
if (program->streams[i])
431
mpegts_base_free_stream (program->streams[i]);
432
g_free (program->streams);
435
gst_tag_list_free (program->tags);
441
mpegts_base_remove_program (MpegTSBase * base, gint program_number)
443
MpegTSBaseProgram *program;
444
MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
446
if (klass->program_stopped) {
448
(MpegTSBaseProgram *) g_hash_table_lookup (base->programs,
449
GINT_TO_POINTER (program_number));
450
klass->program_stopped (base, program);
452
g_hash_table_remove (base->programs, GINT_TO_POINTER (program_number));
456
static MpegTSBaseStream *
457
mpegts_base_program_add_stream (MpegTSBase * base,
458
MpegTSBaseProgram * program, guint16 pid, guint8 stream_type,
459
GstStructure * stream_info)
461
MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
462
MpegTSBaseStream *stream;
464
GST_DEBUG ("pid:0x%04x, stream_type:0x%03x, stream_info:%" GST_PTR_FORMAT,
465
pid, stream_type, stream_info);
467
stream = g_malloc0 (base->stream_size);
469
stream->stream_type = stream_type;
470
stream->stream_info = stream_info;
472
program->streams[pid] = stream;
474
if (klass->stream_added)
475
klass->stream_added (base, stream, program);
481
mpegts_base_free_stream (MpegTSBaseStream * stream)
487
mpegts_base_program_remove_stream (MpegTSBase * base,
488
MpegTSBaseProgram * program, guint16 pid)
490
MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
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, program->streams[pid]);
496
mpegts_base_free_stream (program->streams[pid]);
497
program->streams[pid] = NULL;
501
mpegts_base_deactivate_pmt (MpegTSBase * base, MpegTSBaseProgram * program)
506
GstStructure *stream;
507
const GValue *streams;
509
MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
511
if (program->pmt_info) {
512
/* Inform subclasses we're deactivating this program */
513
if (klass->program_stopped)
514
klass->program_stopped (base, program);
516
streams = gst_structure_id_get_value (program->pmt_info, QUARK_STREAMS);
518
for (i = 0; i < gst_value_list_get_size (streams); ++i) {
519
value = gst_value_list_get_value (streams, i);
520
stream = g_value_get_boxed (value);
521
gst_structure_id_get (stream, QUARK_PID, G_TYPE_UINT, &pid,
522
QUARK_STREAM_TYPE, G_TYPE_UINT, &stream_type, NULL);
523
mpegts_base_program_remove_stream (base, program, (guint16) pid);
524
base->is_pes[pid] = FALSE;
526
/* remove pcr stream */
527
mpegts_base_program_remove_stream (base, program, program->pcr_pid);
528
base->is_pes[program->pcr_pid] = FALSE;
534
mpegts_base_is_psi (MpegTSBase * base, MpegTSPacketizerPacket * packet)
536
gboolean retval = FALSE;
539
static const guint8 si_tables[] =
540
{ 0x00, 0x01, 0x02, 0x03, 0x40, 0x41, 0x42, 0x46, 0x4A,
541
0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
542
0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65,
543
0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
544
0x72, 0x73, 0x7E, 0x7F, TABLE_ID_UNSET
547
if (base->known_psi[packet->pid])
550
/* check is it is a pes pid */
551
if (base->is_pes[packet->pid])
555
if (packet->payload_unit_start_indicator) {
556
table_id = *(packet->data);
558
while (si_tables[i] != TABLE_ID_UNSET) {
559
if (G_UNLIKELY (si_tables[i] == table_id)) {
560
GST_DEBUG_OBJECT (base, "Packet has table id 0x%x", table_id);
567
MpegTSPacketizerStream *stream = (MpegTSPacketizerStream *)
568
base->packetizer->streams[packet->pid];
572
GST_DEBUG_OBJECT (base, "section table id: 0x%x",
573
stream->section_table_id);
574
while (si_tables[i] != TABLE_ID_UNSET) {
575
if (G_UNLIKELY (si_tables[i] == stream->section_table_id)) {
585
GST_LOG_OBJECT (base, "Packet of pid 0x%x is psi: %d", packet->pid, retval);
590
mpegts_base_apply_pat (MpegTSBase * base, GstStructure * pat_info)
593
GstStructure *old_pat;
594
GstStructure *program_info;
595
guint program_number;
597
MpegTSBaseProgram *program;
599
const GValue *programs;
600
MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
603
base->pat = gst_structure_copy (pat_info);
605
GST_INFO_OBJECT (base, "PAT %" GST_PTR_FORMAT, pat_info);
607
gst_element_post_message (GST_ELEMENT_CAST (base),
608
gst_message_new_element (GST_OBJECT (base),
609
gst_structure_copy (pat_info)));
611
programs = gst_structure_id_get_value (pat_info, QUARK_PROGRAMS);
612
/* activate the new table */
613
for (i = 0; i < gst_value_list_get_size (programs); ++i) {
614
value = gst_value_list_get_value (programs, i);
616
program_info = g_value_get_boxed (value);
617
gst_structure_id_get (program_info, QUARK_PROGRAM_NUMBER, G_TYPE_UINT,
618
&program_number, QUARK_PID, G_TYPE_UINT, &pid, NULL);
620
program = mpegts_base_get_program (base, program_number);
622
if (program->pmt_pid != pid) {
623
if (program->pmt_pid != G_MAXUINT16) {
624
/* pmt pid changed */
625
/* FIXME: when this happens it may still be pmt pid of another
626
* program, so setting to False may make it go through expensive
627
* path in is_psi unnecessarily */
628
base->known_psi[program->pmt_pid] = FALSE;
631
program->pmt_pid = pid;
632
base->known_psi[pid] = TRUE;
635
base->known_psi[pid] = TRUE;
636
program = mpegts_base_add_program (base, program_number, pid);
638
program->patcount += 1;
642
/* deactivate the old table */
644
programs = gst_structure_id_get_value (old_pat, QUARK_PROGRAMS);
645
for (i = 0; i < gst_value_list_get_size (programs); ++i) {
646
value = gst_value_list_get_value (programs, i);
648
program_info = g_value_get_boxed (value);
649
gst_structure_id_get (program_info,
650
QUARK_PROGRAM_NUMBER, G_TYPE_UINT, &program_number,
651
QUARK_PID, G_TYPE_UINT, &pid, NULL);
653
program = mpegts_base_get_program (base, program_number);
654
if (program == NULL) {
655
GST_DEBUG_OBJECT (base, "broken PAT, duplicated entry for program %d",
660
if (--program->patcount > 0)
661
/* the program has been referenced by the new pat, keep it */
664
GST_INFO_OBJECT (base, "PAT removing program %" GST_PTR_FORMAT,
667
if (klass->program_stopped) {
668
klass->program_stopped (base, program);
670
mpegts_base_deactivate_pmt (base, program);
671
mpegts_base_remove_program (base, program_number);
672
/* FIXME: when this happens it may still be pmt pid of another
673
* program, so setting to False may make it go through expensive
674
* path in is_psi unnecessarily */
675
base->known_psi[pid] = TRUE;
676
mpegts_packetizer_remove_stream (base->packetizer, pid);
679
gst_structure_free (old_pat);
682
mpegts_base_sync_program_pads (base);
687
mpegts_base_apply_pmt (MpegTSBase * base,
688
guint16 pmt_pid, GstStructure * pmt_info)
690
MpegTSBaseProgram *program;
691
guint program_number;
695
GstStructure *stream;
697
const GValue *new_streams;
699
MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
701
if (G_UNLIKELY (base->first_pat_offset == -1)) {
702
GST_WARNING ("Got pmt without pat first. Returning");
703
/* remove the stream since we won't get another PMT otherwise */
704
mpegts_packetizer_remove_stream (base->packetizer, pmt_pid);
708
gst_structure_id_get (pmt_info,
709
QUARK_PROGRAM_NUMBER, G_TYPE_UINT, &program_number,
710
QUARK_PCR_PID, G_TYPE_UINT, &pcr_pid, NULL);
711
new_streams = gst_structure_id_get_value (pmt_info, QUARK_STREAMS);
713
program = mpegts_base_get_program (base, program_number);
715
/* deactivate old pmt */ ;
716
mpegts_base_deactivate_pmt (base, program);
717
if (program->pmt_info)
718
gst_structure_free (program->pmt_info);
719
program->pmt_info = NULL;
722
base->known_psi[pmt_pid] = TRUE;
723
program = mpegts_base_add_program (base, program_number, pid);
726
/* activate new pmt */
727
program->pmt_info = gst_structure_copy (pmt_info);
728
program->pmt_pid = pmt_pid;
729
program->pcr_pid = pcr_pid;
730
mpegts_base_program_add_stream (base, program, (guint16) pcr_pid, -1, NULL);
731
base->is_pes[pcr_pid] = TRUE;
733
for (i = 0; i < gst_value_list_get_size (new_streams); ++i) {
734
value = gst_value_list_get_value (new_streams, i);
735
stream = g_value_get_boxed (value);
737
gst_structure_id_get (stream, QUARK_PID, G_TYPE_UINT, &pid,
738
QUARK_STREAM_TYPE, G_TYPE_UINT, &stream_type, NULL);
739
base->is_pes[pid] = TRUE;
740
mpegts_base_program_add_stream (base, program,
741
(guint16) pid, (guint8) stream_type, stream);
745
if (klass->program_started != NULL) {
746
klass->program_started (base, program);
749
GST_DEBUG_OBJECT (base, "new pmt %" GST_PTR_FORMAT, pmt_info);
751
gst_element_post_message (GST_ELEMENT_CAST (base),
752
gst_message_new_element (GST_OBJECT (base),
753
gst_structure_copy (pmt_info)));
757
mpegts_base_apply_nit (MpegTSBase * base,
758
guint16 pmt_pid, GstStructure * nit_info)
760
GST_DEBUG_OBJECT (base, "NIT %" GST_PTR_FORMAT, nit_info);
762
gst_element_post_message (GST_ELEMENT_CAST (base),
763
gst_message_new_element (GST_OBJECT (base),
764
gst_structure_copy (nit_info)));
768
mpegts_base_apply_sdt (MpegTSBase * base,
769
guint16 pmt_pid, GstStructure * sdt_info)
771
GST_DEBUG_OBJECT (base, "SDT %" GST_PTR_FORMAT, sdt_info);
773
mpegts_base_get_tags_from_sdt (base, sdt_info);
775
gst_element_post_message (GST_ELEMENT_CAST (base),
776
gst_message_new_element (GST_OBJECT (base),
777
gst_structure_copy (sdt_info)));
781
mpegts_base_apply_eit (MpegTSBase * base,
782
guint16 pmt_pid, GstStructure * eit_info)
784
GST_DEBUG_OBJECT (base, "EIT %" GST_PTR_FORMAT, eit_info);
786
mpegts_base_get_tags_from_eit (base, eit_info);
788
gst_element_post_message (GST_ELEMENT_CAST (base),
789
gst_message_new_element (GST_OBJECT (base),
790
gst_structure_copy (eit_info)));
794
mpegts_base_apply_tdt (MpegTSBase * base,
795
guint16 tdt_pid, GstStructure * tdt_info)
797
gst_element_post_message (GST_ELEMENT_CAST (base),
798
gst_message_new_element (GST_OBJECT (base),
799
gst_structure_copy (tdt_info)));
801
GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base,
802
gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
803
gst_structure_copy (tdt_info)));
808
mpegts_base_handle_psi (MpegTSBase * base, MpegTSPacketizerSection * section)
811
GstStructure *structure = NULL;
813
/* table ids 0x70 - 0x73 do not have a crc */
814
if (G_LIKELY (section->table_id < 0x70 || section->table_id > 0x73)) {
815
if (G_UNLIKELY (mpegts_base_calc_crc32 (GST_BUFFER_DATA (section->buffer),
816
GST_BUFFER_SIZE (section->buffer)) != 0)) {
817
GST_WARNING_OBJECT (base, "bad crc in psi pid 0x%x", section->pid);
822
switch (section->table_id) {
825
structure = mpegts_packetizer_parse_pat (base->packetizer, section);
826
if (G_LIKELY (structure)) {
827
mpegts_base_apply_pat (base, structure);
828
if (base->first_pat_offset == -1) {
830
base->first_pat_offset = GST_BUFFER_OFFSET (section->buffer);
831
GST_DEBUG ("First PAT offset: %" G_GUINT64_FORMAT,
832
base->first_pat_offset);
840
structure = mpegts_packetizer_parse_pmt (base->packetizer, section);
841
if (G_LIKELY (structure))
842
mpegts_base_apply_pmt (base, section->pid, structure);
848
/* NIT, actual network */
850
/* NIT, other network */
851
structure = mpegts_packetizer_parse_nit (base->packetizer, section);
852
if (G_LIKELY (structure))
853
mpegts_base_apply_nit (base, section->pid, structure);
860
structure = mpegts_packetizer_parse_sdt (base->packetizer, section);
861
if (G_LIKELY (structure))
862
mpegts_base_apply_sdt (base, section->pid, structure);
868
/* EIT, present/following */
902
structure = mpegts_packetizer_parse_eit (base->packetizer, section);
903
if (G_LIKELY (structure))
904
mpegts_base_apply_eit (base, section->pid, structure);
909
/* TDT (Time and Date table) */
910
structure = mpegts_packetizer_parse_tdt (base->packetizer, section);
911
if (G_LIKELY (structure))
912
mpegts_base_apply_tdt (base, section->pid, structure);
921
gst_structure_free (structure);
927
mpegts_base_get_tags_from_sdt (MpegTSBase * base, GstStructure * sdt_info)
929
const GValue *services;
932
services = gst_structure_get_value (sdt_info, "services");
934
for (i = 0; i < gst_value_list_get_size (services); i++) {
935
const GstStructure *service;
936
const gchar *sid_str;
939
MpegTSBaseProgram *program;
941
service = gst_value_get_structure (gst_value_list_get_value (services, i));
943
/* get program_number from structure name
944
* which looks like service-%d */
945
sid_str = gst_structure_get_name (service);
946
tmp = g_strstr_len (sid_str, -1, "-");
947
program_number = atoi (++tmp);
949
program = mpegts_base_get_program (base, program_number);
950
if (program && !program->tags) {
951
program->tags = gst_tag_list_new_full (GST_TAG_ARTIST,
952
gst_structure_get_string (service, "name"), NULL);
958
mpegts_base_get_tags_from_eit (MpegTSBase * base, GstStructure * eit_info)
960
const GValue *events;
962
guint program_number;
963
MpegTSBaseProgram *program;
964
gboolean present_following;
966
gst_structure_get_uint (eit_info, "service-id", &program_number);
967
program = mpegts_base_get_program (base, program_number);
969
gst_structure_get_boolean (eit_info, "present-following", &present_following);
971
if (program && present_following) {
972
events = gst_structure_get_value (eit_info, "events");
974
for (i = 0; i < gst_value_list_get_size (events); i++) {
975
const GstStructure *event;
981
event = gst_value_get_structure (gst_value_list_get_value (events, i));
983
title = gst_structure_get_string (event, "name");
984
gst_structure_get_uint (event, "event-id", &event_id);
985
gst_structure_get_uint (event, "running-status", &status);
987
if (title && event_id != program->event_id
988
&& status == RUNNING_STATUS_RUNNING) {
989
gst_structure_get_uint (event, "duration", &duration);
991
program->event_id = event_id;
992
program->tags = gst_tag_list_new_full (GST_TAG_TITLE,
993
title, GST_TAG_DURATION, duration * GST_SECOND, NULL);
1001
mpegts_base_sink_event (GstPad * pad, GstEvent * event)
1004
MpegTSBase *base = GST_MPEGTS_BASE (gst_object_get_parent (GST_OBJECT (pad)));
1006
GST_WARNING_OBJECT (base, "Got event %s",
1007
gst_event_type_get_name (GST_EVENT_TYPE (event)));
1009
switch (GST_EVENT_TYPE (event)) {
1010
case GST_EVENT_NEWSEGMENT:
1011
/* FIXME : STORE NEW SEGMENT ! */
1012
gst_event_unref (event);
1015
case GST_EVENT_FLUSH_STOP:
1016
mpegts_packetizer_clear (base->packetizer);
1019
res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
1020
gst_event_unref (event);
1023
gst_object_unref (base);
1027
static inline GstFlowReturn
1028
mpegts_base_push (MpegTSBase * base, MpegTSPacketizerPacket * packet,
1029
MpegTSPacketizerSection * section)
1031
MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1033
/* Call implementation */
1034
if (G_UNLIKELY (klass->push == NULL)) {
1035
GST_ERROR_OBJECT (base, "Class doesn't have a 'push' implementation !");
1036
return GST_FLOW_ERROR;
1039
return klass->push (base, packet, section);
1042
static GstFlowReturn
1043
mpegts_base_chain (GstPad * pad, GstBuffer * buf)
1045
GstFlowReturn res = GST_FLOW_OK;
1048
MpegTSPacketizerPacketReturn pret;
1049
MpegTSPacketizer2 *packetizer;
1050
MpegTSPacketizerPacket packet;
1052
base = GST_MPEGTS_BASE (gst_object_get_parent (GST_OBJECT (pad)));
1053
packetizer = base->packetizer;
1055
mpegts_packetizer_push (base->packetizer, buf);
1057
mpegts_packetizer_next_packet (base->packetizer,
1058
&packet)) != PACKET_NEED_MORE) && res == GST_FLOW_OK) {
1059
if (G_UNLIKELY (pret == PACKET_BAD))
1060
/* bad header, skip the packet */
1064
if (packet.payload != NULL && mpegts_base_is_psi (base, &packet)) {
1065
MpegTSPacketizerSection section;
1066
based = mpegts_packetizer_push_section (packetizer, &packet, §ion);
1067
if (G_UNLIKELY (!based))
1068
/* bad section data */
1071
if (G_LIKELY (section.complete)) {
1072
/* section complete */
1073
based = mpegts_base_handle_psi (base, §ion);
1074
gst_buffer_unref (section.buffer);
1076
if (G_UNLIKELY (!based))
1080
/* we need to push section packet downstream */
1081
res = mpegts_base_push (base, &packet, §ion);
1083
} else if (base->is_pes[packet.pid]) {
1084
/* push the packet downstream */
1085
res = mpegts_base_push (base, &packet, NULL);
1087
gst_buffer_unref (packet.buffer);
1090
mpegts_packetizer_clear_packet (base->packetizer, &packet);
1093
gst_object_unref (base);
1097
static GstFlowReturn
1098
mpegts_base_scan (MpegTSBase * base)
1103
MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
1105
GST_DEBUG ("Scanning for initial sync point");
1107
/* Find initial sync point */
1108
for (i = 0; i < 10; i++) {
1109
GST_DEBUG ("Grabbing %d => %d",
1110
i * 50 * MPEGTS_MAX_PACKETSIZE, 50 * MPEGTS_MAX_PACKETSIZE);
1111
ret = gst_pad_pull_range (base->sinkpad, i * 50 * MPEGTS_MAX_PACKETSIZE,
1112
50 * MPEGTS_MAX_PACKETSIZE, &buf);
1113
if (G_UNLIKELY (ret != GST_FLOW_OK))
1116
/* Push to packetizer */
1117
mpegts_packetizer_push (base->packetizer, buf);
1119
if (mpegts_packetizer_has_packets (base->packetizer)) {
1120
/* Mark the initial sync point and remember the packetsize */
1121
base->initial_sync_point = base->seek_offset = base->packetizer->offset;
1122
GST_DEBUG ("Sync point is now %" G_GUINT64_FORMAT, base->seek_offset);
1123
base->packetsize = base->packetizer->packet_size;
1125
/* If the subclass can seek for timestamps, do that */
1126
if (klass->find_timestamps) {
1128
mpegts_packetizer_clear (base->packetizer);
1130
ret = klass->find_timestamps (base, 0, &offset);
1132
base->initial_sync_point = base->seek_offset =
1133
base->packetizer->offset = base->first_pat_offset;
1134
GST_DEBUG ("Sync point is now %" G_GUINT64_FORMAT, base->seek_offset);
1140
GST_WARNING ("Didn't find initial sync point");
1141
ret = GST_FLOW_ERROR;
1144
mpegts_packetizer_clear (base->packetizer);
1151
mpegts_base_loop (MpegTSBase * base)
1153
GstFlowReturn ret = GST_FLOW_ERROR;
1154
switch (base->mode) {
1155
case BASE_MODE_SCANNING:
1156
/* Find first sync point */
1157
ret = mpegts_base_scan (base);
1158
if (G_UNLIKELY (ret != GST_FLOW_OK))
1160
base->mode = BASE_MODE_STREAMING;
1161
GST_DEBUG ("Changing to Streaming");
1163
case BASE_MODE_SEEKING:
1164
/* FIXME : yes, we should do something here */
1165
base->mode = BASE_MODE_STREAMING;
1167
case BASE_MODE_STREAMING:
1171
GST_DEBUG ("Pulling data from %" G_GUINT64_FORMAT, base->seek_offset);
1173
ret = gst_pad_pull_range (base->sinkpad, base->seek_offset,
1174
100 * base->packetsize, &buf);
1175
if (G_UNLIKELY (ret != GST_FLOW_OK))
1177
base->seek_offset += GST_BUFFER_SIZE (buf);
1178
ret = mpegts_base_chain (base->sinkpad, buf);
1179
if (G_UNLIKELY (ret != GST_FLOW_OK))
1189
const gchar *reason = gst_flow_get_name (ret);
1190
GST_DEBUG_OBJECT (base, "Pausing task, reason %s", reason);
1191
if (ret == GST_FLOW_UNEXPECTED)
1192
GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, gst_event_new_eos ());
1193
else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_UNEXPECTED) {
1194
GST_ELEMENT_ERROR (base, STREAM, FAILED,
1195
(_("Internal data stream error.")),
1196
("stream stopped, reason %s", reason));
1197
GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, gst_event_new_eos ());
1199
gst_pad_pause_task (base->sinkpad);
1204
mpegts_base_sink_activate (GstPad * pad)
1206
if (gst_pad_check_pull_range (pad)) {
1207
GST_DEBUG_OBJECT (pad, "activating pull");
1208
return gst_pad_activate_pull (pad, TRUE);
1210
GST_DEBUG_OBJECT (pad, "activating push");
1211
return gst_pad_activate_push (pad, TRUE);
1216
mpegts_base_sink_activate_pull (GstPad * pad, gboolean active)
1218
MpegTSBase *base = GST_MPEGTS_BASE (GST_OBJECT_PARENT (pad));
1220
base->mode = BASE_MODE_SCANNING;
1221
return gst_pad_start_task (pad, (GstTaskFunction) mpegts_base_loop, base);
1223
return gst_pad_stop_task (pad);
1227
mpegts_base_sink_activate_push (GstPad * pad, gboolean active)
1233
static GstStateChangeReturn
1234
mpegts_base_change_state (GstElement * element, GstStateChange transition)
1237
GstStateChangeReturn ret;
1239
base = GST_MPEGTS_BASE (element);
1240
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1242
switch (transition) {
1243
case GST_STATE_CHANGE_PAUSED_TO_READY:
1244
mpegts_base_reset (base);
1254
gst_mpegtsbase_plugin_init (GstPlugin * plugin)
1256
GST_DEBUG_CATEGORY_INIT (mpegts_base_debug, "mpegtsbase", 0,
1257
"MPEG transport stream base class");
1259
gst_mpegtsdesc_init_debug ();