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