Index: libhb/Makefile
===================================================================
--- libhb/Makefile	(revision 1147)
+++ libhb/Makefile	(working copy)
@@ -24,7 +24,8 @@
 SRCS = common.c hb.c ports.c scan.c work.c decmpeg2.c encavcodec.c \
 	   update.c demuxmpeg.c fifo.c render.c reader.c muxcommon.c stream.c \
 	   muxmp4.c sync.c decsub.c deca52.c decdca.c encfaac.c declpcm.c encx264.c \
-	   decavcodec.c encxvid.c muxmkv.c muxavi.c enclame.c muxogm.c encvorbis.c \
+	   decavcodec.c encxvid.c muxmkv.c muxavi.c enclame.c muxogm.c \
+	   encvorbis.c enctheora.c \
 	   dvd.c  ipodutil.cpp deblock.c deinterlace.c denoise.c detelecine.c lang.c
 OTMP = $(SRCS:%.c=%.o) 
 OBJS = $(OTMP:%.cpp=%.o)
@@ -37,6 +38,7 @@
 		   ../contrib/lib/libmpeg2.a ../contrib/lib/libmpeg2convert.a \
 		   ../contrib/lib/libvorbis.a ../contrib/lib/libvorbisenc.a \
 		   ../contrib/lib/libvorbisfile.a ../contrib/lib/libogg.a \
+		   ../contrib/lib/libtheora.a \
 		   ../contrib/lib/libsamplerate.a ../contrib/lib/libx264.a \
 		   ../contrib/lib/libxvidcore.a  ../contrib/lib/libmp4v2.a \
 		   ../contrib/lib/libmkv.a ../contrib/lib/libswscale.a
@@ -48,6 +50,7 @@
 		   ../contrib/lib/libmp3lame.a ../contrib/lib/libmpeg2.a \
 		   ../contrib/lib/libmpeg2convert.a ../contrib/lib/libvorbis.a \
 		   ../contrib/lib/libvorbisenc.a ../contrib/lib/libvorbisfile.a \
+		   ../contrib/lib/libtheora.a \
 		   ../contrib/lib/libogg.a ../contrib/lib/libsamplerate.a \
 		   ../contrib/lib/libx264.a ../contrib/lib/libxvidcore.a \
 		   ../contrib/lib/libmp4v2.a ../contrib/lib/libmkv.a \
Index: libhb/hb.h
===================================================================
--- libhb/hb.h	(revision 1147)
+++ libhb/hb.h	(working copy)
@@ -32,6 +32,7 @@
 hb_register( &hb_encfaac ); \
 hb_register( &hb_enclame ); \
 hb_register( &hb_encvorbis ); \
+hb_register( &hb_enctheora ); \
 
 #define hb_init_express(v,u) \
 hb_init_real( v, u ); \
