1 /* MPEG-PS muxer plugin for GStreamer
2 * Copyright 2008 Lin YANG <oxcsnicho@gmail.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19 /*
20 * Unless otherwise indicated, Source Code is licensed under MIT license.
21 * See further explanation attached in License Statement (distributed in the file
22 * LICENSE).
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy of
25 * this software and associated documentation files (the "Software"), to deal in
26 * the Software without restriction, including without limitation the rights to
27 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
28 * of the Software, and to permit persons to whom the Software is furnished to do
29 * so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in all
32 * copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40 * SOFTWARE.
41 */
42
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46 #include <string.h>
47
48 #include "mpegpsmux.h"
49 #include "mpegpsmux_aac.h"
50 #include "mpegpsmux_h264.h"
51
52 GST_DEBUG_CATEGORY (mpegpsmux_debug);
53 #define GST_CAT_DEFAULT mpegpsmux_debug
54
55 enum
56 {
57 PROP_AGGREGATE_GOPS = 1
58 };
59
60 #define DEFAULT_AGGREGATE_GOPS FALSE
61
62 static GstStaticPadTemplate mpegpsmux_sink_factory =
63 GST_STATIC_PAD_TEMPLATE ("sink_%u",
64 GST_PAD_SINK,
65 GST_PAD_REQUEST,
66 GST_STATIC_CAPS ("video/mpeg, "
67 "mpegversion = (int) { 1, 2, 4 }, "
68 "systemstream = (boolean) false; "
69 "video/x-dirac;"
70 "video/x-h264,stream-format=(string)byte-stream,"
71 "alignment=(string){au, nal}; "
72 "audio/mpeg, "
73 "mpegversion = (int) { 1, 2 };"
74 "audio/mpeg, "
75 "mpegversion = (int) 4, stream-format = (string) { raw, adts }; "
76 "audio/x-lpcm, "
77 "width = (int) { 16, 20, 24 }, "
78 "rate = (int) { 48000, 96000 }, "
79 "channels = (int) [ 1, 8 ], "
80 "dynamic_range = (int) [ 0, 255 ], "
81 "emphasis = (boolean) { FALSE, TRUE }, "
82 "mute = (boolean) { FALSE, TRUE }"));
83
84 static GstStaticPadTemplate mpegpsmux_src_factory =
85 GST_STATIC_PAD_TEMPLATE ("src",
86 GST_PAD_SRC,
87 GST_PAD_ALWAYS,
88 GST_STATIC_CAPS ("video/mpeg, "
89 "mpegversion = (int) 2, " "systemstream = (boolean) true")
90 );
91
92 static void gst_mpegpsmux_set_property (GObject * object, guint prop_id,
93 const GValue * value, GParamSpec * pspec);
94 static void gst_mpegpsmux_get_property (GObject * object, guint prop_id,
95 GValue * value, GParamSpec * pspec);
96
97 static void mpegpsmux_finalize (GObject * object);
98 static gboolean new_packet_cb (guint8 * data, guint len, void *user_data);
99
100 static gboolean mpegpsdemux_prepare_srcpad (MpegPsMux * mux);
101 static GstFlowReturn mpegpsmux_collected (GstCollectPads * pads,
102 MpegPsMux * mux);
103 static GstPad *mpegpsmux_request_new_pad (GstElement * element,
104 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
105 static void mpegpsmux_release_pad (GstElement * element, GstPad * pad);
106 static GstStateChangeReturn mpegpsmux_change_state (GstElement * element,
107 GstStateChange transition);
108
109 #define parent_class mpegpsmux_parent_class
110 G_DEFINE_TYPE (MpegPsMux, mpegpsmux, GST_TYPE_ELEMENT);
111 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (mpegpsmux, "mpegpsmux", GST_RANK_PRIMARY,
112 mpegpsmux_get_type (), GST_DEBUG_CATEGORY_INIT (mpegpsmux_debug,
113 "mpegpsmux", 0, "MPEG Program Stream muxer"));
114 static void
mpegpsmux_class_init(MpegPsMuxClass * klass)115 mpegpsmux_class_init (MpegPsMuxClass * klass)
116 {
117 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
118 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
119
120 gobject_class->set_property = gst_mpegpsmux_set_property;
121 gobject_class->get_property = gst_mpegpsmux_get_property;
122 gobject_class->finalize = mpegpsmux_finalize;
123
124 gstelement_class->request_new_pad = mpegpsmux_request_new_pad;
125 gstelement_class->release_pad = mpegpsmux_release_pad;
126 gstelement_class->change_state = mpegpsmux_change_state;
127
128 g_object_class_install_property (gobject_class, PROP_AGGREGATE_GOPS,
129 g_param_spec_boolean ("aggregate-gops", "Aggregate GOPs",
130 "Whether to aggregate GOPs and push them out as buffer lists",
131 DEFAULT_AGGREGATE_GOPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
132
133 gst_element_class_add_static_pad_template (gstelement_class,
134 &mpegpsmux_sink_factory);
135 gst_element_class_add_static_pad_template (gstelement_class,
136 &mpegpsmux_src_factory);
137
138 gst_element_class_set_static_metadata (gstelement_class,
139 "MPEG Program Stream Muxer", "Codec/Muxer",
140 "Multiplexes media streams into an MPEG Program Stream",
141 "Lin YANG <oxcsnicho@gmail.com>");
142 }
143
144 static void
mpegpsmux_init(MpegPsMux * mux)145 mpegpsmux_init (MpegPsMux * mux)
146 {
147 mux->srcpad = gst_pad_new_from_static_template (&mpegpsmux_src_factory,
148 "src");
149 gst_pad_use_fixed_caps (mux->srcpad);
150 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
151
152 mux->collect = gst_collect_pads_new ();
153 gst_collect_pads_set_function (mux->collect,
154 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (mpegpsmux_collected), mux);
155
156 mux->psmux = psmux_new ();
157 psmux_set_write_func (mux->psmux, new_packet_cb, mux);
158
159 mux->first = TRUE;
160 mux->last_flow_ret = GST_FLOW_OK;
161 mux->last_ts = 0; /* XXX: or -1? */
162 }
163
164 static void
mpegpsmux_finalize(GObject * object)165 mpegpsmux_finalize (GObject * object)
166 {
167 MpegPsMux *mux = GST_MPEG_PSMUX (object);
168
169 if (mux->collect) {
170 gst_object_unref (mux->collect);
171 mux->collect = NULL;
172 }
173 if (mux->psmux) {
174 psmux_free (mux->psmux);
175 mux->psmux = NULL;
176 }
177
178 if (mux->gop_list != NULL) {
179 gst_buffer_list_unref (mux->gop_list);
180 mux->gop_list = NULL;
181 }
182
183 G_OBJECT_CLASS (mpegpsmux_parent_class)->finalize (object);
184 }
185
186 static void
gst_mpegpsmux_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)187 gst_mpegpsmux_set_property (GObject * object, guint prop_id,
188 const GValue * value, GParamSpec * pspec)
189 {
190 MpegPsMux *mux = GST_MPEG_PSMUX (object);
191
192 switch (prop_id) {
193 case PROP_AGGREGATE_GOPS:
194 mux->aggregate_gops = g_value_get_boolean (value);
195 break;
196 default:
197 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
198 break;
199 }
200 }
201
202 static void
gst_mpegpsmux_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)203 gst_mpegpsmux_get_property (GObject * object, guint prop_id,
204 GValue * value, GParamSpec * pspec)
205 {
206 MpegPsMux *mux = GST_MPEG_PSMUX (object);
207
208 switch (prop_id) {
209 case PROP_AGGREGATE_GOPS:
210 g_value_set_boolean (value, mux->aggregate_gops);
211 break;
212 default:
213 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
214 break;
215 }
216 }
217
218 static GstFlowReturn
mpegpsmux_create_stream(MpegPsMux * mux,MpegPsPadData * ps_data,GstPad * pad)219 mpegpsmux_create_stream (MpegPsMux * mux, MpegPsPadData * ps_data, GstPad * pad)
220 {
221 /* Create a steam. Fill in codec specific information */
222
223 GstFlowReturn ret = GST_FLOW_ERROR;
224 GstCaps *caps;
225 GstStructure *s;
226 gboolean is_video = FALSE;
227
228 caps = gst_pad_get_current_caps (pad);
229 if (caps == NULL) {
230 GST_DEBUG_OBJECT (pad, "Sink pad caps were not set before pushing");
231 return GST_FLOW_NOT_NEGOTIATED;
232 }
233
234 s = gst_caps_get_structure (caps, 0);
235
236 if (gst_structure_has_name (s, "video/x-dirac")) {
237 GST_DEBUG_OBJECT (pad, "Creating Dirac stream");
238 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_DIRAC);
239 is_video = TRUE;
240 } else if (gst_structure_has_name (s, "audio/x-ac3")) {
241 GST_DEBUG_OBJECT (pad, "Creating AC3 stream");
242 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_PS_AUDIO_AC3);
243 } else if (gst_structure_has_name (s, "audio/x-dts")) {
244 GST_DEBUG_OBJECT (pad, "Creating DTS stream");
245 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_PS_AUDIO_DTS);
246 } else if (gst_structure_has_name (s, "audio/x-lpcm")) {
247 GST_DEBUG_OBJECT (pad, "Creating LPCM stream");
248 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_PS_AUDIO_LPCM);
249 } else if (gst_structure_has_name (s, "video/x-h264")) {
250 const GValue *value;
251 GST_DEBUG_OBJECT (pad, "Creating H264 stream");
252 /* Codec data contains SPS/PPS which need to go in stream for valid ES */
253 value = gst_structure_get_value (s, "codec_data");
254 if (value) {
255 ps_data->codec_data = gst_buffer_ref (gst_value_get_buffer (value));
256 GST_DEBUG_OBJECT (pad, "%" G_GSIZE_FORMAT " bytes of codec data",
257 gst_buffer_get_size (ps_data->codec_data));
258 ps_data->prepare_func = mpegpsmux_prepare_h264;
259 } else {
260 ps_data->codec_data = NULL;
261 }
262 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_H264);
263 is_video = TRUE;
264 } else if (gst_structure_has_name (s, "audio/mpeg")) {
265 gint mpegversion;
266 if (!gst_structure_get_int (s, "mpegversion", &mpegversion)) {
267 GST_ELEMENT_ERROR (pad, STREAM, FORMAT,
268 ("Invalid data format presented"),
269 ("Caps with type audio/mpeg did not have mpegversion"));
270 goto beach;
271 }
272
273 switch (mpegversion) {
274 case 1:
275 GST_DEBUG_OBJECT (pad, "Creating MPEG Audio, version 1 stream");
276 ps_data->stream =
277 psmux_create_stream (mux->psmux, PSMUX_ST_AUDIO_MPEG1);
278 break;
279 case 2:
280 GST_DEBUG_OBJECT (pad, "Creating MPEG Audio, version 2 stream");
281 ps_data->stream =
282 psmux_create_stream (mux->psmux, PSMUX_ST_AUDIO_MPEG2);
283 break;
284 case 4:
285 {
286 const GValue *value;
287 /* Codec data contains SPS/PPS which need to go in stream for valid ES */
288 GST_DEBUG_OBJECT (pad, "Creating MPEG Audio, version 4 stream");
289 value = gst_structure_get_value (s, "codec_data");
290 if (value) {
291 ps_data->codec_data = gst_buffer_ref (gst_value_get_buffer (value));
292 GST_DEBUG_OBJECT (pad, "%" G_GSIZE_FORMAT " bytes of codec data",
293 gst_buffer_get_size (ps_data->codec_data));
294 ps_data->prepare_func = mpegpsmux_prepare_aac;
295 } else {
296 ps_data->codec_data = NULL;
297 }
298 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_AUDIO_AAC);
299 break;
300 }
301 default:
302 GST_WARNING_OBJECT (pad, "unsupported mpegversion %d", mpegversion);
303 goto beach;
304 }
305 } else if (gst_structure_has_name (s, "video/mpeg")) {
306 gint mpegversion;
307 if (!gst_structure_get_int (s, "mpegversion", &mpegversion)) {
308 GST_ELEMENT_ERROR (mux, STREAM, FORMAT,
309 ("Invalid data format presented"),
310 ("Caps with type video/mpeg did not have mpegversion"));
311 goto beach;
312 }
313
314 if (mpegversion == 1) {
315 GST_DEBUG_OBJECT (pad, "Creating MPEG Video, version 1 stream");
316 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_MPEG1);
317 } else if (mpegversion == 2) {
318 GST_DEBUG_OBJECT (pad, "Creating MPEG Video, version 2 stream");
319 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_MPEG2);
320 } else {
321 GST_DEBUG_OBJECT (pad, "Creating MPEG Video, version 4 stream");
322 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_MPEG4);
323 }
324 is_video = TRUE;
325 }
326
327 if (ps_data->stream != NULL) {
328 ps_data->stream_id = ps_data->stream->stream_id;
329 ps_data->stream_id_ext = ps_data->stream->stream_id_ext;
330 GST_DEBUG_OBJECT (pad, "Stream created, stream_id=%04x, stream_id_ext=%04x",
331 ps_data->stream_id, ps_data->stream_id_ext);
332
333 gst_structure_get_int (s, "rate", &ps_data->stream->audio_sampling);
334 gst_structure_get_int (s, "channels", &ps_data->stream->audio_channels);
335 gst_structure_get_int (s, "bitrate", &ps_data->stream->audio_bitrate);
336
337 ret = GST_FLOW_OK;
338
339 if (is_video && mux->video_stream_id == 0) {
340 mux->video_stream_id = ps_data->stream_id;
341 GST_INFO_OBJECT (mux, "video pad stream_id 0x%02x", mux->video_stream_id);
342 }
343 }
344
345 beach:
346 gst_caps_unref (caps);
347 return ret;
348 }
349
350 static GstFlowReturn
mpegpsmux_create_streams(MpegPsMux * mux)351 mpegpsmux_create_streams (MpegPsMux * mux)
352 {
353 /* Create stream for each pad */
354
355 GstFlowReturn ret = GST_FLOW_OK;
356 GSList *walk = mux->collect->data;
357
358 /* Create the streams */
359 while (walk) {
360 GstCollectData *c_data = (GstCollectData *) walk->data;
361 MpegPsPadData *ps_data = (MpegPsPadData *) walk->data;
362
363 walk = g_slist_next (walk);
364
365 if (ps_data->stream == NULL) {
366 ret = mpegpsmux_create_stream (mux, ps_data, c_data->pad);
367 if (ret != GST_FLOW_OK)
368 goto no_stream;
369 }
370 }
371
372 return GST_FLOW_OK;
373 no_stream:
374 GST_ELEMENT_ERROR (mux, STREAM, MUX,
375 ("Could not create handler for stream"), (NULL));
376 return ret;
377 }
378
379 static GstBuffer *
mpegpsmux_queue_buffer_for_stream(MpegPsMux * mux,MpegPsPadData * ps_data)380 mpegpsmux_queue_buffer_for_stream (MpegPsMux * mux, MpegPsPadData * ps_data)
381 {
382 GstCollectData *c_data = (GstCollectData *) ps_data;
383 GstBuffer *buf;
384
385 g_assert (ps_data->queued.buf == NULL);
386
387 buf = gst_collect_pads_peek (mux->collect, c_data);
388 if (buf == NULL)
389 return NULL;
390
391 ps_data->queued.buf = buf;
392
393 /* do any raw -> byte-stream format conversions (e.g. for H.264, AAC) */
394 if (ps_data->prepare_func) {
395 buf = ps_data->prepare_func (buf, ps_data, mux);
396 if (buf) { /* Take the prepared buffer instead */
397 gst_buffer_unref (ps_data->queued.buf);
398 ps_data->queued.buf = buf;
399 } else { /* If data preparation returned NULL, use unprepared one */
400 buf = ps_data->queued.buf;
401 }
402 }
403
404 ps_data->queued.pts = GST_BUFFER_PTS (buf);
405 if (GST_CLOCK_TIME_IS_VALID (ps_data->queued.pts)) {
406 ps_data->queued.pts = gst_segment_to_running_time (&c_data->segment,
407 GST_FORMAT_TIME, ps_data->queued.pts);
408 }
409
410 ps_data->queued.dts = GST_BUFFER_DTS (buf);
411 if (GST_CLOCK_TIME_IS_VALID (ps_data->queued.dts)) {
412 ps_data->queued.dts = gst_segment_to_running_time (&c_data->segment,
413 GST_FORMAT_TIME, ps_data->queued.dts);
414 }
415
416 if (GST_BUFFER_PTS_IS_VALID (buf) && GST_BUFFER_DTS_IS_VALID (buf)) {
417 ps_data->queued.ts = MIN (ps_data->queued.dts, ps_data->queued.pts);
418 } else if (GST_BUFFER_PTS_IS_VALID (buf) && !GST_BUFFER_DTS_IS_VALID (buf)) {
419 ps_data->queued.ts = ps_data->queued.pts;
420 } else if (GST_BUFFER_DTS_IS_VALID (buf) && !GST_BUFFER_PTS_IS_VALID (buf)) {
421 GST_WARNING_OBJECT (c_data->pad, "got DTS without PTS");
422 ps_data->queued.ts = ps_data->queued.dts;
423 } else {
424 ps_data->queued.ts = GST_CLOCK_TIME_NONE;
425 }
426
427 if (ps_data->queued.ts != GST_CLOCK_TIME_NONE)
428 ps_data->last_ts = ps_data->queued.ts;
429
430 GST_DEBUG_OBJECT (mux, "Queued buffer with ts %" GST_TIME_FORMAT ": "
431 "uncorrected pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT ", "
432 "buffer pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " for PID 0x%04x",
433 GST_TIME_ARGS (ps_data->queued.ts),
434 GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
435 GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
436 GST_TIME_ARGS (ps_data->queued.pts),
437 GST_TIME_ARGS (ps_data->queued.dts), ps_data->stream_id);
438
439 return buf;
440 }
441
442 static MpegPsPadData *
mpegpsmux_choose_best_stream(MpegPsMux * mux)443 mpegpsmux_choose_best_stream (MpegPsMux * mux)
444 {
445 /* Choose from which stream to mux with */
446
447 MpegPsPadData *best = NULL;
448 GstCollectData *c_best = NULL;
449 GSList *walk;
450
451 for (walk = mux->collect->data; walk != NULL; walk = g_slist_next (walk)) {
452 GstCollectData *c_data = (GstCollectData *) walk->data;
453 MpegPsPadData *ps_data = (MpegPsPadData *) walk->data;
454
455 if (ps_data->eos == FALSE) {
456 if (ps_data->queued.buf == NULL) {
457 GstBuffer *buf;
458
459 buf = mpegpsmux_queue_buffer_for_stream (mux, ps_data);
460 if (buf == NULL) {
461 GST_DEBUG_OBJECT (mux, "we have EOS");
462 ps_data->eos = TRUE;
463 continue;
464 }
465 }
466
467 /* If we don't yet have a best pad, take this one, otherwise take
468 * whichever has the oldest timestamp */
469 if (best != NULL) {
470 if (ps_data->last_ts == GST_CLOCK_TIME_NONE ||
471 (best->last_ts != GST_CLOCK_TIME_NONE &&
472 ps_data->last_ts < best->last_ts)) {
473 best = ps_data;
474 c_best = c_data;
475 }
476 } else {
477 best = ps_data;
478 c_best = c_data;
479 }
480 }
481 }
482 if (c_best) {
483 gst_buffer_unref (gst_collect_pads_pop (mux->collect, c_best));
484 }
485
486 return best;
487 }
488
489 static GstFlowReturn
mpegpsmux_push_gop_list(MpegPsMux * mux)490 mpegpsmux_push_gop_list (MpegPsMux * mux)
491 {
492 GstFlowReturn flow;
493
494 g_assert (mux->gop_list != NULL);
495
496 GST_DEBUG_OBJECT (mux, "Sending pending GOP of %u buffers",
497 gst_buffer_list_length (mux->gop_list));
498 flow = gst_pad_push_list (mux->srcpad, mux->gop_list);
499 mux->gop_list = NULL;
500 return flow;
501 }
502
503 static GstFlowReturn
mpegpsmux_collected(GstCollectPads * pads,MpegPsMux * mux)504 mpegpsmux_collected (GstCollectPads * pads, MpegPsMux * mux)
505 {
506 /* main muxing function */
507
508 GstFlowReturn ret = GST_FLOW_OK;
509 MpegPsPadData *best = NULL;
510 gboolean keyunit;
511
512 GST_DEBUG_OBJECT (mux, "Pads collected");
513
514 if (mux->first) { /* process block for the first mux */
515 /* iterate through the collect pads and add streams to @mux */
516 ret = mpegpsmux_create_streams (mux);
517 /* Assumption : all pads are already added at this time */
518
519 if (G_UNLIKELY (ret != GST_FLOW_OK))
520 return ret;
521
522 best = mpegpsmux_choose_best_stream (mux);
523
524 /* prepare the src pad (output), return if failed */
525 if (!mpegpsdemux_prepare_srcpad (mux)) {
526 GST_DEBUG_OBJECT (mux, "Failed to send new segment");
527 goto new_seg_fail;
528 }
529
530 mux->first = FALSE;
531 } else {
532 best = mpegpsmux_choose_best_stream (mux);
533 }
534
535 if (best != NULL) {
536 GstBuffer *buf = best->queued.buf;
537 gint64 pts, dts;
538
539 g_assert (buf != NULL);
540
541 GST_LOG_OBJECT (mux,
542 "Chose stream from pad %" GST_PTR_FORMAT " for output (PID: 0x%04x): "
543 "adjusted pts: %" GST_TIME_FORMAT ", dts: %" GST_TIME_FORMAT,
544 best->collect.pad, best->stream_id,
545 GST_TIME_ARGS (best->queued.pts), GST_TIME_ARGS (best->queued.dts));
546
547 /* and convert to mpeg time stamps */
548 pts = GSTTIME_TO_MPEGTIME (best->queued.pts);
549 dts = GSTTIME_TO_MPEGTIME (best->queued.dts);
550
551 /* start of new GOP? */
552 keyunit = !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
553
554 if (keyunit && best->stream_id == mux->video_stream_id
555 && mux->gop_list != NULL) {
556 ret = mpegpsmux_push_gop_list (mux);
557 if (ret != GST_FLOW_OK)
558 goto done;
559 }
560
561 /* give the buffer to libpsmux for processing */
562 psmux_stream_add_data (best->stream, buf, pts, dts, keyunit);
563
564 best->queued.buf = NULL;
565
566 /* write the data from libpsmux to stream */
567 while (psmux_stream_bytes_in_buffer (best->stream) > 0) {
568 GST_LOG_OBJECT (mux, "Before @psmux_write_stream_packet");
569 if (!psmux_write_stream_packet (mux->psmux, best->stream)) {
570 GST_DEBUG_OBJECT (mux, "Failed to write data packet");
571 goto write_fail;
572 }
573 }
574 mux->last_ts = best->last_ts;
575 } else {
576 /* FIXME: Drain all remaining streams */
577 /* At EOS */
578 if (mux->gop_list != NULL)
579 mpegpsmux_push_gop_list (mux);
580
581 if (!psmux_write_end_code (mux->psmux)) {
582 GST_WARNING_OBJECT (mux, "Writing MPEG PS Program end code failed.");
583 }
584 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
585
586 ret = GST_FLOW_EOS;
587 }
588
589 done:
590
591 return ret;
592 new_seg_fail:
593 return GST_FLOW_ERROR;
594 write_fail:
595 /* FIXME: Failed writing data for some reason. Should set appropriate error */
596 return mux->last_flow_ret;
597 }
598
599 static GstPad *
mpegpsmux_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * name,const GstCaps * caps)600 mpegpsmux_request_new_pad (GstElement * element,
601 GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
602 {
603 MpegPsMux *mux = GST_MPEG_PSMUX (element);
604 GstPad *pad = NULL;
605 MpegPsPadData *pad_data = NULL;
606
607 pad = gst_pad_new_from_template (templ, name);
608
609 pad_data = (MpegPsPadData *) gst_collect_pads_add_pad (mux->collect, pad,
610 sizeof (MpegPsPadData), NULL, TRUE);
611 if (pad_data == NULL)
612 goto pad_failure;
613
614 pad_data->last_ts = GST_CLOCK_TIME_NONE;
615 pad_data->codec_data = NULL;
616 pad_data->prepare_func = NULL;
617
618 if (G_UNLIKELY (!gst_element_add_pad (element, pad)))
619 goto could_not_add;
620
621 return pad;
622
623 could_not_add:
624 GST_ELEMENT_ERROR (element, STREAM, FAILED,
625 ("Internal data stream error."), ("Could not add pad to element"));
626 gst_collect_pads_remove_pad (mux->collect, pad);
627 gst_object_unref (pad);
628 return NULL;
629 pad_failure:
630 GST_ELEMENT_ERROR (element, STREAM, FAILED,
631 ("Internal data stream error."), ("Could not add pad to collectpads"));
632 gst_object_unref (pad);
633 return NULL;
634 }
635
636 static void
mpegpsmux_release_pad(GstElement * element,GstPad * pad)637 mpegpsmux_release_pad (GstElement * element, GstPad * pad)
638 {
639 /* unref pad data (and codec data) */
640
641 MpegPsMux *mux = GST_MPEG_PSMUX (element);
642 MpegPsPadData *pad_data = NULL;
643
644 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " being released", pad);
645
646 /* Get the MpegPsPadData out of the pad */
647 GST_OBJECT_LOCK (pad);
648 pad_data = (MpegPsPadData *) gst_pad_get_element_private (pad);
649 if (G_LIKELY (pad_data)) {
650 /* Free codec data reference if any */
651 if (pad_data->codec_data) {
652 GST_DEBUG_OBJECT (element, "releasing codec_data reference");
653 gst_buffer_unref (pad_data->codec_data);
654 pad_data->codec_data = NULL;
655 }
656 if (pad_data->stream_id == mux->video_stream_id)
657 mux->video_stream_id = 0;
658 }
659 GST_OBJECT_UNLOCK (pad);
660
661 gst_collect_pads_remove_pad (mux->collect, pad);
662 }
663
664 static gboolean
new_packet_cb(guint8 * data,guint len,void * user_data)665 new_packet_cb (guint8 * data, guint len, void *user_data)
666 {
667 /* Called when the PsMux has prepared a packet for output. Return FALSE
668 * on error */
669
670 MpegPsMux *mux = (MpegPsMux *) user_data;
671 GstBuffer *buf;
672 GstFlowReturn ret;
673
674 GST_LOG_OBJECT (mux, "Outputting a packet of length %d", len);
675
676 data = g_memdup2 (data, len);
677 buf = gst_buffer_new_wrapped (data, len);
678
679 GST_BUFFER_TIMESTAMP (buf) = mux->last_ts;
680
681 if (mux->aggregate_gops) {
682 if (mux->gop_list == NULL)
683 mux->gop_list = gst_buffer_list_new ();
684
685 gst_buffer_list_add (mux->gop_list, buf);
686 return TRUE;
687 }
688
689 ret = gst_pad_push (mux->srcpad, buf);
690
691 if (G_UNLIKELY (ret != GST_FLOW_OK)) {
692 mux->last_flow_ret = ret;
693 return FALSE;
694 }
695
696 return TRUE;
697 }
698
699 /* prepare the source pad for output */
700 static gboolean
mpegpsdemux_prepare_srcpad(MpegPsMux * mux)701 mpegpsdemux_prepare_srcpad (MpegPsMux * mux)
702 {
703 GstSegment segment;
704 GValue val = { 0, };
705 GList *headers, *l;
706 GstCaps *caps;
707 gchar s_id[32];
708
709 /* stream-start (FIXME: create id based on input ids) */
710 g_snprintf (s_id, sizeof (s_id), "mpegpsmux-%08x", g_random_int ());
711 gst_pad_push_event (mux->srcpad, gst_event_new_stream_start (s_id));
712
713 caps = gst_caps_new_simple ("video/mpeg",
714 "mpegversion", G_TYPE_INT, 2, "systemstream", G_TYPE_BOOLEAN, TRUE, NULL);
715
716 headers = psmux_get_stream_headers (mux->psmux);
717 g_value_init (&val, GST_TYPE_ARRAY);
718 for (l = headers; l != NULL; l = l->next) {
719 GValue buf_val = { 0, };
720
721 g_value_init (&buf_val, GST_TYPE_BUFFER);
722 gst_value_take_buffer (&buf_val, GST_BUFFER (l->data));
723 l->data = NULL;
724 gst_value_array_append_value (&val, &buf_val);
725 g_value_unset (&buf_val);
726 }
727 gst_caps_set_value (caps, "streamheader", &val);
728 g_value_unset (&val);
729 g_list_free (headers);
730
731 /* Set caps on src pad and push new segment */
732 gst_pad_push_event (mux->srcpad, gst_event_new_caps (caps));
733 gst_caps_unref (caps);
734
735 gst_segment_init (&segment, GST_FORMAT_BYTES);
736 gst_pad_push_event (mux->srcpad, gst_event_new_segment (&segment));
737
738 return TRUE;
739 }
740
741 static GstStateChangeReturn
mpegpsmux_change_state(GstElement * element,GstStateChange transition)742 mpegpsmux_change_state (GstElement * element, GstStateChange transition)
743 {
744 /* control the collect pads */
745
746 MpegPsMux *mux = GST_MPEG_PSMUX (element);
747 GstStateChangeReturn ret;
748
749 switch (transition) {
750 case GST_STATE_CHANGE_NULL_TO_READY:
751 break;
752 case GST_STATE_CHANGE_READY_TO_PAUSED:
753 gst_collect_pads_start (mux->collect);
754 break;
755 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
756 break;
757 case GST_STATE_CHANGE_PAUSED_TO_READY:
758 gst_collect_pads_stop (mux->collect);
759 break;
760 case GST_STATE_CHANGE_READY_TO_NULL:
761 break;
762 default:
763 break;
764 }
765
766 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
767
768 switch (transition) {
769 default:
770 break;
771 }
772
773 return ret;
774 }
775
776 static gboolean
plugin_init(GstPlugin * plugin)777 plugin_init (GstPlugin * plugin)
778 {
779 return GST_ELEMENT_REGISTER (mpegpsmux, plugin);
780 }
781
782 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
783 mpegpsmux, "MPEG-PS muxer",
784 plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
785