theora: Split yuv_buffer creation into its own function
[platform/upstream/gstreamer.git] / ext / theora / theoraenc.c
1 /* GStreamer
2  * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-theoraenc
22  * @see_also: theoradec, oggmux
23  *
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.
28  *
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
34  * pixels. The #GstTheoraEnc::border and #GstTheoraEnc::center properties
35  * control how this cropping region will be set up.
36  *
37  * To control the quality of the encoding, the #GstTheoraEnc::bitrate and
38  * #GstTheoraEnc::quality properties can be used. These two properties are
39  * mutualy exclusive. Setting the bitrate property will produce a constant
40  * bitrate (CBR) stream while setting the quality property will produce a
41  * variable bitrate (VBR) stream.
42  *
43  * <refsect2>
44  * <title>Example pipeline</title>
45  * |[
46  * gst-launch -v videotestsrc num-buffers=1000 ! theoraenc ! oggmux ! filesink location=videotestsrc.ogg
47  * ]| This example pipeline will encode a test video source to theora muxed in an
48  * ogg container. Refer to the theoradec documentation to decode the create
49  * stream.
50  * </refsect2>
51  *
52  * Last reviewed on 2006-03-01 (0.10.4)
53  */
54
55 #ifdef HAVE_CONFIG_H
56 #  include "config.h"
57 #endif
58
59 #include "gsttheoraenc.h"
60
61 #include <string.h>
62 #include <stdlib.h>             /* free */
63
64 #include <gst/tag/tag.h>
65
66 #define GST_CAT_DEFAULT theoraenc_debug
67 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
68
69 /* With libtheora-1.0beta1 the granulepos scheme was changed:
70  * where earlier the granulepos refered to the index/beginning
71  * of a frame, it now refers to the end, which matches the use
72  * in vorbis/speex. There don't seem to be defines for the
73  * theora version we're compiling against, so we'll just use
74  * a run-time check for now. See theora_enc_get_ogg_packet_end_time().
75  */
76 static gboolean use_old_granulepos;
77
78 #define GST_TYPE_BORDER_MODE (gst_border_mode_get_type())
79 static GType
80 gst_border_mode_get_type (void)
81 {
82   static GType border_mode_type = 0;
83   static const GEnumValue border_mode[] = {
84     {BORDER_NONE, "No Border", "none"},
85     {BORDER_BLACK, "Black Border", "black"},
86     {BORDER_MIRROR, "Mirror image in borders", "mirror"},
87     {0, NULL, NULL},
88   };
89
90   if (!border_mode_type) {
91     border_mode_type =
92         g_enum_register_static ("GstTheoraEncBorderMode", border_mode);
93   }
94   return border_mode_type;
95 }
96
97 /* taken from theora/lib/toplevel.c */
98 static int
99 _ilog (unsigned int v)
100 {
101   int ret = 0;
102
103   while (v) {
104     ret++;
105     v >>= 1;
106   }
107   return (ret);
108 }
109
110 #define THEORA_DEF_CENTER               TRUE
111 #define THEORA_DEF_BORDER               BORDER_BLACK
112 #define THEORA_DEF_BITRATE              0
113 #define THEORA_DEF_QUALITY              16
114 #define THEORA_DEF_QUICK                TRUE
115 #define THEORA_DEF_KEYFRAME_AUTO        TRUE
116 #define THEORA_DEF_KEYFRAME_FREQ        64
117 #define THEORA_DEF_KEYFRAME_FREQ_FORCE  64
118 #define THEORA_DEF_KEYFRAME_THRESHOLD   80
119 #define THEORA_DEF_KEYFRAME_MINDISTANCE 8
120 #define THEORA_DEF_NOISE_SENSITIVITY    1
121 #define THEORA_DEF_SHARPNESS            0
122 #define THEORA_DEF_SPEEDLEVEL           1
123 enum
124 {
125   ARG_0,
126   ARG_CENTER,
127   ARG_BORDER,
128   ARG_BITRATE,
129   ARG_QUALITY,
130   ARG_QUICK,
131   ARG_KEYFRAME_AUTO,
132   ARG_KEYFRAME_FREQ,
133   ARG_KEYFRAME_FREQ_FORCE,
134   ARG_KEYFRAME_THRESHOLD,
135   ARG_KEYFRAME_MINDISTANCE,
136   ARG_NOISE_SENSITIVITY,
137   ARG_SHARPNESS,
138   ARG_SPEEDLEVEL,
139   /* FILL ME */
140 };
141
142 /* this function does a straight granulepos -> timestamp conversion */
143 static GstClockTime
144 granulepos_to_timestamp (GstTheoraEnc * theoraenc, ogg_int64_t granulepos)
145 {
146   guint64 iframe, pframe;
147   int shift = theoraenc->granule_shift;
148
149   if (granulepos < 0)
150     return GST_CLOCK_TIME_NONE;
151
152   iframe = granulepos >> shift;
153   pframe = granulepos - (iframe << shift);
154
155   /* num and den are 32 bit, so we can safely multiply with GST_SECOND */
156   return gst_util_uint64_scale ((guint64) (iframe + pframe),
157       GST_SECOND * theoraenc->info.fps_denominator,
158       theoraenc->info.fps_numerator);
159 }
160
161 static const GstElementDetails theora_enc_details =
162 GST_ELEMENT_DETAILS ("Theora video encoder",
163     "Codec/Encoder/Video",
164     "encode raw YUV video to a theora stream",
165     "Wim Taymans <wim@fluendo.com>");
166
167 static GstStaticPadTemplate theora_enc_sink_factory =
168 GST_STATIC_PAD_TEMPLATE ("sink",
169     GST_PAD_SINK,
170     GST_PAD_ALWAYS,
171     GST_STATIC_CAPS ("video/x-raw-yuv, "
172         "format = (fourcc) I420, "
173         "framerate = (fraction) [0/1, MAX], "
174         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
175     );
176
177 static GstStaticPadTemplate theora_enc_src_factory =
178 GST_STATIC_PAD_TEMPLATE ("src",
179     GST_PAD_SRC,
180     GST_PAD_ALWAYS,
181     GST_STATIC_CAPS ("video/x-theora")
182     );
183
184 static void
185 _do_init (GType object_type)
186 {
187   const GInterfaceInfo preset_interface_info = {
188     NULL,                       /* interface_init */
189     NULL,                       /* interface_finalize */
190     NULL                        /* interface_data */
191   };
192
193   g_type_add_interface_static (object_type, GST_TYPE_PRESET,
194       &preset_interface_info);
195 }
196
197 GST_BOILERPLATE_FULL (GstTheoraEnc, gst_theora_enc, GstElement,
198     GST_TYPE_ELEMENT, _do_init);
199
200 static gboolean theora_enc_sink_event (GstPad * pad, GstEvent * event);
201 static gboolean theora_enc_src_event (GstPad * pad, GstEvent * event);
202 static GstFlowReturn theora_enc_chain (GstPad * pad, GstBuffer * buffer);
203 static GstStateChangeReturn theora_enc_change_state (GstElement * element,
204     GstStateChange transition);
205 static gboolean theora_enc_sink_setcaps (GstPad * pad, GstCaps * caps);
206 static void theora_enc_get_property (GObject * object, guint prop_id,
207     GValue * value, GParamSpec * pspec);
208 static void theora_enc_set_property (GObject * object, guint prop_id,
209     const GValue * value, GParamSpec * pspec);
210 static void theora_enc_finalize (GObject * object);
211
212 static void
213 gst_theora_enc_base_init (gpointer g_class)
214 {
215   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
216
217   gst_element_class_add_pad_template (element_class,
218       gst_static_pad_template_get (&theora_enc_src_factory));
219   gst_element_class_add_pad_template (element_class,
220       gst_static_pad_template_get (&theora_enc_sink_factory));
221   gst_element_class_set_details (element_class, &theora_enc_details);
222 }
223
224 static void
225 gst_theora_enc_class_init (GstTheoraEncClass * klass)
226 {
227   GObjectClass *gobject_class = (GObjectClass *) klass;
228   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
229
230   gobject_class->set_property = theora_enc_set_property;
231   gobject_class->get_property = theora_enc_get_property;
232   gobject_class->finalize = theora_enc_finalize;
233
234   g_object_class_install_property (gobject_class, ARG_CENTER,
235       g_param_spec_boolean ("center", "Center",
236           "Center image when sizes not multiple of 16", THEORA_DEF_CENTER,
237           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
238   g_object_class_install_property (gobject_class, ARG_BORDER,
239       g_param_spec_enum ("border", "Border",
240           "Border color to add when sizes not multiple of 16",
241           GST_TYPE_BORDER_MODE, THEORA_DEF_BORDER,
242           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
243   /* general encoding stream options */
244   g_object_class_install_property (gobject_class, ARG_BITRATE,
245       g_param_spec_int ("bitrate", "Bitrate", "Compressed video bitrate (kbps)",
246           0, (1 << 24) - 1, THEORA_DEF_BITRATE,
247           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
248   g_object_class_install_property (gobject_class, ARG_QUALITY,
249       g_param_spec_int ("quality", "Quality", "Video quality", 0, 63,
250           THEORA_DEF_QUALITY,
251           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
252   g_object_class_install_property (gobject_class, ARG_QUICK,
253       g_param_spec_boolean ("quick", "Quick", "Quick encoding",
254           THEORA_DEF_QUICK,
255           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
256   g_object_class_install_property (gobject_class, ARG_KEYFRAME_AUTO,
257       g_param_spec_boolean ("keyframe-auto", "Keyframe Auto",
258           "Automatic keyframe detection", THEORA_DEF_KEYFRAME_AUTO,
259           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
260   g_object_class_install_property (gobject_class, ARG_KEYFRAME_FREQ,
261       g_param_spec_int ("keyframe-freq", "Keyframe frequency",
262           "Keyframe frequency", 1, 32768, THEORA_DEF_KEYFRAME_FREQ,
263           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
264   g_object_class_install_property (gobject_class, ARG_KEYFRAME_FREQ_FORCE,
265       g_param_spec_int ("keyframe-force", "Keyframe force",
266           "Force keyframe every N frames", 1, 32768,
267           THEORA_DEF_KEYFRAME_FREQ_FORCE,
268           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
269   g_object_class_install_property (gobject_class, ARG_KEYFRAME_THRESHOLD,
270       g_param_spec_int ("keyframe-threshold", "Keyframe threshold",
271           "Keyframe threshold", 0, 32768, THEORA_DEF_KEYFRAME_THRESHOLD,
272           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
273   g_object_class_install_property (gobject_class, ARG_KEYFRAME_MINDISTANCE,
274       g_param_spec_int ("keyframe-mindistance", "Keyframe mindistance",
275           "Keyframe mindistance", 1, 32768, THEORA_DEF_KEYFRAME_MINDISTANCE,
276           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
277   g_object_class_install_property (gobject_class, ARG_NOISE_SENSITIVITY,
278       g_param_spec_int ("noise-sensitivity", "Noise sensitivity",
279           "Noise sensitivity", 0, 32768, THEORA_DEF_NOISE_SENSITIVITY,
280           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
281   g_object_class_install_property (gobject_class, ARG_SHARPNESS,
282       g_param_spec_int ("sharpness", "Sharpness", "Sharpness", 0, 2,
283           THEORA_DEF_SHARPNESS,
284           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
285   g_object_class_install_property (gobject_class, ARG_SPEEDLEVEL,
286       g_param_spec_int ("speed-level", "Speed level",
287           "Controls the amount of motion vector searching done while "
288           "encoding.  This property requires libtheora version >= 1.0",
289           0, 2, THEORA_DEF_SPEEDLEVEL,
290           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
291
292   gstelement_class->change_state = theora_enc_change_state;
293   GST_DEBUG_CATEGORY_INIT (theoraenc_debug, "theoraenc", 0, "Theora encoder");
294
295   use_old_granulepos = (theora_version_number () <= 0x00030200);
296 }
297
298 static void
299 gst_theora_enc_init (GstTheoraEnc * enc, GstTheoraEncClass * g_class)
300 {
301   enc->sinkpad =
302       gst_pad_new_from_static_template (&theora_enc_sink_factory, "sink");
303   gst_pad_set_chain_function (enc->sinkpad, theora_enc_chain);
304   gst_pad_set_event_function (enc->sinkpad, theora_enc_sink_event);
305   gst_pad_set_setcaps_function (enc->sinkpad, theora_enc_sink_setcaps);
306   gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad);
307
308   enc->srcpad =
309       gst_pad_new_from_static_template (&theora_enc_src_factory, "src");
310   gst_pad_set_event_function (enc->srcpad, theora_enc_src_event);
311   gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad);
312
313   gst_segment_init (&enc->segment, GST_FORMAT_UNDEFINED);
314
315   enc->center = THEORA_DEF_CENTER;
316   enc->border = THEORA_DEF_BORDER;
317
318   enc->video_bitrate = THEORA_DEF_BITRATE;
319   enc->video_quality = THEORA_DEF_QUALITY;
320   enc->quick = THEORA_DEF_QUICK;
321   enc->keyframe_auto = THEORA_DEF_KEYFRAME_AUTO;
322   enc->keyframe_freq = THEORA_DEF_KEYFRAME_FREQ;
323   enc->keyframe_force = THEORA_DEF_KEYFRAME_FREQ_FORCE;
324   enc->keyframe_threshold = THEORA_DEF_KEYFRAME_THRESHOLD;
325   enc->keyframe_mindistance = THEORA_DEF_KEYFRAME_MINDISTANCE;
326   enc->noise_sensitivity = THEORA_DEF_NOISE_SENSITIVITY;
327   enc->sharpness = THEORA_DEF_SHARPNESS;
328
329   enc->granule_shift = _ilog (enc->info.keyframe_frequency_force - 1);
330   GST_DEBUG_OBJECT (enc,
331       "keyframe_frequency_force is %d, granule shift is %d",
332       enc->info.keyframe_frequency_force, enc->granule_shift);
333   enc->expected_ts = GST_CLOCK_TIME_NONE;
334
335   enc->speed_level = THEORA_DEF_SPEEDLEVEL;
336 }
337
338 static void
339 theora_enc_finalize (GObject * object)
340 {
341   GstTheoraEnc *enc = GST_THEORA_ENC (object);
342
343   GST_DEBUG_OBJECT (enc, "Finalizing");
344   theora_clear (&enc->state);
345   theora_comment_clear (&enc->comment);
346   theora_info_clear (&enc->info);
347
348   G_OBJECT_CLASS (parent_class)->finalize (object);
349 }
350
351 static void
352 theora_enc_reset (GstTheoraEnc * enc)
353 {
354   int result;
355
356   theora_clear (&enc->state);
357   result = theora_encode_init (&enc->state, &enc->info);
358   /* We ensure this function cannot fail. */
359   g_assert (result == 0);
360 #ifdef TH_ENCCTL_SET_SPLEVEL
361   theora_control (&enc->state, TH_ENCCTL_SET_SPLEVEL, &enc->speed_level,
362       sizeof (enc->speed_level));
363 #endif
364 }
365
366 static void
367 theora_enc_clear (GstTheoraEnc * enc)
368 {
369   enc->packetno = 0;
370   enc->bytes_out = 0;
371   enc->granulepos_offset = 0;
372   enc->timestamp_offset = 0;
373
374   enc->next_ts = GST_CLOCK_TIME_NONE;
375   enc->next_discont = FALSE;
376   enc->expected_ts = GST_CLOCK_TIME_NONE;
377 }
378
379 static gboolean
380 theora_enc_sink_setcaps (GstPad * pad, GstCaps * caps)
381 {
382   GstStructure *structure = gst_caps_get_structure (caps, 0);
383   GstTheoraEnc *enc = GST_THEORA_ENC (gst_pad_get_parent (pad));
384   const GValue *par;
385   gint fps_n, fps_d;
386
387   gst_structure_get_int (structure, "width", &enc->width);
388   gst_structure_get_int (structure, "height", &enc->height);
389   gst_structure_get_fraction (structure, "framerate", &fps_n, &fps_d);
390   par = gst_structure_get_value (structure, "pixel-aspect-ratio");
391
392   theora_info_clear (&enc->info);
393   theora_info_init (&enc->info);
394   /* Theora has a divisible-by-sixteen restriction for the encoded video size but
395    * we can define a visible area using the frame_width/frame_height */
396   enc->info_width = enc->info.width = (enc->width + 15) & ~15;
397   enc->info_height = enc->info.height = (enc->height + 15) & ~15;
398   enc->info.frame_width = enc->width;
399   enc->info.frame_height = enc->height;
400
401   /* center image if needed */
402   if (enc->center) {
403     /* make sure offset is even, for easier decoding */
404     enc->offset_x = GST_ROUND_UP_2 ((enc->info_width - enc->width) / 2);
405     enc->offset_y = GST_ROUND_UP_2 ((enc->info_height - enc->height) / 2);
406   } else {
407     enc->offset_x = 0;
408     enc->offset_y = 0;
409   }
410   enc->info.offset_x = enc->offset_x;
411   enc->info.offset_y = enc->offset_y;
412
413   enc->info.fps_numerator = enc->fps_n = fps_n;
414   enc->info.fps_denominator = enc->fps_d = fps_d;
415   if (par) {
416     enc->info.aspect_numerator = gst_value_get_fraction_numerator (par);
417     enc->info.aspect_denominator = gst_value_get_fraction_denominator (par);
418   } else {
419     /* setting them to 0 indicates that the decoder can chose a good aspect
420      * ratio, defaulting to 1/1 */
421     enc->info.aspect_numerator = 0;
422     enc->info.aspect_denominator = 0;
423   }
424
425   enc->info.colorspace = OC_CS_UNSPECIFIED;
426   enc->info.target_bitrate = enc->video_bitrate;
427   enc->info.quality = enc->video_quality;
428
429   enc->info.dropframes_p = 0;
430   enc->info.quick_p = (enc->quick ? 1 : 0);
431   enc->info.keyframe_auto_p = (enc->keyframe_auto ? 1 : 0);
432   enc->info.keyframe_frequency = enc->keyframe_freq;
433   enc->info.keyframe_frequency_force = enc->keyframe_force;
434   enc->info.keyframe_data_target_bitrate = enc->video_bitrate * 1.5;
435   enc->info.keyframe_auto_threshold = enc->keyframe_threshold;
436   enc->info.keyframe_mindistance = enc->keyframe_mindistance;
437   enc->info.noise_sensitivity = enc->noise_sensitivity;
438   enc->info.sharpness = enc->sharpness;
439
440   /* as done in theora */
441   enc->granule_shift = _ilog (enc->info.keyframe_frequency_force - 1);
442   GST_DEBUG_OBJECT (enc,
443       "keyframe_frequency_force is %d, granule shift is %d",
444       enc->info.keyframe_frequency_force, enc->granule_shift);
445
446   theora_enc_reset (enc);
447   enc->initialised = TRUE;
448
449   gst_object_unref (enc);
450
451   return TRUE;
452 }
453
454 static guint64
455 granulepos_add (guint64 granulepos, guint64 addend, gint shift)
456 {
457   guint64 iframe, pframe;
458
459   iframe = granulepos >> shift;
460   pframe = granulepos - (iframe << shift);
461   iframe += addend;
462
463   return (iframe << shift) + pframe;
464 }
465
466 /* prepare a buffer for transmission by passing data through libtheora */
467 static GstFlowReturn
468 theora_buffer_from_packet (GstTheoraEnc * enc, ogg_packet * packet,
469     GstClockTime timestamp, GstClockTime running_time,
470     GstClockTime duration, GstBuffer ** buffer)
471 {
472   GstBuffer *buf;
473   GstFlowReturn ret = GST_FLOW_OK;
474
475   buf = gst_buffer_new_and_alloc (packet->bytes);
476   if (!buf) {
477     GST_WARNING_OBJECT (enc, "Could not allocate buffer");
478     ret = GST_FLOW_ERROR;
479     goto done;
480   }
481
482   memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes);
483   gst_buffer_set_caps (buf, GST_PAD_CAPS (enc->srcpad));
484   /* see ext/ogg/README; OFFSET_END takes "our" granulepos, OFFSET its
485    * time representation */
486   GST_BUFFER_OFFSET_END (buf) =
487       granulepos_add (packet->granulepos, enc->granulepos_offset,
488       enc->granule_shift);
489   GST_BUFFER_OFFSET (buf) = granulepos_to_timestamp (enc,
490       GST_BUFFER_OFFSET_END (buf));
491
492   GST_BUFFER_TIMESTAMP (buf) = timestamp;
493   GST_BUFFER_DURATION (buf) = duration;
494
495   if (enc->next_discont) {
496     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
497     enc->next_discont = FALSE;
498   }
499
500   /* the second most significant bit of the first data byte is cleared
501    * for keyframes */
502   if ((packet->packet[0] & 0x40) == 0) {
503     GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
504   } else {
505     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
506   }
507   enc->packetno++;
508
509 done:
510   *buffer = buf;
511   return ret;
512 }
513
514 /* push out the buffer and do internal bookkeeping */
515 static GstFlowReturn
516 theora_push_buffer (GstTheoraEnc * enc, GstBuffer * buffer)
517 {
518   GstFlowReturn ret;
519
520   enc->bytes_out += GST_BUFFER_SIZE (buffer);
521
522   ret = gst_pad_push (enc->srcpad, buffer);
523
524   return ret;
525 }
526
527 static GstFlowReturn
528 theora_push_packet (GstTheoraEnc * enc, ogg_packet * packet,
529     GstClockTime timestamp, GstClockTime running_time, GstClockTime duration)
530 {
531   GstBuffer *buf;
532   GstFlowReturn ret;
533
534   ret =
535       theora_buffer_from_packet (enc, packet, timestamp, running_time, duration,
536       &buf);
537   if (ret == GST_FLOW_OK)
538     ret = theora_push_buffer (enc, buf);
539
540   return ret;
541 }
542
543 static GstCaps *
544 theora_set_header_on_caps (GstCaps * caps, GstBuffer * buf1,
545     GstBuffer * buf2, GstBuffer * buf3)
546 {
547   GstStructure *structure;
548   GValue array = { 0 };
549   GValue value = { 0 };
550
551   caps = gst_caps_make_writable (caps);
552   structure = gst_caps_get_structure (caps, 0);
553
554   /* mark buffers */
555   GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS);
556   GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_IN_CAPS);
557   GST_BUFFER_FLAG_SET (buf3, GST_BUFFER_FLAG_IN_CAPS);
558
559   /* Copy buffers, because we can't use the originals -
560    * it creates a circular refcount with the caps<->buffers */
561   buf1 = gst_buffer_copy (buf1);
562   buf2 = gst_buffer_copy (buf2);
563   buf3 = gst_buffer_copy (buf3);
564
565   /* put copies of the buffers in a fixed list */
566   g_value_init (&array, GST_TYPE_ARRAY);
567
568   g_value_init (&value, GST_TYPE_BUFFER);
569   gst_value_set_buffer (&value, buf1);
570   gst_value_array_append_value (&array, &value);
571   g_value_unset (&value);
572
573   g_value_init (&value, GST_TYPE_BUFFER);
574   gst_value_set_buffer (&value, buf2);
575   gst_value_array_append_value (&array, &value);
576   g_value_unset (&value);
577
578   g_value_init (&value, GST_TYPE_BUFFER);
579   gst_value_set_buffer (&value, buf3);
580   gst_value_array_append_value (&array, &value);
581   g_value_unset (&value);
582
583   gst_structure_set_value (structure, "streamheader", &array);
584   g_value_unset (&array);
585
586   /* Unref our copies */
587   gst_buffer_unref (buf1);
588   gst_buffer_unref (buf2);
589   gst_buffer_unref (buf3);
590
591   return caps;
592 }
593
594 static GstClockTime
595 theora_enc_get_ogg_packet_end_time (GstTheoraEnc * enc, ogg_packet * op)
596 {
597   ogg_int64_t end_granule;
598
599   /* FIXME: remove this hack once we depend on libtheora >= 1.0beta1 */
600   if (G_UNLIKELY (use_old_granulepos)) {
601     /* This is where we hack around theora's broken idea of what granulepos
602      * is -- normally we wouldn't need to add the 1, because granulepos
603      * should be the presentation time of the last sample in the packet, but
604      * theora starts with 0 instead of 1... (update: this only applies to old
605      * bitstream/theora versions, this is fixed with bitstream version 3.2.1) */
606     end_granule = granulepos_add (op->granulepos, 1, enc->granule_shift);
607   } else {
608     end_granule = op->granulepos;
609   }
610   return theora_granule_time (&enc->state, end_granule) * GST_SECOND;
611 }
612
613 static void
614 theora_enc_force_keyframe (GstTheoraEnc * enc)
615 {
616   GstClockTime next_ts;
617
618   /* make sure timestamps increment after resetting the decoder */
619   next_ts = enc->next_ts + enc->timestamp_offset;
620
621   theora_enc_reset (enc);
622   enc->granulepos_offset =
623       gst_util_uint64_scale (next_ts, enc->fps_n, GST_SECOND * enc->fps_d);
624   enc->timestamp_offset = next_ts;
625   enc->next_ts = 0;
626 }
627
628 static gboolean
629 theora_enc_sink_event (GstPad * pad, GstEvent * event)
630 {
631   GstTheoraEnc *enc;
632   ogg_packet op;
633   gboolean res;
634
635   enc = GST_THEORA_ENC (GST_PAD_PARENT (pad));
636
637   switch (GST_EVENT_TYPE (event)) {
638     case GST_EVENT_NEWSEGMENT:
639     {
640       gboolean update;
641       gdouble rate, applied_rate;
642       GstFormat format;
643       gint64 start, stop, time;
644
645       gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
646           &format, &start, &stop, &time);
647
648       gst_segment_set_newsegment_full (&enc->segment, update, rate,
649           applied_rate, format, start, stop, time);
650
651       res = gst_pad_push_event (enc->srcpad, event);
652       break;
653     }
654     case GST_EVENT_EOS:
655       if (enc->initialised) {
656         /* push last packet with eos flag, should not be called */
657         while (theora_encode_packetout (&enc->state, 1, &op)) {
658           GstClockTime next_time =
659               theora_enc_get_ogg_packet_end_time (enc, &op);
660
661           theora_push_packet (enc, &op, GST_CLOCK_TIME_NONE, enc->next_ts,
662               next_time - enc->next_ts);
663           enc->next_ts = next_time;
664         }
665       }
666       res = gst_pad_push_event (enc->srcpad, event);
667       break;
668     case GST_EVENT_FLUSH_STOP:
669       gst_segment_init (&enc->segment, GST_FORMAT_UNDEFINED);
670       res = gst_pad_push_event (enc->srcpad, event);
671       break;
672     case GST_EVENT_CUSTOM_DOWNSTREAM:
673     {
674       const GstStructure *s;
675
676       s = gst_event_get_structure (event);
677
678       if (gst_structure_has_name (s, "GstForceKeyUnit"))
679         theora_enc_force_keyframe (enc);
680       res = gst_pad_push_event (enc->srcpad, event);
681       break;
682     }
683     default:
684       res = gst_pad_push_event (enc->srcpad, event);
685       break;
686   }
687   return res;
688 }
689
690 static gboolean
691 theora_enc_src_event (GstPad * pad, GstEvent * event)
692 {
693   GstTheoraEnc *enc;
694   gboolean res = TRUE;
695
696   enc = GST_THEORA_ENC (GST_PAD_PARENT (pad));
697
698   switch (GST_EVENT_TYPE (event)) {
699     case GST_EVENT_CUSTOM_UPSTREAM:
700     {
701       const GstStructure *s;
702
703       s = gst_event_get_structure (event);
704
705       if (gst_structure_has_name (s, "GstForceKeyUnit")) {
706         GST_OBJECT_LOCK (enc);
707         enc->force_keyframe = TRUE;
708         GST_OBJECT_UNLOCK (enc);
709         /* consume the event */
710         res = TRUE;
711         gst_event_unref (event);
712       } else {
713         res = gst_pad_push_event (enc->sinkpad, event);
714       }
715       break;
716     }
717     default:
718       res = gst_pad_push_event (enc->sinkpad, event);
719       break;
720   }
721
722   return res;
723 }
724
725 static gboolean
726 theora_enc_is_discontinuous (GstTheoraEnc * enc, GstClockTime timestamp,
727     GstClockTime duration)
728 {
729   GstClockTimeDiff max_diff;
730   gboolean ret = FALSE;
731
732   /* Allow 3/4 a frame off */
733   max_diff = (enc->info.fps_denominator * GST_SECOND * 3) /
734       (enc->info.fps_numerator * 4);
735
736   if (timestamp != GST_CLOCK_TIME_NONE
737       && enc->expected_ts != GST_CLOCK_TIME_NONE) {
738     if ((GstClockTimeDiff) (timestamp - enc->expected_ts) > max_diff) {
739       GST_DEBUG_OBJECT (enc, "Incoming TS %" GST_TIME_FORMAT
740           " exceeds expected value %" GST_TIME_FORMAT
741           " by too much, marking discontinuity",
742           GST_TIME_ARGS (timestamp), GST_TIME_ARGS (enc->expected_ts));
743       ret = TRUE;
744     }
745   }
746
747   if (GST_CLOCK_TIME_IS_VALID (duration))
748     enc->expected_ts = timestamp + duration;
749   else
750     enc->expected_ts = GST_CLOCK_TIME_NONE;
751
752   return ret;
753 }
754
755 static void
756 theora_enc_init_yuv_buffer (yuv_buffer * yuv,
757     guint8 * data, gint width, gint height)
758 {
759   yuv->y_width = width;
760   yuv->y_height = height;
761   yuv->y_stride = GST_ROUND_UP_4 (width);
762
763   yuv->uv_width = width / 2;
764   yuv->uv_height = height / 2;
765   yuv->uv_stride = GST_ROUND_UP_8 (width) / 2;
766
767   yuv->y = data;
768   yuv->u = yuv->y + GST_ROUND_UP_2 (height) * yuv->y_stride;
769   yuv->v = yuv->u + GST_ROUND_UP_2 (height) / 2 * yuv->uv_stride;
770 }
771
772 static GstBuffer *
773 theora_enc_resize_buffer (GstTheoraEnc * enc, GstBuffer * buffer)
774 {
775   GstBuffer *newbuf;
776   gint i;
777   guchar *dest_y, *src_y;
778   guchar *dest_u, *src_u;
779   guchar *dest_v, *src_v;
780   gint src_y_stride, src_uv_stride;
781   gint dst_y_stride, dst_uv_stride;
782   gint width, height;
783   gint cwidth, cheight;
784   gint offset_x, right_x, right_border;
785   gint y_size;
786
787   if (enc->width == enc->info_width && enc->height == enc->info_height) {
788     GST_LOG_OBJECT (enc, "no cropping/conversion needed");
789     return buffer;
790   }
791
792   GST_LOG_OBJECT (enc, "cropping/conversion needed for strides");
793   /* source width/height */
794   width = enc->width;
795   height = enc->height;
796   /* soucre chroma width/height */
797   cwidth = width / 2;
798   cheight = height / 2;
799
800   /* source strides as defined in videotestsrc */
801   src_y_stride = GST_ROUND_UP_4 (width);
802   src_uv_stride = GST_ROUND_UP_8 (width) / 2;
803
804   /* destination strides from the real picture width */
805   dst_y_stride = enc->info_width;
806   dst_uv_stride = enc->info_width / 2;
807
808   y_size = enc->info_width * enc->info_height;
809
810   newbuf = gst_buffer_new_and_alloc (y_size * 3 / 2);
811   if (!newbuf) {
812     gst_buffer_unref (buffer);
813     return NULL;
814   }
815   GST_BUFFER_OFFSET (newbuf) = GST_BUFFER_OFFSET_NONE;
816   gst_buffer_set_caps (newbuf, GST_PAD_CAPS (enc->srcpad));
817
818   dest_y = GST_BUFFER_DATA (newbuf);
819   dest_u = dest_y + y_size;
820   dest_v = dest_u + y_size / 4;
821
822   src_y = GST_BUFFER_DATA (buffer);
823   src_u = src_y + src_y_stride * GST_ROUND_UP_2 (height);
824   src_v = src_u + src_uv_stride * GST_ROUND_UP_2 (height) / 2;
825
826   if (enc->border != BORDER_NONE) {
827     /* fill top border */
828     for (i = 0; i < enc->offset_y; i++) {
829       memset (dest_y, 0, dst_y_stride);
830       dest_y += dst_y_stride;
831     }
832   } else {
833     dest_y += dst_y_stride * enc->offset_y;
834   }
835
836   offset_x = enc->offset_x;
837   right_x = width + enc->offset_x;
838   right_border = dst_y_stride - right_x;
839
840   /* copy Y plane */
841   for (i = 0; i < height; i++) {
842     memcpy (dest_y + offset_x, src_y, width);
843     if (enc->border != BORDER_NONE) {
844       memset (dest_y, 0, offset_x);
845       memset (dest_y + right_x, 0, right_border);
846     }
847
848     dest_y += dst_y_stride;
849     src_y += src_y_stride;
850   }
851
852   if (enc->border != BORDER_NONE) {
853     /* fill bottom border */
854     for (i = height + enc->offset_y; i < enc->info.height; i++) {
855       memset (dest_y, 0, dst_y_stride);
856       dest_y += dst_y_stride;
857     }
858
859     /* fill top border chroma */
860     for (i = 0; i < enc->offset_y / 2; i++) {
861       memset (dest_u, 128, dst_uv_stride);
862       memset (dest_v, 128, dst_uv_stride);
863       dest_u += dst_uv_stride;
864       dest_v += dst_uv_stride;
865     }
866   } else {
867     dest_u += dst_uv_stride * enc->offset_y / 2;
868     dest_v += dst_uv_stride * enc->offset_y / 2;
869   }
870
871   offset_x = enc->offset_x / 2;
872   right_x = cwidth + offset_x;
873   right_border = dst_uv_stride - right_x;
874
875   /* copy UV planes */
876   for (i = 0; i < cheight; i++) {
877     memcpy (dest_v + offset_x, src_v, cwidth);
878     memcpy (dest_u + offset_x, src_u, cwidth);
879
880     if (enc->border != BORDER_NONE) {
881       memset (dest_u, 128, offset_x);
882       memset (dest_u + right_x, 128, right_border);
883       memset (dest_v, 128, offset_x);
884       memset (dest_v + right_x, 128, right_border);
885     }
886
887     dest_u += dst_uv_stride;
888     dest_v += dst_uv_stride;
889     src_u += src_uv_stride;
890     src_v += src_uv_stride;
891   }
892
893   if (enc->border != BORDER_NONE) {
894     /* fill bottom border */
895     for (i = cheight + enc->offset_y / 2; i < enc->info_height / 2; i++) {
896       memset (dest_u, 128, dst_uv_stride);
897       memset (dest_v, 128, dst_uv_stride);
898       dest_u += dst_uv_stride;
899       dest_v += dst_uv_stride;
900     }
901   }
902
903   gst_buffer_unref (buffer);
904   return newbuf;
905 }
906
907 static GstFlowReturn
908 theora_enc_chain (GstPad * pad, GstBuffer * buffer)
909 {
910   GstTheoraEnc *enc;
911   ogg_packet op;
912   GstClockTime timestamp, duration, running_time;
913   GstFlowReturn ret;
914   gboolean force_keyframe;
915
916   enc = GST_THEORA_ENC (GST_PAD_PARENT (pad));
917
918   /* we keep track of two timelines.
919    * - The timestamps from the incomming buffers, which we copy to the outgoing
920    *   encoded buffers as-is. We need to do this as we simply forward the
921    *   newsegment events.
922    * - The running_time of the buffers, which we use to construct the granulepos
923    *   in the packets.
924    */
925   timestamp = GST_BUFFER_TIMESTAMP (buffer);
926   duration = GST_BUFFER_DURATION (buffer);
927
928   running_time =
929       gst_segment_to_running_time (&enc->segment, GST_FORMAT_TIME, timestamp);
930   if ((gint64) running_time < 0) {
931     GST_DEBUG_OBJECT (enc, "Dropping buffer, timestamp: %" GST_TIME_FORMAT,
932         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
933     gst_buffer_unref (buffer);
934     return GST_FLOW_OK;
935   }
936
937   /* see if we need to schedule a keyframe */
938   GST_OBJECT_LOCK (enc);
939   force_keyframe = enc->force_keyframe;
940   enc->force_keyframe = FALSE;
941   GST_OBJECT_UNLOCK (enc);
942
943   if (force_keyframe) {
944     GstClockTime stream_time;
945     GstStructure *s;
946
947     stream_time = gst_segment_to_stream_time (&enc->segment,
948         GST_FORMAT_TIME, timestamp);
949
950     s = gst_structure_new ("GstForceKeyUnit",
951         "timestamp", G_TYPE_UINT64, timestamp,
952         "stream-time", G_TYPE_UINT64, stream_time,
953         "running-time", G_TYPE_UINT64, running_time, NULL);
954
955     theora_enc_force_keyframe (enc);
956
957     gst_pad_push_event (enc->srcpad,
958         gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s));
959   }
960
961   /* make sure we copy the discont flag to the next outgoing buffer when it's
962    * set on the incomming buffer */
963   if (GST_BUFFER_IS_DISCONT (buffer)) {
964     enc->next_discont = TRUE;
965   }
966
967   if (enc->packetno == 0) {
968     /* no packets written yet, setup headers */
969     GstCaps *caps;
970     GstBuffer *buf1, *buf2, *buf3;
971
972     enc->granulepos_offset = 0;
973     enc->timestamp_offset = 0;
974
975     GST_DEBUG_OBJECT (enc, "output headers");
976     /* Theora streams begin with three headers; the initial header (with
977        most of the codec setup parameters) which is mandated by the Ogg
978        bitstream spec.  The second header holds any comment fields.  The
979        third header holds the bitstream codebook.  We merely need to
980        make the headers, then pass them to libtheora one at a time;
981        libtheora handles the additional Ogg bitstream constraints */
982
983     /* first packet will get its own page automatically */
984     if (theora_encode_header (&enc->state, &op) != 0)
985       goto encoder_disabled;
986
987     ret =
988         theora_buffer_from_packet (enc, &op, GST_CLOCK_TIME_NONE,
989         GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE, &buf1);
990     if (ret != GST_FLOW_OK) {
991       goto header_buffer_alloc;
992     }
993
994     /* create the remaining theora headers */
995     theora_comment_clear (&enc->comment);
996     theora_comment_init (&enc->comment);
997
998     if (theora_encode_comment (&enc->comment, &op) != 0)
999       goto encoder_disabled;
1000
1001     ret =
1002         theora_buffer_from_packet (enc, &op, GST_CLOCK_TIME_NONE,
1003         GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE, &buf2);
1004     /* Theora expects us to put this packet buffer into an ogg page,
1005      * in which case it becomes the ogg library's responsibility to
1006      * free it. Since we're copying and outputting a gst_buffer,
1007      * we need to free it ourselves. */
1008     if (op.packet)
1009       free (op.packet);
1010
1011     if (ret != GST_FLOW_OK) {
1012       gst_buffer_unref (buf1);
1013       goto header_buffer_alloc;
1014     }
1015
1016     if (theora_encode_tables (&enc->state, &op) != 0)
1017       goto encoder_disabled;
1018
1019     ret =
1020         theora_buffer_from_packet (enc, &op, GST_CLOCK_TIME_NONE,
1021         GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE, &buf3);
1022     if (ret != GST_FLOW_OK) {
1023       gst_buffer_unref (buf1);
1024       gst_buffer_unref (buf2);
1025       goto header_buffer_alloc;
1026     }
1027
1028     /* mark buffers and put on caps */
1029     caps = gst_pad_get_caps (enc->srcpad);
1030     caps = theora_set_header_on_caps (caps, buf1, buf2, buf3);
1031     GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
1032     gst_pad_set_caps (enc->srcpad, caps);
1033
1034     gst_buffer_set_caps (buf1, caps);
1035     gst_buffer_set_caps (buf2, caps);
1036     gst_buffer_set_caps (buf3, caps);
1037
1038     gst_caps_unref (caps);
1039
1040     /* push out the header buffers */
1041     if ((ret = theora_push_buffer (enc, buf1)) != GST_FLOW_OK) {
1042       gst_buffer_unref (buf2);
1043       gst_buffer_unref (buf3);
1044       goto header_push;
1045     }
1046     if ((ret = theora_push_buffer (enc, buf2)) != GST_FLOW_OK) {
1047       gst_buffer_unref (buf3);
1048       goto header_push;
1049     }
1050     if ((ret = theora_push_buffer (enc, buf3)) != GST_FLOW_OK) {
1051       goto header_push;
1052     }
1053
1054     enc->granulepos_offset =
1055         gst_util_uint64_scale (running_time, enc->fps_n,
1056         GST_SECOND * enc->fps_d);
1057     enc->timestamp_offset = running_time;
1058     enc->next_ts = 0;
1059   }
1060
1061   {
1062     yuv_buffer yuv;
1063     gint res;
1064
1065     buffer = theora_enc_resize_buffer (enc, buffer);
1066     if (buffer == NULL)
1067       return GST_FLOW_ERROR;
1068
1069     theora_enc_init_yuv_buffer (&yuv, GST_BUFFER_DATA (buffer), enc->info_width,
1070         enc->info_height);
1071
1072     if (theora_enc_is_discontinuous (enc, running_time, duration)) {
1073       theora_enc_reset (enc);
1074       enc->granulepos_offset =
1075           gst_util_uint64_scale (running_time, enc->fps_n,
1076           GST_SECOND * enc->fps_d);
1077       enc->timestamp_offset = running_time;
1078       enc->next_ts = 0;
1079       enc->next_discont = TRUE;
1080     }
1081
1082     res = theora_encode_YUVin (&enc->state, &yuv);
1083     /* none of the failure cases can happen here */
1084     g_assert (res == 0);
1085
1086     ret = GST_FLOW_OK;
1087     while (theora_encode_packetout (&enc->state, 0, &op)) {
1088       GstClockTime next_time;
1089
1090       next_time = theora_enc_get_ogg_packet_end_time (enc, &op);
1091
1092       ret =
1093           theora_push_packet (enc, &op, timestamp, enc->next_ts,
1094           next_time - enc->next_ts);
1095
1096       enc->next_ts = next_time;
1097       if (ret != GST_FLOW_OK)
1098         goto data_push;
1099     }
1100     gst_buffer_unref (buffer);
1101   }
1102
1103   return ret;
1104
1105   /* ERRORS */
1106 header_buffer_alloc:
1107   {
1108     gst_buffer_unref (buffer);
1109     return ret;
1110   }
1111 header_push:
1112   {
1113     gst_buffer_unref (buffer);
1114     return ret;
1115   }
1116 data_push:
1117   {
1118     gst_buffer_unref (buffer);
1119     return ret;
1120   }
1121 encoder_disabled:
1122   {
1123     GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL),
1124         ("libtheora has been compiled with the encoder disabled"));
1125     gst_buffer_unref (buffer);
1126     return GST_FLOW_ERROR;
1127   }
1128 }
1129
1130 static GstStateChangeReturn
1131 theora_enc_change_state (GstElement * element, GstStateChange transition)
1132 {
1133   GstTheoraEnc *enc;
1134   GstStateChangeReturn ret;
1135
1136   enc = GST_THEORA_ENC (element);
1137
1138   switch (transition) {
1139     case GST_STATE_CHANGE_NULL_TO_READY:
1140       break;
1141     case GST_STATE_CHANGE_READY_TO_PAUSED:
1142       GST_DEBUG_OBJECT (enc, "READY->PAUSED Initing theora state");
1143       theora_info_init (&enc->info);
1144       theora_comment_init (&enc->comment);
1145       enc->packetno = 0;
1146       enc->force_keyframe = FALSE;
1147       break;
1148     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1149       break;
1150     default:
1151       break;
1152   }
1153
1154   ret = parent_class->change_state (element, transition);
1155
1156   switch (transition) {
1157     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1158       break;
1159     case GST_STATE_CHANGE_PAUSED_TO_READY:
1160       GST_DEBUG_OBJECT (enc, "PAUSED->READY Clearing theora state");
1161       theora_clear (&enc->state);
1162       theora_comment_clear (&enc->comment);
1163       theora_info_clear (&enc->info);
1164
1165       theora_enc_clear (enc);
1166       enc->initialised = FALSE;
1167       break;
1168     case GST_STATE_CHANGE_READY_TO_NULL:
1169       break;
1170     default:
1171       break;
1172   }
1173
1174   return ret;
1175 }
1176
1177 static void
1178 theora_enc_set_property (GObject * object, guint prop_id,
1179     const GValue * value, GParamSpec * pspec)
1180 {
1181   GstTheoraEnc *enc = GST_THEORA_ENC (object);
1182
1183   switch (prop_id) {
1184     case ARG_CENTER:
1185       enc->center = g_value_get_boolean (value);
1186       break;
1187     case ARG_BORDER:
1188       enc->border = g_value_get_enum (value);
1189       break;
1190     case ARG_BITRATE:
1191       enc->video_bitrate = g_value_get_int (value) * 1000;
1192       enc->video_quality = 0;
1193       break;
1194     case ARG_QUALITY:
1195       enc->video_quality = g_value_get_int (value);
1196       enc->video_bitrate = 0;
1197       break;
1198     case ARG_QUICK:
1199       enc->quick = g_value_get_boolean (value);
1200       break;
1201     case ARG_KEYFRAME_AUTO:
1202       enc->keyframe_auto = g_value_get_boolean (value);
1203       break;
1204     case ARG_KEYFRAME_FREQ:
1205       enc->keyframe_freq = g_value_get_int (value);
1206       break;
1207     case ARG_KEYFRAME_FREQ_FORCE:
1208       enc->keyframe_force = g_value_get_int (value);
1209       break;
1210     case ARG_KEYFRAME_THRESHOLD:
1211       enc->keyframe_threshold = g_value_get_int (value);
1212       break;
1213     case ARG_KEYFRAME_MINDISTANCE:
1214       enc->keyframe_mindistance = g_value_get_int (value);
1215       break;
1216     case ARG_NOISE_SENSITIVITY:
1217       enc->noise_sensitivity = g_value_get_int (value);
1218       break;
1219     case ARG_SHARPNESS:
1220       enc->sharpness = g_value_get_int (value);
1221       break;
1222     case ARG_SPEEDLEVEL:
1223 #ifdef TH_ENCCTL_SET_SPLEVEL
1224       enc->speed_level = g_value_get_int (value);
1225 #endif
1226       break;
1227     default:
1228       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1229       break;
1230   }
1231 }
1232
1233 static void
1234 theora_enc_get_property (GObject * object, guint prop_id,
1235     GValue * value, GParamSpec * pspec)
1236 {
1237   GstTheoraEnc *enc = GST_THEORA_ENC (object);
1238
1239   switch (prop_id) {
1240     case ARG_CENTER:
1241       g_value_set_boolean (value, enc->center);
1242       break;
1243     case ARG_BORDER:
1244       g_value_set_enum (value, enc->border);
1245       break;
1246     case ARG_BITRATE:
1247       g_value_set_int (value, enc->video_bitrate / 1000);
1248       break;
1249     case ARG_QUALITY:
1250       g_value_set_int (value, enc->video_quality);
1251       break;
1252     case ARG_QUICK:
1253       g_value_set_boolean (value, enc->quick);
1254       break;
1255     case ARG_KEYFRAME_AUTO:
1256       g_value_set_boolean (value, enc->keyframe_auto);
1257       break;
1258     case ARG_KEYFRAME_FREQ:
1259       g_value_set_int (value, enc->keyframe_freq);
1260       break;
1261     case ARG_KEYFRAME_FREQ_FORCE:
1262       g_value_set_int (value, enc->keyframe_force);
1263       break;
1264     case ARG_KEYFRAME_THRESHOLD:
1265       g_value_set_int (value, enc->keyframe_threshold);
1266       break;
1267     case ARG_KEYFRAME_MINDISTANCE:
1268       g_value_set_int (value, enc->keyframe_mindistance);
1269       break;
1270     case ARG_NOISE_SENSITIVITY:
1271       g_value_set_int (value, enc->noise_sensitivity);
1272       break;
1273     case ARG_SHARPNESS:
1274       g_value_set_int (value, enc->sharpness);
1275       break;
1276     case ARG_SPEEDLEVEL:
1277       g_value_set_int (value, enc->speed_level);
1278       break;
1279     default:
1280       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1281       break;
1282   }
1283 }