2 * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
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.
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.
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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-theoraenc
22 * @see_also: theoradec, oggmux
24 * This element encodes raw video into a Theora stream.
25 * <ulink url="http://www.theora.org/">Theora</ulink> is a royalty-free
26 * video codec maintained by the <ulink url="http://www.xiph.org/">Xiph.org
27 * Foundation</ulink>, based on the VP3 codec.
29 * The theora codec internally only supports encoding of images that are a
30 * multiple of 16 pixels in both X and Y direction. It is however perfectly
31 * possible to encode images with other dimensions because an arbitrary
32 * rectangular cropping region can be set up. This element will automatically
33 * set up a correct cropping region if the dimensions are not multiples of 16
36 * To control the quality of the encoding, the #GstTheoraEnc::bitrate and
37 * #GstTheoraEnc::quality properties can be used. These two properties are
38 * mutualy exclusive. Setting the bitrate property will produce a constant
39 * bitrate (CBR) stream while setting the quality property will produce a
40 * variable bitrate (VBR) stream.
43 * <title>Example pipeline</title>
45 * gst-launch -v videotestsrc num-buffers=1000 ! theoraenc ! oggmux ! filesink location=videotestsrc.ogg
46 * ]| This example pipeline will encode a test video source to theora muxed in an
47 * ogg container. Refer to the theoradec documentation to decode the create
51 * Last reviewed on 2006-03-01 (0.10.4)
58 #include "gsttheoraenc.h"
61 #include <stdlib.h> /* free */
63 #include <gst/tag/tag.h>
64 #include <gst/video/video.h>
66 #define GST_CAT_DEFAULT theoraenc_debug
67 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
69 #define GST_TYPE_BORDER_MODE (gst_border_mode_get_type())
71 gst_border_mode_get_type (void)
73 static GType border_mode_type = 0;
74 static const GEnumValue border_mode[] = {
75 {BORDER_NONE, "No Border", "none"},
76 {BORDER_BLACK, "Black Border", "black"},
77 {BORDER_MIRROR, "Mirror image in borders", "mirror"},
81 if (!border_mode_type) {
83 g_enum_register_static ("GstTheoraEncBorderMode", border_mode);
85 return border_mode_type;
88 #define GST_TYPE_MULTIPASS_MODE (gst_multipass_mode_get_type())
90 gst_multipass_mode_get_type (void)
92 static GType multipass_mode_type = 0;
93 static const GEnumValue multipass_mode[] = {
94 {MULTIPASS_MODE_SINGLE_PASS, "Single pass", "single-pass"},
95 {MULTIPASS_MODE_FIRST_PASS, "First pass", "first-pass"},
96 {MULTIPASS_MODE_SECOND_PASS, "Second pass", "second-pass"},
100 if (!multipass_mode_type) {
101 multipass_mode_type =
102 g_enum_register_static ("GstTheoraEncMultipassMode", multipass_mode);
104 return multipass_mode_type;
107 /* taken from theora/lib/toplevel.c */
109 _ilog (unsigned int v)
120 #define THEORA_DEF_BITRATE 0
121 #define THEORA_DEF_QUALITY 48
122 #define THEORA_DEF_KEYFRAME_AUTO TRUE
123 #define THEORA_DEF_KEYFRAME_FREQ 64
124 #define THEORA_DEF_KEYFRAME_FREQ_FORCE 64
125 #define THEORA_DEF_SPEEDLEVEL 1
126 #define THEORA_DEF_VP3_COMPATIBLE FALSE
127 #define THEORA_DEF_DROP_FRAMES TRUE
128 #define THEORA_DEF_CAP_OVERFLOW TRUE
129 #define THEORA_DEF_CAP_UNDERFLOW FALSE
130 #define THEORA_DEF_RATE_BUFFER 0
131 #define THEORA_DEF_MULTIPASS_CACHE_FILE NULL
132 #define THEORA_DEF_MULTIPASS_MODE MULTIPASS_MODE_SINGLE_PASS
143 PROP_KEYFRAME_FREQ_FORCE,
144 PROP_KEYFRAME_THRESHOLD,
145 PROP_KEYFRAME_MINDISTANCE,
146 PROP_NOISE_SENSITIVITY,
154 PROP_MULTIPASS_CACHE_FILE,
159 /* this function does a straight granulepos -> timestamp conversion */
161 granulepos_to_timestamp (GstTheoraEnc * theoraenc, ogg_int64_t granulepos)
163 guint64 iframe, pframe;
164 int shift = theoraenc->info.keyframe_granule_shift;
167 return GST_CLOCK_TIME_NONE;
169 iframe = granulepos >> shift;
170 pframe = granulepos - (iframe << shift);
172 /* num and den are 32 bit, so we can safely multiply with GST_SECOND */
173 return gst_util_uint64_scale ((guint64) (iframe + pframe),
174 GST_SECOND * theoraenc->info.fps_denominator,
175 theoraenc->info.fps_numerator);
178 /* Generate a dummy encoder context for use in th_encode_ctl queries
179 Release with th_encode_free()
180 This and the next routine from theora/examples/libtheora_info.c */
182 dummy_encode_ctx (void)
187 /* set the minimal video parameters */
188 th_info_init (&info);
189 info.frame_width = 320;
190 info.frame_height = 240;
191 info.fps_numerator = 1;
192 info.fps_denominator = 1;
194 /* allocate and initialize a context object */
195 ctx = th_encode_alloc (&info);
197 GST_WARNING ("Failed to allocate dummy encoder context.");
199 /* clear the info struct */
200 th_info_clear (&info);
205 /* Query the current and maximum values for the 'speed level' setting.
206 This can be used to ask the encoder to trade off encoding quality
207 vs. performance cost, for example to adapt to realtime constraints. */
209 check_speed_level (th_enc_ctx * ctx, int *current, int *max)
213 /* query the current speed level */
214 ret = th_encode_ctl (ctx, TH_ENCCTL_GET_SPLEVEL, current, sizeof (int));
216 GST_WARNING ("Error %d getting current speed level.", ret);
219 /* query the maximum speed level, which varies by encoder version */
220 ret = th_encode_ctl (ctx, TH_ENCCTL_GET_SPLEVEL_MAX, max, sizeof (int));
222 GST_WARNING ("Error %d getting maximum speed level.", ret);
229 static GstStaticPadTemplate theora_enc_sink_factory =
230 GST_STATIC_PAD_TEMPLATE ("sink",
233 GST_STATIC_CAPS ("video/x-raw-yuv, "
234 "format = (fourcc) { I420, Y42B, Y444 }, "
235 "framerate = (fraction) [1/MAX, MAX], "
236 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
239 static GstStaticPadTemplate theora_enc_src_factory =
240 GST_STATIC_PAD_TEMPLATE ("src",
243 GST_STATIC_CAPS ("video/x-theora")
246 #define gst_theora_enc_parent_class parent_class
247 G_DEFINE_TYPE_WITH_CODE (GstTheoraEnc, gst_theora_enc,
248 GST_TYPE_ELEMENT, G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL));
250 static gboolean theora_enc_sink_event (GstPad * pad, GstEvent * event);
251 static gboolean theora_enc_src_event (GstPad * pad, GstEvent * event);
252 static GstFlowReturn theora_enc_chain (GstPad * pad, GstBuffer * buffer);
253 static GstStateChangeReturn theora_enc_change_state (GstElement * element,
254 GstStateChange transition);
255 static GstCaps *theora_enc_sink_getcaps (GstPad * pad, GstCaps * filter);
256 static gboolean theora_enc_sink_setcaps (GstPad * pad, GstCaps * caps);
257 static void theora_enc_get_property (GObject * object, guint prop_id,
258 GValue * value, GParamSpec * pspec);
259 static void theora_enc_set_property (GObject * object, guint prop_id,
260 const GValue * value, GParamSpec * pspec);
261 static void theora_enc_finalize (GObject * object);
263 static gboolean theora_enc_write_multipass_cache (GstTheoraEnc * enc,
264 gboolean begin, gboolean eos);
267 gst_theora_enc_class_init (GstTheoraEncClass * klass)
269 GObjectClass *gobject_class = (GObjectClass *) klass;
270 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
272 /* query runtime encoder properties */
274 int default_speed_level = THEORA_DEF_SPEEDLEVEL;
275 int max_speed_level = default_speed_level;
277 GST_DEBUG_CATEGORY_INIT (theoraenc_debug, "theoraenc", 0, "Theora encoder");
279 th_ctx = dummy_encode_ctx ();
281 if (!check_speed_level (th_ctx, &default_speed_level, &max_speed_level))
283 ("Failed to determine settings for the speed-level property.");
284 th_encode_free (th_ctx);
287 gobject_class->set_property = theora_enc_set_property;
288 gobject_class->get_property = theora_enc_get_property;
289 gobject_class->finalize = theora_enc_finalize;
291 g_object_class_install_property (gobject_class, PROP_CENTER,
292 g_param_spec_boolean ("center", "Center",
293 "ignored and kept for API compat only", TRUE,
294 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
295 g_object_class_install_property (gobject_class, PROP_BORDER,
296 g_param_spec_enum ("border", "Border",
297 "ignored and kept for API compat only",
298 GST_TYPE_BORDER_MODE, BORDER_BLACK,
299 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
300 /* general encoding stream options */
301 g_object_class_install_property (gobject_class, PROP_BITRATE,
302 g_param_spec_int ("bitrate", "Bitrate", "Compressed video bitrate (kbps)",
303 0, (1 << 24) - 1, THEORA_DEF_BITRATE,
304 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
305 GST_PARAM_MUTABLE_PLAYING));
306 g_object_class_install_property (gobject_class, PROP_QUALITY,
307 g_param_spec_int ("quality", "Quality", "Video quality", 0, 63,
309 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
310 GST_PARAM_MUTABLE_PLAYING));
311 g_object_class_install_property (gobject_class, PROP_QUICK,
312 g_param_spec_boolean ("quick", "Quick",
313 "ignored and kept for API compat only", TRUE,
314 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
315 g_object_class_install_property (gobject_class, PROP_KEYFRAME_AUTO,
316 g_param_spec_boolean ("keyframe-auto", "Keyframe Auto",
317 "Automatic keyframe detection", THEORA_DEF_KEYFRAME_AUTO,
318 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
319 g_object_class_install_property (gobject_class, PROP_KEYFRAME_FREQ,
320 g_param_spec_int ("keyframe-freq", "Keyframe frequency",
321 "Keyframe frequency", 1, 32768, THEORA_DEF_KEYFRAME_FREQ,
322 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
323 g_object_class_install_property (gobject_class, PROP_KEYFRAME_FREQ_FORCE,
324 g_param_spec_int ("keyframe-force", "Keyframe force",
325 "Force keyframe every N frames", 1, 32768,
326 THEORA_DEF_KEYFRAME_FREQ_FORCE,
327 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
328 g_object_class_install_property (gobject_class, PROP_KEYFRAME_THRESHOLD,
329 g_param_spec_int ("keyframe-threshold", "Keyframe threshold",
330 "ignored and kept for API compat only", 0, 32768, 80,
331 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
332 g_object_class_install_property (gobject_class, PROP_KEYFRAME_MINDISTANCE,
333 g_param_spec_int ("keyframe-mindistance", "Keyframe mindistance",
334 "ignored and kept for API compat only", 1, 32768, 8,
335 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
336 g_object_class_install_property (gobject_class, PROP_NOISE_SENSITIVITY,
337 g_param_spec_int ("noise-sensitivity", "Noise sensitivity",
338 "ignored and kept for API compat only", 0, 32768, 1,
339 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
340 g_object_class_install_property (gobject_class, PROP_SHARPNESS,
341 g_param_spec_int ("sharpness", "Sharpness",
342 "ignored and kept for API compat only", 0, 2, 0,
343 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
344 g_object_class_install_property (gobject_class, PROP_SPEEDLEVEL,
345 g_param_spec_int ("speed-level", "Speed level",
346 "Controls the amount of analysis performed when encoding."
347 " Higher values trade compression quality for speed."
348 " This property requires libtheora version >= 1.0"
349 ", and the maximum value may vary based on encoder version.",
350 0, max_speed_level, default_speed_level,
351 (GParamFlags) G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
352 G_PARAM_STATIC_STRINGS));
353 g_object_class_install_property (gobject_class, PROP_VP3_COMPATIBLE,
354 g_param_spec_boolean ("vp3-compatible", "VP3 Compatible",
355 "Disables non-VP3 compatible features",
356 THEORA_DEF_VP3_COMPATIBLE,
357 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
358 g_object_class_install_property (gobject_class, PROP_DROP_FRAMES,
359 g_param_spec_boolean ("drop-frames", "VP3 Compatible",
360 "Allow or disallow frame dropping",
361 THEORA_DEF_DROP_FRAMES,
362 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
363 g_object_class_install_property (gobject_class, PROP_CAP_OVERFLOW,
364 g_param_spec_boolean ("cap-overflow", "VP3 Compatible",
365 "Enable capping of bit reservoir overflows",
366 THEORA_DEF_CAP_OVERFLOW,
367 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
368 g_object_class_install_property (gobject_class, PROP_CAP_UNDERFLOW,
369 g_param_spec_boolean ("cap-underflow", "VP3 Compatible",
370 "Enable capping of bit reservoir underflows",
371 THEORA_DEF_CAP_UNDERFLOW,
372 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
373 g_object_class_install_property (gobject_class, PROP_RATE_BUFFER,
374 g_param_spec_int ("rate-buffer", "Rate Control Buffer",
375 "Sets the size of the rate control buffer, in units of frames. "
376 "The default value of 0 instructs the encoder to automatically "
377 "select an appropriate value",
378 0, 1000, THEORA_DEF_RATE_BUFFER,
379 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
380 g_object_class_install_property (gobject_class, PROP_MULTIPASS_CACHE_FILE,
381 g_param_spec_string ("multipass-cache-file", "Multipass Cache File",
382 "Multipass cache file", THEORA_DEF_MULTIPASS_CACHE_FILE,
383 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
384 g_object_class_install_property (gobject_class, PROP_MULTIPASS_MODE,
385 g_param_spec_enum ("multipass-mode", "Multipass mode",
386 "Single pass or first/second pass", GST_TYPE_MULTIPASS_MODE,
387 THEORA_DEF_MULTIPASS_MODE,
388 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
390 gst_element_class_add_pad_template (gstelement_class,
391 gst_static_pad_template_get (&theora_enc_src_factory));
392 gst_element_class_add_pad_template (gstelement_class,
393 gst_static_pad_template_get (&theora_enc_sink_factory));
394 gst_element_class_set_details_simple (gstelement_class,
395 "Theora video encoder", "Codec/Encoder/Video",
396 "encode raw YUV video to a theora stream",
397 "Wim Taymans <wim@fluendo.com>");
399 gstelement_class->change_state = theora_enc_change_state;
403 gst_theora_enc_init (GstTheoraEnc * enc)
406 gst_pad_new_from_static_template (&theora_enc_sink_factory, "sink");
407 gst_pad_set_chain_function (enc->sinkpad, theora_enc_chain);
408 gst_pad_set_event_function (enc->sinkpad, theora_enc_sink_event);
409 gst_pad_set_getcaps_function (enc->sinkpad, theora_enc_sink_getcaps);
410 gst_pad_set_setcaps_function (enc->sinkpad, theora_enc_sink_setcaps);
411 gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad);
414 gst_pad_new_from_static_template (&theora_enc_src_factory, "src");
415 gst_pad_set_event_function (enc->srcpad, theora_enc_src_event);
416 gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad);
418 gst_segment_init (&enc->segment, GST_FORMAT_UNDEFINED);
420 enc->video_bitrate = THEORA_DEF_BITRATE;
421 enc->video_quality = THEORA_DEF_QUALITY;
422 enc->keyframe_auto = THEORA_DEF_KEYFRAME_AUTO;
423 enc->keyframe_freq = THEORA_DEF_KEYFRAME_FREQ;
424 enc->keyframe_force = THEORA_DEF_KEYFRAME_FREQ_FORCE;
426 enc->expected_ts = GST_CLOCK_TIME_NONE;
428 /* enc->speed_level is set to the libtheora default by the constructor */
429 enc->vp3_compatible = THEORA_DEF_VP3_COMPATIBLE;
430 enc->drop_frames = THEORA_DEF_DROP_FRAMES;
431 enc->cap_overflow = THEORA_DEF_CAP_OVERFLOW;
432 enc->cap_underflow = THEORA_DEF_CAP_UNDERFLOW;
433 enc->rate_buffer = THEORA_DEF_RATE_BUFFER;
435 enc->multipass_mode = THEORA_DEF_MULTIPASS_MODE;
436 enc->multipass_cache_file = THEORA_DEF_MULTIPASS_CACHE_FILE;
440 theora_enc_clear_multipass_cache (GstTheoraEnc * enc)
442 if (enc->multipass_cache_fd) {
443 g_io_channel_shutdown (enc->multipass_cache_fd, TRUE, NULL);
444 g_io_channel_unref (enc->multipass_cache_fd);
445 enc->multipass_cache_fd = NULL;
448 if (enc->multipass_cache_adapter) {
449 gst_object_unref (enc->multipass_cache_adapter);
450 enc->multipass_cache_adapter = NULL;
455 theora_enc_finalize (GObject * object)
457 GstTheoraEnc *enc = GST_THEORA_ENC (object);
459 GST_DEBUG_OBJECT (enc, "Finalizing");
461 th_encode_free (enc->encoder);
462 th_comment_clear (&enc->comment);
463 th_info_clear (&enc->info);
464 g_free (enc->multipass_cache_file);
466 theora_enc_clear_multipass_cache (enc);
468 G_OBJECT_CLASS (parent_class)->finalize (object);
472 theora_enc_reset (GstTheoraEnc * enc)
474 ogg_uint32_t keyframe_force;
477 GST_OBJECT_LOCK (enc);
478 enc->info.target_bitrate = enc->video_bitrate;
479 enc->info.quality = enc->video_quality;
480 enc->bitrate_changed = FALSE;
481 enc->quality_changed = FALSE;
482 GST_OBJECT_UNLOCK (enc);
485 th_encode_free (enc->encoder);
486 enc->encoder = th_encode_alloc (&enc->info);
487 /* We ensure this function cannot fail. */
488 g_assert (enc->encoder != NULL);
489 th_encode_ctl (enc->encoder, TH_ENCCTL_SET_SPLEVEL, &enc->speed_level,
490 sizeof (enc->speed_level));
491 th_encode_ctl (enc->encoder, TH_ENCCTL_SET_VP3_COMPATIBLE,
492 &enc->vp3_compatible, sizeof (enc->vp3_compatible));
495 if (enc->drop_frames)
496 rate_flags |= TH_RATECTL_DROP_FRAMES;
497 if (enc->drop_frames)
498 rate_flags |= TH_RATECTL_CAP_OVERFLOW;
499 if (enc->drop_frames)
500 rate_flags |= TH_RATECTL_CAP_UNDERFLOW;
501 th_encode_ctl (enc->encoder, TH_ENCCTL_SET_RATE_FLAGS,
502 &rate_flags, sizeof (rate_flags));
504 if (enc->rate_buffer) {
505 th_encode_ctl (enc->encoder, TH_ENCCTL_SET_RATE_BUFFER,
506 &enc->rate_buffer, sizeof (enc->rate_buffer));
511 keyframe_force = enc->keyframe_auto ?
512 enc->keyframe_force : enc->keyframe_freq;
513 th_encode_ctl (enc->encoder, TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
514 &keyframe_force, sizeof (keyframe_force));
516 /* Get placeholder data */
517 if (enc->multipass_cache_fd
518 && enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS)
519 theora_enc_write_multipass_cache (enc, TRUE, FALSE);
523 theora_enc_clear (GstTheoraEnc * enc)
527 enc->granulepos_offset = 0;
528 enc->timestamp_offset = 0;
530 enc->next_ts = GST_CLOCK_TIME_NONE;
531 enc->next_discont = FALSE;
532 enc->expected_ts = GST_CLOCK_TIME_NONE;
536 theora_enc_get_supported_formats (void)
542 th_pixel_fmt pixelformat;
546 TH_PF_420, "I420"}, {
547 TH_PF_422, "Y42B"}, {
550 GString *string = NULL;
553 th_info_init (&info);
554 info.frame_width = 16;
555 info.frame_height = 16;
556 info.fps_numerator = 25;
557 info.fps_denominator = 1;
558 for (i = 0; i < G_N_ELEMENTS (formats); i++) {
559 info.pixel_fmt = formats[i].pixelformat;
561 encoder = th_encode_alloc (&info);
565 GST_LOG ("format %s is supported", formats[i].fourcc);
566 th_encode_free (encoder);
568 if (string == NULL) {
569 string = g_string_new (formats[i].fourcc);
571 g_string_append (string, ", ");
572 g_string_append (string, formats[i].fourcc);
575 th_info_clear (&info);
577 return string == NULL ? NULL : g_string_free (string, FALSE);
581 theora_enc_sink_getcaps (GstPad * pad, GstCaps * filter)
584 char *supported_formats, *caps_string;
586 supported_formats = theora_enc_get_supported_formats ();
587 if (!supported_formats) {
588 GST_WARNING ("no supported formats found. Encoder disabled?");
589 return gst_caps_new_empty ();
592 caps_string = g_strdup_printf ("video/x-raw-yuv, "
593 "format = (fourcc) { %s }, "
594 "framerate = (fraction) [1/MAX, MAX], "
595 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]",
597 caps = gst_caps_from_string (caps_string);
598 g_free (caps_string);
599 g_free (supported_formats);
600 GST_DEBUG ("Supported caps: %" GST_PTR_FORMAT, caps);
603 GstCaps *intersection;
606 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
607 gst_caps_unref (caps);
615 theora_enc_sink_setcaps (GstPad * pad, GstCaps * caps)
617 GstStructure *structure = gst_caps_get_structure (caps, 0);
618 GstTheoraEnc *enc = GST_THEORA_ENC (gst_pad_get_parent (pad));
623 gst_structure_get_fourcc (structure, "format", &fourcc);
624 gst_structure_get_int (structure, "width", &enc->width);
625 gst_structure_get_int (structure, "height", &enc->height);
626 gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d);
627 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
629 th_info_clear (&enc->info);
630 th_info_init (&enc->info);
631 /* Theora has a divisible-by-sixteen restriction for the encoded video size but
632 * we can define a picture area using pic_width/pic_height */
633 enc->info.frame_width = GST_ROUND_UP_16 (enc->width);
634 enc->info.frame_height = GST_ROUND_UP_16 (enc->height);
635 enc->info.pic_width = enc->width;
636 enc->info.pic_height = enc->height;
638 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
639 enc->info.pixel_fmt = TH_PF_420;
641 case GST_MAKE_FOURCC ('Y', '4', '2', 'B'):
642 enc->info.pixel_fmt = TH_PF_422;
644 case GST_MAKE_FOURCC ('Y', '4', '4', '4'):
645 enc->info.pixel_fmt = TH_PF_444;
648 g_assert_not_reached ();
651 enc->info.fps_numerator = enc->fps_n = fps_n;
652 enc->info.fps_denominator = enc->fps_d = fps_d;
654 enc->info.aspect_numerator = gst_value_get_fraction_numerator (par);
655 enc->info.aspect_denominator = gst_value_get_fraction_denominator (par);
657 /* setting them to 0 indicates that the decoder can chose a good aspect
658 * ratio, defaulting to 1/1 */
659 enc->info.aspect_numerator = 0;
660 enc->info.aspect_denominator = 0;
663 enc->info.colorspace = TH_CS_UNSPECIFIED;
665 /* as done in theora */
666 enc->info.keyframe_granule_shift = _ilog (enc->keyframe_force - 1);
667 GST_DEBUG_OBJECT (enc,
668 "keyframe_frequency_force is %d, granule shift is %d",
669 enc->keyframe_force, enc->info.keyframe_granule_shift);
671 theora_enc_reset (enc);
672 enc->initialised = TRUE;
674 gst_object_unref (enc);
680 granulepos_add (guint64 granulepos, guint64 addend, gint shift)
682 guint64 iframe, pframe;
684 iframe = granulepos >> shift;
685 pframe = granulepos - (iframe << shift);
688 return (iframe << shift) + pframe;
691 /* prepare a buffer for transmission by passing data through libtheora */
693 theora_buffer_from_packet (GstTheoraEnc * enc, ogg_packet * packet,
694 GstClockTime timestamp, GstClockTime running_time,
695 GstClockTime duration, GstBuffer ** buffer)
698 GstFlowReturn ret = GST_FLOW_OK;
700 buf = gst_buffer_new_and_alloc (packet->bytes);
702 GST_WARNING_OBJECT (enc, "Could not allocate buffer");
703 ret = GST_FLOW_ERROR;
707 gst_buffer_fill (buf, 0, packet->packet, packet->bytes);
708 /* see ext/ogg/README; OFFSET_END takes "our" granulepos, OFFSET its
709 * time representation */
710 GST_BUFFER_OFFSET_END (buf) =
711 granulepos_add (packet->granulepos, enc->granulepos_offset,
712 enc->info.keyframe_granule_shift);
713 GST_BUFFER_OFFSET (buf) = granulepos_to_timestamp (enc,
714 GST_BUFFER_OFFSET_END (buf));
716 GST_BUFFER_TIMESTAMP (buf) = timestamp;
717 GST_BUFFER_DURATION (buf) = duration;
719 if (enc->next_discont) {
720 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
721 enc->next_discont = FALSE;
724 /* the second most significant bit of the first data byte is cleared
726 if (packet->bytes > 0 && (packet->packet[0] & 0x40) == 0) {
727 GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
729 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
738 /* push out the buffer and do internal bookkeeping */
740 theora_push_buffer (GstTheoraEnc * enc, GstBuffer * buffer)
744 enc->bytes_out += gst_buffer_get_size (buffer);
746 ret = gst_pad_push (enc->srcpad, buffer);
752 theora_push_packet (GstTheoraEnc * enc, ogg_packet * packet,
753 GstClockTime timestamp, GstClockTime running_time, GstClockTime duration)
759 theora_buffer_from_packet (enc, packet, timestamp, running_time, duration,
761 if (ret == GST_FLOW_OK)
762 ret = theora_push_buffer (enc, buf);
768 theora_set_header_on_caps (GstCaps * caps, GSList * buffers)
770 GstStructure *structure;
771 GValue array = { 0 };
772 GValue value = { 0 };
776 caps = gst_caps_make_writable (caps);
777 structure = gst_caps_get_structure (caps, 0);
779 /* put copies of the buffers in a fixed list */
780 g_value_init (&array, GST_TYPE_ARRAY);
782 for (walk = buffers; walk; walk = walk->next) {
786 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_IN_CAPS);
788 /* Copy buffer, because we can't use the original -
789 * it creates a circular refcount with the caps<->buffers */
790 buffer = gst_buffer_copy (buffer);
792 g_value_init (&value, GST_TYPE_BUFFER);
793 gst_value_set_buffer (&value, buffer);
794 gst_value_array_append_value (&array, &value);
795 g_value_unset (&value);
798 gst_buffer_unref (buffer);
801 gst_structure_set_value (structure, "streamheader", &array);
802 g_value_unset (&array);
808 theora_enc_force_keyframe (GstTheoraEnc * enc)
810 GstClockTime next_ts;
812 /* make sure timestamps increment after resetting the decoder */
813 next_ts = enc->next_ts + enc->timestamp_offset;
815 theora_enc_reset (enc);
816 enc->granulepos_offset =
817 gst_util_uint64_scale (next_ts, enc->fps_n, GST_SECOND * enc->fps_d);
818 enc->timestamp_offset = next_ts;
823 theora_enc_sink_event (GstPad * pad, GstEvent * event)
829 enc = GST_THEORA_ENC (GST_PAD_PARENT (pad));
831 switch (GST_EVENT_TYPE (event)) {
832 case GST_EVENT_SEGMENT:
834 gst_event_copy_segment (event, &enc->segment);
836 res = gst_pad_push_event (enc->srcpad, event);
840 if (enc->initialised) {
841 /* push last packet with eos flag, should not be called */
842 while (th_encode_packetout (enc->encoder, 1, &op)) {
843 GstClockTime next_time =
844 th_granule_time (enc->encoder, op.granulepos) * GST_SECOND;
846 theora_push_packet (enc, &op, GST_CLOCK_TIME_NONE, enc->next_ts,
847 next_time - enc->next_ts);
848 enc->next_ts = next_time;
851 if (enc->initialised && enc->multipass_cache_fd
852 && enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS)
853 theora_enc_write_multipass_cache (enc, TRUE, TRUE);
855 theora_enc_clear_multipass_cache (enc);
857 res = gst_pad_push_event (enc->srcpad, event);
859 case GST_EVENT_FLUSH_STOP:
860 gst_segment_init (&enc->segment, GST_FORMAT_UNDEFINED);
861 res = gst_pad_push_event (enc->srcpad, event);
863 case GST_EVENT_CUSTOM_DOWNSTREAM:
865 const GstStructure *s;
867 s = gst_event_get_structure (event);
869 if (gst_structure_has_name (s, "GstForceKeyUnit"))
870 theora_enc_force_keyframe (enc);
871 res = gst_pad_push_event (enc->srcpad, event);
875 res = gst_pad_push_event (enc->srcpad, event);
882 theora_enc_src_event (GstPad * pad, GstEvent * event)
887 enc = GST_THEORA_ENC (GST_PAD_PARENT (pad));
889 switch (GST_EVENT_TYPE (event)) {
890 case GST_EVENT_CUSTOM_UPSTREAM:
892 const GstStructure *s;
894 s = gst_event_get_structure (event);
896 if (gst_structure_has_name (s, "GstForceKeyUnit")) {
897 GST_OBJECT_LOCK (enc);
898 enc->force_keyframe = TRUE;
899 GST_OBJECT_UNLOCK (enc);
900 /* consume the event */
902 gst_event_unref (event);
904 res = gst_pad_push_event (enc->sinkpad, event);
909 res = gst_pad_push_event (enc->sinkpad, event);
917 theora_enc_is_discontinuous (GstTheoraEnc * enc, GstClockTime timestamp,
918 GstClockTime duration)
920 GstClockTimeDiff max_diff;
921 gboolean ret = FALSE;
923 /* Allow 3/4 a frame off */
924 max_diff = (enc->info.fps_denominator * GST_SECOND * 3) /
925 (enc->info.fps_numerator * 4);
927 if (timestamp != GST_CLOCK_TIME_NONE
928 && enc->expected_ts != GST_CLOCK_TIME_NONE) {
929 if ((GstClockTimeDiff) (timestamp - enc->expected_ts) > max_diff) {
930 GST_DEBUG_OBJECT (enc, "Incoming TS %" GST_TIME_FORMAT
931 " exceeds expected value %" GST_TIME_FORMAT
932 " by too much, marking discontinuity",
933 GST_TIME_ARGS (timestamp), GST_TIME_ARGS (enc->expected_ts));
938 if (GST_CLOCK_TIME_IS_VALID (duration))
939 enc->expected_ts = timestamp + duration;
941 enc->expected_ts = GST_CLOCK_TIME_NONE;
947 theora_enc_init_buffer (th_ycbcr_buffer buf, th_info * info, guint8 * data)
949 GstVideoFormat format;
952 switch (info->pixel_fmt) {
954 format = GST_VIDEO_FORMAT_Y444;
957 format = GST_VIDEO_FORMAT_I420;
960 format = GST_VIDEO_FORMAT_Y42B;
963 g_assert_not_reached ();
966 /* According to Theora developer Timothy Terriberry, the Theora
967 * encoder will not use memory outside of pic_width/height, even when
968 * the frame size is bigger. The values outside this region will be encoded
970 * Due to this, setting the frame's width/height as the buffer width/height
971 * is perfectly ok, even though it does not strictly look ok.
973 for (i = 0; i < 3; i++) {
975 gst_video_format_get_component_width (format, i, info->frame_width);
977 gst_video_format_get_component_height (format, i, info->frame_height);
980 data + gst_video_format_get_component_offset (format, i,
981 info->pic_width, info->pic_height);
983 gst_video_format_get_row_stride (format, i, info->pic_width);
988 theora_enc_read_multipass_cache (GstTheoraEnc * enc)
990 GstBuffer *cache_buf;
991 const guint8 *cache_data;
992 gsize bytes_read = 0;
993 gint bytes_consumed = 0;
994 GIOStatus stat = G_IO_STATUS_NORMAL;
995 gboolean done = FALSE;
998 if (gst_adapter_available (enc->multipass_cache_adapter) == 0) {
1002 cache_buf = gst_buffer_new_and_alloc (512);
1004 data = gst_buffer_map (cache_buf, &size, NULL, GST_MAP_READ);
1005 stat = g_io_channel_read_chars (enc->multipass_cache_fd,
1006 (gchar *) data, size, &bytes_read, NULL);
1008 if (bytes_read <= 0) {
1009 gst_buffer_unmap (cache_buf, data, 0);
1010 gst_buffer_unref (cache_buf);
1013 gst_buffer_unmap (cache_buf, data, bytes_read);
1014 gst_adapter_push (enc->multipass_cache_adapter, cache_buf);
1017 if (gst_adapter_available (enc->multipass_cache_adapter) == 0)
1021 MIN (gst_adapter_available (enc->multipass_cache_adapter), 512);
1023 cache_data = gst_adapter_map (enc->multipass_cache_adapter, bytes_read);
1026 th_encode_ctl (enc->encoder, TH_ENCCTL_2PASS_IN, (guint8 *) cache_data,
1028 gst_adapter_unmap (enc->multipass_cache_adapter, 0);
1030 done = bytes_consumed <= 0;
1031 if (bytes_consumed > 0)
1032 gst_adapter_flush (enc->multipass_cache_adapter, bytes_consumed);
1035 if (stat == G_IO_STATUS_ERROR || (stat == G_IO_STATUS_EOF && bytes_read == 0)
1036 || bytes_consumed < 0) {
1037 GST_ELEMENT_ERROR (enc, RESOURCE, READ, (NULL),
1038 ("Failed to read multipass cache file"));
1045 theora_enc_write_multipass_cache (GstTheoraEnc * enc, gboolean begin,
1049 GIOStatus stat = G_IO_STATUS_NORMAL;
1050 gint bytes_read = 0;
1051 gsize bytes_written = 0;
1055 stat = g_io_channel_seek_position (enc->multipass_cache_fd, 0, G_SEEK_SET,
1057 if (stat != G_IO_STATUS_ERROR) {
1060 th_encode_ctl (enc->encoder, TH_ENCCTL_2PASS_OUT, &buf, sizeof (buf));
1062 g_io_channel_write_chars (enc->multipass_cache_fd, buf, bytes_read,
1063 &bytes_written, NULL);
1064 } while (bytes_read > 0 && bytes_written > 0);
1068 if (stat == G_IO_STATUS_ERROR || bytes_read < 0) {
1071 GST_ELEMENT_WARNING (enc, RESOURCE, WRITE, (NULL),
1072 ("Failed to seek to beginning of multipass cache file: %s",
1075 GST_ELEMENT_ERROR (enc, RESOURCE, WRITE, (NULL),
1076 ("Failed to seek to beginning of multipass cache file: %s",
1079 GST_ELEMENT_ERROR (enc, RESOURCE, WRITE, (NULL),
1080 ("Failed to write multipass cache file"));
1090 static GstFlowReturn
1091 theora_enc_chain (GstPad * pad, GstBuffer * buffer)
1095 GstClockTime timestamp, duration, running_time;
1097 gboolean force_keyframe;
1099 enc = GST_THEORA_ENC (GST_PAD_PARENT (pad));
1101 /* we keep track of two timelines.
1102 * - The timestamps from the incomming buffers, which we copy to the outgoing
1103 * encoded buffers as-is. We need to do this as we simply forward the
1104 * newsegment events.
1105 * - The running_time of the buffers, which we use to construct the granulepos
1108 timestamp = GST_BUFFER_TIMESTAMP (buffer);
1109 duration = GST_BUFFER_DURATION (buffer);
1112 gst_segment_to_running_time (&enc->segment, GST_FORMAT_TIME, timestamp);
1113 if ((gint64) running_time < 0) {
1114 GST_DEBUG_OBJECT (enc, "Dropping buffer, timestamp: %" GST_TIME_FORMAT,
1115 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
1116 gst_buffer_unref (buffer);
1120 GST_OBJECT_LOCK (enc);
1121 if (enc->bitrate_changed) {
1122 long int bitrate = enc->video_bitrate;
1124 th_encode_ctl (enc->encoder, TH_ENCCTL_SET_BITRATE, &bitrate,
1126 enc->bitrate_changed = FALSE;
1129 if (enc->quality_changed) {
1130 long int quality = enc->video_quality;
1132 th_encode_ctl (enc->encoder, TH_ENCCTL_SET_QUALITY, &quality,
1134 enc->quality_changed = FALSE;
1137 /* see if we need to schedule a keyframe */
1138 force_keyframe = enc->force_keyframe;
1139 enc->force_keyframe = FALSE;
1140 GST_OBJECT_UNLOCK (enc);
1142 if (force_keyframe) {
1143 GstClockTime stream_time;
1146 stream_time = gst_segment_to_stream_time (&enc->segment,
1147 GST_FORMAT_TIME, timestamp);
1149 s = gst_structure_new ("GstForceKeyUnit",
1150 "timestamp", G_TYPE_UINT64, timestamp,
1151 "stream-time", G_TYPE_UINT64, stream_time,
1152 "running-time", G_TYPE_UINT64, running_time, NULL);
1154 theora_enc_force_keyframe (enc);
1156 gst_pad_push_event (enc->srcpad,
1157 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s));
1160 /* make sure we copy the discont flag to the next outgoing buffer when it's
1161 * set on the incomming buffer */
1162 if (GST_BUFFER_IS_DISCONT (buffer)) {
1163 enc->next_discont = TRUE;
1166 if (enc->packetno == 0) {
1167 /* no packets written yet, setup headers */
1170 GSList *buffers = NULL;
1173 enc->granulepos_offset = 0;
1174 enc->timestamp_offset = 0;
1176 GST_DEBUG_OBJECT (enc, "output headers");
1177 /* Theora streams begin with three headers; the initial header (with
1178 most of the codec setup parameters) which is mandated by the Ogg
1179 bitstream spec. The second header holds any comment fields. The
1180 third header holds the bitstream codebook. We merely need to
1181 make the headers, then pass them to libtheora one at a time;
1182 libtheora handles the additional Ogg bitstream constraints */
1184 /* create the remaining theora headers */
1185 th_comment_clear (&enc->comment);
1186 th_comment_init (&enc->comment);
1189 th_encode_flushheader (enc->encoder, &enc->comment, &op)) > 0) {
1191 theora_buffer_from_packet (enc, &op, GST_CLOCK_TIME_NONE,
1192 GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE, &buf);
1193 if (ret != GST_FLOW_OK) {
1194 goto header_buffer_alloc;
1196 buffers = g_slist_prepend (buffers, buf);
1199 g_slist_foreach (buffers, (GFunc) gst_buffer_unref, NULL);
1200 g_slist_free (buffers);
1201 goto encoder_disabled;
1204 buffers = g_slist_reverse (buffers);
1206 /* mark buffers and put on caps */
1207 caps = gst_pad_get_caps (enc->srcpad, NULL);
1208 caps = theora_set_header_on_caps (caps, buffers);
1209 GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
1210 gst_pad_set_caps (enc->srcpad, caps);
1211 gst_caps_unref (caps);
1213 /* push out the header buffers */
1215 buf = buffers->data;
1216 buffers = g_slist_delete_link (buffers, buffers);
1217 if ((ret = theora_push_buffer (enc, buf)) != GST_FLOW_OK) {
1218 g_slist_foreach (buffers, (GFunc) gst_buffer_unref, NULL);
1219 g_slist_free (buffers);
1224 enc->granulepos_offset =
1225 gst_util_uint64_scale (running_time, enc->fps_n,
1226 GST_SECOND * enc->fps_d);
1227 enc->timestamp_offset = running_time;
1232 th_ycbcr_buffer ycbcr;
1237 data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
1238 theora_enc_init_buffer (ycbcr, &enc->info, data);
1240 if (theora_enc_is_discontinuous (enc, running_time, duration)) {
1241 theora_enc_reset (enc);
1242 enc->granulepos_offset =
1243 gst_util_uint64_scale (running_time, enc->fps_n,
1244 GST_SECOND * enc->fps_d);
1245 enc->timestamp_offset = running_time;
1247 enc->next_discont = TRUE;
1250 if (enc->multipass_cache_fd
1251 && enc->multipass_mode == MULTIPASS_MODE_SECOND_PASS) {
1252 if (!theora_enc_read_multipass_cache (enc)) {
1253 gst_buffer_unmap (buffer, data, size);
1254 ret = GST_FLOW_ERROR;
1255 goto multipass_read_failed;
1259 res = th_encode_ycbcr_in (enc->encoder, ycbcr);
1260 /* none of the failure cases can happen here */
1261 g_assert (res == 0);
1263 if (enc->multipass_cache_fd
1264 && enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS) {
1265 if (!theora_enc_write_multipass_cache (enc, FALSE, FALSE)) {
1266 gst_buffer_unmap (buffer, data, size);
1267 ret = GST_FLOW_ERROR;
1268 goto multipass_write_failed;
1273 while (th_encode_packetout (enc->encoder, 0, &op)) {
1274 GstClockTime next_time;
1276 next_time = th_granule_time (enc->encoder, op.granulepos) * GST_SECOND;
1279 theora_push_packet (enc, &op, timestamp, enc->next_ts,
1280 next_time - enc->next_ts);
1282 enc->next_ts = next_time;
1283 if (ret != GST_FLOW_OK) {
1284 gst_buffer_unmap (buffer, data, size);
1288 gst_buffer_unmap (buffer, data, size);
1289 gst_buffer_unref (buffer);
1295 multipass_read_failed:
1297 gst_buffer_unref (buffer);
1300 multipass_write_failed:
1302 gst_buffer_unref (buffer);
1305 header_buffer_alloc:
1307 gst_buffer_unref (buffer);
1312 gst_buffer_unref (buffer);
1317 gst_buffer_unref (buffer);
1322 GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL),
1323 ("libtheora has been compiled with the encoder disabled"));
1324 gst_buffer_unref (buffer);
1325 return GST_FLOW_ERROR;
1329 static GstStateChangeReturn
1330 theora_enc_change_state (GstElement * element, GstStateChange transition)
1333 GstStateChangeReturn ret;
1335 enc = GST_THEORA_ENC (element);
1337 switch (transition) {
1338 case GST_STATE_CHANGE_NULL_TO_READY:
1340 case GST_STATE_CHANGE_READY_TO_PAUSED:
1341 GST_DEBUG_OBJECT (enc, "READY->PAUSED Initing theora state");
1342 th_info_init (&enc->info);
1343 th_comment_init (&enc->comment);
1345 enc->force_keyframe = FALSE;
1347 if (enc->multipass_mode >= MULTIPASS_MODE_FIRST_PASS) {
1350 if (!enc->multipass_cache_file) {
1351 ret = GST_STATE_CHANGE_FAILURE;
1352 GST_ELEMENT_ERROR (enc, LIBRARY, SETTINGS, (NULL), (NULL));
1355 enc->multipass_cache_fd =
1356 g_io_channel_new_file (enc->multipass_cache_file,
1357 (enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS ? "w" : "r"),
1360 if (enc->multipass_mode == MULTIPASS_MODE_SECOND_PASS)
1361 enc->multipass_cache_adapter = gst_adapter_new ();
1363 if (!enc->multipass_cache_fd) {
1364 ret = GST_STATE_CHANGE_FAILURE;
1365 GST_ELEMENT_ERROR (enc, RESOURCE, OPEN_READ, (NULL),
1366 ("Failed to open multipass cache file: %s", err->message));
1371 g_io_channel_set_encoding (enc->multipass_cache_fd, NULL, NULL);
1374 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1380 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1382 switch (transition) {
1383 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1385 case GST_STATE_CHANGE_PAUSED_TO_READY:
1386 GST_DEBUG_OBJECT (enc, "PAUSED->READY Clearing theora state");
1388 th_encode_free (enc->encoder);
1389 enc->encoder = NULL;
1391 th_comment_clear (&enc->comment);
1392 th_info_clear (&enc->info);
1394 theora_enc_clear (enc);
1395 enc->initialised = FALSE;
1397 case GST_STATE_CHANGE_READY_TO_NULL:
1407 theora_enc_set_property (GObject * object, guint prop_id,
1408 const GValue * value, GParamSpec * pspec)
1410 GstTheoraEnc *enc = GST_THEORA_ENC (object);
1416 case PROP_KEYFRAME_THRESHOLD:
1417 case PROP_KEYFRAME_MINDISTANCE:
1418 case PROP_NOISE_SENSITIVITY:
1419 case PROP_SHARPNESS:
1420 /* kept for API compat, but ignored */
1423 GST_OBJECT_LOCK (enc);
1424 enc->video_bitrate = g_value_get_int (value) * 1000;
1425 enc->bitrate_changed = TRUE;
1426 GST_OBJECT_UNLOCK (enc);
1429 GST_OBJECT_LOCK (enc);
1430 if (GST_STATE (enc) >= GST_STATE_PAUSED && enc->video_bitrate > 0) {
1431 GST_WARNING_OBJECT (object, "Can't change from bitrate to quality mode"
1434 enc->video_quality = g_value_get_int (value);
1435 enc->video_bitrate = 0;
1436 enc->quality_changed = TRUE;
1438 GST_OBJECT_UNLOCK (enc);
1440 case PROP_KEYFRAME_AUTO:
1441 enc->keyframe_auto = g_value_get_boolean (value);
1443 case PROP_KEYFRAME_FREQ:
1444 enc->keyframe_freq = g_value_get_int (value);
1446 case PROP_KEYFRAME_FREQ_FORCE:
1447 enc->keyframe_force = g_value_get_int (value);
1449 case PROP_SPEEDLEVEL:
1450 enc->speed_level = g_value_get_int (value);
1452 th_encode_ctl (enc->encoder, TH_ENCCTL_SET_SPLEVEL, &enc->speed_level,
1453 sizeof (enc->speed_level));
1456 case PROP_VP3_COMPATIBLE:
1457 enc->vp3_compatible = g_value_get_boolean (value);
1459 case PROP_DROP_FRAMES:
1460 enc->drop_frames = g_value_get_boolean (value);
1462 case PROP_CAP_OVERFLOW:
1463 enc->cap_overflow = g_value_get_boolean (value);
1465 case PROP_CAP_UNDERFLOW:
1466 enc->cap_underflow = g_value_get_boolean (value);
1468 case PROP_RATE_BUFFER:
1469 enc->rate_buffer = g_value_get_int (value);
1471 case PROP_MULTIPASS_CACHE_FILE:
1472 enc->multipass_cache_file = g_value_dup_string (value);
1474 case PROP_MULTIPASS_MODE:
1475 enc->multipass_mode = g_value_get_enum (value);
1478 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1484 theora_enc_get_property (GObject * object, guint prop_id,
1485 GValue * value, GParamSpec * pspec)
1487 GstTheoraEnc *enc = GST_THEORA_ENC (object);
1491 g_value_set_boolean (value, TRUE);
1494 g_value_set_enum (value, BORDER_BLACK);
1497 GST_OBJECT_LOCK (enc);
1498 g_value_set_int (value, enc->video_bitrate / 1000);
1499 GST_OBJECT_UNLOCK (enc);
1502 GST_OBJECT_LOCK (enc);
1503 g_value_set_int (value, enc->video_quality);
1504 GST_OBJECT_UNLOCK (enc);
1507 g_value_set_boolean (value, TRUE);
1509 case PROP_KEYFRAME_AUTO:
1510 g_value_set_boolean (value, enc->keyframe_auto);
1512 case PROP_KEYFRAME_FREQ:
1513 g_value_set_int (value, enc->keyframe_freq);
1515 case PROP_KEYFRAME_FREQ_FORCE:
1516 g_value_set_int (value, enc->keyframe_force);
1518 case PROP_KEYFRAME_THRESHOLD:
1519 g_value_set_int (value, 80);
1521 case PROP_KEYFRAME_MINDISTANCE:
1522 g_value_set_int (value, 8);
1524 case PROP_NOISE_SENSITIVITY:
1525 g_value_set_int (value, 1);
1527 case PROP_SHARPNESS:
1528 g_value_set_int (value, 0);
1530 case PROP_SPEEDLEVEL:
1531 g_value_set_int (value, enc->speed_level);
1533 case PROP_VP3_COMPATIBLE:
1534 g_value_set_boolean (value, enc->vp3_compatible);
1536 case PROP_DROP_FRAMES:
1537 g_value_set_boolean (value, enc->drop_frames);
1539 case PROP_CAP_OVERFLOW:
1540 g_value_set_boolean (value, enc->cap_overflow);
1542 case PROP_CAP_UNDERFLOW:
1543 g_value_set_boolean (value, enc->cap_underflow);
1545 case PROP_RATE_BUFFER:
1546 g_value_set_int (value, enc->rate_buffer);
1548 case PROP_MULTIPASS_CACHE_FILE:
1549 g_value_set_string (value, enc->multipass_cache_file);
1551 case PROP_MULTIPASS_MODE:
1552 g_value_set_enum (value, enc->multipass_mode);
1555 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);