1 /* GStreamer mpeg2enc (mjpegtools) wrapper
2 * (c) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3 * (c) 2006 Mark Nauwelaerts <manauw@skynet.be>
4 *
5 * gstmpeg2enc.cc: gstreamer mpeg2enc wrapping
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-mpeg2enc
25 * @see_also: mpeg2dec
26 *
27 * This element encodes raw video into an MPEG-1/2 elementary stream using the
28 * <ulink url="http://mjpeg.sourceforge.net/">mjpegtools</ulink> library.
29 * Documentation on MPEG encoding in general can be found in the
30 * <ulink url="https://sourceforge.net/docman/display_doc.php?docid=3456&group_id=5776">MJPEG Howto</ulink>
31 * and on the various available parameters in the documentation
32 * of the mpeg2enc tool in particular, which shares options with this element.
33 *
34 * <refsect2>
35 * <title>Example pipeline</title>
36 * |[
37 * gst-launch-1.0 videotestsrc num-buffers=1000 ! mpeg2enc ! filesink location=videotestsrc.m1v
38 * ]| This example pipeline will encode a test video source to a an MPEG1
39 * elementary stream (with Generic MPEG1 profile).
40 * <para>
41 * Likely, the #GstMpeg2enc:format property
42 * is most important, as it selects the type of MPEG stream that is produced.
43 * In particular, default property values are dependent on the format,
44 * and can even be forcibly restrained to certain pre-sets (and thereby ignored).
45 * Note that the (S)VCD profiles also restrict the image size, so some scaling
46 * may be needed to accomodate this. The so-called generic profiles (as used
47 * in the example above) allow most parameters to be adjusted.
48 * </para>
49 * |[
50 * gst-launch-1.0 videotestsrc num-buffers=1000 ! videoscale ! mpeg2enc format=1 norm=p ! filesink location=videotestsrc.m1v
51 * ]| This will produce an MPEG1 profile stream according to VCD2.0 specifications
52 * for PAL #GstMpeg2enc:norm (as the image height is dependent on video norm).
53 * </refsect2>
54 */
55
56 #ifdef HAVE_CONFIG_H
57 #include "config.h"
58 #endif
59
60 #include <gst/glib-compat-private.h>
61 #include "gstmpeg2enc.hh"
62
63 GST_DEBUG_CATEGORY (mpeg2enc_debug);
64
65 #define COMMON_VIDEO_CAPS \
66 "width = (int) [ 16, 4096 ], " \
67 "height = (int) [ 16, 4096 ], " \
68 "framerate = " \
69 " (fraction) { 24000/1001, 24/1, 25/1, 30000/1001, 30/1, 50/1, 60000/1001 }"
70
71 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
72 GST_PAD_SINK,
73 GST_PAD_ALWAYS,
74 GST_STATIC_CAPS ("video/x-raw, format = (string) I420, " COMMON_VIDEO_CAPS)
75 );
76
77 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
78 GST_PAD_SRC,
79 GST_PAD_ALWAYS,
80 GST_STATIC_CAPS ("video/mpeg, "
81 "systemstream = (boolean) false, "
82 "mpegversion = (int) { 1, 2 }, " COMMON_VIDEO_CAPS)
83 );
84
85
86 static void gst_mpeg2enc_finalize (GObject * object);
87 static void gst_mpeg2enc_reset (GstMpeg2enc * enc);
88 static gboolean gst_mpeg2enc_setcaps (GstMpeg2enc * enc, GstPad * pad,
89 GstCaps * caps);
90 static gboolean gst_mpeg2enc_sink_query (GstPad * pad, GstObject * parent,
91 GstQuery * query);
92 static gboolean gst_mpeg2enc_sink_event (GstPad * pad, GstObject * parent,
93 GstEvent * event);
94 static void gst_mpeg2enc_loop (GstMpeg2enc * enc);
95 static GstFlowReturn gst_mpeg2enc_chain (GstPad * pad, GstObject * parent,
96 GstBuffer * buffer);
97 static gboolean gst_mpeg2enc_src_activate_mode (GstPad * pad, GstObject * parent,
98 GstPadMode mode, gboolean active);
99 static GstStateChangeReturn gst_mpeg2enc_change_state (GstElement * element,
100 GstStateChange transition);
101
102 static void gst_mpeg2enc_get_property (GObject * object,
103 guint prop_id, GValue * value, GParamSpec * pspec);
104 static void gst_mpeg2enc_set_property (GObject * object,
105 guint prop_id, const GValue * value, GParamSpec * pspec);
106
107 #define gst_mpeg2enc_parent_class parent_class
108 G_DEFINE_TYPE_WITH_CODE (GstMpeg2enc, gst_mpeg2enc, GST_TYPE_ELEMENT,
109 G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL));
110
111 static void
gst_mpeg2enc_class_init(GstMpeg2encClass * klass)112 gst_mpeg2enc_class_init (GstMpeg2encClass * klass)
113 {
114 GObjectClass *object_class = G_OBJECT_CLASS (klass);
115 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
116
117 GST_DEBUG_CATEGORY_INIT (mpeg2enc_debug, "mpeg2enc", 0, "MPEG1/2 encoder");
118
119 object_class->set_property = gst_mpeg2enc_set_property;
120 object_class->get_property = gst_mpeg2enc_get_property;
121
122 /* register properties */
123 GstMpeg2EncOptions::initProperties (object_class);
124
125 object_class->finalize = GST_DEBUG_FUNCPTR (gst_mpeg2enc_finalize);
126
127 element_class->change_state = GST_DEBUG_FUNCPTR (gst_mpeg2enc_change_state);
128
129 gst_element_class_add_static_pad_template (element_class, &src_template);
130 gst_element_class_add_static_pad_template (element_class, &sink_template);
131
132 gst_element_class_set_static_metadata (element_class,
133 "mpeg2enc video encoder", "Codec/Encoder/Video",
134 "High-quality MPEG-1/2 video encoder",
135 "Andrew Stevens <andrew.stevens@nexgo.de>\n"
136 "Ronald Bultje <rbultje@ronald.bitfreak.net>");
137 }
138
139 static void
gst_mpeg2enc_finalize(GObject * object)140 gst_mpeg2enc_finalize (GObject * object)
141 {
142 GstMpeg2enc *enc = GST_MPEG2ENC (object);
143
144 if (enc->encoder) {
145 delete enc->encoder;
146
147 enc->encoder = NULL;
148 }
149 delete enc->options;
150
151 g_queue_free (enc->time);
152 g_mutex_clear (&enc->tlock);
153 g_cond_clear (&enc->cond);
154
155 G_OBJECT_CLASS (parent_class)->finalize (object);
156 }
157
158 static void
gst_mpeg2enc_init(GstMpeg2enc * enc)159 gst_mpeg2enc_init (GstMpeg2enc * enc)
160 {
161 GstElement *element = GST_ELEMENT (enc);
162
163 enc->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
164 gst_pad_set_query_function (enc->sinkpad,
165 GST_DEBUG_FUNCPTR (gst_mpeg2enc_sink_query));
166 gst_pad_set_event_function (enc->sinkpad,
167 GST_DEBUG_FUNCPTR (gst_mpeg2enc_sink_event));
168 gst_pad_set_chain_function (enc->sinkpad,
169 GST_DEBUG_FUNCPTR (gst_mpeg2enc_chain));
170 gst_element_add_pad (element, enc->sinkpad);
171
172 enc->srcpad = gst_pad_new_from_static_template (&src_template, "src");
173 gst_pad_use_fixed_caps (enc->srcpad);
174 gst_pad_set_activatemode_function (enc->srcpad,
175 GST_DEBUG_FUNCPTR (gst_mpeg2enc_src_activate_mode));
176 gst_element_add_pad (element, enc->srcpad);
177
178 enc->options = new GstMpeg2EncOptions ();
179 enc->encoder = NULL;
180
181 enc->buffer = NULL;
182 g_mutex_init (&enc->tlock);
183 g_cond_init (&enc->cond);
184 enc->time = g_queue_new ();
185
186 gst_mpeg2enc_reset (enc);
187 }
188
189 static void
gst_mpeg2enc_reset(GstMpeg2enc * enc)190 gst_mpeg2enc_reset (GstMpeg2enc * enc)
191 {
192 GstBuffer *buf;
193
194 enc->eos = FALSE;
195 enc->srcresult = GST_FLOW_OK;
196
197 /* in case of error'ed ending */
198 if (enc->buffer)
199 gst_buffer_unref (enc->buffer);
200 enc->buffer = NULL;
201 while ((buf = (GstBuffer *) g_queue_pop_head (enc->time)))
202 gst_buffer_unref (buf);
203
204 if (enc->encoder) {
205 delete enc->encoder;
206
207 enc->encoder = NULL;
208 }
209 }
210
211 /* some (!) coding to get caps depending on the video norm and chosen format */
212 static void
gst_mpeg2enc_add_fps(GstStructure * structure,gint fpss[])213 gst_mpeg2enc_add_fps (GstStructure * structure, gint fpss[])
214 {
215 GValue list = { 0, }, fps = {
216 0,};
217 guint n;
218
219 g_value_init (&list, GST_TYPE_LIST);
220 g_value_init (&fps, GST_TYPE_FRACTION);
221 for (n = 0; fpss[n] != 0; n += 2) {
222 gst_value_set_fraction (&fps, fpss[n], fpss[n + 1]);
223 gst_value_list_append_value (&list, &fps);
224 }
225 gst_structure_set_value (structure, "framerate", &list);
226 g_value_unset (&list);
227 g_value_unset (&fps);
228 }
229
230 static inline gint *
gst_mpeg2enc_get_fps(GstMpeg2enc * enc)231 gst_mpeg2enc_get_fps (GstMpeg2enc * enc)
232 {
233 static gint fps_pal[]
234 = { 24, 1, 25, 1, 50, 1, 0 };
235 static gint fps_ntsc[]
236 = { 24000, 1001, 24, 1, 30000, 1001, 30, 1, 60000, 1001, 0 };
237 static gint fps_all[]
238 = { 24000, 1001, 24, 1, 30000, 1001, 30, 1, 60000, 1001, 25, 1, 50, 1, 0 };
239
240 if (enc->options->norm == 'n')
241 return fps_ntsc;
242 else if (enc->options->norm == 0)
243 return fps_all;
244 else
245 return fps_pal;
246 }
247
248 static GstStructure *
gst_mpeg2enc_structure_from_norm(GstMpeg2enc * enc,gint horiz,gint pal_v,gint ntsc_v)249 gst_mpeg2enc_structure_from_norm (GstMpeg2enc * enc, gint horiz,
250 gint pal_v, gint ntsc_v)
251 {
252 GstStructure *structure;
253
254 structure = gst_structure_new ("video/x-raw",
255 "format", G_TYPE_STRING, "I420", NULL);
256
257 switch (enc->options->norm) {
258 case 0:
259 {
260 GValue list = { 0, }
261 , val = {
262 0,};
263
264 g_value_init (&list, GST_TYPE_LIST);
265 g_value_init (&val, G_TYPE_INT);
266 g_value_set_int (&val, pal_v);
267 gst_value_list_append_value (&list, &val);
268 g_value_set_int (&val, ntsc_v);
269 gst_value_list_append_value (&list, &val);
270 gst_structure_set_value (structure, "height", &list);
271 g_value_unset (&list);
272 g_value_unset (&val);
273 break;
274 }
275 case 'n':
276 gst_structure_set (structure, "height", G_TYPE_INT, ntsc_v,
277 (void *) NULL);
278 break;
279 default:
280 gst_structure_set (structure, "height", G_TYPE_INT, pal_v, (void *) NULL);
281 break;
282 }
283 gst_structure_set (structure, "width", G_TYPE_INT, horiz, (void *) NULL);
284 gst_mpeg2enc_add_fps (structure, gst_mpeg2enc_get_fps (enc));
285
286 return structure;
287 }
288
289 static GstCaps *
gst_mpeg2enc_getcaps(GstMpeg2enc * enc,GstPad * pad)290 gst_mpeg2enc_getcaps (GstMpeg2enc * enc, GstPad * pad)
291 {
292 GstCaps *caps;
293
294 caps = gst_pad_get_current_caps (pad);
295 if (caps) {
296 gst_caps_ref (caps);
297 return caps;
298 }
299
300 switch (enc->options->format) {
301 case 1: /* vcd */
302 case 2: /* user vcd */
303 caps = gst_caps_new_full (gst_mpeg2enc_structure_from_norm (enc,
304 352, 288, 240), NULL);
305 break;
306 case 4: /* svcd */
307 case 5: /* user svcd */
308 caps = gst_caps_new_full (gst_mpeg2enc_structure_from_norm (enc,
309 480, 576, 480), NULL);
310 break;
311 case 6: /* vcd stills */
312 /* low resolution */
313 caps = gst_caps_new_full (gst_mpeg2enc_structure_from_norm (enc,
314 352, 288, 240), NULL);
315 /* high resolution */
316 gst_caps_append_structure (caps,
317 gst_mpeg2enc_structure_from_norm (enc, 704, 576, 480));
318 break;
319 case 7: /* svcd stills */
320 /* low resolution */
321 caps = gst_caps_new_full (gst_mpeg2enc_structure_from_norm (enc,
322 480, 576, 480), NULL);
323 /* high resolution */
324 gst_caps_append_structure (caps,
325 gst_mpeg2enc_structure_from_norm (enc, 704, 576, 480));
326 break;
327 case 0:
328 case 3:
329 case 8:
330 case 9:
331 default:
332 caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
333 gst_mpeg2enc_add_fps (gst_caps_get_structure (caps, 0),
334 gst_mpeg2enc_get_fps (enc));
335 break;
336 }
337
338 GST_DEBUG_OBJECT (enc, "returned caps %" GST_PTR_FORMAT, caps);
339 return caps;
340 }
341
342 static gboolean
gst_mpeg2enc_sink_query(GstPad * pad,GstObject * parent,GstQuery * query)343 gst_mpeg2enc_sink_query (GstPad * pad, GstObject * parent,
344 GstQuery * query)
345 {
346 GstMpeg2enc *enc;
347 gboolean res = FALSE;
348
349 enc = GST_MPEG2ENC (parent);
350
351 switch (GST_QUERY_TYPE (query)) {
352 case GST_QUERY_CAPS:
353 {
354 GstCaps *filter, *caps;
355
356 gst_query_parse_caps (query, &filter);
357 caps = gst_mpeg2enc_getcaps (enc, pad);
358 gst_query_set_caps_result (query, caps);
359 gst_caps_unref (caps);
360 res = TRUE;
361 }
362 break;
363 default:
364 res = gst_pad_query_default (pad, parent, query);
365 break;
366 }
367
368 return res;
369 }
370
371 static gboolean
gst_mpeg2enc_setcaps(GstMpeg2enc * enc,GstPad * pad,GstCaps * caps)372 gst_mpeg2enc_setcaps (GstMpeg2enc * enc, GstPad * pad, GstCaps * caps)
373 {
374 GstCaps *othercaps = NULL;
375 gboolean ret;
376
377 /* does not go well to restart stream mid-way */
378 if (enc->encoder)
379 goto refuse_renegotiation;
380
381 /* since mpeg encoder does not really check, let's check caps */
382 if (!gst_video_info_from_caps (&enc->vinfo, caps))
383 goto refuse_caps;
384
385 if (GST_VIDEO_INFO_FORMAT (&enc->vinfo) != GST_VIDEO_FORMAT_I420)
386 goto refuse_caps;
387
388 /* create new encoder with these settings */
389 enc->encoder = new GstMpeg2Encoder (enc->options, GST_ELEMENT (enc), caps);
390
391 if (!enc->encoder->setup ())
392 goto refuse_caps;
393
394 /* and set caps on other side, which should accept anyway */
395 othercaps = enc->encoder->getFormat ();
396 ret = gst_pad_set_caps (enc->srcpad, othercaps);
397 gst_caps_unref (othercaps);
398 othercaps = NULL;
399 if (!ret)
400 goto refuse_caps;
401
402 /* now that we have all the setup and buffers are expected incoming;
403 * task can get going */
404 gst_pad_start_task (enc->srcpad, (GstTaskFunction) gst_mpeg2enc_loop, enc, NULL);
405
406 return TRUE;
407
408 refuse_caps:
409 {
410 GST_WARNING_OBJECT (enc, "refused caps %" GST_PTR_FORMAT, caps);
411
412 if (othercaps)
413 gst_caps_unref (othercaps);
414
415 if (enc->encoder) {
416 delete enc->encoder;
417
418 enc->encoder = NULL;
419 }
420
421 return FALSE;
422 }
423 refuse_renegotiation:
424 {
425 GST_WARNING_OBJECT (enc, "refused renegotiation (to %" GST_PTR_FORMAT ")",
426 caps);
427
428 return FALSE;
429 }
430 }
431
432 static gboolean
gst_mpeg2enc_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)433 gst_mpeg2enc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
434 {
435 GstMpeg2enc *enc;
436 gboolean result = TRUE;
437
438 enc = GST_MPEG2ENC (parent);
439
440 switch (GST_EVENT_TYPE (event)) {
441 case GST_EVENT_FLUSH_START:
442 /* forward event */
443 result = gst_pad_push_event (enc->srcpad, event);
444
445 /* no special action as there is not much to flush;
446 * neither is it possible to halt the mpeg encoding loop */
447 goto done;
448 case GST_EVENT_FLUSH_STOP:
449 /* forward event */
450 result = gst_pad_push_event (enc->srcpad, event);
451 if (!result)
452 goto done;
453
454 /* this clears the error state in case of a failure in encoding task;
455 * so chain function can carry on again */
456 GST_MPEG2ENC_MUTEX_LOCK (enc);
457 enc->srcresult = GST_FLOW_OK;
458 GST_MPEG2ENC_MUTEX_UNLOCK (enc);
459 goto done;
460 case GST_EVENT_EOS:
461 /* inform the encoding task that it can stop now */
462 GST_MPEG2ENC_MUTEX_LOCK (enc);
463 enc->eos = TRUE;
464 GST_MPEG2ENC_SIGNAL (enc);
465 GST_MPEG2ENC_MUTEX_UNLOCK (enc);
466
467 /* eat this event for now, task will send eos when finished */
468 gst_event_unref (event);
469 goto done;
470 case GST_EVENT_CAPS:
471 {
472 GstCaps *caps;
473
474 gst_event_parse_caps (event, &caps);
475 result = gst_mpeg2enc_setcaps (enc, pad, caps);
476 gst_event_unref (event);
477 goto done;
478 }
479 default:
480 /* for a serialized event, wait until an earlier buffer is gone,
481 * though this is no guarantee as to when the encoder is done with it */
482 if (GST_EVENT_IS_SERIALIZED (event)) {
483 GST_MPEG2ENC_MUTEX_LOCK (enc);
484 while (enc->buffer)
485 GST_MPEG2ENC_WAIT (enc);
486 GST_MPEG2ENC_MUTEX_UNLOCK (enc);
487 }
488 break;
489 }
490
491 result = gst_pad_push_event (enc->srcpad, event);
492
493 done:
494 return result;
495 }
496
497 static void
gst_mpeg2enc_loop(GstMpeg2enc * enc)498 gst_mpeg2enc_loop (GstMpeg2enc * enc)
499 {
500 /* do not try to resume or start when output problems;
501 * also ensures a proper (forced) state change */
502 if (enc->srcresult != GST_FLOW_OK)
503 goto ignore;
504
505 if (enc->encoder) {
506 /* note that init performs a pre-fill and therefore needs buffers */
507 enc->encoder->init ();
508 /* task will stay in here during all of the encoding */
509 enc->encoder->encode ();
510
511 /* if not well and truly eos, something strange happened */
512 if (!enc->eos) {
513 GST_ERROR_OBJECT (enc, "encoding task ended without being eos");
514 /* notify the chain function that it's over */
515 GST_MPEG2ENC_MUTEX_LOCK (enc);
516 enc->srcresult = GST_FLOW_ERROR;
517 GST_MPEG2ENC_SIGNAL (enc);
518 GST_MPEG2ENC_MUTEX_UNLOCK (enc);
519 } else {
520 /* send eos if this was not a forced stop or other problem */
521 if (enc->srcresult == GST_FLOW_OK)
522 gst_pad_push_event (enc->srcpad, gst_event_new_eos ());
523 goto eos;
524 }
525 } else {
526 GST_WARNING_OBJECT (enc, "task started without Mpeg2Encoder");
527 }
528
529 /* fall-through */
530 done:
531 {
532 /* no need to run wildly, stopped elsewhere, e.g. state change */
533 GST_DEBUG_OBJECT (enc, "pausing encoding task");
534 gst_pad_pause_task (enc->srcpad);
535
536 return;
537 }
538 eos:
539 {
540 GST_DEBUG_OBJECT (enc, "encoding task reached eos");
541 goto done;
542 }
543 ignore:
544 {
545 GST_DEBUG_OBJECT (enc, "not looping because encoding task encountered %s",
546 gst_flow_get_name (enc->srcresult));
547 goto done;
548 }
549 }
550
551 static GstFlowReturn
gst_mpeg2enc_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)552 gst_mpeg2enc_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
553 {
554 GstMpeg2enc *enc;
555
556 enc = GST_MPEG2ENC (parent);
557
558 if (G_UNLIKELY (!enc->encoder))
559 goto not_negotiated;
560
561 GST_MPEG2ENC_MUTEX_LOCK (enc);
562
563 if (G_UNLIKELY (enc->eos))
564 goto eos;
565
566 if (G_UNLIKELY (enc->srcresult != GST_FLOW_OK))
567 goto ignore;
568
569 /* things look good, now inform the encoding task that a buffer is ready */
570 while (enc->buffer)
571 GST_MPEG2ENC_WAIT (enc);
572 enc->buffer = buffer;
573 g_queue_push_tail (enc->time, gst_buffer_ref (buffer));
574 GST_MPEG2ENC_SIGNAL (enc);
575 GST_MPEG2ENC_MUTEX_UNLOCK (enc);
576
577 /* buffer will be released by task */
578 return GST_FLOW_OK;
579
580 /* special cases */
581 not_negotiated:
582 {
583 GST_ELEMENT_ERROR (enc, CORE, NEGOTIATION, (NULL),
584 ("format wasn't negotiated before chain function"));
585
586 gst_buffer_unref (buffer);
587 return GST_FLOW_NOT_NEGOTIATED;
588 }
589 eos:
590 {
591 GST_DEBUG_OBJECT (enc, "ignoring buffer at end-of-stream");
592 GST_MPEG2ENC_MUTEX_UNLOCK (enc);
593
594 gst_buffer_unref (buffer);
595 return GST_FLOW_EOS;
596 }
597 ignore:
598 {
599 GstFlowReturn ret = enc->srcresult;
600
601 GST_DEBUG_OBJECT (enc,
602 "ignoring buffer because encoding task encountered %s",
603 gst_flow_get_name (enc->srcresult));
604 GST_MPEG2ENC_MUTEX_UNLOCK (enc);
605
606 gst_buffer_unref (buffer);
607 return ret;
608 }
609 }
610
611 static void
gst_mpeg2enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)612 gst_mpeg2enc_get_property (GObject * object,
613 guint prop_id, GValue * value, GParamSpec * pspec)
614 {
615 GST_MPEG2ENC (object)->options->getProperty (prop_id, value);
616 }
617
618 static void
gst_mpeg2enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)619 gst_mpeg2enc_set_property (GObject * object,
620 guint prop_id, const GValue * value, GParamSpec * pspec)
621 {
622 GST_MPEG2ENC (object)->options->setProperty (prop_id, value);
623 }
624
625 static gboolean
gst_mpeg2enc_src_activate_mode(GstPad * pad,GstObject * parent,GstPadMode mode,gboolean active)626 gst_mpeg2enc_src_activate_mode (GstPad * pad, GstObject * parent,
627 GstPadMode mode, gboolean active)
628 {
629 gboolean result = TRUE;
630 GstMpeg2enc *enc;
631
632 enc = GST_MPEG2ENC (parent);
633
634 if (mode != GST_PAD_MODE_PUSH)
635 return FALSE;
636
637 if (active) {
638 /* setcaps will start task once encoder is setup */
639 } else {
640 /* can only end the encoding loop by forcing eos */
641 GST_MPEG2ENC_MUTEX_LOCK (enc);
642 enc->eos = TRUE;
643 enc->srcresult = GST_FLOW_FLUSHING;
644 GST_MPEG2ENC_SIGNAL (enc);
645 GST_MPEG2ENC_MUTEX_UNLOCK (enc);
646
647 /* encoding loop should have ended now and can be joined */
648 result = gst_pad_stop_task (pad);
649 }
650
651 return result;
652 }
653
654 static GstStateChangeReturn
gst_mpeg2enc_change_state(GstElement * element,GstStateChange transition)655 gst_mpeg2enc_change_state (GstElement * element, GstStateChange transition)
656 {
657 GstMpeg2enc *enc = GST_MPEG2ENC (element);
658 GstStateChangeReturn ret;
659
660 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
661 if (ret == GST_STATE_CHANGE_FAILURE)
662 goto done;
663
664 switch (transition) {
665 case GST_STATE_CHANGE_PAUSED_TO_READY:
666 gst_mpeg2enc_reset (enc);
667 break;
668 default:
669 break;
670 }
671
672 done:
673 return ret;
674 }
675
676 #ifndef GST_DISABLE_GST_DEBUG
677
678 static mjpeg_log_handler_t old_handler = NULL;
679
680 /* note that this will affect all mjpegtools elements/threads */
681 static void
gst_mpeg2enc_log_callback(log_level_t level,const char * message)682 gst_mpeg2enc_log_callback (log_level_t level, const char *message)
683 {
684 GstDebugLevel gst_level;
685
686 #if GST_MJPEGTOOLS_API >= 10903
687 static const gint mjpeg_log_error = mjpeg_loglev_t ("error");
688 static const gint mjpeg_log_warn = mjpeg_loglev_t ("warn");
689 static const gint mjpeg_log_info = mjpeg_loglev_t ("info");
690 static const gint mjpeg_log_debug = mjpeg_loglev_t ("debug");
691 #else
692 static const gint mjpeg_log_error = LOG_ERROR;
693 static const gint mjpeg_log_warn = LOG_WARN;
694 static const gint mjpeg_log_info = LOG_INFO;
695 static const gint mjpeg_log_debug = LOG_DEBUG;
696 #endif
697
698 if (level == mjpeg_log_error) {
699 gst_level = GST_LEVEL_ERROR;
700 } else if (level == mjpeg_log_warn) {
701 gst_level = GST_LEVEL_WARNING;
702 } else if (level == mjpeg_log_info) {
703 gst_level = GST_LEVEL_INFO;
704 } else if (level == mjpeg_log_debug) {
705 gst_level = GST_LEVEL_DEBUG;
706 } else {
707 gst_level = GST_LEVEL_INFO;
708 }
709
710 /* message could have a % in it, do not segfault in such case */
711 gst_debug_log (mpeg2enc_debug, gst_level, "", "", 0, NULL, "%s", message);
712
713 /* chain up to the old handler;
714 * this could actually be a handler from another mjpegtools based
715 * plugin; in which case messages can come out double or from
716 * the wrong plugin (element)... */
717 old_handler (level, message);
718 }
719 #endif
720
721 static gboolean
plugin_init(GstPlugin * plugin)722 plugin_init (GstPlugin * plugin)
723 {
724 #ifndef GST_DISABLE_GST_DEBUG
725 old_handler = mjpeg_log_set_handler (gst_mpeg2enc_log_callback);
726 g_assert (old_handler != NULL);
727 #endif
728 /* in any case, we do not want default handler output */
729 mjpeg_default_handler_verbosity (0);
730
731 return gst_element_register (plugin, "mpeg2enc",
732 GST_RANK_MARGINAL, GST_TYPE_MPEG2ENC);
733 }
734
735 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
736 GST_VERSION_MINOR,
737 mpeg2enc,
738 "High-quality MPEG-1/2 video encoder",
739 plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
740