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
112 static void
mpegpsmux_class_init(MpegPsMuxClass * klass)113 mpegpsmux_class_init (MpegPsMuxClass * klass)
114 {
115 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
116 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
117
118 gobject_class->set_property = gst_mpegpsmux_set_property;
119 gobject_class->get_property = gst_mpegpsmux_get_property;
120 gobject_class->finalize = mpegpsmux_finalize;
121
122 gstelement_class->request_new_pad = mpegpsmux_request_new_pad;
123 gstelement_class->release_pad = mpegpsmux_release_pad;
124 gstelement_class->change_state = mpegpsmux_change_state;
125
126 g_object_class_install_property (gobject_class, PROP_AGGREGATE_GOPS,
127 g_param_spec_boolean ("aggregate-gops", "Aggregate GOPs",
128 "Whether to aggregate GOPs and push them out as buffer lists",
129 DEFAULT_AGGREGATE_GOPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
130
131 gst_element_class_add_static_pad_template (gstelement_class,
132 &mpegpsmux_sink_factory);
133 gst_element_class_add_static_pad_template (gstelement_class,
134 &mpegpsmux_src_factory);
135
136 gst_element_class_set_static_metadata (gstelement_class,
137 "MPEG Program Stream Muxer", "Codec/Muxer",
138 "Multiplexes media streams into an MPEG Program Stream",
139 "Lin YANG <oxcsnicho@gmail.com>");
140 }
141
142 static void
mpegpsmux_init(MpegPsMux * mux)143 mpegpsmux_init (MpegPsMux * mux)
144 {
145 mux->srcpad = gst_pad_new_from_static_template (&mpegpsmux_src_factory,
146 "src");
147 gst_pad_use_fixed_caps (mux->srcpad);
148 gst_element_add_pad (GST_ELEMENT (mux), mux->srcpad);
149
150 mux->collect = gst_collect_pads_new ();
151 gst_collect_pads_set_function (mux->collect,
152 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (mpegpsmux_collected), mux);
153
154 mux->psmux = psmux_new ();
155 psmux_set_write_func (mux->psmux, new_packet_cb, mux);
156
157 mux->first = TRUE;
158 mux->last_flow_ret = GST_FLOW_OK;
159 mux->last_ts = 0; /* XXX: or -1? */
160 }
161
162 static void
mpegpsmux_finalize(GObject * object)163 mpegpsmux_finalize (GObject * object)
164 {
165 MpegPsMux *mux = GST_MPEG_PSMUX (object);
166
167 if (mux->collect) {
168 gst_object_unref (mux->collect);
169 mux->collect = NULL;
170 }
171 if (mux->psmux) {
172 psmux_free (mux->psmux);
173 mux->psmux = NULL;
174 }
175
176 if (mux->gop_list != NULL) {
177 gst_buffer_list_unref (mux->gop_list);
178 mux->gop_list = NULL;
179 }
180
181 G_OBJECT_CLASS (mpegpsmux_parent_class)->finalize (object);
182 }
183
184 static void
gst_mpegpsmux_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)185 gst_mpegpsmux_set_property (GObject * object, guint prop_id,
186 const GValue * value, GParamSpec * pspec)
187 {
188 MpegPsMux *mux = GST_MPEG_PSMUX (object);
189
190 switch (prop_id) {
191 case PROP_AGGREGATE_GOPS:
192 mux->aggregate_gops = g_value_get_boolean (value);
193 break;
194 default:
195 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
196 break;
197 }
198 }
199
200 static void
gst_mpegpsmux_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)201 gst_mpegpsmux_get_property (GObject * object, guint prop_id,
202 GValue * value, GParamSpec * pspec)
203 {
204 MpegPsMux *mux = GST_MPEG_PSMUX (object);
205
206 switch (prop_id) {
207 case PROP_AGGREGATE_GOPS:
208 g_value_set_boolean (value, mux->aggregate_gops);
209 break;
210 default:
211 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
212 break;
213 }
214 }
215
216 static GstFlowReturn
mpegpsmux_create_stream(MpegPsMux * mux,MpegPsPadData * ps_data,GstPad * pad)217 mpegpsmux_create_stream (MpegPsMux * mux, MpegPsPadData * ps_data, GstPad * pad)
218 {
219 /* Create a steam. Fill in codec specific information */
220
221 GstFlowReturn ret = GST_FLOW_ERROR;
222 GstCaps *caps;
223 GstStructure *s;
224 gboolean is_video = FALSE;
225
226 caps = gst_pad_get_current_caps (pad);
227 if (caps == NULL) {
228 GST_DEBUG_OBJECT (pad, "Sink pad caps were not set before pushing");
229 return GST_FLOW_NOT_NEGOTIATED;
230 }
231
232 s = gst_caps_get_structure (caps, 0);
233
234 if (gst_structure_has_name (s, "video/x-dirac")) {
235 GST_DEBUG_OBJECT (pad, "Creating Dirac stream");
236 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_DIRAC);
237 is_video = TRUE;
238 } else if (gst_structure_has_name (s, "audio/x-ac3")) {
239 GST_DEBUG_OBJECT (pad, "Creating AC3 stream");
240 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_PS_AUDIO_AC3);
241 } else if (gst_structure_has_name (s, "audio/x-dts")) {
242 GST_DEBUG_OBJECT (pad, "Creating DTS stream");
243 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_PS_AUDIO_DTS);
244 } else if (gst_structure_has_name (s, "audio/x-lpcm")) {
245 GST_DEBUG_OBJECT (pad, "Creating LPCM stream");
246 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_PS_AUDIO_LPCM);
247 } else if (gst_structure_has_name (s, "video/x-h264")) {
248 const GValue *value;
249 GST_DEBUG_OBJECT (pad, "Creating H264 stream");
250 /* Codec data contains SPS/PPS which need to go in stream for valid ES */
251 value = gst_structure_get_value (s, "codec_data");
252 if (value) {
253 ps_data->codec_data = gst_buffer_ref (gst_value_get_buffer (value));
254 GST_DEBUG_OBJECT (pad, "%" G_GSIZE_FORMAT " bytes of codec data",
255 gst_buffer_get_size (ps_data->codec_data));
256 ps_data->prepare_func = mpegpsmux_prepare_h264;
257 } else {
258 ps_data->codec_data = NULL;
259 }
260 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_H264);
261 is_video = TRUE;
262 } else if (gst_structure_has_name (s, "audio/mpeg")) {
263 gint mpegversion;
264 if (!gst_structure_get_int (s, "mpegversion", &mpegversion)) {
265 GST_ELEMENT_ERROR (pad, STREAM, FORMAT,
266 ("Invalid data format presented"),
267 ("Caps with type audio/mpeg did not have mpegversion"));
268 goto beach;
269 }
270
271 switch (mpegversion) {
272 case 1:
273 GST_DEBUG_OBJECT (pad, "Creating MPEG Audio, version 1 stream");
274 ps_data->stream =
275 psmux_create_stream (mux->psmux, PSMUX_ST_AUDIO_MPEG1);
276 break;
277 case 2:
278 GST_DEBUG_OBJECT (pad, "Creating MPEG Audio, version 2 stream");
279 ps_data->stream =
280 psmux_create_stream (mux->psmux, PSMUX_ST_AUDIO_MPEG2);
281 break;
282 case 4:
283 {
284 const GValue *value;
285 /* Codec data contains SPS/PPS which need to go in stream for valid ES */
286 GST_DEBUG_OBJECT (pad, "Creating MPEG Audio, version 4 stream");
287 value = gst_structure_get_value (s, "codec_data");
288 if (value) {
289 ps_data->codec_data = gst_buffer_ref (gst_value_get_buffer (value));
290 GST_DEBUG_OBJECT (pad, "%" G_GSIZE_FORMAT " bytes of codec data",
291 gst_buffer_get_size (ps_data->codec_data));
292 ps_data->prepare_func = mpegpsmux_prepare_aac;
293 } else {
294 ps_data->codec_data = NULL;
295 }
296 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_AUDIO_AAC);
297 break;
298 }
299 default:
300 GST_WARNING_OBJECT (pad, "unsupported mpegversion %d", mpegversion);
301 goto beach;
302 }
303 } else if (gst_structure_has_name (s, "video/mpeg")) {
304 gint mpegversion;
305 if (!gst_structure_get_int (s, "mpegversion", &mpegversion)) {
306 GST_ELEMENT_ERROR (mux, STREAM, FORMAT,
307 ("Invalid data format presented"),
308 ("Caps with type video/mpeg did not have mpegversion"));
309 goto beach;
310 }
311
312 if (mpegversion == 1) {
313 GST_DEBUG_OBJECT (pad, "Creating MPEG Video, version 1 stream");
314 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_MPEG1);
315 } else if (mpegversion == 2) {
316 GST_DEBUG_OBJECT (pad, "Creating MPEG Video, version 2 stream");
317 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_MPEG2);
318 } else {
319 GST_DEBUG_OBJECT (pad, "Creating MPEG Video, version 4 stream");
320 ps_data->stream = psmux_create_stream (mux->psmux, PSMUX_ST_VIDEO_MPEG4);
321 }
322 is_video = TRUE;
323 }
324
325 if (ps_data->stream != NULL) {
326 ps_data->stream_id = ps_data->stream->stream_id;
327 ps_data->stream_id_ext = ps_data->stream->stream_id_ext;
328 GST_DEBUG_OBJECT (pad, "Stream created, stream_id=%04x, stream_id_ext=%04x",
329 ps_data->stream_id, ps_data->stream_id_ext);
330
331 gst_structure_get_int (s, "rate", &ps_data->stream->audio_sampling);
332 gst_structure_get_int (s, "channels", &ps_data->stream->audio_channels);
333 gst_structure_get_int (s, "bitrate", &ps_data->stream->audio_bitrate);
334
335 ret = GST_FLOW_OK;
336
337 if (is_video && mux->video_stream_id == 0) {
338 mux->video_stream_id = ps_data->stream_id;
339 GST_INFO_OBJECT (mux, "video pad stream_id 0x%02x", mux->video_stream_id);
340 }
341 }
342
343 beach:
344 gst_caps_unref (caps);
345 return ret;
346 }
347
348 static GstFlowReturn
mpegpsmux_create_streams(MpegPsMux * mux)349 mpegpsmux_create_streams (MpegPsMux * mux)
350 {
351 /* Create stream for each pad */
352
353 GstFlowReturn ret = GST_FLOW_OK;
354 GSList *walk = mux->collect->data;
355
356 /* Create the streams */
357 while (walk) {
358 GstCollectData *c_data = (GstCollectData *) walk->data;
359 MpegPsPadData *ps_data = (MpegPsPadData *) walk->data;
360
361 walk = g_slist_next (walk);
362
363 if (ps_data->stream == NULL) {
364 ret = mpegpsmux_create_stream (mux, ps_data, c_data->pad);
365 if (ret != GST_FLOW_OK)
366 goto no_stream;
367 }
368 }
369
370 return GST_FLOW_OK;
371 no_stream:
372 GST_ELEMENT_ERROR (mux, STREAM, MUX,
373 ("Could not create handler for stream"), (NULL));
374 return ret;
375 }
376
377 static GstBuffer *
mpegpsmux_queue_buffer_for_stream(MpegPsMux * mux,MpegPsPadData * ps_data)378 mpegpsmux_queue_buffer_for_stream (MpegPsMux * mux, MpegPsPadData * ps_data)
379 {
380 GstCollectData *c_data = (GstCollectData *) ps_data;
381 GstBuffer *buf;
382
383 g_assert (ps_data->queued.buf == NULL);
384
385 buf = gst_collect_pads_peek (mux->collect, c_data);
386 if (buf == NULL)
387 return NULL;
388
389 ps_data->queued.buf = buf;
390
391 /* do any raw -> byte-stream format conversions (e.g. for H.264, AAC) */
392 if (ps_data->prepare_func) {
393 buf = ps_data->prepare_func (buf, ps_data, mux);
394 if (buf) { /* Take the prepared buffer instead */
395 gst_buffer_unref (ps_data->queued.buf);
396 ps_data->queued.buf = buf;
397 } else { /* If data preparation returned NULL, use unprepared one */
398 buf = ps_data->queued.buf;
399 }
400 }
401
402 ps_data->queued.pts = GST_BUFFER_PTS (buf);
403 if (GST_CLOCK_TIME_IS_VALID (ps_data->queued.pts)) {
404 ps_data->queued.pts = gst_segment_to_running_time (&c_data->segment,
405 GST_FORMAT_TIME, ps_data->queued.pts);
406 }
407
408 ps_data->queued.dts = GST_BUFFER_DTS (buf);
409 if (GST_CLOCK_TIME_IS_VALID (ps_data->queued.dts)) {
410 ps_data->queued.dts = gst_segment_to_running_time (&c_data->segment,
411 GST_FORMAT_TIME, ps_data->queued.dts);
412 }
413
414 if (GST_BUFFER_PTS_IS_VALID (buf) && GST_BUFFER_DTS_IS_VALID (buf)) {
415 ps_data->queued.ts = MIN (ps_data->queued.dts, ps_data->queued.pts);
416 } else if (GST_BUFFER_PTS_IS_VALID (buf) && !GST_BUFFER_DTS_IS_VALID (buf)) {
417 ps_data->queued.ts = ps_data->queued.pts;
418 } else if (GST_BUFFER_DTS_IS_VALID (buf) && !GST_BUFFER_PTS_IS_VALID (buf)) {
419 GST_WARNING_OBJECT (c_data->pad, "got DTS without PTS");
420 ps_data->queued.ts = ps_data->queued.dts;
421 } else {
422 ps_data->queued.ts = GST_CLOCK_TIME_NONE;
423 }
424
425 if (ps_data->queued.ts != GST_CLOCK_TIME_NONE)
426 ps_data->last_ts = ps_data->queued.ts;
427
428 GST_DEBUG_OBJECT (mux, "Queued buffer with ts %" GST_TIME_FORMAT ": "
429 "uncorrected pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT ", "
430 "buffer pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT " for PID 0x%04x",
431 GST_TIME_ARGS (ps_data->queued.ts),
432 GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
433 GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
434 GST_TIME_ARGS (ps_data->queued.pts),
435 GST_TIME_ARGS (ps_data->queued.dts), ps_data->stream_id);
436
437 return buf;
438 }
439
440 static MpegPsPadData *
mpegpsmux_choose_best_stream(MpegPsMux * mux)441 mpegpsmux_choose_best_stream (MpegPsMux * mux)
442 {
443 /* Choose from which stream to mux with */
444
445 MpegPsPadData *best = NULL;
446 GstCollectData *c_best = NULL;
447 GSList *walk;
448
449 for (walk = mux->collect->data; walk != NULL; walk = g_slist_next (walk)) {
450 GstCollectData *c_data = (GstCollectData *) walk->data;
451 MpegPsPadData *ps_data = (MpegPsPadData *) walk->data;
452
453 if (ps_data->eos == FALSE) {
454 if (ps_data->queued.buf == NULL) {
455 GstBuffer *buf;
456
457 buf = mpegpsmux_queue_buffer_for_stream (mux, ps_data);
458 if (buf == NULL) {
459 GST_DEBUG_OBJECT (mux, "we have EOS");
460 ps_data->eos = TRUE;
461 continue;
462 }
463 }
464
465 /* If we don't yet have a best pad, take this one, otherwise take
466 * whichever has the oldest timestamp */
467 if (best != NULL) {
468 if (ps_data->last_ts == GST_CLOCK_TIME_NONE ||
469 (best->last_ts != GST_CLOCK_TIME_NONE &&
470 ps_data->last_ts < best->last_ts)) {
471 best = ps_data;
472 c_best = c_data;
473 }
474 } else {
475 best = ps_data;
476 c_best = c_data;
477 }
478 }
479 }
480 if (c_best) {
481 gst_buffer_unref (gst_collect_pads_pop (mux->collect, c_best));
482 }
483
484 return best;
485 }
486
487 static GstFlowReturn
mpegpsmux_push_gop_list(MpegPsMux * mux)488 mpegpsmux_push_gop_list (MpegPsMux * mux)
489 {
490 GstFlowReturn flow;
491
492 g_assert (mux->gop_list != NULL);
493
494 GST_DEBUG_OBJECT (mux, "Sending pending GOP of %u buffers",
495 gst_buffer_list_length (mux->gop_list));
496 flow = gst_pad_push_list (mux->srcpad, mux->gop_list);
497 mux->gop_list = NULL;
498 return flow;
499 }
500
501 static GstFlowReturn
mpegpsmux_collected(GstCollectPads * pads,MpegPsMux * mux)502 mpegpsmux_collected (GstCollectPads * pads, MpegPsMux * mux)
503 {
504 /* main muxing function */
505
506 GstFlowReturn ret = GST_FLOW_OK;
507 MpegPsPadData *best = NULL;
508 gboolean keyunit;
509
510 GST_DEBUG_OBJECT (mux, "Pads collected");
511
512 if (mux->first) { /* process block for the first mux */
513 /* iterate through the collect pads and add streams to @mux */
514 ret = mpegpsmux_create_streams (mux);
515 /* Assumption : all pads are already added at this time */
516
517 if (G_UNLIKELY (ret != GST_FLOW_OK))
518 return ret;
519
520 best = mpegpsmux_choose_best_stream (mux);
521
522 /* prepare the src pad (output), return if failed */
523 if (!mpegpsdemux_prepare_srcpad (mux)) {
524 GST_DEBUG_OBJECT (mux, "Failed to send new segment");
525 goto new_seg_fail;
526 }
527
528 mux->first = FALSE;
529 } else {
530 best = mpegpsmux_choose_best_stream (mux);
531 }
532
533 if (best != NULL) {
534 GstBuffer *buf = best->queued.buf;
535 gint64 pts, dts;
536
537 g_assert (buf != NULL);
538
539 GST_LOG_OBJECT (mux,
540 "Chose stream from pad %" GST_PTR_FORMAT " for output (PID: 0x%04x): "
541 "adjusted pts: %" GST_TIME_FORMAT ", dts: %" GST_TIME_FORMAT,
542 best->collect.pad, best->stream_id,
543 GST_TIME_ARGS (best->queued.pts), GST_TIME_ARGS (best->queued.dts));
544
545 /* and convert to mpeg time stamps */
546 pts = GSTTIME_TO_MPEGTIME (best->queued.pts);
547 dts = GSTTIME_TO_MPEGTIME (best->queued.dts);
548
549 /* start of new GOP? */
550 keyunit = !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
551
552 if (keyunit && best->stream_id == mux->video_stream_id
553 && mux->gop_list != NULL) {
554 ret = mpegpsmux_push_gop_list (mux);
555 if (ret != GST_FLOW_OK)
556 goto done;
557 }
558
559 /* give the buffer to libpsmux for processing */
560 psmux_stream_add_data (best->stream, buf, pts, dts, keyunit);
561
562 best->queued.buf = NULL;
563
564 /* write the data from libpsmux to stream */
565 while (psmux_stream_bytes_in_buffer (best->stream) > 0) {
566 GST_LOG_OBJECT (mux, "Before @psmux_write_stream_packet");
567 if (!psmux_write_stream_packet (mux->psmux, best->stream)) {
568 GST_DEBUG_OBJECT (mux, "Failed to write data packet");
569 goto write_fail;
570 }
571 }
572 mux->last_ts = best->last_ts;
573 } else {
574 /* FIXME: Drain all remaining streams */
575 /* At EOS */
576 if (mux->gop_list != NULL)
577 mpegpsmux_push_gop_list (mux);
578
579 if (!psmux_write_end_code (mux->psmux)) {
580 GST_WARNING_OBJECT (mux, "Writing MPEG PS Program end code failed.");
581 }
582 gst_pad_push_event (mux->srcpad, gst_event_new_eos ());
583
584 ret = GST_FLOW_EOS;
585 }
586
587 done:
588
589 return ret;
590 new_seg_fail:
591 return GST_FLOW_ERROR;
592 write_fail:
593 /* FIXME: Failed writing data for some reason. Should set appropriate error */
594 return mux->last_flow_ret;
595 }
596
597 static GstPad *
mpegpsmux_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * name,const GstCaps * caps)598 mpegpsmux_request_new_pad (GstElement * element,
599 GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
600 {
601 MpegPsMux *mux = GST_MPEG_PSMUX (element);
602 GstPad *pad = NULL;
603 MpegPsPadData *pad_data = NULL;
604
605 pad = gst_pad_new_from_template (templ, name);
606
607 pad_data = (MpegPsPadData *) gst_collect_pads_add_pad (mux->collect, pad,
608 sizeof (MpegPsPadData), NULL, TRUE);
609 if (pad_data == NULL)
610 goto pad_failure;
611
612 pad_data->last_ts = GST_CLOCK_TIME_NONE;
613 pad_data->codec_data = NULL;
614 pad_data->prepare_func = NULL;
615
616 if (G_UNLIKELY (!gst_element_add_pad (element, pad)))
617 goto could_not_add;
618
619 return pad;
620
621 could_not_add:
622 GST_ELEMENT_ERROR (element, STREAM, FAILED,
623 ("Internal data stream error."), ("Could not add pad to element"));
624 gst_collect_pads_remove_pad (mux->collect, pad);
625 gst_object_unref (pad);
626 return NULL;
627 pad_failure:
628 GST_ELEMENT_ERROR (element, STREAM, FAILED,
629 ("Internal data stream error."), ("Could not add pad to collectpads"));
630 gst_object_unref (pad);
631 return NULL;
632 }
633
634 static void
mpegpsmux_release_pad(GstElement * element,GstPad * pad)635 mpegpsmux_release_pad (GstElement * element, GstPad * pad)
636 {
637 /* unref pad data (and codec data) */
638
639 MpegPsMux *mux = GST_MPEG_PSMUX (element);
640 MpegPsPadData *pad_data = NULL;
641
642 GST_DEBUG_OBJECT (mux, "Pad %" GST_PTR_FORMAT " being released", pad);
643
644 /* Get the MpegPsPadData out of the pad */
645 GST_OBJECT_LOCK (pad);
646 pad_data = (MpegPsPadData *) gst_pad_get_element_private (pad);
647 if (G_LIKELY (pad_data)) {
648 /* Free codec data reference if any */
649 if (pad_data->codec_data) {
650 GST_DEBUG_OBJECT (element, "releasing codec_data reference");
651 gst_buffer_unref (pad_data->codec_data);
652 pad_data->codec_data = NULL;
653 }
654 if (pad_data->stream_id == mux->video_stream_id)
655 mux->video_stream_id = 0;
656 }
657 GST_OBJECT_UNLOCK (pad);
658
659 gst_collect_pads_remove_pad (mux->collect, pad);
660 }
661
662 static gboolean
new_packet_cb(guint8 * data,guint len,void * user_data)663 new_packet_cb (guint8 * data, guint len, void *user_data)
664 {
665 /* Called when the PsMux has prepared a packet for output. Return FALSE
666 * on error */
667
668 MpegPsMux *mux = (MpegPsMux *) user_data;
669 GstBuffer *buf;
670 GstFlowReturn ret;
671
672 GST_LOG_OBJECT (mux, "Outputting a packet of length %d", len);
673
674 data = g_memdup (data, len);
675 buf = gst_buffer_new_wrapped (data, len);
676
677 GST_BUFFER_TIMESTAMP (buf) = mux->last_ts;
678
679 if (mux->aggregate_gops) {
680 if (mux->gop_list == NULL)
681 mux->gop_list = gst_buffer_list_new ();
682
683 gst_buffer_list_add (mux->gop_list, buf);
684 return TRUE;
685 }
686
687 ret = gst_pad_push (mux->srcpad, buf);
688
689 if (G_UNLIKELY (ret != GST_FLOW_OK)) {
690 mux->last_flow_ret = ret;
691 return FALSE;
692 }
693
694 return TRUE;
695 }
696
697 /* prepare the source pad for output */
698 static gboolean
mpegpsdemux_prepare_srcpad(MpegPsMux * mux)699 mpegpsdemux_prepare_srcpad (MpegPsMux * mux)
700 {
701 GstSegment segment;
702 GValue val = { 0, };
703 GList *headers, *l;
704 GstCaps *caps;
705 gchar s_id[32];
706
707 /* stream-start (FIXME: create id based on input ids) */
708 g_snprintf (s_id, sizeof (s_id), "mpegpsmux-%08x", g_random_int ());
709 gst_pad_push_event (mux->srcpad, gst_event_new_stream_start (s_id));
710
711 caps = gst_caps_new_simple ("video/mpeg",
712 "mpegversion", G_TYPE_INT, 2, "systemstream", G_TYPE_BOOLEAN, TRUE, NULL);
713
714 headers = psmux_get_stream_headers (mux->psmux);
715 g_value_init (&val, GST_TYPE_ARRAY);
716 for (l = headers; l != NULL; l = l->next) {
717 GValue buf_val = { 0, };
718
719 g_value_init (&buf_val, GST_TYPE_BUFFER);
720 gst_value_take_buffer (&buf_val, GST_BUFFER (l->data));
721 l->data = NULL;
722 gst_value_array_append_value (&val, &buf_val);
723 g_value_unset (&buf_val);
724 }
725 gst_caps_set_value (caps, "streamheader", &val);
726 g_value_unset (&val);
727 g_list_free (headers);
728
729 /* Set caps on src pad and push new segment */
730 gst_pad_push_event (mux->srcpad, gst_event_new_caps (caps));
731 gst_caps_unref (caps);
732
733 gst_segment_init (&segment, GST_FORMAT_BYTES);
734 gst_pad_push_event (mux->srcpad, gst_event_new_segment (&segment));
735
736 return TRUE;
737 }
738
739 static GstStateChangeReturn
mpegpsmux_change_state(GstElement * element,GstStateChange transition)740 mpegpsmux_change_state (GstElement * element, GstStateChange transition)
741 {
742 /* control the collect pads */
743
744 MpegPsMux *mux = GST_MPEG_PSMUX (element);
745 GstStateChangeReturn ret;
746
747 switch (transition) {
748 case GST_STATE_CHANGE_NULL_TO_READY:
749 break;
750 case GST_STATE_CHANGE_READY_TO_PAUSED:
751 gst_collect_pads_start (mux->collect);
752 break;
753 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
754 break;
755 case GST_STATE_CHANGE_PAUSED_TO_READY:
756 gst_collect_pads_stop (mux->collect);
757 break;
758 case GST_STATE_CHANGE_READY_TO_NULL:
759 break;
760 default:
761 break;
762 }
763
764 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
765
766 switch (transition) {
767 default:
768 break;
769 }
770
771 return ret;
772 }
773
774 static gboolean
plugin_init(GstPlugin * plugin)775 plugin_init (GstPlugin * plugin)
776 {
777 if (!gst_element_register (plugin, "mpegpsmux", GST_RANK_PRIMARY,
778 mpegpsmux_get_type ()))
779 return FALSE;
780
781 GST_DEBUG_CATEGORY_INIT (mpegpsmux_debug, "mpegpsmux", 0,
782 "MPEG Program Stream muxer");
783
784 return TRUE;
785 }
786
787 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
788 mpegpsmux, "MPEG-PS muxer",
789 plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
790