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