Index: libhb/enctheora.c
===================================================================
--- libhb/enctheora.c	(revision 0)
+++ libhb/enctheora.c	(revision 0)
@@ -0,0 +1,329 @@
+/* $Id: $
+
+   This file is part of the HandBrake source code.
+   Homepage: <http://handbrake.m0k.org/>.
+   It may be used under the terms of the GNU General Public License. */
+
+#include <stdarg.h>
+
+#include "hb.h"
+
+#include "theora/theora.h"
+
+int  enctheoraInit( hb_work_object_t *, hb_job_t * );
+int  enctheoraWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
+void enctheoraClose( hb_work_object_t * );
+
+hb_work_object_t hb_enctheora =
+{
+    WORK_ENCTHEORA,
+    "Theora encoder (libtheora)",
+    enctheoraInit,
+    enctheoraWork,
+    enctheoraClose
+};
+
+struct hb_work_private_s
+{
+    hb_job_t       * job;
+    theora_state     te;
+	ogg_packet       header[3];
+	yuv_buffer       yuv;
+	ogg_packet       op;
+};
+
+/***********************************************************************
+ * hb_work_enctheora_init
+ ***********************************************************************
+ *
+ **********************************************************************/
+int enctheoraInit( hb_work_object_t * w, hb_job_t * job )
+{
+    theora_info ti;
+	theora_comment tc;
+	
+    hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
+    w->private_data = pv;
+
+    pv->job = job;
+
+    theora_info_init(&ti);
+	ti.width = job->width;
+	ti.height = job->height;
+	
+	/* job frames should always be a multiple of 16 */
+	if ( job->width & 4 )
+	{
+		hb_log( "enctheora: WARNING: frame width %d is not a multiple of 16!", job->width);
+	}
+	ti.frame_width = job->width;
+	ti.offset_x = 0;
+	if ( job->height & 4 )
+	{
+		hb_log( "enctheora: WARNING: frame height %d is not a multiple of 16!", job->height);
+	}
+	ti.frame_height = job->height;
+	ti.offset_y = 0;
+	
+	ti.fps_numerator = job->vrate;
+	ti.fps_denominator = job->vrate_base;
+	
+    if( job->pixel_ratio )
+    {
+        ti.aspect_numerator = job->pixel_aspect_width;
+        ti.aspect_denominator = job->pixel_aspect_height;
+
+        hb_log( "enctheora: encoding with pixel aspect ratio %d:%d",
+                ti.aspect_numerator, ti.aspect_denominator );
+    }
+	else
+	{
+		ti.aspect_numerator = 0;
+		ti.aspect_denominator = 0;
+		
+		hb_log( "enctheora: encoding with an unknown aspect ratio (displayed as 1:1)" );
+	}
+	
+	/* todo: this should be guessable for DVD from the input resolution */
+	ti.colorspace = OC_CS_UNSPECIFIED;
+	ti.pixelformat = OC_PF_420;
+
+    if( job->vquality >= 0.0 && job->vquality <= 1.0 )
+    {
+		/* quality rate-control target */
+		ti.quality = 63.0 * job->vquality;
+		ti.target_bitrate = 0;
+    }
+    else
+    {
+        /* bitrate rate-control target */
+		ti.quality = 0;
+		ti.target_bitrate = job->vbitrate;
+	}
+	
+    hb_log( "enctheora: opening libtheora" );
+    theora_encode_init( &(pv->te), &ti );
+	theora_info_clear( &ti );
+
+    /* first header packet (parameters) */
+	theora_encode_header( &pv->te, &pv->header[0] );
+	
+	/* second header packet (metadata) */
+	theora_comment_init( &tc );
+	theora_comment_add_tag( &tc, "Encoder", "HandBreak" );
+	theora_encode_comment( &tc, &(pv->header[1]) );
+	theora_comment_clear( &tc );
+	
+	/* third header packet (tables) */
+	theora_encode_tables( &(pv->te), &(pv->header[2]) );
+	
+	return 0;
+}
+
+void enctheoraClose( hb_work_object_t * w )
+{
+    hb_work_private_t * pv = w->private_data;
+	
+	theora_clear( &(pv->te) );
+	ogg_packet_clear( &(pv->header[2]) );
+	ogg_packet_clear( &(pv->header[1]) );
+	ogg_packet_clear( &(pv->header[0]) );
+	ogg_packet_clear( &(pv->op) );
+
+    free( pv );
+    w->private_data = NULL;
+}
+
+int enctheoraWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+                  hb_buffer_t ** buf_out )
+{
+    hb_work_private_t * pv = w->private_data;
+    hb_job_t    * job = pv->job;
+    hb_buffer_t * in = *buf_in, * buf;
+
+#if 0
+    if( in->data )
+    {
+        /* XXX avoid this memcpy ? */
+        memcpy( pv->pic_in.img.plane[0], in->data, job->width * job->height );
+        if( job->grayscale )
+        {
+            /* XXX x264 has currently no option for grayscale encoding */
+            memset( pv->pic_in.img.plane[1], 0x80, job->width * job->height / 4 );
+            memset( pv->pic_in.img.plane[2], 0x80, job->width * job->height / 4 );
+        }
+        else
+        {
+            memcpy( pv->pic_in.img.plane[1], in->data + job->width * job->height,
+                    job->width * job->height / 4 );
+            memcpy( pv->pic_in.img.plane[2], in->data + 5 * job->width *
+                    job->height / 4, job->width * job->height / 4 );
+        }
+
+        if( in->new_chap && job->chapter_markers )
+        {
+            /* chapters have to start with an IDR frame so request that this
+               frame be coded as IDR. Since there may be up to 16 frames
+               currently buffered in the encoder remember the timestamp so
+               when this frame finally pops out of the encoder we'll mark
+               its buffer as the start of a chapter. */
+            pv->pic_in.i_type = X264_TYPE_IDR;
+            if( pv->next_chap == 0 )
+            {
+                pv->next_chap = in->start;
+            }
+            /* don't let 'work_loop' put a chapter mark on the wrong buffer */
+            in->new_chap = 0;
+        }
+        else
+        {
+            pv->pic_in.i_type = X264_TYPE_AUTO;
+        }
+        pv->pic_in.i_qpplus1 = 0;
+
+        // Remember current PTS value, use as DTS later
+        pv->dts_start[pv->dts_write_index & (MAX_INFLIGHT_FRAMES-1)] = in->start;
+        pv->dts_stop[pv->dts_write_index & (MAX_INFLIGHT_FRAMES-1)]  = in->stop;
+        pv->dts_write_index++;
+
+        /* Feed the input DTS to x264 so it can figure out proper output PTS */
+        pv->pic_in.i_pts = in->start;
+
+        x264_encoder_encode( pv->x264, &nal, &i_nal,
+                             &pv->pic_in, &pic_out );        
+    }
+    else
+    {
+        x264_encoder_encode( pv->x264, &nal, &i_nal,
+                             NULL, &pic_out );
+        /* No more delayed B frames */
+        if( i_nal == 0 )
+        {
+            *buf_out = NULL;
+            return HB_WORK_DONE;
+        }
+        else
+        {
+        /*  Since we output at least one more frame, drop another empty
+            one onto our input fifo.  We'll keep doing this automatically
+            until we stop getting frames out of the encoder. */
+            hb_fifo_push(w->fifo_in, hb_buffer_init(0));
+        }
+    }
+
+    if( i_nal )
+    {
+        /* Should be way too large */
+        buf        = hb_buffer_init( 3 * job->width * job->height / 2 );
+        buf->size  = 0;
+        buf->start = in->start;
+        buf->stop  = in->stop;
+        buf->frametype   = 0;
+
+        int64_t dts_start, dts_stop;
+
+        /* Get next DTS value to use */
+        dts_start = pv->dts_start[pv->dts_read_index & (MAX_INFLIGHT_FRAMES-1)];
+        dts_stop  = pv->dts_stop[pv->dts_read_index & (MAX_INFLIGHT_FRAMES-1)];
+        pv->dts_read_index++;
+
+        for( i = 0; i < i_nal; i++ )
+        {
+            int size, data;
+
+            data = buf->alloc - buf->size;
+            if( ( size = x264_nal_encode( buf->data + buf->size, &data,
+                                          1, &nal[i] ) ) < 1 )
+            {
+                continue;
+            }
+
+            if( job->mux & HB_MUX_AVI )
+            {
+                if( nal[i].i_ref_idc == NAL_PRIORITY_HIGHEST )
+                {
+                    buf->frametype = HB_FRAME_KEY;
+                }
+                buf->size += size;
+                continue;
+            }
+
+            /* H.264 in .mp4 */
+            switch( buf->data[buf->size+4] & 0x1f )
+            {
+                case 0x7:
+                case 0x8:
+                    /* SPS, PPS */
+                    break;
+
+                default:
+                    /* H.264 in mp4 (stolen from mp4creator) */
+                    buf->data[buf->size+0] = ( ( size - 4 ) >> 24 ) & 0xFF;
+                    buf->data[buf->size+1] = ( ( size - 4 ) >> 16 ) & 0xFF;
+                    buf->data[buf->size+2] = ( ( size - 4 ) >>  8 ) & 0xFF;
+                    buf->data[buf->size+3] = ( ( size - 4 ) >>  0 ) & 0xFF;
+                    switch( pic_out.i_type )
+                    {
+                    /*  Decide what type of frame we have. */
+                        case X264_TYPE_IDR:
+                            buf->frametype = HB_FRAME_IDR;
+                            /* if we have a chapter marker pending and this
+                               frame's presentation time stamp is at or after
+                               the marker's time stamp, use this as the
+                               chapter start. */
+                            if( pv->next_chap != 0 && pv->next_chap <= pic_out.i_pts )
+                            {
+                                pv->next_chap = 0;
+                                buf->new_chap = 1;
+                            }
+                            break;
+                        case X264_TYPE_I:
+                            buf->frametype = HB_FRAME_I;
+                            break;
+                        case X264_TYPE_P:
+                            buf->frametype = HB_FRAME_P;
+                            break;
+                        case X264_TYPE_B:
+                            buf->frametype = HB_FRAME_B;
+                            break;
+                    /*  This is for b-pyramid, which has reference b-frames
+                        However, it doesn't seem to ever be used... */
+                        case X264_TYPE_BREF:
+                            buf->frametype = HB_FRAME_BREF;
+                            break;
+                    /*  If it isn't the above, what type of frame is it?? */
+                        default:
+                            buf->frametype = 0;
+                    }
+
+
+                    /* Store the output presentation time stamp
+                       from x264 for use by muxmp4 in off-setting
+                       b-frames with the CTTS atom.
+                       For now, just add 1000000 to the offset so that the
+                       value is pretty much guaranteed to be positive.  The
+                       muxing code will minimize the renderOffset at the end. */
+
+                    buf->renderOffset = pic_out.i_pts - dts_start + 1000000;
+
+                    /* Send out the next dts values */
+                    buf->start = dts_start;
+                    buf->stop  = dts_stop;
+
+                    buf->size += size;
+            }
+        }
+    }
+
+    else
+        buf = NULL;
+
+    *buf_out = buf;
+#endif
+
+    return HB_WORK_OK;
+}
+
+
+
+
Index: libhb/internal.h
===================================================================
--- libhb/internal.h	(revision 1147)
+++ libhb/internal.h	(working copy)
@@ -202,7 +202,8 @@
     WORK_DECLPCM,
     WORK_ENCFAAC,
     WORK_ENCLAME,
