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; };