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: .
+ It may be used under the terms of the GNU General Public License. */
+
+#include
+
+#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 = ""; };
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 = ""; };
0DFA5C7E0B8DD3B60020BC09 /* declpcm.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = declpcm.c; path = ../libhb/declpcm.c; sourceTree = SOURCE_ROOT; };