+
+/*****************************************************************************
+ * encoder_sys_t : theora encoder descriptor
+ *****************************************************************************/
+struct encoder_sys_t
+{
+ /*
+ * Input properties
+ */
+ vlc_bool_t b_headers;
+
+ /*
+ * Theora properties
+ */
+ theora_info ti; /* theora bitstream settings */
+ theora_comment tc; /* theora comment header */
+ theora_state td; /* theora bitstream user comments */
+
+ /*
+ * Common properties
+ */
+ mtime_t i_pts;
+};
+
+/*****************************************************************************
+ * OpenEncoder: probe the encoder and return score
+ *****************************************************************************/
+static int OpenEncoder( vlc_object_t *p_this )
+{
+ encoder_t *p_enc = (encoder_t *)p_this;
+ encoder_sys_t *p_sys = p_enc->p_sys;
+
+ if( p_enc->fmt_out.i_codec != VLC_FOURCC('t','h','e','o') )
+ {
+ return VLC_EGENERIC;
+ }
+
+ if( p_enc->fmt_in.video.i_width % 16 ||
+ p_enc->fmt_in.video.i_height % 16 )
+ {
+ msg_Err( p_enc, "Theora video encoding requires dimensions which are "
+ "multiples of 16. Which is not the case here (%dx%d)",
+ p_enc->fmt_in.video.i_width, p_enc->fmt_in.video.i_height );
+ return VLC_EGENERIC;
+ }
+
+ /* Allocate the memory needed to store the decoder's structure */
+ if( ( p_sys = (encoder_sys_t *)malloc(sizeof(encoder_sys_t)) ) == NULL )
+ {
+ msg_Err( p_enc, "out of memory" );
+ return VLC_EGENERIC;
+ }
+ p_enc->p_sys = p_sys;
+
+ p_enc->pf_header = Headers;
+ p_enc->pf_encode_video = Encode;
+ p_enc->fmt_in.i_codec = VLC_FOURCC('I','4','2','0');
+
+#define frame_x_offset 0
+#define frame_y_offset 0
+#define video_hzn 25
+#define video_hzd 1
+#define video_q 5
+
+ theora_info_init( &p_sys->ti );
+
+ p_sys->ti.width = p_enc->fmt_in.video.i_width;
+ p_sys->ti.height = p_enc->fmt_in.video.i_height;
+ p_sys->ti.frame_width = p_enc->fmt_in.video.i_width;
+ p_sys->ti.frame_height = p_enc->fmt_in.video.i_height;
+ p_sys->ti.offset_x = frame_x_offset;
+ p_sys->ti.offset_y = frame_y_offset;
+ p_sys->ti.fps_numerator = video_hzn;
+ p_sys->ti.fps_denominator = video_hzd;
+
+ if( p_enc->fmt_in.video.i_aspect )
+ {
+ p_sys->ti.aspect_numerator = p_enc->fmt_in.video.i_aspect;
+ p_sys->ti.aspect_denominator = VOUT_ASPECT_FACTOR;
+ }
+ else
+ {
+ p_sys->ti.aspect_numerator = 4;
+ p_sys->ti.aspect_denominator = 3;
+ }
+
+ p_sys->ti.target_bitrate = p_enc->fmt_out.i_bitrate;
+ p_sys->ti.quality = video_q;
+
+ p_sys->ti.dropframes_p = 0;
+ p_sys->ti.quick_p = 1;
+ p_sys->ti.keyframe_auto_p = 1;
+ p_sys->ti.keyframe_frequency = 64;
+ p_sys->ti.keyframe_frequency_force = 64;
+ p_sys->ti.keyframe_data_target_bitrate = p_enc->fmt_out.i_bitrate * 1.5;
+ p_sys->ti.keyframe_auto_threshold = 80;
+ p_sys->ti.keyframe_mindistance = 8;
+ p_sys->ti.noise_sensitivity = 1;
+
+ theora_encode_init( &p_sys->td, &p_sys->ti );
+ theora_info_clear( &p_sys->ti );
+ theora_comment_init( &p_sys->tc );
+
+ p_sys->b_headers = VLC_FALSE;
+
+ return VLC_SUCCESS;
+}
+
+/****************************************************************************
+ * Encode: the whole thing
+ ****************************************************************************
+ * This function spits out ogg packets.
+ ****************************************************************************/
+static block_t *Headers( encoder_t *p_enc )
+{
+ encoder_sys_t *p_sys = p_enc->p_sys;
+ block_t *p_chain = NULL;
+
+ /* Create theora headers */
+ if( !p_sys->b_headers )
+ {
+ ogg_packet oggpackets;
+ int i;
+ block_t *p_block;
+
+ /* Ogg packet to block */
+ for( i = 0; i < 3; i++ )
+ {
+ switch( i )
+ {
+ case 0:
+ theora_encode_header( &p_sys->td, &oggpackets );
+ break;
+ case 1:
+ theora_encode_comment( &p_sys->tc, &oggpackets );
+ break;
+ case 2:
+ theora_encode_tables( &p_sys->td, &oggpackets );
+ break;
+ }
+
+ p_block = block_New( p_enc, oggpackets.bytes );
+ memcpy( p_block->p_buffer, oggpackets.packet, oggpackets.bytes );
+ p_block->i_dts = p_block->i_pts = p_block->i_length = 0;
+ block_ChainAppend( &p_chain, p_block );
+ }
+
+ p_sys->b_headers = VLC_TRUE;
+ }
+
+ return p_chain;
+}
+
+/****************************************************************************
+ * Encode: the whole thing
+ ****************************************************************************
+ * This function spits out ogg packets.
+ ****************************************************************************/
+static block_t *Encode( encoder_t *p_enc, picture_t *p_pict )
+{
+ encoder_sys_t *p_sys = p_enc->p_sys;
+ ogg_packet oggpacket;
+ block_t *p_block;
+ yuv_buffer yuv;
+
+ /* Theora is a one-frame-in, one-frame-out system. Submit a frame
+ * for compression and pull out the packet. */
+
+ yuv.y_width = p_pict->p[0].i_visible_pitch;
+ yuv.y_height = p_pict->p[0].i_lines;
+ yuv.y_stride = p_pict->p[0].i_pitch;
+
+ yuv.uv_width = p_pict->p[1].i_visible_pitch;
+ yuv.uv_height = p_pict->p[1].i_lines;
+ yuv.uv_stride = p_pict->p[1].i_pitch;
+
+ yuv.y = p_pict->p[0].p_pixels;
+ yuv.u = p_pict->p[1].p_pixels;
+ yuv.v = p_pict->p[2].p_pixels;
+
+ if( theora_encode_YUVin( &p_sys->td, &yuv ) < 0 )
+ {
+ msg_Warn( p_enc, "failed encoding a frame" );
+ return NULL;
+ }
+
+ theora_encode_packetout( &p_sys->td, 0, &oggpacket );
+
+ /* Ogg packet to block */
+ p_block = block_New( p_enc, oggpacket.bytes );
+ memcpy( p_block->p_buffer, oggpacket.packet, oggpacket.bytes );
+ p_block->i_dts = p_block->i_pts = p_pict->date;;
+
+ return p_block;
+}
+
+/*****************************************************************************
+ * CloseEncoder: theora encoder destruction
+ *****************************************************************************/
+static void CloseEncoder( vlc_object_t *p_this )
+{
+ encoder_t *p_enc = (encoder_t *)p_this;
+ encoder_sys_t *p_sys = p_enc->p_sys;
+
+ theora_info_clear( &p_sys->ti );
+ theora_comment_clear( &p_sys->tc );
+
+ free( p_sys );
+}