• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
3  * Copyright (c) 2012 Collabora Ltd.
4  *	Author : Edward Hervey <edward@collabora.com>
5  *      Author : Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 /**
24  * SECTION:element-theoraenc
25  * @title: theoraenc
26  * @see_also: theoradec, oggmux
27  *
28  * This element encodes raw video into a Theora stream.
29  * [Theora](http://www.theora.org/) is a royalty-free
30  * video codec maintained by the [Xiph.org Foundation](http://www.xiph.org/),
31  * based on the VP3 codec.
32  *
33  * The theora codec internally only supports encoding of images that are a
34  * multiple of 16 pixels in both X and Y direction. It is however perfectly
35  * possible to encode images with other dimensions because an arbitrary
36  * rectangular cropping region can be set up. This element will automatically
37  * set up a correct cropping region if the dimensions are not multiples of 16
38  * pixels.
39  *
40  * To control the quality of the encoding, the #GstTheoraEnc:bitrate and
41  * #GstTheoraEnc:quality properties can be used. These two properties are
42  * mutualy exclusive. Setting the bitrate property will produce a constant
43  * bitrate (CBR) stream while setting the quality property will produce a
44  * variable bitrate (VBR) stream.
45  *
46  * A videorate element is often required in front of theoraenc, especially
47  * when transcoding and when putting Theora into the Ogg container.
48  *
49  * ## Example pipeline
50  * |[
51  * gst-launch-1.0 -v videotestsrc num-buffers=500 ! video/x-raw,width=1280,height=720 ! queue ! progressreport ! theoraenc ! oggmux ! filesink location=videotestsrc.ogg
52  * ]|
53  *  This example pipeline will encode a test video source to theora muxed in an
54  * ogg container. Refer to the theoradec documentation to decode the create
55  * stream.
56  *
57  */
58 
59 #ifdef HAVE_CONFIG_H
60 #include "config.h"
61 #endif
62 
63 #include <string.h>
64 #include <stdlib.h>             /* free */
65 
66 #include <gst/tag/tag.h>
67 #include <gst/video/video.h>
68 #include <gst/video/gstvideometa.h>
69 
70 #include "gsttheoraenc.h"
71 
72 #define GST_CAT_DEFAULT theoraenc_debug
73 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
74 
75 #define GST_TYPE_MULTIPASS_MODE (gst_multipass_mode_get_type())
76 static GType
gst_multipass_mode_get_type(void)77 gst_multipass_mode_get_type (void)
78 {
79   static GType multipass_mode_type = 0;
80   static const GEnumValue multipass_mode[] = {
81     {MULTIPASS_MODE_SINGLE_PASS, "Single pass", "single-pass"},
82     {MULTIPASS_MODE_FIRST_PASS, "First pass", "first-pass"},
83     {MULTIPASS_MODE_SECOND_PASS, "Second pass", "second-pass"},
84     {0, NULL, NULL},
85   };
86 
87   if (!multipass_mode_type) {
88     multipass_mode_type =
89         g_enum_register_static ("GstTheoraEncMultipassMode", multipass_mode);
90   }
91   return multipass_mode_type;
92 }
93 
94 /* taken from theora/lib/toplevel.c */
95 static int
_ilog(unsigned int v)96 _ilog (unsigned int v)
97 {
98   int ret = 0;
99 
100   while (v) {
101     ret++;
102     v >>= 1;
103   }
104   return (ret);
105 }
106 
107 #define THEORA_DEF_BITRATE              0
108 #define THEORA_DEF_QUALITY              48
109 #define THEORA_DEF_KEYFRAME_AUTO        TRUE
110 #define THEORA_DEF_KEYFRAME_FREQ        64
111 #define THEORA_DEF_KEYFRAME_FREQ_FORCE  64
112 #define THEORA_DEF_SPEEDLEVEL           1
113 #define THEORA_DEF_VP3_COMPATIBLE       FALSE
114 #define THEORA_DEF_DROP_FRAMES          TRUE
115 #define THEORA_DEF_CAP_OVERFLOW         TRUE
116 #define THEORA_DEF_CAP_UNDERFLOW        FALSE
117 #define THEORA_DEF_RATE_BUFFER          0
118 #define THEORA_DEF_MULTIPASS_CACHE_FILE NULL
119 #define THEORA_DEF_MULTIPASS_MODE       MULTIPASS_MODE_SINGLE_PASS
120 enum
121 {
122   PROP_0,
123   PROP_BITRATE,
124   PROP_QUALITY,
125   PROP_KEYFRAME_AUTO,
126   PROP_KEYFRAME_FREQ,
127   PROP_KEYFRAME_FREQ_FORCE,
128   PROP_SPEEDLEVEL,
129   PROP_VP3_COMPATIBLE,
130   PROP_DROP_FRAMES,
131   PROP_CAP_OVERFLOW,
132   PROP_CAP_UNDERFLOW,
133   PROP_RATE_BUFFER,
134   PROP_MULTIPASS_CACHE_FILE,
135   PROP_MULTIPASS_MODE
136       /* FILL ME */
137 };
138 
139 /* this function does a straight granulepos -> timestamp conversion */
140 static GstClockTime
granulepos_to_timestamp(GstTheoraEnc * theoraenc,ogg_int64_t granulepos)141 granulepos_to_timestamp (GstTheoraEnc * theoraenc, ogg_int64_t granulepos)
142 {
143   guint64 iframe, pframe;
144   int shift = theoraenc->info.keyframe_granule_shift;
145 
146   if (granulepos < 0)
147     return GST_CLOCK_TIME_NONE;
148 
149   iframe = granulepos >> shift;
150   pframe = granulepos - (iframe << shift);
151 
152   /* num and den are 32 bit, so we can safely multiply with GST_SECOND */
153   return gst_util_uint64_scale ((guint64) (iframe + pframe),
154       GST_SECOND * theoraenc->info.fps_denominator,
155       theoraenc->info.fps_numerator);
156 }
157 
158 static GstStaticPadTemplate theora_enc_sink_factory =
159 GST_STATIC_PAD_TEMPLATE ("sink",
160     GST_PAD_SINK,
161     GST_PAD_ALWAYS,
162     GST_STATIC_CAPS ("video/x-raw, "
163         "format = (string) { I420, Y42B, Y444 }, "
164         "framerate = (fraction) [1/MAX, MAX], "
165         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
166     );
167 
168 static GstStaticPadTemplate theora_enc_src_factory =
169 GST_STATIC_PAD_TEMPLATE ("src",
170     GST_PAD_SRC,
171     GST_PAD_ALWAYS,
172     GST_STATIC_CAPS ("video/x-theora, "
173         "framerate = (fraction) [1/MAX, MAX], "
174         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
175     );
176 
177 #define gst_theora_enc_parent_class parent_class
178 G_DEFINE_TYPE (GstTheoraEnc, gst_theora_enc, GST_TYPE_VIDEO_ENCODER);
179 GST_ELEMENT_REGISTER_DEFINE (theoraenc, "theoraenc",
180     GST_RANK_PRIMARY, GST_TYPE_THEORA_ENC);
181 
182 static gboolean theora_enc_start (GstVideoEncoder * enc);
183 static gboolean theora_enc_stop (GstVideoEncoder * enc);
184 static gboolean theora_enc_flush (GstVideoEncoder * enc);
185 static gboolean theora_enc_set_format (GstVideoEncoder * enc,
186     GstVideoCodecState * state);
187 static GstFlowReturn theora_enc_handle_frame (GstVideoEncoder * enc,
188     GstVideoCodecFrame * frame);
189 static GstFlowReturn theora_enc_pre_push (GstVideoEncoder * benc,
190     GstVideoCodecFrame * frame);
191 static GstFlowReturn theora_enc_finish (GstVideoEncoder * enc);
192 static gboolean theora_enc_propose_allocation (GstVideoEncoder * encoder,
193     GstQuery * query);
194 
195 static GstCaps *theora_enc_getcaps (GstVideoEncoder * encoder,
196     GstCaps * filter);
197 static void theora_enc_get_property (GObject * object, guint prop_id,
198     GValue * value, GParamSpec * pspec);
199 static void theora_enc_set_property (GObject * object, guint prop_id,
200     const GValue * value, GParamSpec * pspec);
201 static void theora_enc_finalize (GObject * object);
202 
203 static gboolean theora_enc_write_multipass_cache (GstTheoraEnc * enc,
204     gboolean begin, gboolean eos);
205 
206 static void
gst_theora_enc_class_init(GstTheoraEncClass * klass)207 gst_theora_enc_class_init (GstTheoraEncClass * klass)
208 {
209   GObjectClass *gobject_class = (GObjectClass *) klass;
210   GstElementClass *element_class = (GstElementClass *) klass;
211   GstVideoEncoderClass *gstvideo_encoder_class =
212       GST_VIDEO_ENCODER_CLASS (klass);
213 
214   gobject_class->set_property = theora_enc_set_property;
215   gobject_class->get_property = theora_enc_get_property;
216   gobject_class->finalize = theora_enc_finalize;
217 
218   gst_element_class_add_static_pad_template (element_class,
219       &theora_enc_src_factory);
220   gst_element_class_add_static_pad_template (element_class,
221       &theora_enc_sink_factory);
222   gst_element_class_set_static_metadata (element_class, "Theora video encoder",
223       "Codec/Encoder/Video", "encode raw YUV video to a theora stream",
224       "Wim Taymans <wim@fluendo.com>");
225 
226   gstvideo_encoder_class->start = GST_DEBUG_FUNCPTR (theora_enc_start);
227   gstvideo_encoder_class->stop = GST_DEBUG_FUNCPTR (theora_enc_stop);
228   gstvideo_encoder_class->flush = GST_DEBUG_FUNCPTR (theora_enc_flush);
229   gstvideo_encoder_class->set_format =
230       GST_DEBUG_FUNCPTR (theora_enc_set_format);
231   gstvideo_encoder_class->handle_frame =
232       GST_DEBUG_FUNCPTR (theora_enc_handle_frame);
233   gstvideo_encoder_class->pre_push = GST_DEBUG_FUNCPTR (theora_enc_pre_push);
234   gstvideo_encoder_class->finish = GST_DEBUG_FUNCPTR (theora_enc_finish);
235   gstvideo_encoder_class->getcaps = GST_DEBUG_FUNCPTR (theora_enc_getcaps);
236   gstvideo_encoder_class->propose_allocation =
237       GST_DEBUG_FUNCPTR (theora_enc_propose_allocation);
238 
239   /* general encoding stream options */
240   g_object_class_install_property (gobject_class, PROP_BITRATE,
241       g_param_spec_int ("bitrate", "Bitrate", "Compressed video bitrate (kbps)",
242           0, (1 << 24) - 1, THEORA_DEF_BITRATE,
243           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
244           GST_PARAM_MUTABLE_PLAYING));
245   g_object_class_install_property (gobject_class, PROP_QUALITY,
246       g_param_spec_int ("quality", "Quality", "Video quality", 0, 63,
247           THEORA_DEF_QUALITY,
248           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
249           GST_PARAM_MUTABLE_PLAYING));
250   g_object_class_install_property (gobject_class, PROP_KEYFRAME_AUTO,
251       g_param_spec_boolean ("keyframe-auto", "Keyframe Auto",
252           "Automatic keyframe detection", THEORA_DEF_KEYFRAME_AUTO,
253           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
254   g_object_class_install_property (gobject_class, PROP_KEYFRAME_FREQ,
255       g_param_spec_int ("keyframe-freq", "Keyframe frequency",
256           "Keyframe frequency", 1, 32768, THEORA_DEF_KEYFRAME_FREQ,
257           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
258   g_object_class_install_property (gobject_class, PROP_KEYFRAME_FREQ_FORCE,
259       g_param_spec_int ("keyframe-force", "Keyframe force",
260           "Force keyframe every N frames", 1, 32768,
261           THEORA_DEF_KEYFRAME_FREQ_FORCE,
262           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
263   g_object_class_install_property (gobject_class, PROP_SPEEDLEVEL,
264       g_param_spec_int ("speed-level", "Speed level",
265           "Controls the amount of motion vector searching done while encoding",
266           0, 3, THEORA_DEF_SPEEDLEVEL,
267           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
268   g_object_class_install_property (gobject_class, PROP_VP3_COMPATIBLE,
269       g_param_spec_boolean ("vp3-compatible", "VP3 compatible",
270           "Disables non-VP3 compatible features",
271           THEORA_DEF_VP3_COMPATIBLE,
272           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
273   g_object_class_install_property (gobject_class, PROP_DROP_FRAMES,
274       g_param_spec_boolean ("drop-frames", "Drop frames",
275           "Allow or disallow frame dropping",
276           THEORA_DEF_DROP_FRAMES,
277           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
278   g_object_class_install_property (gobject_class, PROP_CAP_OVERFLOW,
279       g_param_spec_boolean ("cap-overflow", "Cap overflow",
280           "Enable capping of bit reservoir overflows",
281           THEORA_DEF_CAP_OVERFLOW,
282           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
283   g_object_class_install_property (gobject_class, PROP_CAP_UNDERFLOW,
284       g_param_spec_boolean ("cap-underflow", "Cap underflow",
285           "Enable capping of bit reservoir underflows",
286           THEORA_DEF_CAP_UNDERFLOW,
287           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
288   g_object_class_install_property (gobject_class, PROP_RATE_BUFFER,
289       g_param_spec_int ("rate-buffer", "Rate Control Buffer",
290           "Sets the size of the rate control buffer, in units of frames.  "
291           "The default value of 0 instructs the encoder to automatically "
292           "select an appropriate value",
293           0, 1000, THEORA_DEF_RATE_BUFFER,
294           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
295   g_object_class_install_property (gobject_class, PROP_MULTIPASS_CACHE_FILE,
296       g_param_spec_string ("multipass-cache-file", "Multipass Cache File",
297           "Multipass cache file", THEORA_DEF_MULTIPASS_CACHE_FILE,
298           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
299   g_object_class_install_property (gobject_class, PROP_MULTIPASS_MODE,
300       g_param_spec_enum ("multipass-mode", "Multipass mode",
301           "Single pass or first/second pass", GST_TYPE_MULTIPASS_MODE,
302           THEORA_DEF_MULTIPASS_MODE,
303           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
304 
305   GST_DEBUG_CATEGORY_INIT (theoraenc_debug, "theoraenc", 0, "Theora encoder");
306 
307   gst_type_mark_as_plugin_api (GST_TYPE_MULTIPASS_MODE, 0);
308 }
309 
310 static void
gst_theora_enc_init(GstTheoraEnc * enc)311 gst_theora_enc_init (GstTheoraEnc * enc)
312 {
313   GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_ENCODER_SINK_PAD (enc));
314 
315   enc->video_bitrate = THEORA_DEF_BITRATE;
316   enc->video_quality = THEORA_DEF_QUALITY;
317   enc->keyframe_auto = THEORA_DEF_KEYFRAME_AUTO;
318   enc->keyframe_freq = THEORA_DEF_KEYFRAME_FREQ;
319   enc->keyframe_force = THEORA_DEF_KEYFRAME_FREQ_FORCE;
320 
321   enc->speed_level = THEORA_DEF_SPEEDLEVEL;
322   enc->vp3_compatible = THEORA_DEF_VP3_COMPATIBLE;
323   enc->drop_frames = THEORA_DEF_DROP_FRAMES;
324   enc->cap_overflow = THEORA_DEF_CAP_OVERFLOW;
325   enc->cap_underflow = THEORA_DEF_CAP_UNDERFLOW;
326   enc->rate_buffer = THEORA_DEF_RATE_BUFFER;
327 
328   enc->multipass_mode = THEORA_DEF_MULTIPASS_MODE;
329   enc->multipass_cache_file = THEORA_DEF_MULTIPASS_CACHE_FILE;
330 }
331 
332 static void
theora_enc_clear_multipass_cache(GstTheoraEnc * enc)333 theora_enc_clear_multipass_cache (GstTheoraEnc * enc)
334 {
335   if (enc->multipass_cache_fd) {
336     g_io_channel_shutdown (enc->multipass_cache_fd, TRUE, NULL);
337     g_io_channel_unref (enc->multipass_cache_fd);
338     enc->multipass_cache_fd = NULL;
339   }
340 
341   if (enc->multipass_cache_adapter) {
342     gst_object_unref (enc->multipass_cache_adapter);
343     enc->multipass_cache_adapter = NULL;
344   }
345 }
346 
347 static void
theora_enc_finalize(GObject * object)348 theora_enc_finalize (GObject * object)
349 {
350   GstTheoraEnc *enc = GST_THEORA_ENC (object);
351 
352   GST_DEBUG_OBJECT (enc, "Finalizing");
353   if (enc->encoder)
354     th_encode_free (enc->encoder);
355   th_comment_clear (&enc->comment);
356   th_info_clear (&enc->info);
357   g_free (enc->multipass_cache_file);
358 
359   theora_enc_clear_multipass_cache (enc);
360 
361   if (enc->input_state)
362     gst_video_codec_state_unref (enc->input_state);
363 
364   G_OBJECT_CLASS (parent_class)->finalize (object);
365 }
366 
367 static gboolean
theora_enc_flush(GstVideoEncoder * encoder)368 theora_enc_flush (GstVideoEncoder * encoder)
369 {
370   GstTheoraEnc *enc = GST_THEORA_ENC (encoder);
371   ogg_uint32_t keyframe_force;
372   int rate_flags;
373 
374 
375   if (enc->input_state == NULL) {
376     GST_INFO_OBJECT (enc, "Not configured yet, returning FALSE");
377 
378     return FALSE;
379   }
380 
381   GST_OBJECT_LOCK (enc);
382   enc->info.target_bitrate = enc->video_bitrate;
383   enc->info.quality = enc->video_quality;
384   enc->bitrate_changed = FALSE;
385   enc->quality_changed = FALSE;
386   GST_OBJECT_UNLOCK (enc);
387 
388   if (enc->encoder)
389     th_encode_free (enc->encoder);
390 
391   enc->encoder = th_encode_alloc (&enc->info);
392   /* We ensure this function cannot fail. */
393   g_assert (enc->encoder != NULL);
394   th_encode_ctl (enc->encoder, TH_ENCCTL_SET_SPLEVEL, &enc->speed_level,
395       sizeof (enc->speed_level));
396   th_encode_ctl (enc->encoder, TH_ENCCTL_SET_VP3_COMPATIBLE,
397       &enc->vp3_compatible, sizeof (enc->vp3_compatible));
398 
399   rate_flags = 0;
400   if (enc->drop_frames)
401     rate_flags |= TH_RATECTL_DROP_FRAMES;
402   if (enc->drop_frames)
403     rate_flags |= TH_RATECTL_CAP_OVERFLOW;
404   if (enc->drop_frames)
405     rate_flags |= TH_RATECTL_CAP_UNDERFLOW;
406   th_encode_ctl (enc->encoder, TH_ENCCTL_SET_RATE_FLAGS,
407       &rate_flags, sizeof (rate_flags));
408 
409   if (enc->rate_buffer) {
410     th_encode_ctl (enc->encoder, TH_ENCCTL_SET_RATE_BUFFER,
411         &enc->rate_buffer, sizeof (enc->rate_buffer));
412   } else {
413     /* FIXME */
414   }
415 
416   keyframe_force = enc->keyframe_auto ?
417       enc->keyframe_force : enc->keyframe_freq;
418   th_encode_ctl (enc->encoder, TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
419       &keyframe_force, sizeof (keyframe_force));
420 
421   /* Get placeholder data */
422   if (enc->multipass_cache_fd
423       && enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS)
424     theora_enc_write_multipass_cache (enc, TRUE, FALSE);
425 
426   return TRUE;
427 }
428 
429 static gboolean
theora_enc_start(GstVideoEncoder * benc)430 theora_enc_start (GstVideoEncoder * benc)
431 {
432   GstTheoraEnc *enc;
433 
434   GST_DEBUG_OBJECT (benc, "start: init theora");
435   enc = GST_THEORA_ENC (benc);
436 
437   if (enc->multipass_mode >= MULTIPASS_MODE_FIRST_PASS) {
438     GError *err = NULL;
439 
440     if (!enc->multipass_cache_file) {
441       GST_ELEMENT_ERROR (enc, LIBRARY, SETTINGS, (NULL), (NULL));
442       return FALSE;
443     }
444     enc->multipass_cache_fd =
445         g_io_channel_new_file (enc->multipass_cache_file,
446         (enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS ? "w" : "r"), &err);
447 
448     if (enc->multipass_mode == MULTIPASS_MODE_SECOND_PASS)
449       enc->multipass_cache_adapter = gst_adapter_new ();
450 
451     if (!enc->multipass_cache_fd) {
452       GST_ELEMENT_ERROR (enc, RESOURCE, OPEN_READ, (NULL),
453           ("Failed to open multipass cache file: %s", err->message));
454       g_error_free (err);
455       return FALSE;
456     }
457 
458     g_io_channel_set_encoding (enc->multipass_cache_fd, NULL, NULL);
459   }
460 
461   enc->packetno = 0;
462   enc->initialised = FALSE;
463 
464   return TRUE;
465 }
466 
467 static gboolean
theora_enc_stop(GstVideoEncoder * benc)468 theora_enc_stop (GstVideoEncoder * benc)
469 {
470   GstTheoraEnc *enc;
471 
472   GST_DEBUG_OBJECT (benc, "stop: clearing theora state");
473   enc = GST_THEORA_ENC (benc);
474 
475   if (enc->encoder)
476     th_encode_free (enc->encoder);
477   enc->encoder = NULL;
478   th_comment_clear (&enc->comment);
479   th_info_clear (&enc->info);
480 
481   if (enc->input_state)
482     gst_video_codec_state_unref (enc->input_state);
483   enc->input_state = NULL;
484 
485   /* Everything else is handled in reset() */
486   theora_enc_clear_multipass_cache (enc);
487 
488   return TRUE;
489 }
490 
491 static char *
theora_enc_get_supported_formats(void)492 theora_enc_get_supported_formats (void)
493 {
494   th_enc_ctx *encoder;
495   th_info info;
496   struct
497   {
498     th_pixel_fmt pixelformat;
499     const char *fourcc;
500   } formats[] = {
501     {
502     TH_PF_420, "I420"}, {
503     TH_PF_422, "Y42B"}, {
504     TH_PF_444, "Y444"}
505   };
506   GString *string = NULL;
507   guint i;
508 
509   th_info_init (&info);
510   info.frame_width = 16;
511   info.frame_height = 16;
512   info.fps_numerator = 25;
513   info.fps_denominator = 1;
514   for (i = 0; i < G_N_ELEMENTS (formats); i++) {
515     info.pixel_fmt = formats[i].pixelformat;
516 
517     encoder = th_encode_alloc (&info);
518     if (encoder == NULL)
519       continue;
520 
521     GST_LOG ("format %s is supported", formats[i].fourcc);
522     th_encode_free (encoder);
523 
524     if (string == NULL) {
525       string = g_string_new (formats[i].fourcc);
526     } else {
527       g_string_append (string, ", ");
528       g_string_append (string, formats[i].fourcc);
529     }
530   }
531   th_info_clear (&info);
532 
533   return string == NULL ? NULL : g_string_free (string, FALSE);
534 }
535 
536 static GstCaps *
theora_enc_getcaps(GstVideoEncoder * encoder,GstCaps * filter)537 theora_enc_getcaps (GstVideoEncoder * encoder, GstCaps * filter)
538 {
539   GstCaps *caps, *ret;
540   char *supported_formats, *caps_string;
541 
542   supported_formats = theora_enc_get_supported_formats ();
543   if (!supported_formats) {
544     GST_WARNING ("no supported formats found. Encoder disabled?");
545     return gst_caps_new_empty ();
546   }
547 
548   caps_string = g_strdup_printf ("video/x-raw, "
549       "format = (string) { %s }, "
550       "framerate = (fraction) [1/MAX, MAX], "
551       "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]",
552       supported_formats);
553   caps = gst_caps_from_string (caps_string);
554   g_free (caps_string);
555   g_free (supported_formats);
556   GST_DEBUG ("Supported caps: %" GST_PTR_FORMAT, caps);
557 
558   ret = gst_video_encoder_proxy_getcaps (encoder, caps, filter);
559   gst_caps_unref (caps);
560 
561   return ret;
562 }
563 
564 static gboolean
theora_enc_set_format(GstVideoEncoder * benc,GstVideoCodecState * state)565 theora_enc_set_format (GstVideoEncoder * benc, GstVideoCodecState * state)
566 {
567   GstTheoraEnc *enc = GST_THEORA_ENC (benc);
568   GstVideoInfo *info = &state->info;
569 
570   enc->width = GST_VIDEO_INFO_WIDTH (info);
571   enc->height = GST_VIDEO_INFO_HEIGHT (info);
572 
573   th_info_clear (&enc->info);
574   th_info_init (&enc->info);
575   /* Theora has a divisible-by-sixteen restriction for the encoded video size but
576    * we can define a picture area using pic_width/pic_height */
577   enc->info.frame_width = GST_ROUND_UP_16 (enc->width);
578   enc->info.frame_height = GST_ROUND_UP_16 (enc->height);
579   enc->info.pic_width = enc->width;
580   enc->info.pic_height = enc->height;
581   switch (GST_VIDEO_INFO_FORMAT (info)) {
582     case GST_VIDEO_FORMAT_I420:
583       enc->info.pixel_fmt = TH_PF_420;
584       break;
585     case GST_VIDEO_FORMAT_Y42B:
586       enc->info.pixel_fmt = TH_PF_422;
587       break;
588     case GST_VIDEO_FORMAT_Y444:
589       enc->info.pixel_fmt = TH_PF_444;
590       break;
591     default:
592       g_assert_not_reached ();
593   }
594 
595   enc->info.fps_numerator = enc->fps_n = GST_VIDEO_INFO_FPS_N (info);
596   enc->info.fps_denominator = enc->fps_d = GST_VIDEO_INFO_FPS_D (info);
597   enc->info.aspect_numerator = GST_VIDEO_INFO_PAR_N (info);
598   enc->info.aspect_denominator = GST_VIDEO_INFO_PAR_D (info);
599 
600   enc->info.colorspace = TH_CS_UNSPECIFIED;
601 
602   /* Save input state */
603   if (enc->input_state)
604     gst_video_codec_state_unref (enc->input_state);
605   enc->input_state = gst_video_codec_state_ref (state);
606 
607   /* as done in theora */
608   enc->info.keyframe_granule_shift = _ilog (enc->keyframe_force - 1);
609   GST_DEBUG_OBJECT (enc,
610       "keyframe_frequency_force is %d, granule shift is %d",
611       enc->keyframe_force, enc->info.keyframe_granule_shift);
612 
613   theora_enc_flush (benc);
614   enc->initialised = TRUE;
615 
616   return TRUE;
617 }
618 
619 static GstFlowReturn
theora_enc_pre_push(GstVideoEncoder * benc,GstVideoCodecFrame * frame)620 theora_enc_pre_push (GstVideoEncoder * benc, GstVideoCodecFrame * frame)
621 {
622   GstTheoraEnc *enc = GST_THEORA_ENC (benc);
623   guint64 pfn;
624 
625   /* see ext/ogg/README; OFFSET_END takes "our" granulepos, OFFSET its
626    * time representation */
627   /* granulepos from sync frame */
628   pfn = frame->presentation_frame_number - frame->distance_from_sync;
629   /* correct to correspond to linear running time */
630   pfn -= enc->pfn_offset;
631   pfn += enc->granulepos_offset + 1;
632   /* granulepos */
633   GST_BUFFER_OFFSET_END (frame->output_buffer) =
634       (pfn << enc->info.keyframe_granule_shift) + frame->distance_from_sync;
635   GST_BUFFER_OFFSET (frame->output_buffer) = granulepos_to_timestamp (enc,
636       GST_BUFFER_OFFSET_END (frame->output_buffer));
637 
638   return GST_FLOW_OK;
639 }
640 
641 static GstFlowReturn
theora_push_packet(GstTheoraEnc * enc,ogg_packet * packet)642 theora_push_packet (GstTheoraEnc * enc, ogg_packet * packet)
643 {
644   GstVideoEncoder *benc;
645   GstFlowReturn ret;
646   GstVideoCodecFrame *frame;
647 
648   benc = GST_VIDEO_ENCODER (enc);
649 
650   frame = gst_video_encoder_get_oldest_frame (benc);
651   if (gst_video_encoder_allocate_output_frame (benc, frame,
652           packet->bytes) != GST_FLOW_OK) {
653     GST_WARNING_OBJECT (enc, "Could not allocate buffer");
654     gst_video_codec_frame_unref (frame);
655     ret = GST_FLOW_ERROR;
656     goto done;
657   }
658 
659   if (packet->bytes > 0)
660     gst_buffer_fill (frame->output_buffer, 0, packet->packet, packet->bytes);
661 
662   /* the second most significant bit of the first data byte is cleared
663    * for keyframes */
664   if (packet->bytes > 0 && (packet->packet[0] & 0x40) == 0) {
665     GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
666   } else {
667     GST_VIDEO_CODEC_FRAME_UNSET_SYNC_POINT (frame);
668   }
669   enc->packetno++;
670 
671   ret = gst_video_encoder_finish_frame (benc, frame);
672 
673 done:
674   return ret;
675 }
676 
677 static GstCaps *
theora_set_header_on_caps(GstCaps * caps,GList * buffers)678 theora_set_header_on_caps (GstCaps * caps, GList * buffers)
679 {
680   GstStructure *structure;
681   GValue array = { 0 };
682   GValue value = { 0 };
683   GstBuffer *buffer;
684   GList *walk;
685 
686   caps = gst_caps_make_writable (caps);
687   structure = gst_caps_get_structure (caps, 0);
688 
689   /* put copies of the buffers in a fixed list */
690   g_value_init (&array, GST_TYPE_ARRAY);
691 
692   for (walk = buffers; walk; walk = walk->next) {
693     buffer = walk->data;
694     g_value_init (&value, GST_TYPE_BUFFER);
695     gst_value_set_buffer (&value, buffer);
696     gst_value_array_append_value (&array, &value);
697     g_value_unset (&value);
698   }
699 
700   gst_structure_take_value (structure, "streamheader", &array);
701 
702   return caps;
703 }
704 
705 static void
theora_enc_init_buffer(th_ycbcr_buffer buf,GstVideoFrame * frame)706 theora_enc_init_buffer (th_ycbcr_buffer buf, GstVideoFrame * frame)
707 {
708   GstVideoInfo vinfo;
709   guint i;
710 
711   /* According to Theora developer Timothy Terriberry, the Theora
712    * encoder will not use memory outside of pic_width/height, even when
713    * the frame size is bigger. The values outside this region will be encoded
714    * to default values.
715    * Due to this, setting the frame's width/height as the buffer width/height
716    * is perfectly ok, even though it does not strictly look ok.
717    */
718 
719   gst_video_info_init (&vinfo);
720   gst_video_info_set_format (&vinfo, GST_VIDEO_FRAME_FORMAT (frame),
721       GST_ROUND_UP_16 (GST_VIDEO_FRAME_WIDTH (frame)),
722       GST_ROUND_UP_16 (GST_VIDEO_FRAME_HEIGHT (frame)));
723 
724   for (i = 0; i < 3; i++) {
725     buf[i].width = GST_VIDEO_INFO_COMP_WIDTH (&vinfo, i);
726     buf[i].height = GST_VIDEO_INFO_COMP_HEIGHT (&vinfo, i);
727     buf[i].data = GST_VIDEO_FRAME_COMP_DATA (frame, i);
728     buf[i].stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, i);
729   }
730 }
731 
732 static gboolean
theora_enc_read_multipass_cache(GstTheoraEnc * enc)733 theora_enc_read_multipass_cache (GstTheoraEnc * enc)
734 {
735   GstBuffer *cache_buf;
736   const guint8 *cache_data;
737   gsize bytes_read = 0;
738   gssize bytes_consumed = 0;
739   GIOStatus stat = G_IO_STATUS_NORMAL;
740   gboolean done = FALSE;
741 
742   while (!done) {
743     if (gst_adapter_available (enc->multipass_cache_adapter) == 0) {
744       GstMapInfo minfo;
745 
746       cache_buf = gst_buffer_new_allocate (NULL, 512, NULL);
747 
748       gst_buffer_map (cache_buf, &minfo, GST_MAP_WRITE);
749 
750       stat = g_io_channel_read_chars (enc->multipass_cache_fd,
751           (gchar *) minfo.data, minfo.size, &bytes_read, NULL);
752 
753       if (bytes_read <= 0) {
754         gst_buffer_unmap (cache_buf, &minfo);
755         gst_buffer_unref (cache_buf);
756         break;
757       } else {
758         gst_buffer_unmap (cache_buf, &minfo);
759         gst_buffer_resize (cache_buf, 0, bytes_read);
760 
761         gst_adapter_push (enc->multipass_cache_adapter, cache_buf);
762       }
763     }
764     if (gst_adapter_available (enc->multipass_cache_adapter) == 0)
765       break;
766 
767     bytes_read =
768         MIN (gst_adapter_available (enc->multipass_cache_adapter), 512);
769 
770     cache_data = gst_adapter_map (enc->multipass_cache_adapter, bytes_read);
771 
772     bytes_consumed =
773         th_encode_ctl (enc->encoder, TH_ENCCTL_2PASS_IN, (guint8 *) cache_data,
774         bytes_read);
775     gst_adapter_unmap (enc->multipass_cache_adapter);
776 
777     done = bytes_consumed <= 0;
778     if (bytes_consumed > 0)
779       gst_adapter_flush (enc->multipass_cache_adapter, bytes_consumed);
780   }
781 
782   if (stat == G_IO_STATUS_ERROR || (stat == G_IO_STATUS_EOF && bytes_read == 0)
783       || bytes_consumed < 0) {
784     GST_ELEMENT_ERROR (enc, RESOURCE, READ, (NULL),
785         ("Failed to read multipass cache file"));
786     return FALSE;
787   }
788   return TRUE;
789 }
790 
791 static gboolean
theora_enc_write_multipass_cache(GstTheoraEnc * enc,gboolean begin,gboolean eos)792 theora_enc_write_multipass_cache (GstTheoraEnc * enc, gboolean begin,
793     gboolean eos)
794 {
795   GError *err = NULL;
796   GIOStatus stat = G_IO_STATUS_NORMAL;
797   gint bytes_read = 0;
798   gsize bytes_written = 0;
799   gchar *buf;
800 
801   if (begin) {
802     stat = g_io_channel_seek_position (enc->multipass_cache_fd, 0, G_SEEK_SET,
803         &err);
804 
805     if (stat == G_IO_STATUS_ERROR) {
806       if (eos)
807         GST_ELEMENT_WARNING (enc, RESOURCE, WRITE, (NULL),
808             ("Failed to seek to beginning of multipass cache file: %s",
809                 err->message));
810       else
811         GST_ELEMENT_ERROR (enc, RESOURCE, WRITE, (NULL),
812             ("Failed to seek to beginning of multipass cache file: %s",
813                 err->message));
814       g_error_free (err);
815       return FALSE;
816     }
817   }
818 
819 
820   do {
821     bytes_read =
822         th_encode_ctl (enc->encoder, TH_ENCCTL_2PASS_OUT, &buf, sizeof (buf));
823     if (bytes_read > 0)
824       g_io_channel_write_chars (enc->multipass_cache_fd, buf, bytes_read,
825           &bytes_written, &err);
826   } while (bytes_read > 0 && bytes_written > 0 && !err);
827 
828   if (bytes_read < 0 || err) {
829     if (bytes_read < 0) {
830       GST_ELEMENT_ERROR (enc, RESOURCE, WRITE, (NULL),
831           ("Failed to read multipass cache data: %d", bytes_read));
832     } else {
833       GST_ELEMENT_ERROR (enc, RESOURCE, WRITE, (NULL),
834           ("Failed to write multipass cache file: %s", err->message));
835     }
836     if (err)
837       g_error_free (err);
838 
839     return FALSE;
840   }
841 
842   return TRUE;
843 }
844 
845 static void
theora_enc_reset_ts(GstTheoraEnc * enc,GstClockTime running_time,gint pfn)846 theora_enc_reset_ts (GstTheoraEnc * enc, GstClockTime running_time, gint pfn)
847 {
848   enc->granulepos_offset =
849       gst_util_uint64_scale (running_time, enc->fps_n, GST_SECOND * enc->fps_d);
850   enc->timestamp_offset = running_time;
851   enc->pfn_offset = pfn;
852 }
853 
854 static GstBuffer *
theora_enc_buffer_from_header_packet(GstTheoraEnc * enc,ogg_packet * packet)855 theora_enc_buffer_from_header_packet (GstTheoraEnc * enc, ogg_packet * packet)
856 {
857   GstBuffer *outbuf;
858 
859   outbuf =
860       gst_video_encoder_allocate_output_buffer (GST_VIDEO_ENCODER (enc),
861       packet->bytes);
862   gst_buffer_fill (outbuf, 0, packet->packet, packet->bytes);
863   GST_BUFFER_OFFSET (outbuf) = 0;
864   GST_BUFFER_OFFSET_END (outbuf) = 0;
865   GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
866   GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
867   GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_HEADER);
868 
869   GST_DEBUG ("created header packet buffer, %u bytes",
870       (guint) gst_buffer_get_size (outbuf));
871   return outbuf;
872 }
873 
874 static GstFlowReturn
theora_enc_handle_frame(GstVideoEncoder * benc,GstVideoCodecFrame * frame)875 theora_enc_handle_frame (GstVideoEncoder * benc, GstVideoCodecFrame * frame)
876 {
877   GstTheoraEnc *enc;
878   ogg_packet op;
879   GstClockTime timestamp, running_time;
880   GstFlowReturn ret;
881   gboolean force_keyframe;
882 
883   enc = GST_THEORA_ENC (benc);
884 
885   /* we keep track of two timelines.
886    * - The timestamps from the incoming buffers, which we copy to the outgoing
887    *   encoded buffers as-is. We need to do this as we simply forward the
888    *   newsegment events.
889    * - The running_time of the buffers, which we use to construct the granulepos
890    *   in the packets.
891    */
892   timestamp = frame->pts;
893 
894   /* incoming buffers are clipped, so this should be positive */
895   running_time =
896       gst_segment_to_running_time (&GST_VIDEO_ENCODER_INPUT_SEGMENT (enc),
897       GST_FORMAT_TIME, timestamp);
898 
899   GST_OBJECT_LOCK (enc);
900   if (enc->bitrate_changed) {
901     long int bitrate = enc->video_bitrate;
902 
903     th_encode_ctl (enc->encoder, TH_ENCCTL_SET_BITRATE, &bitrate,
904         sizeof (long int));
905     enc->bitrate_changed = FALSE;
906   }
907 
908   if (enc->quality_changed) {
909     long int quality = enc->video_quality;
910 
911     th_encode_ctl (enc->encoder, TH_ENCCTL_SET_QUALITY, &quality,
912         sizeof (long int));
913     enc->quality_changed = FALSE;
914   }
915 
916   /* see if we need to schedule a keyframe */
917   force_keyframe = GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame);
918   GST_OBJECT_UNLOCK (enc);
919 
920   if (enc->packetno == 0) {
921     /* no packets written yet, setup headers */
922     GstCaps *caps;
923     GstBuffer *buf;
924     GList *buffers = NULL;
925     int result;
926     GstVideoCodecState *state;
927 
928     enc->granulepos_offset = 0;
929     enc->timestamp_offset = 0;
930 
931     GST_DEBUG_OBJECT (enc, "output headers");
932     /* Theora streams begin with three headers; the initial header (with
933        most of the codec setup parameters) which is mandated by the Ogg
934        bitstream spec.  The second header holds any comment fields.  The
935        third header holds the bitstream codebook.  We merely need to
936        make the headers, then pass them to libtheora one at a time;
937        libtheora handles the additional Ogg bitstream constraints */
938 
939     /* create the remaining theora headers */
940     th_comment_clear (&enc->comment);
941     th_comment_init (&enc->comment);
942 
943     while ((result =
944             th_encode_flushheader (enc->encoder, &enc->comment, &op)) > 0) {
945       buf = theora_enc_buffer_from_header_packet (enc, &op);
946       buffers = g_list_prepend (buffers, buf);
947     }
948     if (result < 0) {
949       g_list_foreach (buffers, (GFunc) gst_buffer_unref, NULL);
950       g_list_free (buffers);
951       goto encoder_disabled;
952     }
953 
954     buffers = g_list_reverse (buffers);
955 
956     /* mark buffers and put on caps */
957     caps = gst_caps_new_empty_simple ("video/x-theora");
958     caps = theora_set_header_on_caps (caps, buffers);
959     state = gst_video_encoder_set_output_state (benc, caps, enc->input_state);
960 
961     GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, state->caps);
962 
963     gst_video_codec_state_unref (state);
964 
965     gst_video_encoder_negotiate (GST_VIDEO_ENCODER (enc));
966 
967     gst_video_encoder_set_headers (benc, buffers);
968 
969     theora_enc_reset_ts (enc, running_time, frame->presentation_frame_number);
970   }
971 
972   {
973     th_ycbcr_buffer ycbcr;
974     gint res, keyframe_interval;
975     GstVideoFrame vframe;
976 
977     if (force_keyframe) {
978       /* if we want a keyframe, temporarily reset the max keyframe interval
979        * to 1, which will cause libtheora to emit one. There is no API to
980        * request a keyframe at the moment. */
981       keyframe_interval = 1;
982       th_encode_ctl (enc->encoder, TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
983           &keyframe_interval, sizeof (keyframe_interval));
984     }
985 
986     if (enc->multipass_cache_fd
987         && enc->multipass_mode == MULTIPASS_MODE_SECOND_PASS) {
988       if (!theora_enc_read_multipass_cache (enc)) {
989         ret = GST_FLOW_ERROR;
990         goto multipass_read_failed;
991       }
992     }
993 
994     gst_video_frame_map (&vframe, &enc->input_state->info, frame->input_buffer,
995         GST_MAP_READ);
996     theora_enc_init_buffer (ycbcr, &vframe);
997 
998     res = th_encode_ycbcr_in (enc->encoder, ycbcr);
999     gst_video_frame_unmap (&vframe);
1000 
1001     /* none of the failure cases can happen here */
1002     g_assert (res == 0);
1003 
1004     if (enc->multipass_cache_fd
1005         && enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS) {
1006       if (!theora_enc_write_multipass_cache (enc, FALSE, FALSE)) {
1007         ret = GST_FLOW_ERROR;
1008         goto multipass_write_failed;
1009       }
1010     }
1011 
1012     ret = GST_FLOW_OK;
1013     while (th_encode_packetout (enc->encoder, 0, &op)) {
1014       /* Reset the max keyframe interval to its original state, and reset
1015        * the flag so we don't create more keyframes if we loop */
1016       if (force_keyframe) {
1017         keyframe_interval =
1018             enc->keyframe_auto ? enc->keyframe_force : enc->keyframe_freq;
1019         th_encode_ctl (enc->encoder, TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
1020             &keyframe_interval, sizeof (keyframe_interval));
1021         force_keyframe = FALSE;
1022       }
1023 
1024       ret = theora_push_packet (enc, &op);
1025       if (ret != GST_FLOW_OK)
1026         goto beach;
1027     }
1028   }
1029 
1030 beach:
1031   gst_video_codec_frame_unref (frame);
1032   return ret;
1033 
1034   /* ERRORS */
1035 multipass_read_failed:
1036   {
1037     gst_video_codec_frame_unref (frame);
1038     return ret;
1039   }
1040 multipass_write_failed:
1041   {
1042     gst_video_codec_frame_unref (frame);
1043     return ret;
1044   }
1045 encoder_disabled:
1046   {
1047     gst_video_codec_frame_unref (frame);
1048     GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL),
1049         ("libtheora has been compiled with the encoder disabled"));
1050     return GST_FLOW_ERROR;
1051   }
1052 }
1053 
1054 static gboolean
theora_enc_finish(GstVideoEncoder * benc)1055 theora_enc_finish (GstVideoEncoder * benc)
1056 {
1057   GstTheoraEnc *enc;
1058   ogg_packet op;
1059 
1060   enc = GST_THEORA_ENC (benc);
1061 
1062   if (enc->initialised) {
1063     /* push last packet with eos flag, should not be called */
1064     while (th_encode_packetout (enc->encoder, 1, &op)) {
1065       theora_push_packet (enc, &op);
1066     }
1067   }
1068   if (enc->initialised && enc->multipass_cache_fd
1069       && enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS)
1070     theora_enc_write_multipass_cache (enc, TRUE, TRUE);
1071 
1072   theora_enc_clear_multipass_cache (enc);
1073 
1074   return TRUE;
1075 }
1076 
1077 static gboolean
theora_enc_propose_allocation(GstVideoEncoder * encoder,GstQuery * query)1078 theora_enc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
1079 {
1080   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1081 
1082   return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
1083       query);
1084 }
1085 
1086 static void
theora_enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1087 theora_enc_set_property (GObject * object, guint prop_id,
1088     const GValue * value, GParamSpec * pspec)
1089 {
1090   GstTheoraEnc *enc = GST_THEORA_ENC (object);
1091 
1092   switch (prop_id) {
1093     case PROP_BITRATE:
1094       GST_OBJECT_LOCK (enc);
1095       enc->video_bitrate = g_value_get_int (value) * 1000;
1096       enc->video_quality = 0;
1097       enc->bitrate_changed = TRUE;
1098       GST_OBJECT_UNLOCK (enc);
1099       break;
1100     case PROP_QUALITY:
1101       GST_OBJECT_LOCK (enc);
1102       if (GST_STATE (enc) >= GST_STATE_PAUSED && enc->video_quality == 0) {
1103         GST_WARNING_OBJECT (object, "Can't change from bitrate to quality mode"
1104             " while playing");
1105       } else {
1106         enc->video_quality = g_value_get_int (value);
1107         enc->video_bitrate = 0;
1108         enc->quality_changed = TRUE;
1109       }
1110       GST_OBJECT_UNLOCK (enc);
1111       break;
1112     case PROP_KEYFRAME_AUTO:
1113       enc->keyframe_auto = g_value_get_boolean (value);
1114       break;
1115     case PROP_KEYFRAME_FREQ:
1116       enc->keyframe_freq = g_value_get_int (value);
1117       break;
1118     case PROP_KEYFRAME_FREQ_FORCE:
1119       enc->keyframe_force = g_value_get_int (value);
1120       break;
1121     case PROP_SPEEDLEVEL:
1122       enc->speed_level = g_value_get_int (value);
1123       break;
1124     case PROP_VP3_COMPATIBLE:
1125       enc->vp3_compatible = g_value_get_boolean (value);
1126       break;
1127     case PROP_DROP_FRAMES:
1128       enc->drop_frames = g_value_get_boolean (value);
1129       break;
1130     case PROP_CAP_OVERFLOW:
1131       enc->cap_overflow = g_value_get_boolean (value);
1132       break;
1133     case PROP_CAP_UNDERFLOW:
1134       enc->cap_underflow = g_value_get_boolean (value);
1135       break;
1136     case PROP_RATE_BUFFER:
1137       enc->rate_buffer = g_value_get_int (value);
1138       break;
1139     case PROP_MULTIPASS_CACHE_FILE:
1140       enc->multipass_cache_file = g_value_dup_string (value);
1141       break;
1142     case PROP_MULTIPASS_MODE:
1143       enc->multipass_mode = g_value_get_enum (value);
1144       break;
1145     default:
1146       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1147       break;
1148   }
1149 }
1150 
1151 static void
theora_enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1152 theora_enc_get_property (GObject * object, guint prop_id,
1153     GValue * value, GParamSpec * pspec)
1154 {
1155   GstTheoraEnc *enc = GST_THEORA_ENC (object);
1156 
1157   switch (prop_id) {
1158     case PROP_BITRATE:
1159       GST_OBJECT_LOCK (enc);
1160       g_value_set_int (value, enc->video_bitrate / 1000);
1161       GST_OBJECT_UNLOCK (enc);
1162       break;
1163     case PROP_QUALITY:
1164       GST_OBJECT_LOCK (enc);
1165       g_value_set_int (value, enc->video_quality);
1166       GST_OBJECT_UNLOCK (enc);
1167       break;
1168     case PROP_KEYFRAME_AUTO:
1169       g_value_set_boolean (value, enc->keyframe_auto);
1170       break;
1171     case PROP_KEYFRAME_FREQ:
1172       g_value_set_int (value, enc->keyframe_freq);
1173       break;
1174     case PROP_KEYFRAME_FREQ_FORCE:
1175       g_value_set_int (value, enc->keyframe_force);
1176       break;
1177     case PROP_SPEEDLEVEL:
1178       g_value_set_int (value, enc->speed_level);
1179       break;
1180     case PROP_VP3_COMPATIBLE:
1181       g_value_set_boolean (value, enc->vp3_compatible);
1182       break;
1183     case PROP_DROP_FRAMES:
1184       g_value_set_boolean (value, enc->drop_frames);
1185       break;
1186     case PROP_CAP_OVERFLOW:
1187       g_value_set_boolean (value, enc->cap_overflow);
1188       break;
1189     case PROP_CAP_UNDERFLOW:
1190       g_value_set_boolean (value, enc->cap_underflow);
1191       break;
1192     case PROP_RATE_BUFFER:
1193       g_value_set_int (value, enc->rate_buffer);
1194       break;
1195     case PROP_MULTIPASS_CACHE_FILE:
1196       g_value_set_string (value, enc->multipass_cache_file);
1197       break;
1198     case PROP_MULTIPASS_MODE:
1199       g_value_set_enum (value, enc->multipass_mode);
1200       break;
1201     default:
1202       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1203       break;
1204   }
1205 }
1206