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_parse_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, bytes_consumed = 0;
993 GIOStatus stat = G_IO_STATUS_NORMAL;
994 gboolean done = FALSE;
997 if (gst_adapter_available (enc->multipass_cache_adapter) == 0) {
1001 cache_buf = gst_buffer_new_and_alloc (512);
1003 data = gst_buffer_map (cache_buf, &size, NULL, GST_MAP_READ);
1004 stat = g_io_channel_read_chars (enc->multipass_cache_fd,
1005 (gchar *) data, size, &bytes_read, NULL);
1007 if (bytes_read <= 0) {
1008 gst_buffer_unmap (cache_buf, data, 0);
1009 gst_buffer_unref (cache_buf);
1012 gst_buffer_unmap (cache_buf, data, bytes_read);
1013 gst_adapter_push (enc->multipass_cache_adapter, cache_buf);
1016 if (gst_adapter_available (enc->multipass_cache_adapter) == 0)
1020 MIN (gst_adapter_available (enc->multipass_cache_adapter), 512);
1022 cache_data = gst_adapter_map (enc->multipass_cache_adapter, bytes_read);
1025 th_encode_ctl (enc->encoder, TH_ENCCTL_2PASS_IN, (guint8 *) cache_data,
1027 gst_adapter_unmap (enc->multipass_cache_adapter, 0);
1029 done = bytes_consumed <= 0;
1030 if (bytes_consumed > 0)
1031 gst_adapter_flush (enc->multipass_cache_adapter, bytes_consumed);
1034 if (stat == G_IO_STATUS_ERROR || (stat == G_IO_STATUS_EOF && bytes_read == 0)
1035 || bytes_consumed < 0) {
1036 GST_ELEMENT_ERROR (enc, RESOURCE, READ, (NULL),
1037 ("Failed to read multipass cache file"));
1044 theora_enc_write_multipass_cache (GstTheoraEnc * enc, gboolean begin,
1048 GIOStatus stat = G_IO_STATUS_NORMAL;
1049 gint bytes_read = 0;
1050 gsize bytes_written = 0;
1054 stat = g_io_channel_seek_position (enc->multipass_cache_fd, 0, G_SEEK_SET,
1056 if (stat != G_IO_STATUS_ERROR) {
1059 th_encode_ctl (enc->encoder, TH_ENCCTL_2PASS_OUT, &buf, sizeof (buf));
1061 g_io_channel_write_chars (enc->multipass_cache_fd, buf, bytes_read,
1062 &bytes_written, NULL);
1063 } while (bytes_read > 0 && bytes_written > 0);
1067 if (stat == G_IO_STATUS_ERROR || bytes_read < 0 || bytes_written < 0) {
1070 GST_ELEMENT_WARNING (enc, RESOURCE, WRITE, (NULL),
1071 ("Failed to seek to beginning of multipass cache file: %s",
1074 GST_ELEMENT_ERROR (enc, RESOURCE, WRITE, (NULL),
1075 ("Failed to seek to beginning of multipass cache file: %s",
1078 GST_ELEMENT_ERROR (enc, RESOURCE, WRITE, (NULL),
1079 ("Failed to write multipass cache file"));
1089 static GstFlowReturn
1090 theora_enc_chain (GstPad * pad, GstBuffer * buffer)
1094 GstClockTime timestamp, duration, running_time;
1096 gboolean force_keyframe;
1098 enc = GST_THEORA_ENC (GST_PAD_PARENT (pad));
1100 /* we keep track of two timelines.
1101 * - The timestamps from the incomming buffers, which we copy to the outgoing
1102 * encoded buffers as-is. We need to do this as we simply forward the
1103 * newsegment events.
1104 * - The running_time of the buffers, which we use to construct the granulepos
1107 timestamp = GST_BUFFER_TIMESTAMP (buffer);
1108 duration = GST_BUFFER_DURATION (buffer);
1111 gst_segment_to_running_time (&enc->segment, GST_FORMAT_TIME, timestamp);
1112 if ((gint64) running_time < 0) {
1113 GST_DEBUG_OBJECT (enc, "Dropping buffer, timestamp: %" GST_TIME_FORMAT,
1114 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
1115 gst_buffer_unref (buffer);
1119 GST_OBJECT_LOCK (enc);
1120 if (enc->bitrate_changed) {
1121 long int bitrate = enc->video_bitrate;
1123 th_encode_ctl (enc->encoder, TH_ENCCTL_SET_BITRATE, &bitrate,
1125 enc->bitrate_changed = FALSE;
1128 if (enc->quality_changed) {
1129 long int quality = enc->video_quality;
1131 th_encode_ctl (enc->encoder, TH_ENCCTL_SET_QUALITY, &quality,
1133 enc->quality_changed = FALSE;
1136 /* see if we need to schedule a keyframe */
1137 force_keyframe = enc->force_keyframe;
1138 enc->force_keyframe = FALSE;
1139 GST_OBJECT_UNLOCK (enc);
1141 if (force_keyframe) {
1142 GstClockTime stream_time;
1145 stream_time = gst_segment_to_stream_time (&enc->segment,
1146 GST_FORMAT_TIME, timestamp);
1148 s = gst_structure_new ("GstForceKeyUnit",
1149 "timestamp", G_TYPE_UINT64, timestamp,
1150 "stream-time", G_TYPE_UINT64, stream_time,
1151 "running-time", G_TYPE_UINT64, running_time, NULL);
1153 theora_enc_force_keyframe (enc);
1155 gst_pad_push_event (enc->srcpad,
1156 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s));
1159 /* make sure we copy the discont flag to the next outgoing buffer when it's
1160 * set on the incomming buffer */
1161 if (GST_BUFFER_IS_DISCONT (buffer)) {
1162 enc->next_discont = TRUE;
1165 if (enc->packetno == 0) {
1166 /* no packets written yet, setup headers */
1169 GSList *buffers = NULL;
1172 enc->granulepos_offset = 0;
1173 enc->timestamp_offset = 0;
1175 GST_DEBUG_OBJECT (enc, "output headers");
1176 /* Theora streams begin with three headers; the initial header (with
1177 most of the codec setup parameters) which is mandated by the Ogg
1178 bitstream spec. The second header holds any comment fields. The
1179 third header holds the bitstream codebook. We merely need to
1180 make the headers, then pass them to libtheora one at a time;
1181 libtheora handles the additional Ogg bitstream constraints */
1183 /* create the remaining theora headers */
1184 th_comment_clear (&enc->comment);
1185 th_comment_init (&enc->comment);
1188 th_encode_flushheader (enc->encoder, &enc->comment, &op)) > 0) {
1190 theora_buffer_from_packet (enc, &op, GST_CLOCK_TIME_NONE,
1191 GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE, &buf);
1192 if (ret != GST_FLOW_OK) {
1193 goto header_buffer_alloc;
1195 buffers = g_slist_prepend (buffers, buf);
1198 g_slist_foreach (buffers, (GFunc) gst_buffer_unref, NULL);
1199 g_slist_free (buffers);
1200 goto encoder_disabled;
1203 buffers = g_slist_reverse (buffers);
1205 /* mark buffers and put on caps */
1206 caps = gst_pad_get_caps (enc->srcpad, NULL);
1207 caps = theora_set_header_on_caps (caps, buffers);
1208 GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
1209 gst_pad_set_caps (enc->srcpad, caps);
1210 gst_caps_unref (caps);
1212 /* push out the header buffers */
1214 buf = buffers->data;
1215 buffers = g_slist_delete_link (buffers, buffers);
1216 if ((ret = theora_push_buffer (enc, buf)) != GST_FLOW_OK) {
1217 g_slist_foreach (buffers, (GFunc) gst_buffer_unref, NULL);
1218 g_slist_free (buffers);
1223 enc->granulepos_offset =
1224 gst_util_uint64_scale (running_time, enc->fps_n,
1225 GST_SECOND * enc->fps_d);
1226 enc->timestamp_offset = running_time;
1231 th_ycbcr_buffer ycbcr;
1236 data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
1237 theora_enc_init_buffer (ycbcr, &enc->info, data);
1239 if (theora_enc_is_discontinuous (enc, running_time, duration)) {
1240 theora_enc_reset (enc);
1241 enc->granulepos_offset =
1242 gst_util_uint64_scale (running_time, enc->fps_n,
1243 GST_SECOND * enc->fps_d);
1244 enc->timestamp_offset = running_time;
1246 enc->next_discont = TRUE;
1249 if (enc->multipass_cache_fd
1250 && enc->multipass_mode == MULTIPASS_MODE_SECOND_PASS) {
1251 if (!theora_enc_read_multipass_cache (enc)) {
1252 gst_buffer_unmap (buffer, data, size);
1253 ret = GST_FLOW_ERROR;
1254 goto multipass_read_failed;
1258 res = th_encode_ycbcr_in (enc->encoder, ycbcr);
1259 /* none of the failure cases can happen here */
1260 g_assert (res == 0);
1262 if (enc->multipass_cache_fd
1263 && enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS) {
1264 if (!theora_enc_write_multipass_cache (enc, FALSE, FALSE)) {
1265 gst_buffer_unmap (buffer, data, size);
1266 ret = GST_FLOW_ERROR;
1267 goto multipass_write_failed;
1272 while (th_encode_packetout (enc->encoder, 0, &op)) {
1273 GstClockTime next_time;
1275 next_time = th_granule_time (enc->encoder, op.granulepos) * GST_SECOND;
1278 theora_push_packet (enc, &op, timestamp, enc->next_ts,
1279 next_time - enc->next_ts);
1281 enc->next_ts = next_time;
1282 if (ret != GST_FLOW_OK) {
1283 gst_buffer_unmap (buffer, data, size);
1287 gst_buffer_unmap (buffer, data, size);
1288 gst_buffer_unref (buffer);
1294 multipass_read_failed:
1296 gst_buffer_unref (buffer);
1299 multipass_write_failed:
1301 gst_buffer_unref (buffer);
1304 header_buffer_alloc:
1306 gst_buffer_unref (buffer);
1311 gst_buffer_unref (buffer);
1316 gst_buffer_unref (buffer);
1321 GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL),
1322 ("libtheora has been compiled with the encoder disabled"));
1323 gst_buffer_unref (buffer);
1324 return GST_FLOW_ERROR;
1328 static GstStateChangeReturn
1329 theora_enc_change_state (GstElement * element, GstStateChange transition)
1332 GstStateChangeReturn ret;
1334 enc = GST_THEORA_ENC (element);
1336 switch (transition) {
1337 case GST_STATE_CHANGE_NULL_TO_READY:
1339 case GST_STATE_CHANGE_READY_TO_PAUSED:
1340 GST_DEBUG_OBJECT (enc, "READY->PAUSED Initing theora state");
1341 th_info_init (&enc->info);
1342 th_comment_init (&enc->comment);
1344 enc->force_keyframe = FALSE;
1346 if (enc->multipass_mode >= MULTIPASS_MODE_FIRST_PASS) {
1349 if (!enc->multipass_cache_file) {
1350 ret = GST_STATE_CHANGE_FAILURE;
1351 GST_ELEMENT_ERROR (enc, LIBRARY, SETTINGS, (NULL), (NULL));
1354 enc->multipass_cache_fd =
1355 g_io_channel_new_file (enc->multipass_cache_file,
1356 (enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS ? "w" : "r"),
1359 if (enc->multipass_mode == MULTIPASS_MODE_SECOND_PASS)
1360 enc->multipass_cache_adapter = gst_adapter_new ();
1362 if (!enc->multipass_cache_fd) {
1363 ret = GST_STATE_CHANGE_FAILURE;
1364 GST_ELEMENT_ERROR (enc, RESOURCE, OPEN_READ, (NULL),
1365 ("Failed to open multipass cache file: %s", err->message));
1370 g_io_channel_set_encoding (enc->multipass_cache_fd, NULL, NULL);
1373 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1379 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1381 switch (transition) {
1382 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1384 case GST_STATE_CHANGE_PAUSED_TO_READY:
1385 GST_DEBUG_OBJECT (enc, "PAUSED->READY Clearing theora state");
1387 th_encode_free (enc->encoder);
1388 enc->encoder = NULL;
1390 th_comment_clear (&enc->comment);
1391 th_info_clear (&enc->info);
1393 theora_enc_clear (enc);
1394 enc->initialised = FALSE;
1396 case GST_STATE_CHANGE_READY_TO_NULL:
1406 theora_enc_set_property (GObject * object, guint prop_id,
1407 const GValue * value, GParamSpec * pspec)
1409 GstTheoraEnc *enc = GST_THEORA_ENC (object);
1415 case PROP_KEYFRAME_THRESHOLD:
1416 case PROP_KEYFRAME_MINDISTANCE:
1417 case PROP_NOISE_SENSITIVITY:
1418 case PROP_SHARPNESS:
1419 /* kept for API compat, but ignored */
1422 GST_OBJECT_LOCK (enc);
1423 enc->video_bitrate = g_value_get_int (value) * 1000;
1424 enc->bitrate_changed = TRUE;
1425 GST_OBJECT_UNLOCK (enc);
1428 GST_OBJECT_LOCK (enc);
1429 if (GST_STATE (enc) >= GST_STATE_PAUSED && enc->video_bitrate > 0) {
1430 GST_WARNING_OBJECT (object, "Can't change from bitrate to quality mode"
1433 enc->video_quality = g_value_get_int (value);
1434 enc->video_bitrate = 0;
1435 enc->quality_changed = TRUE;
1437 GST_OBJECT_UNLOCK (enc);
1439 case PROP_KEYFRAME_AUTO:
1440 enc->keyframe_auto = g_value_get_boolean (value);
1442 case PROP_KEYFRAME_FREQ:
1443 enc->keyframe_freq = g_value_get_int (value);
1445 case PROP_KEYFRAME_FREQ_FORCE:
1446 enc->keyframe_force = g_value_get_int (value);
1448 case PROP_SPEEDLEVEL:
1449 enc->speed_level = g_value_get_int (value);
1451 th_encode_ctl (enc->encoder, TH_ENCCTL_SET_SPLEVEL, &enc->speed_level,
1452 sizeof (enc->speed_level));
1455 case PROP_VP3_COMPATIBLE:
1456 enc->vp3_compatible = g_value_get_boolean (value);
1458 case PROP_DROP_FRAMES:
1459 enc->drop_frames = g_value_get_boolean (value);
1461 case PROP_CAP_OVERFLOW:
1462 enc->cap_overflow = g_value_get_boolean (value);
1464 case PROP_CAP_UNDERFLOW:
1465 enc->cap_underflow = g_value_get_boolean (value);
1467 case PROP_RATE_BUFFER:
1468 enc->rate_buffer = g_value_get_int (value);
1470 case PROP_MULTIPASS_CACHE_FILE:
1471 enc->multipass_cache_file = g_value_dup_string (value);
1473 case PROP_MULTIPASS_MODE:
1474 enc->multipass_mode = g_value_get_enum (value);
1477 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1483 theora_enc_get_property (GObject * object, guint prop_id,
1484 GValue * value, GParamSpec * pspec)
1486 GstTheoraEnc *enc = GST_THEORA_ENC (object);
1490 g_value_set_boolean (value, TRUE);
1493 g_value_set_enum (value, BORDER_BLACK);
1496 GST_OBJECT_LOCK (enc);
1497 g_value_set_int (value, enc->video_bitrate / 1000);
1498 GST_OBJECT_UNLOCK (enc);
1501 GST_OBJECT_LOCK (enc);
1502 g_value_set_int (value, enc->video_quality);
1503 GST_OBJECT_UNLOCK (enc);
1506 g_value_set_boolean (value, TRUE);
1508 case PROP_KEYFRAME_AUTO:
1509 g_value_set_boolean (value, enc->keyframe_auto);
1511 case PROP_KEYFRAME_FREQ:
1512 g_value_set_int (value, enc->keyframe_freq);
1514 case PROP_KEYFRAME_FREQ_FORCE:
1515 g_value_set_int (value, enc->keyframe_force);
1517 case PROP_KEYFRAME_THRESHOLD:
1518 g_value_set_int (value, 80);
1520 case PROP_KEYFRAME_MINDISTANCE:
1521 g_value_set_int (value, 8);
1523 case PROP_NOISE_SENSITIVITY:
1524 g_value_set_int (value, 1);
1526 case PROP_SHARPNESS:
1527 g_value_set_int (value, 0);
1529 case PROP_SPEEDLEVEL:
1530 g_value_set_int (value, enc->speed_level);
1532 case PROP_VP3_COMPATIBLE:
1533 g_value_set_boolean (value, enc->vp3_compatible);
1535 case PROP_DROP_FRAMES:
1536 g_value_set_boolean (value, enc->drop_frames);
1538 case PROP_CAP_OVERFLOW:
1539 g_value_set_boolean (value, enc->cap_overflow);
1541 case PROP_CAP_UNDERFLOW:
1542 g_value_set_boolean (value, enc->cap_underflow);
1544 case PROP_RATE_BUFFER:
1545 g_value_set_int (value, enc->rate_buffer);
1547 case PROP_MULTIPASS_CACHE_FILE:
1548 g_value_set_string (value, enc->multipass_cache_file);
1550 case PROP_MULTIPASS_MODE:
1551 g_value_set_enum (value, enc->multipass_mode);
1554 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);