-    WORK_ENCVORBIS
+    WORK_ENCVORBIS,
+    WORK_ENCTHEORA
 };
 
 enum
Index: libhb/common.h
===================================================================
--- libhb/common.h	(revision 1147)
+++ libhb/common.h	(working copy)
@@ -523,6 +523,7 @@
 extern hb_work_object_t hb_encfaac;
 extern hb_work_object_t hb_enclame;
 extern hb_work_object_t hb_encvorbis;
+extern hb_work_object_t hb_enctheora;
 
 #define FILTER_OK      0
 #define FILTER_DELAY   1
Index: macosx/HandBrake.xcodeproj/project.pbxproj
===================================================================
--- macosx/HandBrake.xcodeproj/project.pbxproj	(revision 1147)
+++ macosx/HandBrake.xcodeproj/project.pbxproj	(working copy)
@@ -204,7 +204,7 @@
 /* Begin PBXFileReference section */
 		089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		0D096DFF0B707D1200A845D4 /* libhb.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libhb.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
-		0D6E35760B6BD4F0005AABB3 /* HandBrake.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HandBrake.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		0D6E35760B6BD4F0005AABB3 /* HandBrake.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = HandBrake.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		0DF377970B7BF99A00115CB0 /* fakexcode.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = fakexcode.cpp; path = ../test/fakexcode.cpp; sourceTree = SOURCE_ROOT; };
 		0DFA5C7A0B8DD1E90020BC09 /* HandBrake.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = HandBrake.icns; sourceTree = "<group>"; };
 		0DFA5C7E0B8DD3B60020BC09 /* declpcm.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = declpcm.c; path = ../libhb/declpcm.c; sourceTree = SOURCE_ROOT; };
