Branch data Line data Source code
1 : : /*
2 : : * mpegtsparse.c -
3 : : * Copyright (C) 2007 Alessandro Decina
4 : : *
5 : : * Authors:
6 : : * Alessandro Decina <alessandro@nnva.org>
7 : : * Zaheer Abbas Merali <zaheerabbas at merali dot org>
8 : : *
9 : : * This library is free software; you can redistribute it and/or
10 : : * modify it under the terms of the GNU Library General Public
11 : : * License as published by the Free Software Foundation; either
12 : : * version 2 of the License, or (at your option) any later version.
13 : : *
14 : : * This library is distributed in the hope that it will be useful,
15 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : : * Library General Public License for more details.
18 : : *
19 : : * You should have received a copy of the GNU Library General Public
20 : : * License along with this library; if not, write to the
21 : : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 : : * Boston, MA 02111-1307, USA.
23 : : */
24 : :
25 : : #ifdef HAVE_CONFIG_H
26 : : #include "config.h"
27 : : #endif
28 : :
29 : : #include <stdlib.h>
30 : :
31 : : #include "mpegtsparse.h"
32 : : #include "gstmpegdesc.h"
33 : :
34 : : /* latency in mseconds */
35 : : #define TS_LATENCY 700
36 : :
37 : : #define TABLE_ID_UNSET 0xFF
38 : : #define RUNNING_STATUS_RUNNING 4
39 : :
40 : : GST_DEBUG_CATEGORY_STATIC (mpegts_parse_debug);
41 : : #define GST_CAT_DEFAULT mpegts_parse_debug
42 : :
43 : : typedef struct _MpegTSParsePad MpegTSParsePad;
44 : :
45 : : typedef struct
46 : : {
47 : : guint16 pid;
48 : : guint8 stream_type;
49 : : } MpegTSParseStream;
50 : :
51 : : typedef struct
52 : : {
53 : : gint program_number;
54 : : guint16 pmt_pid;
55 : : guint16 pcr_pid;
56 : : GstStructure *pmt_info;
57 : : GHashTable *streams;
58 : : gint patcount;
59 : : gint selected;
60 : : gboolean active;
61 : : MpegTSParsePad *tspad;
62 : : } MpegTSParseProgram;
63 : :
64 : : struct _MpegTSParsePad
65 : : {
66 : : GstPad *pad;
67 : :
68 : : /* the program number that the peer wants on this pad */
69 : : gint program_number;
70 : : MpegTSParseProgram *program;
71 : :
72 : : /* set to FALSE before a push and TRUE after */
73 : : gboolean pushed;
74 : :
75 : : /* the return of the latest push */
76 : : GstFlowReturn flow_return;
77 : :
78 : : GstTagList *tags;
79 : : guint event_id;
80 : : };
81 : :
82 : : static GQuark QUARK_PROGRAMS;
83 : : static GQuark QUARK_PROGRAM_NUMBER;
84 : : static GQuark QUARK_PID;
85 : : static GQuark QUARK_PCR_PID;
86 : : static GQuark QUARK_STREAMS;
87 : : static GQuark QUARK_STREAM_TYPE;
88 : :
89 : : static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
90 : : GST_PAD_SINK,
91 : : GST_PAD_ALWAYS,
92 : : GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
93 : : );
94 : :
95 : : static GstStaticPadTemplate src_template =
96 : : GST_STATIC_PAD_TEMPLATE ("src%d", GST_PAD_SRC,
97 : : GST_PAD_REQUEST,
98 : : GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
99 : : );
100 : :
101 : : static GstStaticPadTemplate program_template =
102 : : GST_STATIC_PAD_TEMPLATE ("program_%d", GST_PAD_SRC,
103 : : GST_PAD_SOMETIMES,
104 : : GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
105 : : );
106 : :
107 : : enum
108 : : {
109 : : ARG_0,
110 : : PROP_PROGRAM_NUMBERS,
111 : : /* FILL ME */
112 : : };
113 : :
114 : : static void mpegts_parse_set_property (GObject * object, guint prop_id,
115 : : const GValue * value, GParamSpec * pspec);
116 : : static void mpegts_parse_get_property (GObject * object, guint prop_id,
117 : : GValue * value, GParamSpec * pspec);
118 : : static void mpegts_parse_dispose (GObject * object);
119 : : static void mpegts_parse_finalize (GObject * object);
120 : :
121 : : static MpegTSParsePad *mpegts_parse_create_tspad (MpegTSParse * parse,
122 : : const gchar * name);
123 : : static void mpegts_parse_destroy_tspad (MpegTSParse * parse,
124 : : MpegTSParsePad * tspad);
125 : : static GstPad *mpegts_parse_activate_program (MpegTSParse * parse,
126 : : MpegTSParseProgram * program);
127 : : static void mpegts_parse_free_program (MpegTSParseProgram * program);
128 : : static void mpegts_parse_free_stream (MpegTSParseStream * ptream);
129 : : static void mpegts_parse_reset_selected_programs (MpegTSParse * parse,
130 : : gchar * programs);
131 : :
132 : : static void mpegts_parse_pad_removed (GstElement * element, GstPad * pad);
133 : : static GstPad *mpegts_parse_request_new_pad (GstElement * element,
134 : : GstPadTemplate * templ, const gchar * name);
135 : : static void mpegts_parse_release_pad (GstElement * element, GstPad * pad);
136 : : static GstFlowReturn mpegts_parse_chain (GstPad * pad, GstBuffer * buf);
137 : : static gboolean mpegts_parse_sink_event (GstPad * pad, GstEvent * event);
138 : : static GstStateChangeReturn mpegts_parse_change_state (GstElement * element,
139 : : GstStateChange transition);
140 : : static gboolean mpegts_parse_src_pad_query (GstPad * pad, GstQuery * query);
141 : : static void _extra_init (GType type);
142 : : static void mpegts_parse_get_tags_from_sdt (MpegTSParse * parse,
143 : : GstStructure * sdt_info);
144 : : static void mpegts_parse_get_tags_from_eit (MpegTSParse * parse,
145 : : GstStructure * eit_info);
146 : :
147 [ + + ]: 55 : GST_BOILERPLATE_FULL (MpegTSParse, mpegts_parse, GstElement, GST_TYPE_ELEMENT,
148 : 55 : _extra_init);
149 : :
150 : : static const guint32 crc_tab[256] = {
151 : : 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
152 : : 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
153 : : 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
154 : : 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
155 : : 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
156 : : 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
157 : : 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
158 : : 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
159 : : 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
160 : : 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
161 : : 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
162 : : 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
163 : : 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
164 : : 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
165 : : 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
166 : : 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
167 : : 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
168 : : 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
169 : : 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
170 : : 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
171 : : 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
172 : : 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
173 : : 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
174 : : 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
175 : : 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
176 : : 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
177 : : 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
178 : : 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
179 : : 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
180 : : 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
181 : : 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
182 : : 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
183 : : 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
184 : : 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
185 : : 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
186 : : 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
187 : : 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
188 : : 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
189 : : 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
190 : : 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
191 : : 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
192 : : 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
193 : : 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
194 : : };
195 : :
196 : : /* relicenced to LGPL from fluendo ts demuxer */
197 : : static guint32
198 : 0 : mpegts_parse_calc_crc32 (guint8 * data, guint datalen)
199 : : {
200 : : gint i;
201 : 0 : guint32 crc = 0xffffffff;
202 : :
203 [ # # ]: 0 : for (i = 0; i < datalen; i++) {
204 : 0 : crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff];
205 : : }
206 : 0 : return crc;
207 : : }
208 : :
209 : : static void
210 : 6 : _extra_init (GType type)
211 : : {
212 : 6 : QUARK_PROGRAMS = g_quark_from_string ("programs");
213 : 6 : QUARK_PROGRAM_NUMBER = g_quark_from_string ("program-number");
214 : 6 : QUARK_PID = g_quark_from_string ("pid");
215 : 6 : QUARK_PCR_PID = g_quark_from_string ("pcr-pid");
216 : 6 : QUARK_STREAMS = g_quark_from_string ("streams");
217 : 6 : QUARK_STREAM_TYPE = g_quark_from_string ("stream-type");
218 : 6 : }
219 : :
220 : : static void
221 : 6 : mpegts_parse_base_init (gpointer klass)
222 : : {
223 : 6 : GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
224 : :
225 : 6 : gst_element_class_add_pad_template (element_class,
226 : : gst_static_pad_template_get (&sink_template));
227 : 6 : gst_element_class_add_pad_template (element_class,
228 : : gst_static_pad_template_get (&src_template));
229 : 6 : gst_element_class_add_pad_template (element_class,
230 : : gst_static_pad_template_get (&program_template));
231 : :
232 : 6 : gst_element_class_set_details_simple (element_class,
233 : : "MPEG transport stream parser", "Codec/Parser",
234 : : "Parses MPEG2 transport streams",
235 : : "Alessandro Decina <alessandro@nnva.org>, "
236 : : "Zaheer Abbas Merali <zaheerabbas at merali dot org>");
237 : 6 : }
238 : :
239 : : static void
240 : 6 : mpegts_parse_class_init (MpegTSParseClass * klass)
241 : : {
242 : : GObjectClass *gobject_class;
243 : : GstElementClass *element_class;
244 : :
245 : 6 : element_class = GST_ELEMENT_CLASS (klass);
246 : 6 : element_class->pad_removed = mpegts_parse_pad_removed;
247 : 6 : element_class->request_new_pad = mpegts_parse_request_new_pad;
248 : 6 : element_class->release_pad = mpegts_parse_release_pad;
249 : 6 : element_class->change_state = mpegts_parse_change_state;
250 : :
251 : 6 : gobject_class = G_OBJECT_CLASS (klass);
252 : 6 : gobject_class->set_property = mpegts_parse_set_property;
253 : 6 : gobject_class->get_property = mpegts_parse_get_property;
254 : 6 : gobject_class->dispose = mpegts_parse_dispose;
255 : 6 : gobject_class->finalize = mpegts_parse_finalize;
256 : :
257 : 6 : g_object_class_install_property (gobject_class, PROP_PROGRAM_NUMBERS,
258 : : g_param_spec_string ("program-numbers",
259 : : "Program Numbers",
260 : : "Colon separated list of programs", "",
261 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
262 : 6 : }
263 : :
264 : : static gboolean
265 : 7 : foreach_psi_pid_remove (gpointer key, gpointer value, gpointer data)
266 : : {
267 : 7 : return TRUE;
268 : : }
269 : :
270 : : static void
271 : 10 : mpegts_parse_reset (MpegTSParse * parse)
272 : : {
273 : 10 : mpegts_packetizer_clear (parse->packetizer);
274 : 10 : g_hash_table_foreach_remove (parse->psi_pids, foreach_psi_pid_remove, NULL);
275 : :
276 : : /* PAT */
277 : 10 : g_hash_table_insert (parse->psi_pids,
278 : : GINT_TO_POINTER (0), GINT_TO_POINTER (1));
279 : : /* pmt pids will be added and removed dynamically */
280 : :
281 : 10 : }
282 : :
283 : : static void
284 : 3 : mpegts_parse_init (MpegTSParse * parse, MpegTSParseClass * klass)
285 : : {
286 : 3 : parse->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
287 : 3 : gst_pad_set_chain_function (parse->sinkpad, mpegts_parse_chain);
288 : 3 : gst_pad_set_event_function (parse->sinkpad, mpegts_parse_sink_event);
289 : 3 : gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
290 : :
291 : 3 : parse->disposed = FALSE;
292 : 3 : parse->need_sync_program_pads = FALSE;
293 : 3 : parse->packetizer = mpegts_packetizer_new ();
294 : 3 : parse->program_numbers = g_strdup ("");
295 : 3 : parse->pads_to_add = NULL;
296 : 3 : parse->pads_to_remove = NULL;
297 : 3 : parse->programs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
298 : : NULL, (GDestroyNotify) mpegts_parse_free_program);
299 : 3 : parse->psi_pids = g_hash_table_new (g_direct_hash, g_direct_equal);
300 : 3 : parse->pes_pids = g_hash_table_new (g_direct_hash, g_direct_equal);
301 : 3 : mpegts_parse_reset (parse);
302 : :
303 : 3 : }
304 : :
305 : : static void
306 : 3 : mpegts_parse_dispose (GObject * object)
307 : : {
308 : 3 : MpegTSParse *parse = GST_MPEGTS_PARSE (object);
309 : :
310 [ + - ]: 3 : if (!parse->disposed) {
311 : 3 : g_object_unref (parse->packetizer);
312 : 3 : parse->disposed = TRUE;
313 : : }
314 : :
315 [ + - ]: 3 : if (G_OBJECT_CLASS (parent_class)->dispose)
316 : 3 : G_OBJECT_CLASS (parent_class)->dispose (object);
317 : 3 : }
318 : :
319 : : static void
320 : 3 : mpegts_parse_finalize (GObject * object)
321 : : {
322 : 3 : MpegTSParse *parse = GST_MPEGTS_PARSE (object);
323 : :
324 : 3 : g_free (parse->program_numbers);
325 [ - + ]: 3 : if (parse->pat) {
326 : 0 : gst_structure_free (parse->pat);
327 : 0 : parse->pat = NULL;
328 : : }
329 : 3 : g_hash_table_destroy (parse->programs);
330 : 3 : g_hash_table_destroy (parse->psi_pids);
331 : 3 : g_hash_table_destroy (parse->pes_pids);
332 : :
333 [ + - ]: 3 : if (G_OBJECT_CLASS (parent_class)->finalize)
334 : 3 : G_OBJECT_CLASS (parent_class)->finalize (object);
335 : 3 : }
336 : :
337 : : static void
338 : 0 : mpegts_parse_set_property (GObject * object, guint prop_id,
339 : : const GValue * value, GParamSpec * pspec)
340 : : {
341 : 0 : MpegTSParse *parse = GST_MPEGTS_PARSE (object);
342 : :
343 [ # # ]: 0 : switch (prop_id) {
344 : : case PROP_PROGRAM_NUMBERS:
345 : 0 : mpegts_parse_reset_selected_programs (parse, g_value_dup_string (value));
346 : 0 : break;
347 : : default:
348 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
349 : : }
350 : 0 : }
351 : :
352 : : static void
353 : 0 : mpegts_parse_get_property (GObject * object, guint prop_id,
354 : : GValue * value, GParamSpec * pspec)
355 : : {
356 : 0 : MpegTSParse *parse = GST_MPEGTS_PARSE (object);
357 : :
358 [ # # ]: 0 : switch (prop_id) {
359 : : case PROP_PROGRAM_NUMBERS:
360 : 0 : g_value_set_string (value, parse->program_numbers);
361 : 0 : break;
362 : : default:
363 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
364 : : }
365 : 0 : }
366 : :
367 : : static MpegTSParseProgram *
368 : 0 : mpegts_parse_add_program (MpegTSParse * parse,
369 : : gint program_number, guint16 pmt_pid)
370 : : {
371 : : MpegTSParseProgram *program;
372 : :
373 : 0 : program = g_new0 (MpegTSParseProgram, 1);
374 : 0 : program->program_number = program_number;
375 : 0 : program->pmt_pid = pmt_pid;
376 : 0 : program->pcr_pid = G_MAXUINT16;
377 : 0 : program->streams = g_hash_table_new_full (g_direct_hash, g_direct_equal,
378 : : NULL, (GDestroyNotify) mpegts_parse_free_stream);
379 : 0 : program->patcount = 0;
380 : 0 : program->selected = 0;
381 : 0 : program->active = FALSE;
382 : :
383 : 0 : g_hash_table_insert (parse->programs,
384 : 0 : GINT_TO_POINTER (program_number), program);
385 : :
386 : 0 : return program;
387 : : }
388 : :
389 : : static MpegTSParseProgram *
390 : 0 : mpegts_parse_get_program (MpegTSParse * parse, gint program_number)
391 : : {
392 : : MpegTSParseProgram *program;
393 : :
394 : 0 : program = (MpegTSParseProgram *) g_hash_table_lookup (parse->programs,
395 : 0 : GINT_TO_POINTER ((gint) program_number));
396 : :
397 : 0 : return program;
398 : : }
399 : :
400 : : static GstPad *
401 : 0 : mpegts_parse_activate_program (MpegTSParse * parse,
402 : : MpegTSParseProgram * program)
403 : : {
404 : : MpegTSParsePad *tspad;
405 : : gchar *pad_name;
406 : :
407 : 0 : pad_name = g_strdup_printf ("program_%d", program->program_number);
408 : :
409 : 0 : tspad = mpegts_parse_create_tspad (parse, pad_name);
410 : 0 : tspad->program_number = program->program_number;
411 : 0 : tspad->program = program;
412 : 0 : program->tspad = tspad;
413 : 0 : g_free (pad_name);
414 : 0 : gst_pad_set_active (tspad->pad, TRUE);
415 : 0 : program->active = TRUE;
416 : :
417 : 0 : return tspad->pad;
418 : : }
419 : :
420 : : static GstPad *
421 : 0 : mpegts_parse_deactivate_program (MpegTSParse * parse,
422 : : MpegTSParseProgram * program)
423 : : {
424 : : MpegTSParsePad *tspad;
425 : :
426 : 0 : tspad = program->tspad;
427 : 0 : gst_pad_set_active (tspad->pad, FALSE);
428 : 0 : program->active = FALSE;
429 : :
430 : : /* tspad will be destroyed in GstElementClass::pad_removed */
431 : :
432 : 0 : return tspad->pad;
433 : : }
434 : :
435 : : static void
436 : 0 : mpegts_parse_free_program (MpegTSParseProgram * program)
437 : : {
438 [ # # ]: 0 : if (program->pmt_info)
439 : 0 : gst_structure_free (program->pmt_info);
440 : :
441 : 0 : g_hash_table_destroy (program->streams);
442 : :
443 : 0 : g_free (program);
444 : 0 : }
445 : :
446 : : static void
447 : 0 : mpegts_parse_remove_program (MpegTSParse * parse, gint program_number)
448 : : {
449 : 0 : g_hash_table_remove (parse->programs, GINT_TO_POINTER (program_number));
450 : 0 : }
451 : :
452 : : static void
453 : 0 : mpegts_parse_sync_program_pads (MpegTSParse * parse)
454 : : {
455 : : GList *walk;
456 : :
457 [ # # ]: 0 : GST_INFO_OBJECT (parse, "begin sync pads");
458 [ # # ]: 0 : for (walk = parse->pads_to_remove; walk; walk = walk->next)
459 : 0 : gst_element_remove_pad (GST_ELEMENT (parse), GST_PAD (walk->data));
460 : :
461 [ # # ]: 0 : for (walk = parse->pads_to_add; walk; walk = walk->next)
462 : 0 : gst_element_add_pad (GST_ELEMENT (parse), GST_PAD (walk->data));
463 : :
464 [ # # ]: 0 : if (parse->pads_to_add)
465 : 0 : g_list_free (parse->pads_to_add);
466 : :
467 [ # # ]: 0 : if (parse->pads_to_remove)
468 : 0 : g_list_free (parse->pads_to_remove);
469 : :
470 : 0 : GST_OBJECT_LOCK (parse);
471 : 0 : parse->pads_to_remove = NULL;
472 : 0 : parse->pads_to_add = NULL;
473 : 0 : parse->need_sync_program_pads = FALSE;
474 : 0 : GST_OBJECT_UNLOCK (parse);
475 : :
476 [ # # ]: 0 : GST_INFO_OBJECT (parse, "end sync pads");
477 : 0 : }
478 : :
479 : :
480 : : static MpegTSParseStream *
481 : 0 : mpegts_parse_program_add_stream (MpegTSParse * parse,
482 : : MpegTSParseProgram * program, guint16 pid, guint8 stream_type)
483 : : {
484 : : MpegTSParseStream *stream;
485 : :
486 : 0 : stream = g_new0 (MpegTSParseStream, 1);
487 : 0 : stream->pid = pid;
488 : 0 : stream->stream_type = stream_type;
489 : :
490 : 0 : g_hash_table_insert (program->streams, GINT_TO_POINTER ((gint) pid), stream);
491 : :
492 : 0 : return stream;
493 : : }
494 : :
495 : : static void
496 : 0 : foreach_program_activate_or_deactivate (gpointer key, gpointer value,
497 : : gpointer data)
498 : : {
499 : 0 : MpegTSParse *parse = GST_MPEGTS_PARSE (data);
500 : 0 : MpegTSParseProgram *program = (MpegTSParseProgram *) value;
501 : :
502 : : /* at this point selected programs have program->selected == 2,
503 : : * unselected programs thay may have to be deactivated have selected == 1 and
504 : : * unselected inactive programs have selected == 0 */
505 : :
506 [ # # # # ]: 0 : switch (--program->selected) {
507 : : case 1:
508 : : /* selected */
509 [ # # ][ # # ]: 0 : if (!program->active && program->pmt_pid != G_MAXUINT16)
510 : 0 : parse->pads_to_add = g_list_append (parse->pads_to_add,
511 : 0 : mpegts_parse_activate_program (parse, program));
512 : 0 : break;
513 : : case 0:
514 : : /* unselected */
515 [ # # ]: 0 : if (program->active)
516 : 0 : parse->pads_to_remove = g_list_append (parse->pads_to_remove,
517 : 0 : mpegts_parse_deactivate_program (parse, program));
518 : 0 : break;
519 : : case -1:
520 : : /* was already unselected */
521 : 0 : program->selected = 0;
522 : 0 : break;
523 : : default:
524 : 0 : g_return_if_reached ();
525 : : }
526 : : }
527 : :
528 : : static void
529 : 0 : mpegts_parse_reset_selected_programs (MpegTSParse * parse,
530 : : gchar * program_numbers)
531 : : {
532 : 0 : GST_OBJECT_LOCK (parse);
533 [ # # ]: 0 : if (parse->program_numbers)
534 : 0 : g_free (parse->program_numbers);
535 : :
536 : 0 : parse->program_numbers = program_numbers;
537 : :
538 [ # # ]: 0 : if (*parse->program_numbers != '\0') {
539 : : gint program_number;
540 : : MpegTSParseProgram *program;
541 : : gchar **progs, **walk;
542 : :
543 : 0 : progs = g_strsplit (parse->program_numbers, ":", 0);
544 : :
545 : 0 : walk = progs;
546 [ # # ]: 0 : while (*walk != NULL) {
547 : 0 : program_number = strtol (*walk, NULL, 0);
548 : 0 : program = mpegts_parse_get_program (parse, program_number);
549 [ # # ]: 0 : if (program == NULL)
550 : : /* create the program, it will get activated once we get a PMT for it */
551 : 0 : program = mpegts_parse_add_program (parse, program_number, G_MAXUINT16);
552 : :
553 : 0 : program->selected = 2;
554 : 0 : ++walk;
555 : : }
556 : 0 : g_strfreev (progs);
557 : : }
558 : :
559 : 0 : g_hash_table_foreach (parse->programs,
560 : : foreach_program_activate_or_deactivate, parse);
561 : :
562 [ # # ][ # # ]: 0 : if (parse->pads_to_remove || parse->pads_to_add)
563 : 0 : parse->need_sync_program_pads = TRUE;
564 : 0 : GST_OBJECT_UNLOCK (parse);
565 : 0 : }
566 : :
567 : : static void
568 : 0 : mpegts_parse_free_stream (MpegTSParseStream * stream)
569 : : {
570 : 0 : g_free (stream);
571 : 0 : }
572 : :
573 : : static void
574 : 0 : mpegts_parse_program_remove_stream (MpegTSParse * parse,
575 : : MpegTSParseProgram * program, guint16 pid)
576 : : {
577 : 0 : g_hash_table_remove (program->streams, GINT_TO_POINTER ((gint) pid));
578 : 0 : }
579 : :
580 : : static void
581 : 0 : mpegts_parse_deactivate_pmt (MpegTSParse * parse, MpegTSParseProgram * program)
582 : : {
583 : : gint i;
584 : : guint pid;
585 : : guint stream_type;
586 : : GstStructure *stream;
587 : : const GValue *streams;
588 : : const GValue *value;
589 : :
590 [ # # ]: 0 : if (program->pmt_info) {
591 : 0 : streams = gst_structure_id_get_value (program->pmt_info, QUARK_STREAMS);
592 : :
593 [ # # ]: 0 : for (i = 0; i < gst_value_list_get_size (streams); ++i) {
594 : 0 : value = gst_value_list_get_value (streams, i);
595 : 0 : stream = g_value_get_boxed (value);
596 : 0 : gst_structure_id_get (stream, QUARK_PID, G_TYPE_UINT, &pid,
597 : : QUARK_STREAM_TYPE, G_TYPE_UINT, &stream_type, NULL);
598 : 0 : mpegts_parse_program_remove_stream (parse, program, (guint16) pid);
599 : 0 : g_hash_table_remove (parse->pes_pids, GINT_TO_POINTER ((gint) pid));
600 : : }
601 : :
602 : : /* remove pcr stream */
603 : 0 : mpegts_parse_program_remove_stream (parse, program, program->pcr_pid);
604 : 0 : g_hash_table_remove (parse->pes_pids,
605 : 0 : GINT_TO_POINTER ((gint) program->pcr_pid));
606 : : }
607 : 0 : }
608 : :
609 : : static MpegTSParsePad *
610 : 0 : mpegts_parse_create_tspad (MpegTSParse * parse, const gchar * pad_name)
611 : : {
612 : : GstPad *pad;
613 : : MpegTSParsePad *tspad;
614 : :
615 : 0 : pad = gst_pad_new_from_static_template (&program_template, pad_name);
616 : 0 : gst_pad_set_query_function (pad,
617 : 0 : GST_DEBUG_FUNCPTR (mpegts_parse_src_pad_query));
618 : :
619 : : /* create our wrapper */
620 : 0 : tspad = g_new0 (MpegTSParsePad, 1);
621 : 0 : tspad->pad = pad;
622 : 0 : tspad->program_number = -1;
623 : 0 : tspad->program = NULL;
624 : 0 : tspad->pushed = FALSE;
625 : 0 : tspad->flow_return = GST_FLOW_NOT_LINKED;
626 : 0 : gst_pad_set_element_private (pad, tspad);
627 : :
628 : 0 : return tspad;
629 : : }
630 : :
631 : : static void
632 : 0 : mpegts_parse_destroy_tspad (MpegTSParse * parse, MpegTSParsePad * tspad)
633 : : {
634 [ # # ]: 0 : if (tspad->tags) {
635 : 0 : gst_tag_list_free (tspad->tags);
636 : : }
637 : :
638 : : /* free the wrapper */
639 : 0 : g_free (tspad);
640 : 0 : }
641 : :
642 : : static void
643 : 3 : mpegts_parse_pad_removed (GstElement * element, GstPad * pad)
644 : : {
645 : : MpegTSParsePad *tspad;
646 : 3 : MpegTSParse *parse = GST_MPEGTS_PARSE (element);
647 : :
648 [ + - ]: 3 : if (gst_pad_get_direction (pad) == GST_PAD_SINK)
649 : 3 : return;
650 : :
651 : 0 : tspad = (MpegTSParsePad *) gst_pad_get_element_private (pad);
652 : 0 : mpegts_parse_destroy_tspad (parse, tspad);
653 : :
654 [ # # ]: 0 : if (GST_ELEMENT_CLASS (parent_class)->pad_removed)
655 : 0 : GST_ELEMENT_CLASS (parent_class)->pad_removed (element, pad);
656 : : }
657 : :
658 : : static GstPad *
659 : 0 : mpegts_parse_request_new_pad (GstElement * element, GstPadTemplate * template,
660 : : const gchar * unused)
661 : : {
662 : : MpegTSParse *parse;
663 : : gchar *name;
664 : : GstPad *pad;
665 : :
666 [ # # ]: 0 : g_return_val_if_fail (template != NULL, NULL);
667 [ # # ][ # # ]: 0 : g_return_val_if_fail (GST_IS_MPEGTS_PARSE (element), NULL);
[ # # ][ # # ]
668 : :
669 : 0 : parse = GST_MPEGTS_PARSE (element);
670 : :
671 : 0 : GST_OBJECT_LOCK (element);
672 : 0 : name = g_strdup_printf ("src%d", parse->req_pads++);
673 : 0 : GST_OBJECT_UNLOCK (element);
674 : :
675 : 0 : pad = mpegts_parse_create_tspad (parse, name)->pad;
676 : 0 : gst_pad_set_active (pad, TRUE);
677 : 0 : gst_element_add_pad (element, pad);
678 : 0 : g_free (name);
679 : :
680 : 0 : return pad;
681 : : }
682 : :
683 : : static void
684 : 0 : mpegts_parse_release_pad (GstElement * element, GstPad * pad)
685 : : {
686 [ # # ][ # # ]: 0 : g_return_if_fail (GST_IS_MPEGTS_PARSE (element));
[ # # ][ # # ]
687 : :
688 : 0 : gst_pad_set_active (pad, FALSE);
689 : : /* we do the cleanup in GstElement::pad-removed */
690 : 0 : gst_element_remove_pad (element, pad);
691 : : }
692 : :
693 : : static GstFlowReturn
694 : 0 : mpegts_parse_tspad_push_section (MpegTSParse * parse, MpegTSParsePad * tspad,
695 : : MpegTSPacketizerSection * section, GstBuffer * buffer)
696 : : {
697 : 0 : GstFlowReturn ret = GST_FLOW_NOT_LINKED;
698 : 0 : gboolean to_push = TRUE;
699 : :
700 [ # # ]: 0 : if (tspad->program_number != -1) {
701 [ # # ]: 0 : if (tspad->program) {
702 : : /* we push all sections to all pads except PMTs which we
703 : : * only push to pads meant to receive that program number */
704 [ # # ]: 0 : if (section->table_id == 0x02) {
705 : : /* PMT */
706 [ # # ]: 0 : if (section->subtable_extension != tspad->program_number)
707 : 0 : to_push = FALSE;
708 : : }
709 : : } else {
710 : : /* there's a program filter on the pad but the PMT for the program has not
711 : : * been parsed yet, ignore the pad until we get a PMT */
712 : 0 : to_push = FALSE;
713 : 0 : ret = GST_FLOW_OK;
714 : : }
715 : : }
716 [ # # ]: 0 : GST_DEBUG_OBJECT (parse,
717 : : "pushing section: %d program number: %d table_id: %d", to_push,
718 : : tspad->program_number, section->table_id);
719 [ # # ]: 0 : if (to_push) {
720 : 0 : ret = gst_pad_push (tspad->pad, buffer);
721 : : } else {
722 : 0 : gst_buffer_unref (buffer);
723 [ # # ]: 0 : if (gst_pad_is_linked (tspad->pad))
724 : 0 : ret = GST_FLOW_OK;
725 : : }
726 : :
727 : 0 : return ret;
728 : : }
729 : :
730 : : static GstFlowReturn
731 : 0 : mpegts_parse_tspad_push (MpegTSParse * parse, MpegTSParsePad * tspad,
732 : : guint16 pid, GstBuffer * buffer)
733 : : {
734 : 0 : GstFlowReturn ret = GST_FLOW_NOT_LINKED;
735 : 0 : GHashTable *pad_pids = NULL;
736 : :
737 [ # # ]: 0 : if (tspad->program_number != -1) {
738 [ # # ]: 0 : if (tspad->program) {
739 : 0 : pad_pids = tspad->program->streams;
740 : :
741 [ # # ]: 0 : if (tspad->tags) {
742 : 0 : gst_element_found_tags_for_pad (GST_ELEMENT_CAST (parse),
743 : : tspad->pad, tspad->tags);
744 : 0 : tspad->tags = NULL;
745 : : }
746 : : } else {
747 : : /* there's a program filter on the pad but the PMT for the program has not
748 : : * been parsed yet, ignore the pad until we get a PMT */
749 : 0 : gst_buffer_unref (buffer);
750 : 0 : ret = GST_FLOW_OK;
751 : 0 : goto out;
752 : : }
753 : : }
754 : :
755 [ # # # # ]: 0 : if (pad_pids == NULL ||
756 : 0 : g_hash_table_lookup (pad_pids, GINT_TO_POINTER ((gint) pid)) != NULL) {
757 : : /* push if there's no filter or if the pid is in the filter */
758 : 0 : ret = gst_pad_push (tspad->pad, buffer);
759 : : } else {
760 : 0 : gst_buffer_unref (buffer);
761 [ # # ]: 0 : if (gst_pad_is_linked (tspad->pad))
762 : 0 : ret = GST_FLOW_OK;
763 : : }
764 : :
765 : : out:
766 : 0 : return ret;
767 : : }
768 : :
769 : : static void
770 : 0 : pad_clear_for_push (GstPad * pad, MpegTSParse * parse)
771 : : {
772 : 0 : MpegTSParsePad *tspad = (MpegTSParsePad *) gst_pad_get_element_private (pad);
773 : :
774 : 0 : tspad->flow_return = GST_FLOW_NOT_LINKED;
775 : 0 : tspad->pushed = FALSE;
776 : 0 : }
777 : :
778 : : static GstFlowReturn
779 : 0 : mpegts_parse_push (MpegTSParse * parse, MpegTSPacketizerPacket * packet,
780 : : MpegTSPacketizerSection * section)
781 : : {
782 : : guint32 pads_cookie;
783 : 0 : gboolean done = FALSE;
784 : 0 : GstPad *pad = NULL;
785 : : MpegTSParsePad *tspad;
786 : : guint16 pid;
787 : : GstBuffer *buffer;
788 : : GstFlowReturn ret;
789 : : GList *srcpads;
790 : :
791 : 0 : pid = packet->pid;
792 : 0 : buffer = gst_buffer_make_metadata_writable (packet->buffer);
793 : : /* we have the same caps on all the src pads */
794 : 0 : gst_buffer_set_caps (buffer, parse->packetizer->caps);
795 : :
796 : 0 : GST_OBJECT_LOCK (parse);
797 : : /* clear tspad->pushed on pads */
798 : 0 : g_list_foreach (GST_ELEMENT_CAST (parse)->srcpads,
799 : : (GFunc) pad_clear_for_push, parse);
800 [ # # ]: 0 : if (GST_ELEMENT_CAST (parse)->srcpads)
801 : 0 : ret = GST_FLOW_NOT_LINKED;
802 : : else
803 : 0 : ret = GST_FLOW_OK;
804 : :
805 : : /* Get cookie and source pads list */
806 : 0 : pads_cookie = GST_ELEMENT_CAST (parse)->pads_cookie;
807 : 0 : srcpads = GST_ELEMENT_CAST (parse)->srcpads;
808 [ # # ]: 0 : if (G_LIKELY (srcpads)) {
809 : 0 : pad = GST_PAD_CAST (srcpads->data);
810 : 0 : g_object_ref (pad);
811 : : }
812 : 0 : GST_OBJECT_UNLOCK (parse);
813 : :
814 [ # # ][ # # ]: 0 : while (pad && !done) {
815 : 0 : tspad = gst_pad_get_element_private (pad);
816 : :
817 [ # # ]: 0 : if (G_LIKELY (!tspad->pushed)) {
818 : : /* ref the buffer as gst_pad_push takes a ref but we want to reuse the
819 : : * same buffer for next pushes */
820 : 0 : gst_buffer_ref (buffer);
821 [ # # ]: 0 : if (section) {
822 : 0 : tspad->flow_return =
823 : 0 : mpegts_parse_tspad_push_section (parse, tspad, section, buffer);
824 : : } else {
825 : 0 : tspad->flow_return =
826 : 0 : mpegts_parse_tspad_push (parse, tspad, pid, buffer);
827 : : }
828 : 0 : tspad->pushed = TRUE;
829 : :
830 [ # # ][ # # ]: 0 : if (G_UNLIKELY (tspad->flow_return != GST_FLOW_OK
831 : : && tspad->flow_return != GST_FLOW_NOT_LINKED)) {
832 : : /* return the error upstream */
833 : 0 : ret = tspad->flow_return;
834 : 0 : done = TRUE;
835 : : }
836 : :
837 : : }
838 : :
839 [ # # ]: 0 : if (ret == GST_FLOW_NOT_LINKED)
840 : 0 : ret = tspad->flow_return;
841 : :
842 : 0 : g_object_unref (pad);
843 : :
844 [ # # ]: 0 : if (G_UNLIKELY (!done)) {
845 : 0 : GST_OBJECT_LOCK (parse);
846 [ # # ]: 0 : if (G_UNLIKELY (pads_cookie != GST_ELEMENT_CAST (parse)->pads_cookie)) {
847 : : /* resync */
848 [ # # ]: 0 : GST_DEBUG ("resync");
849 : 0 : pads_cookie = GST_ELEMENT_CAST (parse)->pads_cookie;
850 : 0 : srcpads = GST_ELEMENT_CAST (parse)->srcpads;
851 : : } else {
852 [ # # ]: 0 : GST_DEBUG ("getting next pad");
853 : : /* Get next pad */
854 [ # # ]: 0 : srcpads = g_list_next (srcpads);
855 : : }
856 : :
857 [ # # ]: 0 : if (srcpads) {
858 : 0 : pad = GST_PAD_CAST (srcpads->data);
859 : 0 : g_object_ref (pad);
860 : : } else
861 : 0 : done = TRUE;
862 : 0 : GST_OBJECT_UNLOCK (parse);
863 : : }
864 : : }
865 : :
866 : 0 : gst_buffer_unref (buffer);
867 : 0 : packet->buffer = NULL;
868 : :
869 : 0 : return ret;
870 : : }
871 : :
872 : : static gboolean
873 : 0 : mpegts_parse_is_psi (MpegTSParse * parse, MpegTSPacketizerPacket * packet)
874 : : {
875 : 0 : gboolean retval = FALSE;
876 : : guint8 table_id;
877 : : int i;
878 : : static const guint8 si_tables[] =
879 : : { 0x00, 0x01, 0x02, 0x03, 0x40, 0x41, 0x42, 0x46, 0x4A, 0x4E, 0x4F, 0x50,
880 : : 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C,
881 : : 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
882 : : 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x7E,
883 : : 0x7F, TABLE_ID_UNSET
884 : : };
885 [ # # ]: 0 : if (g_hash_table_lookup (parse->psi_pids,
886 : 0 : GINT_TO_POINTER ((gint) packet->pid)) != NULL)
887 : 0 : retval = TRUE;
888 : : /* check is it is a pes pid */
889 [ # # ]: 0 : if (g_hash_table_lookup (parse->pes_pids,
890 : 0 : GINT_TO_POINTER ((gint) packet->pid)) != NULL)
891 : 0 : return FALSE;
892 [ # # ]: 0 : if (!retval) {
893 [ # # ]: 0 : if (packet->payload_unit_start_indicator) {
894 : 0 : table_id = *(packet->data);
895 : 0 : i = 0;
896 [ # # ]: 0 : while (si_tables[i] != TABLE_ID_UNSET) {
897 [ # # ]: 0 : if (G_UNLIKELY (si_tables[i] == table_id)) {
898 [ # # ]: 0 : GST_DEBUG_OBJECT (parse, "Packet has table id 0x%x", table_id);
899 : 0 : retval = TRUE;
900 : 0 : break;
901 : : }
902 : 0 : i++;
903 : : }
904 : : } else {
905 : 0 : MpegTSPacketizerStream *stream = parse->packetizer->streams[packet->pid];
906 [ # # ]: 0 : if (stream) {
907 : 0 : i = 0;
908 [ # # ]: 0 : GST_DEBUG_OBJECT (parse, "section table id: 0x%x",
909 : : stream->section_table_id);
910 [ # # ]: 0 : while (si_tables[i] != TABLE_ID_UNSET) {
911 [ # # ]: 0 : if (G_UNLIKELY (si_tables[i] == stream->section_table_id)) {
912 : 0 : retval = TRUE;
913 : 0 : break;
914 : : }
915 : 0 : i++;
916 : : }
917 : : }
918 : : }
919 : : }
920 : :
921 [ # # ]: 0 : GST_DEBUG_OBJECT (parse, "Packet of pid 0x%x is psi: %d", packet->pid,
922 : : retval);
923 : 0 : return retval;
924 : : }
925 : :
926 : : static void
927 : 0 : mpegts_parse_apply_pat (MpegTSParse * parse, GstStructure * pat_info)
928 : : {
929 : : const GValue *value;
930 : : GstStructure *old_pat;
931 : : GstStructure *program_info;
932 : : guint program_number;
933 : : guint pid;
934 : : MpegTSParseProgram *program;
935 : : gint i;
936 : : const GValue *programs;
937 : :
938 : 0 : old_pat = parse->pat;
939 : 0 : parse->pat = gst_structure_copy (pat_info);
940 : :
941 [ # # ]: 0 : GST_INFO_OBJECT (parse, "PAT %" GST_PTR_FORMAT, pat_info);
942 : :
943 : 0 : gst_element_post_message (GST_ELEMENT_CAST (parse),
944 : 0 : gst_message_new_element (GST_OBJECT (parse),
945 : : gst_structure_copy (pat_info)));
946 : :
947 : 0 : GST_OBJECT_LOCK (parse);
948 : 0 : programs = gst_structure_id_get_value (pat_info, QUARK_PROGRAMS);
949 : : /* activate the new table */
950 [ # # ]: 0 : for (i = 0; i < gst_value_list_get_size (programs); ++i) {
951 : 0 : value = gst_value_list_get_value (programs, i);
952 : :
953 : 0 : program_info = g_value_get_boxed (value);
954 : 0 : gst_structure_id_get (program_info, QUARK_PROGRAM_NUMBER, G_TYPE_UINT,
955 : : &program_number, QUARK_PID, G_TYPE_UINT, &pid, NULL);
956 : :
957 : 0 : program = mpegts_parse_get_program (parse, program_number);
958 [ # # ]: 0 : if (program) {
959 [ # # ]: 0 : if (program->pmt_pid != pid) {
960 [ # # ]: 0 : if (program->pmt_pid != G_MAXUINT16) {
961 : : /* pmt pid changed */
962 : 0 : g_hash_table_remove (parse->psi_pids,
963 : 0 : GINT_TO_POINTER ((gint) program->pmt_pid));
964 : : }
965 : :
966 : 0 : program->pmt_pid = pid;
967 : 0 : g_hash_table_insert (parse->psi_pids,
968 : 0 : GINT_TO_POINTER ((gint) pid), GINT_TO_POINTER (1));
969 : : }
970 : : } else {
971 : 0 : g_hash_table_insert (parse->psi_pids,
972 : 0 : GINT_TO_POINTER ((gint) pid), GINT_TO_POINTER (1));
973 : 0 : program = mpegts_parse_add_program (parse, program_number, pid);
974 : : }
975 : 0 : program->patcount += 1;
976 [ # # ][ # # ]: 0 : if (program->selected && !program->active)
977 : 0 : parse->pads_to_add = g_list_append (parse->pads_to_add,
978 : 0 : mpegts_parse_activate_program (parse, program));
979 : : }
980 : :
981 [ # # ]: 0 : if (old_pat) {
982 : : /* deactivate the old table */
983 : :
984 : 0 : programs = gst_structure_id_get_value (old_pat, QUARK_PROGRAMS);
985 [ # # ]: 0 : for (i = 0; i < gst_value_list_get_size (programs); ++i) {
986 : 0 : value = gst_value_list_get_value (programs, i);
987 : :
988 : 0 : program_info = g_value_get_boxed (value);
989 : 0 : gst_structure_id_get (program_info,
990 : : QUARK_PROGRAM_NUMBER, G_TYPE_UINT, &program_number,
991 : : QUARK_PID, G_TYPE_UINT, &pid, NULL);
992 : :
993 : 0 : program = mpegts_parse_get_program (parse, program_number);
994 [ # # ]: 0 : if (program == NULL) {
995 [ # # ]: 0 : GST_DEBUG_OBJECT (parse, "broken PAT, duplicated entry for program %d",
996 : : program_number);
997 : 0 : continue;
998 : : }
999 : :
1000 [ # # ]: 0 : if (--program->patcount > 0)
1001 : : /* the program has been referenced by the new pat, keep it */
1002 : 0 : continue;
1003 : :
1004 [ # # ]: 0 : GST_INFO_OBJECT (parse, "PAT removing program %" GST_PTR_FORMAT,
1005 : : program_info);
1006 : :
1007 [ # # ]: 0 : if (program->active)
1008 : 0 : parse->pads_to_remove = g_list_append (parse->pads_to_remove,
1009 : 0 : mpegts_parse_deactivate_program (parse, program));
1010 : :
1011 : 0 : mpegts_parse_deactivate_pmt (parse, program);
1012 : 0 : mpegts_parse_remove_program (parse, program_number);
1013 : 0 : g_hash_table_remove (parse->psi_pids, GINT_TO_POINTER ((gint) pid));
1014 : 0 : mpegts_packetizer_remove_stream (parse->packetizer, pid);
1015 : : }
1016 : :
1017 : 0 : gst_structure_free (old_pat);
1018 : : }
1019 : :
1020 : 0 : GST_OBJECT_UNLOCK (parse);
1021 : :
1022 : 0 : mpegts_parse_sync_program_pads (parse);
1023 : 0 : }
1024 : :
1025 : : static void
1026 : 0 : mpegts_parse_apply_pmt (MpegTSParse * parse,
1027 : : guint16 pmt_pid, GstStructure * pmt_info)
1028 : : {
1029 : : MpegTSParseProgram *program;
1030 : : guint program_number;
1031 : : guint pcr_pid;
1032 : : guint pid;
1033 : : guint stream_type;
1034 : : GstStructure *stream;
1035 : : gint i;
1036 : : const GValue *new_streams;
1037 : : const GValue *value;
1038 : :
1039 : 0 : gst_structure_id_get (pmt_info,
1040 : : QUARK_PROGRAM_NUMBER, G_TYPE_UINT, &program_number,
1041 : : QUARK_PCR_PID, G_TYPE_UINT, &pcr_pid, NULL);
1042 : 0 : new_streams = gst_structure_id_get_value (pmt_info, QUARK_STREAMS);
1043 : :
1044 : 0 : GST_OBJECT_LOCK (parse);
1045 : 0 : program = mpegts_parse_get_program (parse, program_number);
1046 [ # # ]: 0 : if (program) {
1047 : : /* deactivate old pmt */
1048 : 0 : mpegts_parse_deactivate_pmt (parse, program);
1049 [ # # ]: 0 : if (program->pmt_info)
1050 : 0 : gst_structure_free (program->pmt_info);
1051 : 0 : program->pmt_info = NULL;
1052 : : } else {
1053 : : /* no PAT?? */
1054 : 0 : g_hash_table_insert (parse->psi_pids,
1055 : 0 : GINT_TO_POINTER ((gint) pmt_pid), GINT_TO_POINTER (1));
1056 : 0 : program = mpegts_parse_add_program (parse, program_number, pid);
1057 : : }
1058 : :
1059 : : /* activate new pmt */
1060 : 0 : program->pmt_info = gst_structure_copy (pmt_info);
1061 : 0 : program->pmt_pid = pmt_pid;
1062 : 0 : program->pcr_pid = pcr_pid;
1063 : 0 : mpegts_parse_program_add_stream (parse, program, (guint16) pcr_pid, -1);
1064 : 0 : g_hash_table_insert (parse->pes_pids, GINT_TO_POINTER ((gint) pcr_pid),
1065 : : GINT_TO_POINTER (1));
1066 : :
1067 [ # # ]: 0 : for (i = 0; i < gst_value_list_get_size (new_streams); ++i) {
1068 : 0 : value = gst_value_list_get_value (new_streams, i);
1069 : 0 : stream = g_value_get_boxed (value);
1070 : :
1071 : 0 : gst_structure_id_get (stream, QUARK_PID, G_TYPE_UINT, &pid,
1072 : : QUARK_STREAM_TYPE, G_TYPE_UINT, &stream_type, NULL);
1073 : 0 : mpegts_parse_program_add_stream (parse, program,
1074 : 0 : (guint16) pid, (guint8) stream_type);
1075 : 0 : g_hash_table_insert (parse->pes_pids, GINT_TO_POINTER ((gint) pid),
1076 : : GINT_TO_POINTER ((gint) 1));
1077 : :
1078 : : }
1079 : 0 : GST_OBJECT_UNLOCK (parse);
1080 : :
1081 [ # # ]: 0 : GST_DEBUG_OBJECT (parse, "new pmt %" GST_PTR_FORMAT, pmt_info);
1082 : :
1083 : 0 : gst_element_post_message (GST_ELEMENT_CAST (parse),
1084 : 0 : gst_message_new_element (GST_OBJECT (parse),
1085 : : gst_structure_copy (pmt_info)));
1086 : 0 : }
1087 : :
1088 : : static void
1089 : 0 : mpegts_parse_apply_nit (MpegTSParse * parse,
1090 : : guint16 pmt_pid, GstStructure * nit_info)
1091 : : {
1092 : 0 : gst_element_post_message (GST_ELEMENT_CAST (parse),
1093 : 0 : gst_message_new_element (GST_OBJECT (parse),
1094 : : gst_structure_copy (nit_info)));
1095 : 0 : }
1096 : :
1097 : : static void
1098 : 0 : mpegts_parse_apply_sdt (MpegTSParse * parse,
1099 : : guint16 pmt_pid, GstStructure * sdt_info)
1100 : : {
1101 : 0 : mpegts_parse_get_tags_from_sdt (parse, sdt_info);
1102 : :
1103 : 0 : gst_element_post_message (GST_ELEMENT_CAST (parse),
1104 : 0 : gst_message_new_element (GST_OBJECT (parse),
1105 : : gst_structure_copy (sdt_info)));
1106 : 0 : }
1107 : :
1108 : : static void
1109 : 0 : mpegts_parse_apply_eit (MpegTSParse * parse,
1110 : : guint16 pmt_pid, GstStructure * eit_info)
1111 : : {
1112 : 0 : mpegts_parse_get_tags_from_eit (parse, eit_info);
1113 : :
1114 : 0 : gst_element_post_message (GST_ELEMENT_CAST (parse),
1115 : 0 : gst_message_new_element (GST_OBJECT (parse),
1116 : : gst_structure_copy (eit_info)));
1117 : 0 : }
1118 : :
1119 : : static void
1120 : 0 : mpegts_parse_apply_tdt (MpegTSParse * parse,
1121 : : guint16 tdt_pid, GstStructure * tdt_info)
1122 : : {
1123 : 0 : gst_element_post_message (GST_ELEMENT_CAST (parse),
1124 : 0 : gst_message_new_element (GST_OBJECT (parse),
1125 : : gst_structure_copy (tdt_info)));
1126 : :
1127 : 0 : gst_element_send_event (GST_ELEMENT_CAST (parse),
1128 : : gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
1129 : : gst_structure_copy (tdt_info)));
1130 : 0 : }
1131 : :
1132 : : static gboolean
1133 : 0 : mpegts_parse_handle_psi (MpegTSParse * parse, MpegTSPacketizerSection * section)
1134 : : {
1135 : 0 : gboolean res = TRUE;
1136 : 0 : GstStructure *structure = NULL;
1137 : :
1138 : : /* table ids 0x70 - 0x73 do not have a crc */
1139 [ # # ][ # # ]: 0 : if (G_LIKELY (section->table_id < 0x70 || section->table_id > 0x73)) {
1140 [ # # ]: 0 : if (G_UNLIKELY (mpegts_parse_calc_crc32 (GST_BUFFER_DATA (section->buffer),
1141 : : GST_BUFFER_SIZE (section->buffer)) != 0)) {
1142 [ # # ]: 0 : GST_WARNING_OBJECT (parse, "bad crc in psi pid 0x%x", section->pid);
1143 : 0 : return FALSE;
1144 : : }
1145 : : }
1146 : :
1147 [ # # # # : 0 : switch (section->table_id) {
# # # ]
1148 : : case 0x00:
1149 : : /* PAT */
1150 : 0 : structure = mpegts_packetizer_parse_pat (parse->packetizer, section);
1151 [ # # ]: 0 : if (G_LIKELY (structure))
1152 : 0 : mpegts_parse_apply_pat (parse, structure);
1153 : : else
1154 : 0 : res = FALSE;
1155 : :
1156 : 0 : break;
1157 : : case 0x02:
1158 : 0 : structure = mpegts_packetizer_parse_pmt (parse->packetizer, section);
1159 [ # # ]: 0 : if (G_LIKELY (structure))
1160 : 0 : mpegts_parse_apply_pmt (parse, section->pid, structure);
1161 : : else
1162 : 0 : res = FALSE;
1163 : :
1164 : 0 : break;
1165 : : case 0x40:
1166 : : /* NIT, actual network */
1167 : : case 0x41:
1168 : : /* NIT, other network */
1169 : 0 : structure = mpegts_packetizer_parse_nit (parse->packetizer, section);
1170 [ # # ]: 0 : if (G_LIKELY (structure))
1171 : 0 : mpegts_parse_apply_nit (parse, section->pid, structure);
1172 : : else
1173 : 0 : res = FALSE;
1174 : :
1175 : 0 : break;
1176 : : case 0x42:
1177 : : case 0x46:
1178 : 0 : structure = mpegts_packetizer_parse_sdt (parse->packetizer, section);
1179 [ # # ]: 0 : if (G_LIKELY (structure))
1180 : 0 : mpegts_parse_apply_sdt (parse, section->pid, structure);
1181 : : else
1182 : 0 : res = FALSE;
1183 : 0 : break;
1184 : : case 0x4E:
1185 : : case 0x4F:
1186 : : /* EIT, present/following */
1187 : : case 0x50:
1188 : : case 0x51:
1189 : : case 0x52:
1190 : : case 0x53:
1191 : : case 0x54:
1192 : : case 0x55:
1193 : : case 0x56:
1194 : : case 0x57:
1195 : : case 0x58:
1196 : : case 0x59:
1197 : : case 0x5A:
1198 : : case 0x5B:
1199 : : case 0x5C:
1200 : : case 0x5D:
1201 : : case 0x5E:
1202 : : case 0x5F:
1203 : : case 0x60:
1204 : : case 0x61:
1205 : : case 0x62:
1206 : : case 0x63:
1207 : : case 0x64:
1208 : : case 0x65:
1209 : : case 0x66:
1210 : : case 0x67:
1211 : : case 0x68:
1212 : : case 0x69:
1213 : : case 0x6A:
1214 : : case 0x6B:
1215 : : case 0x6C:
1216 : : case 0x6D:
1217 : : case 0x6E:
1218 : : case 0x6F:
1219 : : /* EIT, schedule */
1220 : 0 : structure = mpegts_packetizer_parse_eit (parse->packetizer, section);
1221 [ # # ]: 0 : if (G_LIKELY (structure))
1222 : 0 : mpegts_parse_apply_eit (parse, section->pid, structure);
1223 : : else
1224 : 0 : res = FALSE;
1225 : 0 : break;
1226 : : case 0x70:
1227 : : /* TDT (Time and Date table) */
1228 : 0 : structure = mpegts_packetizer_parse_tdt (parse->packetizer, section);
1229 [ # # ]: 0 : if (G_LIKELY (structure))
1230 : 0 : mpegts_parse_apply_tdt (parse, section->pid, structure);
1231 : : else
1232 : 0 : res = FALSE;
1233 : 0 : break;
1234 : : default:
1235 : 0 : break;
1236 : : }
1237 : :
1238 [ # # ]: 0 : if (structure)
1239 : 0 : gst_structure_free (structure);
1240 : :
1241 : 0 : return res;
1242 : : }
1243 : :
1244 : : static void
1245 : 0 : mpegts_parse_get_tags_from_sdt (MpegTSParse * parse, GstStructure * sdt_info)
1246 : : {
1247 : : const GValue *services;
1248 : : guint i;
1249 : :
1250 : 0 : services = gst_structure_get_value (sdt_info, "services");
1251 : :
1252 [ # # ]: 0 : for (i = 0; i < gst_value_list_get_size (services); i++) {
1253 : : const GstStructure *service;
1254 : : const gchar *sid_str;
1255 : : gchar *tmp;
1256 : : gint program_number;
1257 : : MpegTSParseProgram *program;
1258 : :
1259 : 0 : service = gst_value_get_structure (gst_value_list_get_value (services, i));
1260 : :
1261 : : /* get program_number from structure name
1262 : : * which looks like service-%d */
1263 : 0 : sid_str = gst_structure_get_name (service);
1264 : 0 : tmp = g_strstr_len (sid_str, -1, "-");
1265 : 0 : program_number = atoi (++tmp);
1266 : :
1267 : 0 : program = mpegts_parse_get_program (parse, program_number);
1268 [ # # ][ # # ]: 0 : if (program && program->tspad && !program->tspad->tags) {
[ # # ]
1269 : 0 : program->tspad->tags = gst_tag_list_new_full (GST_TAG_ARTIST,
1270 : : gst_structure_get_string (service, "name"), NULL);
1271 : : }
1272 : : }
1273 : 0 : }
1274 : :
1275 : : static void
1276 : 0 : mpegts_parse_get_tags_from_eit (MpegTSParse * parse, GstStructure * eit_info)
1277 : : {
1278 : : const GValue *events;
1279 : : guint i;
1280 : : guint program_number;
1281 : : MpegTSParseProgram *program;
1282 : : gboolean present_following;
1283 : :
1284 : 0 : gst_structure_get_uint (eit_info, "service-id", &program_number);
1285 : 0 : program = mpegts_parse_get_program (parse, program_number);
1286 : :
1287 : 0 : gst_structure_get_boolean (eit_info, "present-following", &present_following);
1288 : :
1289 [ # # ][ # # ]: 0 : if (program && program->tspad && present_following) {
[ # # ]
1290 : 0 : events = gst_structure_get_value (eit_info, "events");
1291 : :
1292 [ # # ]: 0 : for (i = 0; i < gst_value_list_get_size (events); i++) {
1293 : : const GstStructure *event;
1294 : : const gchar *title;
1295 : : guint status;
1296 : : guint event_id;
1297 : : guint duration;
1298 : :
1299 : 0 : event = gst_value_get_structure (gst_value_list_get_value (events, i));
1300 : :
1301 : 0 : title = gst_structure_get_string (event, "name");
1302 : 0 : gst_structure_get_uint (event, "event-id", &event_id);
1303 : 0 : gst_structure_get_uint (event, "running-status", &status);
1304 : :
1305 [ # # ][ # # ]: 0 : if (title && event_id != program->tspad->event_id
1306 [ # # ]: 0 : && status == RUNNING_STATUS_RUNNING) {
1307 : 0 : gst_structure_get_uint (event, "duration", &duration);
1308 : :
1309 : 0 : program->tspad->event_id = event_id;
1310 : 0 : program->tspad->tags = gst_tag_list_new_full (GST_TAG_TITLE,
1311 : : title, GST_TAG_DURATION, duration * GST_SECOND, NULL);
1312 : : }
1313 : : }
1314 : : }
1315 : 0 : }
1316 : :
1317 : : static gboolean
1318 : 0 : mpegts_parse_sink_event (GstPad * pad, GstEvent * event)
1319 : : {
1320 : : gboolean res;
1321 : 0 : MpegTSParse *parse =
1322 : 0 : GST_MPEGTS_PARSE (gst_object_get_parent (GST_OBJECT (pad)));
1323 : :
1324 [ # # ]: 0 : switch (GST_EVENT_TYPE (event)) {
1325 : : case GST_EVENT_FLUSH_STOP:
1326 : 0 : mpegts_packetizer_clear (parse->packetizer);
1327 : 0 : res = gst_pad_event_default (pad, event);
1328 : 0 : break;
1329 : : default:
1330 : 0 : res = gst_pad_event_default (pad, event);
1331 : : }
1332 : :
1333 : 0 : gst_object_unref (parse);
1334 : 0 : return res;
1335 : : }
1336 : :
1337 : : static GstFlowReturn
1338 : 0 : mpegts_parse_chain (GstPad * pad, GstBuffer * buf)
1339 : : {
1340 : 0 : GstFlowReturn res = GST_FLOW_OK;
1341 : : MpegTSParse *parse;
1342 : : gboolean parsed;
1343 : : MpegTSPacketizerPacketReturn pret;
1344 : : MpegTSPacketizer *packetizer;
1345 : : MpegTSPacketizerPacket packet;
1346 : :
1347 : 0 : parse = GST_MPEGTS_PARSE (gst_object_get_parent (GST_OBJECT (pad)));
1348 : 0 : packetizer = parse->packetizer;
1349 : :
1350 : 0 : mpegts_packetizer_push (parse->packetizer, buf);
1351 [ # # ]: 0 : while (((pret =
1352 : 0 : mpegts_packetizer_next_packet (parse->packetizer,
1353 [ # # ]: 0 : &packet)) != PACKET_NEED_MORE) && res == GST_FLOW_OK) {
1354 [ # # ]: 0 : if (G_UNLIKELY (pret == PACKET_BAD))
1355 : : /* bad header, skip the packet */
1356 : 0 : goto next;
1357 : :
1358 : : /* parse PSI data */
1359 [ # # ][ # # ]: 0 : if (packet.payload != NULL && mpegts_parse_is_psi (parse, &packet)) {
1360 : : MpegTSPacketizerSection section;
1361 : :
1362 : 0 : parsed = mpegts_packetizer_push_section (packetizer, &packet, §ion);
1363 [ # # ]: 0 : if (G_UNLIKELY (!parsed))
1364 : : /* bad section data */
1365 : 0 : goto next;
1366 : :
1367 [ # # ]: 0 : if (G_LIKELY (section.complete)) {
1368 : : /* section complete */
1369 : 0 : parsed = mpegts_parse_handle_psi (parse, §ion);
1370 : 0 : gst_buffer_unref (section.buffer);
1371 : :
1372 [ # # ]: 0 : if (G_UNLIKELY (!parsed))
1373 : : /* bad PSI table */
1374 : 0 : goto next;
1375 : : }
1376 : : /* we need to push section packet downstream */
1377 : 0 : res = mpegts_parse_push (parse, &packet, §ion);
1378 : :
1379 : : } else {
1380 : : /* push the packet downstream */
1381 : 0 : res = mpegts_parse_push (parse, &packet, NULL);
1382 : : }
1383 : :
1384 : : next:
1385 : 0 : mpegts_packetizer_clear_packet (parse->packetizer, &packet);
1386 : : }
1387 : :
1388 [ # # ]: 0 : if (parse->need_sync_program_pads)
1389 : 0 : mpegts_parse_sync_program_pads (parse);
1390 : :
1391 : 0 : gst_object_unref (parse);
1392 : 0 : return res;
1393 : : }
1394 : :
1395 : : static GstStateChangeReturn
1396 : 34 : mpegts_parse_change_state (GstElement * element, GstStateChange transition)
1397 : : {
1398 : : MpegTSParse *parse;
1399 : : GstStateChangeReturn ret;
1400 : :
1401 : 34 : parse = GST_MPEGTS_PARSE (element);
1402 : 34 : ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1403 : :
1404 [ + + ]: 34 : switch (transition) {
1405 : : case GST_STATE_CHANGE_PAUSED_TO_READY:
1406 : 7 : mpegts_parse_reset (parse);
1407 : 7 : break;
1408 : : default:
1409 : 27 : break;
1410 : : }
1411 : :
1412 : 34 : return ret;
1413 : : }
1414 : :
1415 : : static gboolean
1416 : 0 : mpegts_parse_src_pad_query (GstPad * pad, GstQuery * query)
1417 : : {
1418 : 0 : MpegTSParse *parse = GST_MPEGTS_PARSE (gst_pad_get_parent (pad));
1419 : : gboolean res;
1420 : :
1421 [ # # ]: 0 : switch (GST_QUERY_TYPE (query)) {
1422 : : case GST_QUERY_LATENCY:
1423 : : {
1424 [ # # ]: 0 : if ((res = gst_pad_peer_query (parse->sinkpad, query))) {
1425 : : gboolean is_live;
1426 : : GstClockTime min_latency, max_latency;
1427 : :
1428 : 0 : gst_query_parse_latency (query, &is_live, &min_latency, &max_latency);
1429 [ # # ]: 0 : if (is_live) {
1430 : 0 : min_latency += TS_LATENCY * GST_MSECOND;
1431 [ # # ]: 0 : if (max_latency != GST_CLOCK_TIME_NONE)
1432 : 0 : max_latency += TS_LATENCY * GST_MSECOND;
1433 : : }
1434 : :
1435 : 0 : gst_query_set_latency (query, is_live, min_latency, max_latency);
1436 : : }
1437 : :
1438 : 0 : break;
1439 : : }
1440 : : default:
1441 : 0 : res = gst_pad_query_default (pad, query);
1442 : : }
1443 : 0 : gst_object_unref (parse);
1444 : 0 : return res;
1445 : : }
1446 : :
1447 : : gboolean
1448 : 6 : gst_mpegtsparse_plugin_init (GstPlugin * plugin)
1449 : : {
1450 [ + - ]: 6 : GST_DEBUG_CATEGORY_INIT (mpegts_parse_debug, "mpegtsparse", 0,
1451 : : "MPEG transport stream parser");
1452 : :
1453 : 6 : gst_mpegtsdesc_init_debug ();
1454 : :
1455 : 6 : return gst_element_register (plugin, "mpegtsparse",
1456 : : GST_RANK_NONE, GST_TYPE_MPEGTS_PARSE);
1457 : : }
|