1 /* GStreamer mplex (mjpegtools) wrapper
2 * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3 * (c) 2008 Mark Nauwelaerts <mnauw@users.sourceforge.net>
4 *
5 * gstmplex.cc: gstreamer mplex wrapper
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 /**
24 * SECTION:element-mplex
25 * @see_also: mpeg2enc
26 *
27 * This element is an audio/video multiplexer for MPEG-1/2 video streams
28 * and (un)compressed audio streams such as AC3, MPEG layer I/II/III.
29 * It is based on the [mjpegtools](http://mjpeg.sourceforge.net/) library.
30 * Documentation on creating MPEG videos in general can be found in the
31 * [MJPEG Howto](https://sourceforge.net/docman/display_doc.php?docid=3456&group_id=5776)
32 * and the man-page of the mplex tool documents the properties of this element,
33 * which are shared with the mplex tool.
34 *
35 * ## Example pipeline
36 *
37 * |[
38 * gst-launch-1.0 -v videotestsrc num-buffers=1000 ! mpeg2enc ! mplex ! filesink location=videotestsrc.mpg
39 * ]| This example pipeline will encode a test video source to an
40 * MPEG1 elementary stream and multiplexes this to an MPEG system stream.
41 *
42 * If several streams are being multiplexed, there should (as usual) be
43 * a queue in each stream, and due to mplex' buffering the capacities of these
44 * may have to be set to a few times the default settings to prevent the
45 * pipeline stalling.
46 */
47
48 #ifdef HAVE_CONFIG_H
49 #include "config.h"
50 #endif
51
52 #include <string.h>
53
54 #include <gst/glib-compat-private.h>
55 #include <gst/audio/audio.h>
56
57 #include "gstmplex.hh"
58 #include "gstmplexoutputstream.hh"
59 #include "gstmplexibitstream.hh"
60 #include "gstmplexjob.hh"
61
62 GST_DEBUG_CATEGORY (mplex_debug);
63
64 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
65 GST_PAD_SRC,
66 GST_PAD_ALWAYS,
67 GST_STATIC_CAPS ("video/mpeg, systemstream = (boolean) true ")
68 );
69
70 static GstStaticPadTemplate video_sink_templ =
71 GST_STATIC_PAD_TEMPLATE ("video_%u",
72 GST_PAD_SINK,
73 GST_PAD_REQUEST,
74 GST_STATIC_CAPS ("video/mpeg, "
75 "mpegversion = (int) { 1, 2 }, "
76 "systemstream = (boolean) false, "
77 "width = (int) [ 16, 4096 ], "
78 "height = (int) [ 16, 4096 ], framerate = (fraction) [ 0, MAX ]")
79 );
80
81 #define COMMON_AUDIO_CAPS \
82 "channels = (int) [ 1, 8 ], " \
83 "rate = (int) [ 8000, 96000 ]"
84
85 static GstStaticPadTemplate audio_sink_templ =
86 GST_STATIC_PAD_TEMPLATE ("audio_%u",
87 GST_PAD_SINK,
88 GST_PAD_REQUEST,
89 GST_STATIC_CAPS ("audio/mpeg, "
90 "mpegversion = (int) 1, "
91 "layer = (int) [ 1, 3 ], "
92 COMMON_AUDIO_CAPS "; "
93 "audio/x-ac3, "
94 COMMON_AUDIO_CAPS "; "
95 "audio/x-dts; "
96 "audio/x-raw, "
97 "format = (string) { S16BE, S20BE, S24BE }, "
98 "rate = (int) { 48000, 96000 }, " "channels = (int) [ 1, 6 ]")
99 );
100
101 /* FIXME: subtitles */
102
103 static void gst_mplex_finalize (GObject * object);
104 static void gst_mplex_reset (GstMplex * mplex);
105 static void gst_mplex_loop (GstMplex * mplex);
106 static GstPad *gst_mplex_request_new_pad (GstElement * element,
107 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
108 static void gst_mplex_release_pad (GstElement * element, GstPad * pad);
109 static gboolean gst_mplex_src_activate_mode (GstPad * pad, GstObject * parent,
110 GstPadMode mode, gboolean active);
111 static GstStateChangeReturn gst_mplex_change_state (GstElement * element,
112 GstStateChange transition);
113
114 static void gst_mplex_get_property (GObject * object,
115 guint prop_id, GValue * value, GParamSpec * pspec);
116 static void gst_mplex_set_property (GObject * object,
117 guint prop_id, const GValue * value, GParamSpec * pspec);
118 static gboolean mplex_element_init (GstPlugin * plugin);
119
120 #define parent_class gst_mplex_parent_class
121 G_DEFINE_TYPE (GstMplex, gst_mplex, GST_TYPE_ELEMENT);
122 GST_ELEMENT_REGISTER_DEFINE_CUSTOM (mplex, mplex_element_init);
123
124 static void
gst_mplex_class_init(GstMplexClass * klass)125 gst_mplex_class_init (GstMplexClass * klass)
126 {
127 GObjectClass *object_class = G_OBJECT_CLASS (klass);
128 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
129
130 GST_DEBUG_CATEGORY_INIT (mplex_debug, "mplex", 0, "MPEG video/audio muxer");
131
132 object_class->set_property = gst_mplex_set_property;
133 object_class->get_property = gst_mplex_get_property;
134
135 /* register properties */
136 GstMplexJob::initProperties (object_class);
137
138 object_class->finalize = GST_DEBUG_FUNCPTR (gst_mplex_finalize);
139
140 element_class->change_state = GST_DEBUG_FUNCPTR (gst_mplex_change_state);
141 element_class->request_new_pad =
142 GST_DEBUG_FUNCPTR (gst_mplex_request_new_pad);
143 element_class->release_pad = GST_DEBUG_FUNCPTR (gst_mplex_release_pad);
144
145 gst_element_class_set_static_metadata (element_class,
146 "mplex video multiplexer", "Codec/Muxer",
147 "High-quality MPEG/DVD/SVCD/VCD video/audio multiplexer",
148 "Andrew Stevens <andrew.stevens@nexgo.de>\n"
149 "Ronald Bultje <rbultje@ronald.bitfreak.net>\n"
150 "Mark Nauwelaerts <mnauw@users.sourceforge.net>");
151
152 gst_element_class_add_static_pad_template (element_class, &src_templ);
153 gst_element_class_add_static_pad_template (element_class, &video_sink_templ);
154 gst_element_class_add_static_pad_template (element_class, &audio_sink_templ);
155 }
156
157 static void
gst_mplex_finalize(GObject * object)158 gst_mplex_finalize (GObject * object)
159 {
160 GstMplex *mplex = GST_MPLEX (object);
161 GSList *walk;
162
163 /* release all pads */
164 walk = mplex->pads;
165 while (walk) {
166 GstMplexPad *mpad = (GstMplexPad *) walk->data;
167
168 if (mpad->pad)
169 gst_object_unref (mpad->pad);
170 mpad->pad = NULL;
171 walk = walk->next;
172 }
173
174 /* clean up what's left of them */
175 gst_mplex_reset (mplex);
176
177 /* ... and of the rest */
178 delete mplex->job;
179
180 g_mutex_clear (&mplex->tlock);
181
182 G_OBJECT_CLASS (parent_class)->finalize (object);
183 }
184
185 static void
gst_mplex_init(GstMplex * mplex)186 gst_mplex_init (GstMplex * mplex)
187 {
188 GstElement *element = GST_ELEMENT (mplex);
189
190 mplex->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
191 gst_element_add_pad (element, mplex->srcpad);
192 gst_pad_use_fixed_caps (mplex->srcpad);
193 gst_pad_set_activatemode_function (mplex->srcpad,
194 GST_DEBUG_FUNCPTR (gst_mplex_src_activate_mode));
195
196 mplex->job = new GstMplexJob ();
197 mplex->num_apads = 0;
198 mplex->num_vpads = 0;
199
200 g_mutex_init (&mplex->tlock);
201
202 gst_mplex_reset (mplex);
203 }
204
205 static void
gst_mplex_reset(GstMplex * mplex)206 gst_mplex_reset (GstMplex * mplex)
207 {
208 GSList *walk;
209 GSList *nlist = NULL;
210
211 mplex->eos = FALSE;
212 mplex->srcresult = GST_FLOW_CUSTOM_SUCCESS;
213
214 /* reset existing streams */
215 walk = mplex->pads;
216 while (walk != NULL) {
217 GstMplexPad *mpad;
218
219 mpad = (GstMplexPad *) walk->data;
220
221 mpad->needed = 0;
222 mpad->eos = FALSE;
223 gst_adapter_clear (mpad->adapter);
224 if (mpad->bs) {
225 delete mpad->bs;
226
227 mpad->bs = NULL;
228 }
229
230 if (!mpad->pad) {
231 g_cond_clear (&mpad->cond);
232 g_object_unref (mpad->adapter);
233 g_free (mpad);
234 } else
235 nlist = g_slist_append (nlist, mpad);
236
237 walk = walk->next;
238 }
239
240 g_slist_free (mplex->pads);
241 mplex->pads = nlist;
242
243 /* clear mplex stuff */
244 /* clean up stream settings */
245 while (!mplex->job->streams.empty ()) {
246 delete mplex->job->streams.back ();
247
248 mplex->job->streams.pop_back ();
249 }
250 while (!mplex->job->video_param.empty ()) {
251 delete mplex->job->video_param.back ();
252
253 mplex->job->video_param.pop_back ();
254 }
255 while (!mplex->job->lpcm_param.empty ()) {
256 delete mplex->job->lpcm_param.back ();
257
258 mplex->job->lpcm_param.pop_back ();
259 }
260 mplex->job->audio_tracks = 0;
261 mplex->job->video_tracks = 0;
262 mplex->job->lpcm_tracks = 0;
263 }
264
265 static gboolean
gst_mplex_setcaps(GstPad * pad,GstCaps * caps)266 gst_mplex_setcaps (GstPad * pad, GstCaps * caps)
267 {
268 GstMplex *mplex;
269 const gchar *mime;
270 GstStructure *structure;
271 StreamKind type;
272 JobStream *jobstream;
273 GstMplexIBitStream *inputstream;
274 GstMplexPad *mpad;
275 GstCaps *othercaps, *templ;
276 gboolean ret = TRUE;
277
278 mplex = GST_MPLEX (GST_PAD_PARENT (pad));
279
280 /* does not go well to negotiate when started */
281 if (mplex->srcresult != GST_FLOW_CUSTOM_SUCCESS)
282 goto refuse_renegotiation;
283
284 /* since muxer does not really check much ... */
285 templ = gst_pad_get_pad_template_caps (pad);
286 othercaps = gst_caps_intersect (caps, templ);
287 gst_caps_unref (templ);
288 if (othercaps)
289 gst_caps_unref (othercaps);
290 else
291 goto refuse_caps;
292
293 /* set the fixed template caps on the srcpad, should accept without objection */
294 othercaps = gst_pad_get_pad_template_caps (mplex->srcpad);
295 ret = gst_pad_set_caps (mplex->srcpad, othercaps);
296 gst_caps_unref (othercaps);
297 if (!ret)
298 goto refuse_caps;
299
300 structure = gst_caps_get_structure (caps, 0);
301 mime = gst_structure_get_name (structure);
302
303 if (!strcmp (mime, "video/mpeg")) { /* video */
304 VideoParams *params;
305
306 type = MPEG_VIDEO;
307 if (mplex->job->bufsize)
308 params = VideoParams::Checked (mplex->job->bufsize);
309 else
310 params = VideoParams::Default (mplex->job->mux_format);
311 /* set standard values if forced by the selected profile */
312 if (params->Force (mplex->job->mux_format))
313 GST_WARNING_OBJECT (mplex,
314 "overriding non-standard option due to selected profile");
315
316 mplex->job->video_param.push_back (params);
317 mplex->job->video_tracks++;
318 } else { /* audio */
319 if (!strcmp (mime, "audio/mpeg")) {
320 type = MPEG_AUDIO;
321 } else if (!strcmp (mime, "audio/x-ac3")) {
322 type = AC3_AUDIO;
323 } else if (!strcmp (mime, "audio/x-dts")) {
324 type = DTS_AUDIO;
325 } else if (!strcmp (mime, "audio/x-raw")) {
326 LpcmParams *params;
327 gint bits, chans, rate;
328 GstAudioInfo info;
329
330 type = LPCM_AUDIO;
331
332 gst_audio_info_init (&info);
333 if (!gst_audio_info_from_caps (&info, caps))
334 goto refuse_caps;
335
336 rate = GST_AUDIO_INFO_RATE (&info);
337 chans = GST_AUDIO_INFO_CHANNELS (&info);
338 bits = GST_AUDIO_INFO_DEPTH (&info);
339
340 /* set LPCM params */
341 params = LpcmParams::Checked (rate, chans, bits);
342
343 mplex->job->lpcm_param.push_back (params);
344 mplex->job->lpcm_tracks++;
345 } else
346 goto refuse_caps;
347
348 mplex->job->audio_tracks++;
349 }
350
351 mpad = (GstMplexPad *) gst_pad_get_element_private (pad);
352 g_return_val_if_fail (mpad, FALSE);
353 inputstream = new GstMplexIBitStream (mpad);
354 mpad->bs = inputstream;
355 jobstream = new JobStream (inputstream, type);
356 mplex->job->streams.push_back (jobstream);
357
358 return TRUE;
359
360 refuse_caps:
361 {
362 GST_WARNING_OBJECT (mplex, "refused caps %" GST_PTR_FORMAT, caps);
363
364 /* undo if we were a bit too fast/confident */
365 if (gst_pad_has_current_caps (mplex->srcpad))
366 gst_pad_set_caps (mplex->srcpad, NULL);
367
368 return FALSE;
369 }
370 refuse_renegotiation:
371 {
372 GST_WARNING_OBJECT (mplex, "already started; "
373 "refused (re)negotiation (to %" GST_PTR_FORMAT ")", caps);
374
375 return FALSE;
376 }
377 }
378
379 static void
gst_mplex_loop(GstMplex * mplex)380 gst_mplex_loop (GstMplex * mplex)
381 {
382 GstMplexOutputStream *out = NULL;
383 Multiplexor *mux = NULL;
384 GSList *walk;
385 GstSegment segment;
386
387 /* do not try to resume muxing after it finished
388 * this can be relevant mainly/only in case of forced state change */
389 if (mplex->eos)
390 goto eos;
391
392 /* inform downstream about what's coming */
393 gst_segment_init (&segment, GST_FORMAT_BYTES);
394 gst_pad_push_event (mplex->srcpad, gst_event_new_segment (&segment));
395
396 /* hm (!) each inputstream really needs an initial read
397 * so that all is internally in the proper state */
398 walk = mplex->pads;
399 while (walk != NULL) {
400 GstMplexPad *mpad;
401
402 mpad = (GstMplexPad *) walk->data;
403 mpad->bs->ReadBuffer ();
404
405 walk = walk->next;
406 }
407
408 /* create new multiplexer with inputs/output */
409 out = new GstMplexOutputStream (mplex, mplex->srcpad);
410 #if GST_MJPEGTOOLS_API >= 10900
411 mux = new Multiplexor (*mplex->job, *out, NULL);
412 #else
413 mux = new Multiplexor (*mplex->job, *out);
414 #endif
415
416 if (mux) {
417 mux->Multiplex ();
418 delete mux;
419 delete out;
420
421 /* if not well and truly eos, something strange happened */
422 if (!mplex->eos) {
423 GST_ERROR_OBJECT (mplex, "muxing task ended without being eos");
424 /* notify there is no point in collecting any more */
425 GST_MPLEX_MUTEX_LOCK (mplex);
426 mplex->srcresult = GST_FLOW_ERROR;
427 GST_MPLEX_SIGNAL_ALL (mplex);
428 GST_MPLEX_MUTEX_UNLOCK (mplex);
429 } else
430 goto eos;
431 } else {
432 GST_WARNING_OBJECT (mplex, "failed to create Multiplexor");
433 }
434
435 /* fall-through */
436 done:
437 {
438 /* no need to run wildly, stopped elsewhere, e.g. state change */
439 GST_DEBUG_OBJECT (mplex, "pausing muxing task");
440 gst_pad_pause_task (mplex->srcpad);
441
442 return;
443 }
444 eos:
445 {
446 GST_DEBUG_OBJECT (mplex, "encoding task reached eos");
447 goto done;
448 }
449 }
450
451 static gboolean
gst_mplex_sink_event(GstPad * sinkpad,GstObject * parent,GstEvent * event)452 gst_mplex_sink_event (GstPad * sinkpad, GstObject * parent, GstEvent * event)
453 {
454 GstMplex *mplex;
455 GstMplexPad *mpad;
456 gboolean result = TRUE;
457
458 mplex = (GstMplex *) parent;
459 mpad = (GstMplexPad *) gst_pad_get_element_private (sinkpad);
460 g_return_val_if_fail (mpad, FALSE);
461
462 switch (GST_EVENT_TYPE (event)) {
463 case GST_EVENT_FLUSH_START:
464 /* forward event */
465 gst_pad_event_default (sinkpad, parent, event);
466
467 /* now unblock the chain function */
468 GST_MPLEX_MUTEX_LOCK (mplex);
469 mplex->srcresult = GST_FLOW_FLUSHING;
470 GST_MPLEX_SIGNAL (mplex, mpad);
471 GST_MPLEX_MUTEX_UNLOCK (mplex);
472 /* no way to pause/restart loop task */
473 goto done;
474 case GST_EVENT_FLUSH_STOP:
475 /* forward event */
476 gst_pad_event_default (sinkpad, parent, event);
477
478 /* clear state and resume */
479 GST_MPLEX_MUTEX_LOCK (mplex);
480 gst_adapter_clear (mpad->adapter);
481 mplex->srcresult = GST_FLOW_OK;
482 GST_MPLEX_MUTEX_UNLOCK (mplex);
483 goto done;
484 case GST_EVENT_SEGMENT:
485 /* eat segments; we make our own (byte)stream */
486 gst_event_unref (event);
487 goto done;
488 case GST_EVENT_EOS:
489 /* inform this pad that it can stop now */
490 GST_MPLEX_MUTEX_LOCK (mplex);
491 mpad->eos = TRUE;
492 GST_MPLEX_SIGNAL (mplex, mpad);
493 GST_MPLEX_MUTEX_UNLOCK (mplex);
494
495 /* eat this event for now, task will send eos when finished */
496 gst_event_unref (event);
497 goto done;
498 case GST_EVENT_CAPS:
499 {
500 GstCaps *caps;
501
502 gst_event_parse_caps (event, &caps);
503 result = gst_mplex_setcaps (sinkpad, caps);
504 gst_event_unref (event);
505 goto done;
506
507 }
508 default:
509 /* for a serialized event, wait until earlier data is gone,
510 * though this is no guarantee as to when task is done with it.
511 * Only wait if loop has been started already */
512 if (GST_EVENT_IS_SERIALIZED (event)) {
513 GST_MPLEX_MUTEX_LOCK (mplex);
514 while (mplex->srcresult == GST_FLOW_OK && !mpad->needed)
515 GST_MPLEX_WAIT (mplex, mpad);
516 GST_MPLEX_MUTEX_UNLOCK (mplex);
517 }
518 break;
519 }
520
521 result = gst_pad_event_default (sinkpad, parent, event);
522
523 done:
524 return result;
525 }
526
527 /* starts task if conditions are right for it
528 * must be called with mutex_lock held */
529 static void
gst_mplex_start_task(GstMplex * mplex)530 gst_mplex_start_task (GstMplex * mplex)
531 {
532 /* start task to create multiplexor and start muxing */
533 if (G_UNLIKELY (mplex->srcresult == GST_FLOW_CUSTOM_SUCCESS)
534 && mplex->job->video_tracks == mplex->num_vpads
535 && mplex->job->audio_tracks == mplex->num_apads) {
536 gst_pad_start_task (mplex->srcpad, (GstTaskFunction) gst_mplex_loop, mplex,
537 NULL);
538 mplex->srcresult = GST_FLOW_OK;
539 }
540 }
541
542 static GstFlowReturn
gst_mplex_chain(GstPad * sinkpad,GstObject * parent,GstBuffer * buffer)543 gst_mplex_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * buffer)
544 {
545 GstMplex *mplex;
546 GstMplexPad *mpad;
547
548 mplex = (GstMplex *) parent;
549 mpad = (GstMplexPad *) gst_pad_get_element_private (sinkpad);
550 g_return_val_if_fail (mpad, GST_FLOW_ERROR);
551
552 /* check if pad were properly negotiated and set up */
553 if (G_UNLIKELY (!mpad->bs)) {
554 GST_ELEMENT_ERROR (mplex, CORE, NEGOTIATION, (NULL),
555 ("input pad has not been set up prior to chain function"));
556 return GST_FLOW_NOT_NEGOTIATED;
557 }
558
559 GST_MPLEX_MUTEX_LOCK (mplex);
560
561 gst_mplex_start_task (mplex);
562
563 if (G_UNLIKELY (mpad->eos))
564 goto eos;
565
566 if (G_UNLIKELY (mplex->srcresult != GST_FLOW_OK))
567 goto ignore;
568
569 gst_adapter_push (mpad->adapter, buffer);
570 buffer = NULL;
571 while (gst_adapter_available (mpad->adapter) >= mpad->needed) {
572 GST_MPLEX_SIGNAL (mplex, mpad);
573 GST_MPLEX_WAIT (mplex, mpad);
574 /* may have become flushing or in error */
575 if (G_UNLIKELY (mplex->srcresult != GST_FLOW_OK))
576 goto ignore;
577 /* or been removed */
578 if (G_UNLIKELY (mpad->eos))
579 goto eos;
580 }
581
582 GST_MPLEX_MUTEX_UNLOCK (mplex);
583
584 return GST_FLOW_OK;
585
586 /* special cases */
587 eos:
588 {
589 GST_DEBUG_OBJECT (mplex, "ignoring buffer at end-of-stream");
590 GST_MPLEX_MUTEX_UNLOCK (mplex);
591
592 gst_buffer_unref (buffer);
593 return GST_FLOW_EOS;
594 }
595 ignore:
596 {
597 GstFlowReturn ret = mplex->srcresult;
598
599 GST_DEBUG_OBJECT (mplex, "ignoring buffer because src task encountered %s",
600 gst_flow_get_name (ret));
601 GST_MPLEX_MUTEX_UNLOCK (mplex);
602
603 if (buffer)
604 gst_buffer_unref (buffer);
605 return ret;
606 }
607 }
608
609 static GstPad *
gst_mplex_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * name,const GstCaps * caps)610 gst_mplex_request_new_pad (GstElement * element,
611 GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
612 {
613 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
614 GstMplex *mplex = GST_MPLEX (element);
615 gchar *padname;
616 GstPad *newpad;
617 GstMplexPad *mpad;
618
619 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
620 GST_DEBUG_OBJECT (mplex, "request pad audio %d", mplex->num_apads);
621 padname = g_strdup_printf ("audio_%u", mplex->num_apads++);
622 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
623 GST_DEBUG_OBJECT (mplex, "request pad video %d", mplex->num_vpads);
624 padname = g_strdup_printf ("video_%u", mplex->num_vpads++);
625 } else {
626 GST_WARNING_OBJECT (mplex, "This is not our template!");
627 return NULL;
628 }
629
630 newpad = gst_pad_new_from_template (templ, padname);
631 g_free (padname);
632
633 mpad = g_new0 (GstMplexPad, 1);
634 mpad->adapter = gst_adapter_new ();
635 g_cond_init (&mpad->cond);
636 gst_object_ref (newpad);
637 mpad->pad = newpad;
638
639 gst_pad_set_chain_function (newpad, GST_DEBUG_FUNCPTR (gst_mplex_chain));
640 gst_pad_set_event_function (newpad, GST_DEBUG_FUNCPTR (gst_mplex_sink_event));
641 gst_pad_set_element_private (newpad, mpad);
642 gst_element_add_pad (element, newpad);
643 mplex->pads = g_slist_append (mplex->pads, mpad);
644
645 return newpad;
646 }
647
648 static void
gst_mplex_release_pad(GstElement * element,GstPad * pad)649 gst_mplex_release_pad (GstElement * element, GstPad * pad)
650 {
651 GstMplex *mplex = GST_MPLEX (element);
652 GstMplexPad *mpad;
653
654 g_return_if_fail (pad);
655 mpad = (GstMplexPad *) gst_pad_get_element_private (pad);
656 g_return_if_fail (mpad);
657
658 if (gst_element_remove_pad (element, pad)) {
659 gchar *padname;
660
661 GST_MPLEX_MUTEX_LOCK (mplex);
662 mpad->eos = TRUE;
663 g_assert (mpad->pad == pad);
664 mpad->pad = NULL;
665 /* wake up if waiting on this pad */
666 GST_MPLEX_SIGNAL (mplex, mpad);
667
668 padname = gst_object_get_name (GST_OBJECT (pad));
669 /* now only drop what might be last ref */
670 gst_object_unref (pad);
671 if (strstr (padname, "audio")) {
672 mplex->num_apads--;
673 } else {
674 mplex->num_vpads--;
675 }
676 g_free (padname);
677
678 /* may now be up to us to get things going */
679 if (GST_STATE (element) > GST_STATE_READY)
680 gst_mplex_start_task (mplex);
681 GST_MPLEX_MUTEX_UNLOCK (mplex);
682 }
683 }
684
685 static void
gst_mplex_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)686 gst_mplex_get_property (GObject * object,
687 guint prop_id, GValue * value, GParamSpec * pspec)
688 {
689 GST_MPLEX (object)->job->getProperty (prop_id, value);
690 }
691
692 static void
gst_mplex_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)693 gst_mplex_set_property (GObject * object,
694 guint prop_id, const GValue * value, GParamSpec * pspec)
695 {
696 GST_MPLEX (object)->job->setProperty (prop_id, value);
697 }
698
699 static gboolean
gst_mplex_src_activate_mode(GstPad * pad,GstObject * parent,GstPadMode mode,gboolean active)700 gst_mplex_src_activate_mode (GstPad * pad, GstObject * parent,
701 GstPadMode mode, gboolean active)
702 {
703 gboolean result = TRUE;
704 GstMplex *mplex;
705
706 mplex = GST_MPLEX (parent);
707
708 if (mode != GST_PAD_MODE_PUSH)
709 return FALSE;
710
711 if (active) {
712 /* chain will start task once all streams have been setup */
713 } else {
714 /* end the muxing loop by forcing eos and unblock chains */
715 GST_MPLEX_MUTEX_LOCK (mplex);
716 mplex->eos = TRUE;
717 mplex->srcresult = GST_FLOW_FLUSHING;
718 GST_MPLEX_SIGNAL_ALL (mplex);
719 GST_MPLEX_MUTEX_UNLOCK (mplex);
720
721 /* muxing loop should have ended now and can be joined */
722 result = gst_pad_stop_task (pad);
723 }
724
725 return result;
726 }
727
728 static GstStateChangeReturn
gst_mplex_change_state(GstElement * element,GstStateChange transition)729 gst_mplex_change_state (GstElement * element, GstStateChange transition)
730 {
731 GstMplex *mplex = GST_MPLEX (element);
732 GstStateChangeReturn ret;
733
734 switch (transition) {
735 case GST_STATE_CHANGE_NULL_TO_READY:
736 break;
737 case GST_STATE_CHANGE_READY_TO_PAUSED:
738 break;
739 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
740 break;
741 default:
742 break;
743 }
744
745 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
746 if (ret == GST_STATE_CHANGE_FAILURE)
747 goto done;
748
749 switch (transition) {
750 case GST_STATE_CHANGE_PAUSED_TO_READY:
751 gst_mplex_reset (mplex);
752 break;
753 default:
754 break;
755 }
756
757 done:
758 return ret;
759 }
760
761 #ifndef GST_DISABLE_GST_DEBUG
762
763 static mjpeg_log_handler_t old_handler = NULL;
764
765 /* note that this will affect all mjpegtools elements/threads */
766 static void
gst_mplex_log_callback(log_level_t level,const char * message)767 gst_mplex_log_callback (log_level_t level, const char *message)
768 {
769 GstDebugLevel gst_level;
770
771 #if GST_MJPEGTOOLS_API >= 10900
772 static const gint mjpeg_log_error = mjpeg_loglev_t ("error");
773 static const gint mjpeg_log_warn = mjpeg_loglev_t ("warn");
774 static const gint mjpeg_log_info = mjpeg_loglev_t ("info");
775 static const gint mjpeg_log_debug = mjpeg_loglev_t ("debug");
776 #else
777 static const gint mjpeg_log_error = LOG_ERROR;
778 static const gint mjpeg_log_warn = LOG_WARN;
779 static const gint mjpeg_log_info = LOG_INFO;
780 static const gint mjpeg_log_debug = LOG_DEBUG;
781 #endif
782
783 if (level == mjpeg_log_error) {
784 gst_level = GST_LEVEL_ERROR;
785 } else if (level == mjpeg_log_warn) {
786 gst_level = GST_LEVEL_WARNING;
787 } else if (level == mjpeg_log_info) {
788 gst_level = GST_LEVEL_INFO;
789 } else if (level == mjpeg_log_debug) {
790 gst_level = GST_LEVEL_DEBUG;
791 } else {
792 gst_level = GST_LEVEL_INFO;
793 }
794
795 /* message could have a % in it, do not segfault in such case */
796 gst_debug_log (mplex_debug, gst_level, "", "", 0, NULL, "%s", message);
797
798 /* chain up to the old handler;
799 * this could actually be a handler from another mjpegtools based
800 * gstreamer element; in which case messages can come out double or from
801 * the wrong element ... */
802 old_handler (level, message);
803 }
804 #endif
805
806 static gboolean
mplex_element_init(GstPlugin * plugin)807 mplex_element_init (GstPlugin * plugin)
808 {
809 #ifndef GST_DISABLE_GST_DEBUG
810 old_handler = mjpeg_log_set_handler (gst_mplex_log_callback);
811 g_assert (old_handler != NULL);
812 #endif
813 /* in any case, we do not want default handler output */
814 mjpeg_default_handler_verbosity (0);
815
816 return gst_element_register (plugin, "mplex", GST_RANK_NONE, GST_TYPE_MPLEX);
817 }
818
819 static gboolean
plugin_init(GstPlugin * plugin)820 plugin_init (GstPlugin * plugin)
821 {
822 return GST_ELEMENT_REGISTER (mplex, plugin);
823 }
824
825 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
826 GST_VERSION_MINOR,
827 mplex,
828 "High-quality MPEG/DVD/SVCD/VCD video/audio multiplexer",
829 plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
830