• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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