diff options
Diffstat (limited to 'libmediafork')
35 files changed, 9822 insertions, 0 deletions
diff --git a/libmediafork/Jamfile b/libmediafork/Jamfile new file mode 100644 index 000000000..10a30ed32 --- /dev/null +++ b/libmediafork/Jamfile @@ -0,0 +1,19 @@ +# $Id: Jamfile,v 1.34 2005/10/15 18:05:03 titer Exp $ +# +# 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. + +SubDir TOP libmediafork ; + +LIBMEDIAFORK_SRC = +ipodutil.cpp common.c mediafork.c ports.c scan.c work.c decmpeg2.c encavcodec.c update.c +demuxmpeg.c fifo.c render.c reader.c muxcommon.c muxmp4.c sync.c +decsub.c deca52.c encfaac.c declpcm.c encx264.c decavcodec.c encxvid.c +muxavi.c enclame.c muxogm.c encvorbis.c dvd.c ; + +Library libmediafork : $(LIBMEDIAFORK_SRC) ; + +ObjectCcFlags $(LIBMEDIAFORK_SRC) : -I$(TOP)/contrib/include ; +ObjectDefines $(LIBMEDIAFORK_SRC) : __LIBMEDIAFORK__ ; +ObjectC++Flags $(LIBMEDIAFORK_SRC) : -I$(TOP)/contrib/include ; diff --git a/libmediafork/Makefile b/libmediafork/Makefile new file mode 100644 index 000000000..0ce595c44 --- /dev/null +++ b/libmediafork/Makefile @@ -0,0 +1,105 @@ +SYSTEM = $(shell uname -s) + +ifeq ($(SYSTEM),Linux) + SYSDEF=-DSYS_LINUX +endif + +ifeq ($(SYSTEM),CYGWIN_NT-5.1) + SYSDEF=-DSYS_CYGWIN +endif + +ifeq ($(SYSTEM),FreeBSD) + CFLAGS += -DSYS_FREEBSD + LDFLAGS += -pthread -lm +endif + +ifeq ($(SYSTEM),NetBSD) + CFLAGS += -DSYS_NETBSD + LDFLAGS += -lpthread -lm +endif + +ifeq ($(SYSTEM),Linux) + CFLAGS += -DSYS_LINUX + LDFLAGS += -lpthread -lm +endif + +SRCS = common.c mediafork.c ports.c scan.c work.c decmpeg2.c encavcodec.c \ + update.c demuxmpeg.c fifo.c render.c reader.c muxcommon.c \ + muxmp4.c sync.c decsub.c deca52.c encfaac.c declpcm.c encx264.c \ + decavcodec.c encxvid.c muxavi.c enclame.c muxogm.c encvorbis.c \ + dvd.c ipodutil.cpp +OTMP = $(SRCS:%.c=%.o) +OBJS = $(OTMP:%.cpp=%.o) + +ifeq ($(SYSTEM),CYGWIN_NT-5.1) +CONTRIBS = ../contrib/lib/liba52.a ../contrib/lib/libavformat.a \ + ../contrib/lib/libavcodec.a ../contrib/lib/libavutil.a \ + ../contrib/lib/libdvdread.a \ + ../contrib/lib/libfaac.a ../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/libogg.a \ + ../contrib/lib/libsamplerate.a ../contrib/lib/libx264.a \ + ../contrib/lib/libxvidcore.a ../contrib/lib/libmp4v2.a +else +CONTRIBS = ../contrib/lib/liba52.a ../contrib/lib/libavformat.a \ + ../contrib/lib/libavcodec.a ../contrib/lib/libavutil.a \ + ../contrib/lib/libdvdread.a ../contrib/lib/libdvdcss.a \ + ../contrib/lib/libfaac.a ../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/libogg.a \ + ../contrib/lib/libsamplerate.a ../contrib/lib/libx264.a \ + ../contrib/lib/libxvidcore.a ../contrib/lib/libmp4v2.a +endif +BUILD = $(shell date "+%Y%m%d") +CFLAGS += -I../contrib/include -D__LIBMEDIAFORK__ -DUSE_PTHREAD -DHB_VERSION=\"0.8.0b1\" -DHB_BUILD=$(BUILD) $(SYSDEF) + +CXXFLAGS += -I../contrib/include -D__LIBMEDIAFORK__ -DUSE_PTHREAD -DHB_VERSION=\"0.8.0b1\" -DHB_BUILD=$(BUILD) $(SYSDEF) + +ifeq ($(SYSTEM),CYGWIN_NT-5.1) +all: libmediafork.a libmediafork.dll +else +all: libmediafork.a libmediafork.so +endif + +libmediafork.a: $(OBJS) + @echo "Library $@" + @ar ru $@ $(OBJS) + @ranlib $@ + +libmediafork.so: $(OBJS) + @echo "Shared library $@" + @g++ -o $@ $(OBJS) $(CONTRIBS) -shared $(CFLAGS) || \ + ( echo "Compile line for $@ was:"; echo $$CMD; false ) + +libmediafork.dll: $(OBJS) + @echo "Shared library $@" + @g++ -o $@ $(OBJS) $(CONTRIBS) -shared $(CFLAGS) || \ + ( echo "Compile line for $@ was:"; echo $$CMD; false ) + +%.o: %.c + @echo "Cc $@" + @CMD="$(CC) $(CFLAGS) -o $@ -c $<"; $$CMD || \ + ( echo "Compile line for $@ was:"; echo $$CMD; false ) +%.o: %.cpp + @echo "Cc $@" + @CMD="$(CC) $(CFLAGS) -o $@ -c $<"; $$CMD || \ + ( echo "Compile line for $@ was:"; echo $$CMD; false ) + + +clean: + @echo "Clean libmediafork.a" + @$(RM) libmediafork.* + @echo "Clean libmediafork.so" + @$(RM) libmediafork.so + @echo "Clean $(OBJS)" + @$(RM) $(OBJS) + +.depend: $(SRCS) + @echo "Checking dependencies..." + @$(RM) .depend + @$(foreach SRC, $(SRCS), $(CC) -MM $(SRC) $(CFLAGS) >> .depend;) + +-include .depend + diff --git a/libmediafork/common.c b/libmediafork/common.c new file mode 100644 index 000000000..7b41a93fb --- /dev/null +++ b/libmediafork/common.c @@ -0,0 +1,527 @@ +/* $Id: common.c,v 1.15 2005/03/17 19:22:47 titer Exp $ + + 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 <time.h> +#include <sys/time.h> + +#include "common.h" + +/********************************************************************** + * Global variables + *********************************************************************/ +hb_rate_t hb_video_rates[] = +{ { "5", 5400000 }, { "10", 2700000 }, { "12", 2250000 }, + { "15", 1800000 }, { "23.976", 1126125 }, { "24", 1125000 }, + { "25", 1080000 }, { "29.97", 900900 } }; +int hb_video_rates_count = sizeof( hb_video_rates ) / + sizeof( hb_rate_t ); + +hb_rate_t hb_audio_rates[] = +{ { "22.05", 22050 }, { "24", 24000 }, { "32", 32000 }, + { "44.1", 44100 }, { "48", 48000 } }; +int hb_audio_rates_count = sizeof( hb_audio_rates ) / + sizeof( hb_rate_t ); +int hb_audio_rates_default = 3; /* 44100 Hz */ + +hb_rate_t hb_audio_bitrates[] = +{ { "32", 32 }, { "40", 40 }, { "48", 48 }, { "56", 56 }, + { "64", 64 }, { "80", 80 }, { "96", 96 }, { "112", 112 }, + { "128", 128 }, { "160", 160 }, { "192", 192 }, { "224", 224 }, + { "256", 256 }, { "320", 320 } }; +int hb_audio_bitrates_count = sizeof( hb_audio_bitrates ) / + sizeof( hb_rate_t ); +int hb_audio_bitrates_default = 8; /* 128 kbps */ + +/********************************************************************** + * hb_reduce + ********************************************************************** + * Given a numerator (num) and a denominator (den), reduce them to an + * equivalent fraction and store the result in x and y. + *********************************************************************/ +void hb_reduce( int *x, int *y, int num, int den ) +{ + int lower = MIN( num, den ); + int i; + *x = num; + *y = den; + for( i = lower - 1; i > 1; --i ) + { + if( ( num % i == 0 ) && ( den % i == 0 ) ) + { + *x = num / i; + *y = den / i; + break; + } + } +} + +/********************************************************************** + * hb_fix_aspect + ********************************************************************** + * Given the output width (if HB_KEEP_WIDTH) or height + * (HB_KEEP_HEIGHT) and the current crop values, calculates the + * correct height or width in order to respect the DVD aspect ratio + *********************************************************************/ +void hb_fix_aspect( hb_job_t * job, int keep ) +{ + hb_title_t * title = job->title; + int i; + + /* Sanity checks: + Widths and heights must be multiples of 16 and greater than or + equal to 16 + Crop values must be multiples of 2, greater than or equal to 0 + and less than half of the dimension */ + job->width = MULTIPLE_16( job->width ); + job->height = MULTIPLE_16( job->height ); + job->width = MAX( 16, job->width ); + job->height = MAX( 16, job->height ); + for( i = 0; i < 4; i++ ) + { + job->crop[i] = EVEN( job->crop[i] ); + job->crop[i] = MAX( 0, job->crop[i] ); + if( i < 2 ) + { + /* Top, bottom */ + job->crop[i] = MIN( job->crop[i], ( title->height / 2 ) - 2 ); + } + else + { + /* Left, right */ + job->crop[i] = MIN( job->crop[i], ( title->width / 2 ) - 2 ); + } + } + + if( keep == HB_KEEP_WIDTH ) + { + job->height = MULTIPLE_16( + (uint64_t) job->width * title->width * HB_ASPECT_BASE * + ( title->height - job->crop[0] - job->crop[1] ) / + ( (uint64_t) title->height * title->aspect * + ( title->width - job->crop[2] - job->crop[3] ) ) ); + job->height = MAX( 16, job->height ); + } + else + { + job->width = MULTIPLE_16( + (uint64_t) job->height * title->height * title->aspect * + ( title->width - job->crop[2] - job->crop[3] ) / + ( (uint64_t) title->width * HB_ASPECT_BASE * + ( title->height - job->crop[0] - job->crop[1] ) ) ); + job->width = MAX( 16, job->width ); + } +} + +/********************************************************************** + * hb_calc_bitrate + ********************************************************************** + * size: in megabytes + *********************************************************************/ +int hb_calc_bitrate( hb_job_t * job, int size ) +{ + int64_t avail = (int64_t) size * 1024 * 1024; + int64_t length; + int overhead; + int samples_per_frame; + int i; + + hb_title_t * title = job->title; + hb_chapter_t * chapter; + hb_audio_t * audio; + + /* How many overhead bytes are used for each frame + (quite guessed) */ + switch( job->mux ) + { + case HB_MUX_MP4: + case HB_MUX_PSP: + case HB_MUX_IPOD: + overhead = 6; + break; + case HB_MUX_AVI: + overhead = 24; + break; + case HB_MUX_OGM: + overhead = 6; + break; + default: + return 0; + } + + /* How many audio samples we put in each frame */ + switch( job->acodec ) + { + case HB_ACODEC_FAAC: + case HB_ACODEC_VORBIS: + samples_per_frame = 1024; + break; + case HB_ACODEC_LAME: + samples_per_frame = 1152; + break; + case HB_ACODEC_AC3: + samples_per_frame = 1536; + break; + default: + return 0; + } + + /* Get the duration in seconds */ + length = 0; + for( i = job->chapter_start; i <= job->chapter_end; i++ ) + { + chapter = hb_list_item( title->list_chapter, i - 1 ); + length += chapter->duration; + } + length += 135000; + length /= 90000; + + /* Video overhead */ + avail -= length * job->vrate * overhead / job->vrate_base; + + for( i = 0; job->audios[i] >= 0; i++ ) + { + /* Audio data */ + int abitrate; + if( job->acodec & HB_ACODEC_AC3 ) + { + audio = hb_list_item( title->list_audio, job->audios[i] ); + abitrate = audio->bitrate / 8; + } + else + { + abitrate = job->abitrate * 1000 / 8; + } + avail -= length * abitrate; + + /* Audio overhead */ + avail -= length * job->arate * overhead / samples_per_frame; + } + + if( avail < 0 ) + { + return 0; + } + + return ( avail / ( 125 * length ) ); +} + +/********************************************************************** + * hb_list implementation + ********************************************************************** + * Basic and slow, but enough for what we need + *********************************************************************/ + +#define HB_LIST_DEFAULT_SIZE 20 + +struct hb_list_s +{ + /* Pointers to items in the list */ + void ** items; + + /* How many (void *) allocated in 'items' */ + int items_alloc; + + /* How many valid pointers in 'items' */ + int items_count; +}; + +/********************************************************************** + * hb_list_init + ********************************************************************** + * Allocates an empty list ready for HB_LIST_DEFAULT_SIZE items + *********************************************************************/ +hb_list_t * hb_list_init() +{ + hb_list_t * l; + + l = calloc( sizeof( hb_list_t ), 1 ); + l->items = calloc( HB_LIST_DEFAULT_SIZE * sizeof( void * ), 1 ); + l->items_alloc = HB_LIST_DEFAULT_SIZE; + + return l; +} + +/********************************************************************** + * hb_list_count + ********************************************************************** + * Returns the number of items currently in the list + *********************************************************************/ +int hb_list_count( hb_list_t * l ) +{ + return l->items_count; +} + +/********************************************************************** + * hb_list_add + ********************************************************************** + * Adds an item at the end of the list, making it bigger if necessary. + * Can safely be called with a NULL pointer to add, it will be ignored. + *********************************************************************/ +void hb_list_add( hb_list_t * l, void * p ) +{ + if( !p ) + { + return; + } + + if( l->items_count == l->items_alloc ) + { + /* We need a bigger boat */ + l->items_alloc += HB_LIST_DEFAULT_SIZE; + l->items = realloc( l->items, + l->items_alloc * sizeof( void * ) ); + } + + l->items[l->items_count] = p; + (l->items_count)++; +} + +/********************************************************************** + * hb_list_rem + ********************************************************************** + * Remove an item from the list. Bad things will happen if called + * with a NULL pointer or if the item is not in the list. + *********************************************************************/ +void hb_list_rem( hb_list_t * l, void * p ) +{ + int i; + + /* Find the item in the list */ + for( i = 0; i < l->items_count; i++ ) + { + if( l->items[i] == p ) + { + break; + } + } + + /* Shift all items after it sizeof( void * ) bytes earlier */ + memmove( &l->items[i], &l->items[i+1], + ( l->items_count - i - 1 ) * sizeof( void * ) ); + + (l->items_count)--; +} + +/********************************************************************** + * hb_list_item + ********************************************************************** + * Returns item at position i, or NULL if there are not that many + * items in the list + *********************************************************************/ +void * hb_list_item( hb_list_t * l, int i ) +{ + if( i < 0 || i >= l->items_count ) + { + return NULL; + } + + return l->items[i]; +} + +/********************************************************************** + * hb_list_bytes + ********************************************************************** + * Assuming all items are of type hb_buffer_t, returns the total + * number of bytes in the list + *********************************************************************/ +int hb_list_bytes( hb_list_t * l ) +{ + hb_buffer_t * buf; + int ret; + int i; + + ret = 0; + for( i = 0; i < hb_list_count( l ); i++ ) + { + buf = hb_list_item( l, i ); + ret += buf->size - buf->cur; + } + + return ret; +} + +/********************************************************************** + * hb_list_seebytes + ********************************************************************** + * Assuming all items are of type hb_buffer_t, copy <size> bytes from + * the list to <dst>, keeping the list unmodified. + *********************************************************************/ +void hb_list_seebytes( hb_list_t * l, uint8_t * dst, int size ) +{ + hb_buffer_t * buf; + int copied; + int copying; + int i; + + for( i = 0, copied = 0; copied < size; i++ ) + { + buf = hb_list_item( l, i ); + copying = MIN( buf->size - buf->cur, size - copied ); + memcpy( &dst[copied], &buf->data[buf->cur], copying ); + copied += copying; + } +} + +/********************************************************************** + * hb_list_getbytes + ********************************************************************** + * Assuming all items are of type hb_buffer_t, copy <size> bytes from + * the list to <dst>. What's copied is removed from the list. + * The variable pointed by <pts> is set to the PTS of the buffer the + * first byte has been got from. + * The variable pointed by <pos> is set to the position of that byte + * in that buffer. + *********************************************************************/ +void hb_list_getbytes( hb_list_t * l, uint8_t * dst, int size, + uint64_t * pts, int * pos ) +{ + hb_buffer_t * buf; + int copied; + int copying; + uint8_t has_pts; + + /* So we won't have to deal with NULL pointers */ + uint64_t dummy1; + int dummy2; + if( !pts ) pts = &dummy1; + if( !pos ) pos = &dummy2; + + for( copied = 0, has_pts = 0; copied < size; ) + { + buf = hb_list_item( l, 0 ); + copying = MIN( buf->size - buf->cur, size - copied ); + memcpy( &dst[copied], &buf->data[buf->cur], copying ); + + if( !has_pts ) + { + *pts = buf->start; + *pos = buf->cur; + has_pts = 1; + } + + buf->cur += copying; + if( buf->cur >= buf->size ) + { + hb_list_rem( l, buf ); + hb_buffer_close( &buf ); + } + + copied += copying; + } +} + +/********************************************************************** + * hb_list_empty + ********************************************************************** + * Assuming all items are of type hb_buffer_t, close them all and + * close the list. + *********************************************************************/ +void hb_list_empty( hb_list_t ** _l ) +{ + hb_list_t * l = *_l; + hb_buffer_t * b; + + while( ( b = hb_list_item( l, 0 ) ) ) + { + hb_list_rem( l, b ); + hb_buffer_close( &b ); + } + + hb_list_close( _l ); +} + +/********************************************************************** + * hb_list_close + ********************************************************************** + * Free memory allocated by hb_list_init. Does NOT free contents of + * items still in the list. + *********************************************************************/ +void hb_list_close( hb_list_t ** _l ) +{ + hb_list_t * l = *_l; + + free( l->items ); + free( l ); + + *_l = NULL; +} + +/********************************************************************** + * hb_log + ********************************************************************** + * If verbose mode is one, print message with timestamp. Messages + * longer than 80 characters are stripped ;p + *********************************************************************/ +void hb_log( char * log, ... ) +{ + char string[82]; /* 80 chars + \n + \0 */ + time_t _now; + struct tm * now; + va_list args; + + if( !getenv( "HB_DEBUG" ) ) + { + /* We don't want to print it */ + return; + } + + /* Get the time */ + _now = time( NULL ); + now = localtime( &_now ); + sprintf( string, "[%02d:%02d:%02d] ", + now->tm_hour, now->tm_min, now->tm_sec ); + + /* Convert the message to a string */ + va_start( args, log ); + vsnprintf( string + 11, 69, log, args ); + va_end( args ); + + /* Add the end of line */ + strcat( string, "\n" ); + + /* Print it */ + fprintf( stderr, "%s", string ); +} + +/********************************************************************** + * hb_title_init + ********************************************************************** + * + *********************************************************************/ +hb_title_t * hb_title_init( char * dvd, int index ) +{ + hb_title_t * t; + + t = calloc( sizeof( hb_title_t ), 1 ); + + t->index = index; + t->list_audio = hb_list_init(); + t->list_chapter = hb_list_init(); + t->list_subtitle = hb_list_init(); + strcat( t->dvd, dvd ); + + return t; +} + +/********************************************************************** + * hb_title_close + ********************************************************************** + * + *********************************************************************/ +void hb_title_close( hb_title_t ** _t ) +{ + hb_title_t * t = *_t; + + hb_list_close( &t->list_audio ); + hb_list_close( &t->list_chapter ); + hb_list_close( &t->list_subtitle ); + free( t->job ); + + free( t ); + *_t = NULL; +} + diff --git a/libmediafork/common.h b/libmediafork/common.h new file mode 100644 index 000000000..ed9d7c59a --- /dev/null +++ b/libmediafork/common.h @@ -0,0 +1,383 @@ +/* $Id: common.h,v 1.51 2005/11/04 13:09:40 titer Exp $ + + 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. */ + +#ifndef HB_COMMON_H +#define HB_COMMON_H + +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> + +#ifndef MIN +#define MIN( a, b ) ( (a) > (b) ? (b) : (a) ) +#endif +#ifndef MAX +#define MAX( a, b ) ( (a) > (b) ? (a) : (b) ) +#endif + +#define EVEN( a ) ( (a) + ( (a) & 1 ) ) +#define MULTIPLE_16( a ) ( 16 * ( ( (a) + 8 ) / 16 ) ) + +typedef struct hb_handle_s hb_handle_t; +typedef struct hb_list_s hb_list_t; +typedef struct hb_rate_s hb_rate_t; +typedef struct hb_job_s hb_job_t; +typedef struct hb_title_s hb_title_t; +typedef struct hb_chapter_s hb_chapter_t; +typedef struct hb_audio_s hb_audio_t; +typedef struct hb_subtitle_s hb_subtitle_t; +typedef struct hb_state_s hb_state_t; +typedef union hb_esconfig_u hb_esconfig_t; +typedef struct hb_work_private_s hb_work_private_t; +typedef struct hb_work_object_s hb_work_object_t; +typedef struct hb_buffer_s hb_buffer_t; +typedef struct hb_fifo_s hb_fifo_t; +typedef struct hb_lock_s hb_lock_t; + +#include "ports.h" +#ifdef __LIBMEDIAFORK__ +#include "internal.h" +#endif + +hb_list_t * hb_list_init(); +int hb_list_count( hb_list_t * ); +void hb_list_add( hb_list_t *, void * ); +void hb_list_rem( hb_list_t *, void * ); +void * hb_list_item( hb_list_t *, int ); +void hb_list_close( hb_list_t ** ); + +void hb_reduce( int *x, int *y, int num, int den ); + +#define HB_KEEP_WIDTH 0 +#define HB_KEEP_HEIGHT 1 +void hb_fix_aspect( hb_job_t * job, int keep ); + +int hb_calc_bitrate( hb_job_t *, int size ); + +struct hb_rate_s +{ + char * string; + int rate; +}; + +#define HB_ASPECT_BASE 9 +#define HB_VIDEO_RATE_BASE 27000000 + +extern hb_rate_t hb_video_rates[]; +extern int hb_video_rates_count; +extern hb_rate_t hb_audio_rates[]; +extern int hb_audio_rates_count; +extern int hb_audio_rates_default; +extern hb_rate_t hb_audio_bitrates[]; +extern int hb_audio_bitrates_count; +extern int hb_audio_bitrates_default; + +/****************************************************************************** + * hb_job_t: settings to be filled by the UI + *****************************************************************************/ +struct hb_job_s +{ + /* Pointer to the title to be ripped */ + hb_title_t * title; + + /* Chapter selection */ + int chapter_start; + int chapter_end; + + /* Picture settings: + crop: must be multiples of 2 (top/bottom/left/right) + deinterlace: 0 or 1 + width: must be a multiple of 16 + height: must be a multiple of 16 + keep_ratio: used by UIs + pixel_ratio: store pixel aspect ratio in the video + pixel_aspect_width: numerator for pixel aspect ratio + pixel_aspect_height: denominator for pixel aspect ratio */ + + int crop[4]; + int deinterlace; + int width; + int height; + int keep_ratio; + int grayscale; + int pixel_ratio; + int pixel_aspect_width; + int pixel_aspect_height; + + /* Video settings: + vcodec: output codec + vquality: output quality (0.0..1.0) + if < 0.0 or > 1.0, bitrate is used instead + vbitrate: output bitrate (kbps) + pass: 0, 1 or 2 + vrate, vrate_base: output framerate is vrate / vrate_base */ +#define HB_VCODEC_MASK 0x0000FF +#define HB_VCODEC_FFMPEG 0x000001 +#define HB_VCODEC_XVID 0x000002 +#define HB_VCODEC_X264 0x000004 + + int vcodec; + float vquality; + int vbitrate; + int vrate; + int vrate_base; + int pass; + int h264_13; + int h264_level; + int crf; + + /* Audio tracks: + Indexes in hb_title_t's audios list, starting from 0. + -1 indicates the end of the list */ + int audios[8]; + + /* Audio settings: + acodec: output codec + abitrate: output bitrate (kbps) + arate: output samplerate (Hz) + HB_ACODEC_AC3 means pass-through, then abitrate and arate are + ignored */ +#define HB_ACODEC_MASK 0x00FF00 +#define HB_ACODEC_FAAC 0x000100 +#define HB_ACODEC_LAME 0x000200 +#define HB_ACODEC_VORBIS 0x000400 +#define HB_ACODEC_AC3 0x000800 +#define HB_ACODEC_MPGA 0x001000 +#define HB_ACODEC_LPCM 0x002000 + int acodec; + int abitrate; + int arate; + + /* Subtitle settings: + subtitle: index in hb_title_t's subtitles list, starting + from 0. -1 means no subtitle */ + int subtitle; + + /* Muxer settings + mux: output file format + file: file path */ +#define HB_MUX_MASK 0xFF0000 +#define HB_MUX_MP4 0x010000 +#define HB_MUX_PSP 0x020000 +#define HB_MUX_AVI 0x040000 +#define HB_MUX_OGM 0x080000 +#define HB_MUX_IPOD 0x100000 + + int mux; + char * file; + +#ifdef __LIBMEDIAFORK__ + /* Internal data */ + hb_handle_t * h; + hb_lock_t * pause; + volatile int * die; + volatile int done; + + hb_fifo_t * fifo_mpeg2; /* MPEG-2 video ES */ + hb_fifo_t * fifo_raw; /* Raw pictures */ + hb_fifo_t * fifo_sync; /* Raw pictures, framerate corrected */ + hb_fifo_t * fifo_render; /* Raw pictures, scaled */ + hb_fifo_t * fifo_mpeg4; /* MPEG-4 video ES */ + + hb_thread_t * reader; + hb_thread_t * muxer; + + hb_list_t * list_work; + + hb_esconfig_t config; + + hb_mux_data_t * mux_data; +#endif +}; + +struct hb_audio_s +{ + int id; + char lang[1024]; + char lang_simple[1024]; + int codec; + int rate; + int bitrate; + int channels; + +#ifdef __LIBMEDIAFORK__ + /* Internal data */ + hb_fifo_t * fifo_in; /* AC3/MPEG/LPCM ES */ + hb_fifo_t * fifo_raw; /* Raw audio */ + hb_fifo_t * fifo_sync; /* Resampled, synced raw audio */ + hb_fifo_t * fifo_out; /* MP3/AAC/Vorbis ES */ + + hb_esconfig_t config; + hb_mux_data_t * mux_data; +#endif +}; + +struct hb_chapter_s +{ + int index; + int cell_start; + int cell_end; + int block_start; + int block_end; + int block_count; + + /* Visual-friendly duration */ + int hours; + int minutes; + int seconds; + + /* Exact duration (in 1/90000s) */ + uint64_t duration; +}; + +struct hb_subtitle_s +{ + int id; + char lang[1024]; + +#ifdef __LIBMEDIAFORK__ + /* Internal data */ + hb_fifo_t * fifo_in; /* SPU ES */ + hb_fifo_t * fifo_raw; /* Decodec SPU */ +#endif +}; + +struct hb_title_s +{ + char dvd[1024]; + char name[1024]; + int index; + int vts; + int ttn; + int cell_start; + int cell_end; + int block_start; + int block_end; + int block_count; + + /* Visual-friendly duration */ + int hours; + int minutes; + int seconds; + + /* Exact duration (in 1/90000s) */ + uint64_t duration; + + int width; + int height; + int aspect; + int rate; + int rate_base; + int crop[4]; + + uint32_t palette[16]; + + hb_list_t * list_chapter; + hb_list_t * list_audio; + hb_list_t * list_subtitle; + + /* Job template for this title */ + hb_job_t * job; +}; + + +struct hb_state_s +{ +#define HB_STATE_IDLE 1 +#define HB_STATE_SCANNING 2 +#define HB_STATE_SCANDONE 4 +#define HB_STATE_WORKING 8 +#define HB_STATE_PAUSED 16 +#define HB_STATE_WORKDONE 32 +#define HB_STATE_MUXING 64 + int state; + + union + { + struct + { + /* HB_STATE_SCANNING */ + int title_cur; + int title_count; + } scanning; + + struct + { + /* HB_STATE_WORKING */ + float progress; + int job_cur; + int job_count; + float rate_cur; + float rate_avg; + int hours; + int minutes; + int seconds; + } working; + + struct + { + /* HB_STATE_WORKDONE */ +#define HB_ERROR_NONE 0 +#define HB_ERROR_CANCELED 1 +#define HB_ERROR_UNKNOWN 2 + int error; + } workdone; + + struct + { + /* HB_STATE_MUXING */ + float progress; + } muxing; + } param; +}; + +struct hb_work_object_s +{ + int id; + char * name; + +#ifdef __LIBMEDIAFORK__ + int (* init) ( hb_work_object_t *, hb_job_t * ); + int (* work) ( hb_work_object_t *, hb_buffer_t **, + hb_buffer_t ** ); + void (* close) ( hb_work_object_t * ); + + hb_fifo_t * fifo_in; + hb_fifo_t * fifo_out; + hb_esconfig_t * config; + + hb_work_private_t * private_data; + + hb_thread_t * thread; + volatile int * done; + + hb_work_object_t * next; + int thread_sleep_interval; +#endif +}; + +extern hb_work_object_t hb_sync; +extern hb_work_object_t hb_decmpeg2; +extern hb_work_object_t hb_decsub; +extern hb_work_object_t hb_render; +extern hb_work_object_t hb_encavcodec; +extern hb_work_object_t hb_encxvid; +extern hb_work_object_t hb_encx264; +extern hb_work_object_t hb_deca52; +extern hb_work_object_t hb_decavcodec; +extern hb_work_object_t hb_declpcm; +extern hb_work_object_t hb_encfaac; +extern hb_work_object_t hb_enclame; +extern hb_work_object_t hb_encvorbis; + +#endif diff --git a/libmediafork/deca52.c b/libmediafork/deca52.c new file mode 100644 index 000000000..c7688ed3b --- /dev/null +++ b/libmediafork/deca52.c @@ -0,0 +1,203 @@ +/* $Id: deca52.c,v 1.14 2005/03/03 17:21:57 titer Exp $ + + 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 "mediafork.h" + +#include "a52dec/a52.h" + +struct hb_work_private_s +{ + hb_job_t * job; + + /* liba52 handle */ + a52_state_t * state; + + int flags_in; + int flags_out; + int rate; + int bitrate; + float level; + + int error; + int sync; + int size; + + uint8_t frame[3840]; + + hb_list_t * list; +}; + +int deca52Init( hb_work_object_t *, hb_job_t * ); +int deca52Work( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +void deca52Close( hb_work_object_t * ); + +hb_work_object_t hb_deca52 = +{ + WORK_DECA52, + "AC3 decoder", + deca52Init, + deca52Work, + deca52Close +}; + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static hb_buffer_t * Decode( hb_work_object_t * w ); + +/*********************************************************************** + * hb_work_deca52_init + *********************************************************************** + * Allocate the work object, initialize liba52 + **********************************************************************/ +int deca52Init( hb_work_object_t * w, hb_job_t * job ) +{ + hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + + pv->job = job; + + pv->list = hb_list_init(); + pv->state = a52_init( 0 ); + pv->flags_out = A52_STEREO; + pv->level = 32768.0; + + return 0; +} + +/*********************************************************************** + * Close + *********************************************************************** + * Free memory + **********************************************************************/ +void deca52Close( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + a52_free( pv->state ); +} + +/*********************************************************************** + * Work + *********************************************************************** + * Add the given buffer to the data we already have, and decode as much + * as we can + **********************************************************************/ +int deca52Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * buf; + + hb_list_add( pv->list, *buf_in ); + *buf_in = NULL; + + /* If we got more than a frame, chain raw buffers */ + *buf_out = buf = Decode( w ); + while( buf ) + { + buf->next = Decode( w ); + buf = buf->next; + } + + return HB_WORK_OK; +} + +/*********************************************************************** + * Decode + *********************************************************************** + * + **********************************************************************/ +static hb_buffer_t * Decode( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * buf; + int i, j; + uint64_t pts; + int pos; + + /* Get a frame header if don't have one yet */ + if( !pv->sync ) + { + while( hb_list_bytes( pv->list ) >= 7 ) + { + /* We have 7 bytes, check if this is a correct header */ + hb_list_seebytes( pv->list, pv->frame, 7 ); + pv->size = a52_syncinfo( pv->frame, &pv->flags_in, &pv->rate, + &pv->bitrate ); + if( pv->size ) + { + /* It is. W00t. */ + if( pv->error ) + { + hb_log( "a52_syncinfo ok" ); + } + pv->error = 0; + pv->sync = 1; + break; + } + + /* It is not */ + if( !pv->error ) + { + hb_log( "a52_syncinfo failed" ); + pv->error = 1; + } + + /* Try one byte later */ + hb_list_getbytes( pv->list, pv->frame, 1, NULL, NULL ); + } + } + + if( !pv->sync || + hb_list_bytes( pv->list ) < pv->size ) + { + /* Need more data */ + return NULL; + } + + /* Get the whole frame */ + hb_list_getbytes( pv->list, pv->frame, pv->size, &pts, &pos ); + + /* AC3 passthrough: don't decode the AC3 frame */ + if( pv->job->acodec & HB_ACODEC_AC3 ) + { + buf = hb_buffer_init( pv->size ); + memcpy( buf->data, pv->frame, pv->size ); + buf->start = pts + ( pos / pv->size ) * 6 * 256 * 90000 / pv->rate; + buf->stop = buf->start + 6 * 256 * 90000 / pv->rate; + pv->sync = 0; + return buf; + } + + /* Feed liba52 */ + a52_frame( pv->state, pv->frame, &pv->flags_out, &pv->level, 0 ); + + /* 6 blocks per frame, 256 samples per block, 2 channels */ + buf = hb_buffer_init( 3072 * sizeof( float ) ); + buf->start = pts + ( pos / pv->size ) * 6 * 256 * 90000 / pv->rate; + buf->stop = buf->start + 6 * 256 * 90000 / pv->rate; + + for( i = 0; i < 6; i++ ) + { + sample_t * samples_in; + float * samples_out; + + a52_block( pv->state ); + samples_in = a52_samples( pv->state ); + samples_out = ((float *) buf->data) + 512 * i; + + /* Interleave */ + for( j = 0; j < 256; j++ ) + { + samples_out[2*j] = samples_in[j]; + samples_out[2*j+1] = samples_in[256+j]; + } + } + + pv->sync = 0; + return buf; +} + diff --git a/libmediafork/decavcodec.c b/libmediafork/decavcodec.c new file mode 100644 index 000000000..dfc61dcd5 --- /dev/null +++ b/libmediafork/decavcodec.c @@ -0,0 +1,134 @@ +/* $Id: decavcodec.c,v 1.6 2005/03/06 04:08:54 titer Exp $ + + 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 "mediafork.h" + +#include "ffmpeg/avcodec.h" + +int decavcodecInit( hb_work_object_t *, hb_job_t * ); +int decavcodecWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +void decavcodecClose( hb_work_object_t * ); + +hb_work_object_t hb_decavcodec = +{ + WORK_DECAVCODEC, + "MPGA decoder (libavcodec)", + decavcodecInit, + decavcodecWork, + decavcodecClose +}; + +struct hb_work_private_s +{ + hb_job_t * job; + + AVCodecContext * context; + int64_t pts_last; +}; + + +/*********************************************************************** + * hb_work_decavcodec_init + *********************************************************************** + * + **********************************************************************/ +int decavcodecInit( hb_work_object_t * w, hb_job_t * job ) +{ + AVCodec * codec; + hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + + pv->job = job; + + codec = avcodec_find_decoder( CODEC_ID_MP2 ); + pv->context = avcodec_alloc_context(); + avcodec_open( pv->context, codec ); + pv->pts_last = -1; + + return 0; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +void decavcodecClose( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + avcodec_close( pv->context ); +} + +/*********************************************************************** + * Work + *********************************************************************** + * + **********************************************************************/ +int decavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * in = *buf_in, * buf, * last = NULL; + int pos, len, out_size, i; + short buffer[AVCODEC_MAX_AUDIO_FRAME_SIZE]; + uint64_t cur; + + *buf_out = NULL; + + if( in->start < 0 || + ( pv->pts_last > 0 && + in->start > pv->pts_last && + in->start - pv->pts_last < 5000 ) ) /* Hacky */ + { + cur = pv->pts_last; + } + else + { + cur = in->start; + } + + pos = 0; + while( pos < in->size ) + { + len = avcodec_decode_audio( pv->context, buffer, &out_size, + in->data + pos, in->size - pos ); + if( out_size ) + { + short * s16; + float * fl32; + + buf = hb_buffer_init( 2 * out_size ); + + buf->start = cur; + buf->stop = cur + 90000 * ( out_size / 4 ) / + pv->context->sample_rate; + cur = buf->stop; + + s16 = buffer; + fl32 = (float *) buf->data; + for( i = 0; i < out_size / 2; i++ ) + { + fl32[i] = s16[i]; + } + + if( last ) + { + last = last->next = buf; + } + else + { + *buf_out = last = buf; + } + } + + pos += len; + } + + pv->pts_last = cur; + + return HB_WORK_OK; +} + diff --git a/libmediafork/declpcm.c b/libmediafork/declpcm.c new file mode 100644 index 000000000..718cf559d --- /dev/null +++ b/libmediafork/declpcm.c @@ -0,0 +1,90 @@ +/* $Id: declpcm.c,v 1.8 2005/11/04 14:44:01 titer Exp $ + + 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 "mediafork.h" + +int declpcmInit( hb_work_object_t *, hb_job_t * ); +int declpcmWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +void declpcmClose( hb_work_object_t * ); + +hb_work_object_t hb_declpcm = +{ + WORK_DECLPCM, + "LPCM decoder", + declpcmInit, + declpcmWork, + declpcmClose +}; + +int declpcmInit( hb_work_object_t * w, hb_job_t * job ) +{ + return 0; +} + +int declpcmWork( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_buffer_t * in = *buf_in, * out; + int samplerate = 0; + int count; + uint8_t * samples_u8; + float * samples_fl32; + int i; + uint64_t duration; + + *buf_out = NULL; + + if( in->data[5] != 0x80 ) + { + hb_log( "no LPCM frame sync (%02x)", in->data[5] ); + return HB_WORK_OK; + } + + switch( ( in->data[4] >> 4 ) & 0x3 ) + { + case 0: + samplerate = 48000; + break; + case 1: + samplerate = 96000;//32000; /* FIXME vlc says it is 96000 */ + break; + case 2: + samplerate = 44100; + break; + case 3: + samplerate = 32000; + break; + } + + count = ( in->size - 6 ) / 2; + out = hb_buffer_init( count * sizeof( float ) ); + duration = count * 90000 / samplerate / 2; + out->start = in->start; + out->stop = out->start + duration; + + samples_u8 = in->data + 6; + samples_fl32 = (float *) out->data; + + /* Big endian int16 -> float conversion */ + for( i = 0; i < count; i++ ) + { +#ifdef WORDS_BIGENDIAN + samples_fl32[0] = *( (int16_t *) samples_u8 ); +#else + samples_fl32[0] = (int16_t) ( ( samples_u8[0] << 8 ) | samples_u8[1] ); +#endif + samples_u8 += 2; + samples_fl32 += 1; + } + + *buf_out = out; + + return HB_WORK_OK; +} + +void declpcmClose( hb_work_object_t * w ) +{ +} diff --git a/libmediafork/decmpeg2.c b/libmediafork/decmpeg2.c new file mode 100644 index 000000000..e3188dfe2 --- /dev/null +++ b/libmediafork/decmpeg2.c @@ -0,0 +1,253 @@ +/* $Id: decmpeg2.c,v 1.12 2005/03/03 16:30:42 titer Exp $ + + 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 "mediafork.h" + +#include "mpeg2dec/mpeg2.h" + +/********************************************************************** + * hb_libmpeg2_t + ********************************************************************** + * A convenient libmpeg wrapper, used both here and in scan.c + *********************************************************************/ +struct hb_libmpeg2_s +{ + mpeg2dec_t * libmpeg2; + const mpeg2_info_t * info; + int width; + int height; + int rate; + int got_iframe; + int64_t last_pts; +}; + +/********************************************************************** + * hb_libmpeg2_init + ********************************************************************** + * + *********************************************************************/ +hb_libmpeg2_t * hb_libmpeg2_init() +{ + hb_libmpeg2_t * m = calloc( sizeof( hb_libmpeg2_t ), 1 ); + + m->libmpeg2 = mpeg2_init(); + m->info = mpeg2_info( m->libmpeg2 ); + m->last_pts = -1; + + return m; +} + +/********************************************************************** + * hb_libmpeg2_decode + ********************************************************************** + * + *********************************************************************/ +int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, + hb_list_t * list_raw ) +{ + mpeg2_state_t state; + hb_buffer_t * buf; + uint8_t * data; + + /* Feed libmpeg2 */ + if( buf_es->start > -1 ) + { + mpeg2_tag_picture( m->libmpeg2, buf_es->start >> 32, + buf_es->start & 0xFFFFFFFF ); + } + mpeg2_buffer( m->libmpeg2, buf_es->data, + buf_es->data + buf_es->size ); + + for( ;; ) + { + state = mpeg2_parse( m->libmpeg2 ); + if( state == STATE_BUFFER ) + { + /* Require some more data */ + break; + } + else if( state == STATE_SEQUENCE ) + { + if( !( m->width && m->height && m->rate ) ) + { + m->width = m->info->sequence->width; + m->height = m->info->sequence->height; + m->rate = m->info->sequence->frame_period; + + if( m->rate == 900900 ) + { + /* 29.97 fps. 3:2 pulldown might, or might not be + used. I can't find a way to know, so we always + output 23.976 */ + m->rate = 1126125; + } + } + } + else if( ( state == STATE_SLICE || state == STATE_END ) && + m->info->display_fbuf ) + { + if( ( m->info->display_picture->flags & + PIC_MASK_CODING_TYPE ) == PIC_FLAG_CODING_TYPE_I ) + { + m->got_iframe = 1; + } + + if( m->got_iframe ) + { + buf = hb_buffer_init( m->width * m->height * 3 / 2 ); + data = buf->data; + + memcpy( data, m->info->display_fbuf->buf[0], + m->width * m->height ); + data += m->width * m->height; + memcpy( data, m->info->display_fbuf->buf[1], + m->width * m->height / 4 ); + data += m->width * m->height / 4; + memcpy( data, m->info->display_fbuf->buf[2], + m->width * m->height / 4 ); + + if( m->info->display_picture->flags & PIC_FLAG_TAGS ) + { + buf->start = + ( (uint64_t) m->info->display_picture->tag << 32 ) | + ( (uint64_t) m->info->display_picture->tag2 ); + } + else if( m->last_pts > -1 ) + { + /* For some reason nb_fields is sometimes 1 while it + should be 2 */ + buf->start = m->last_pts + + MAX( 2, m->info->display_picture->nb_fields ) * + m->info->sequence->frame_period / 600; + } + else + { + buf->start = -1; + } + m->last_pts = buf->start; + + hb_list_add( list_raw, buf ); + } + } + else if( state == STATE_INVALID ) + { + mpeg2_reset( m->libmpeg2, 0 ); + } + } + return 1; +} + +/********************************************************************** + * hb_libmpeg2_info + ********************************************************************** + * + *********************************************************************/ +void hb_libmpeg2_info( hb_libmpeg2_t * m, int * width, int * height, + int * rate ) +{ + *width = m->width; + *height = m->height; + *rate = m->rate; +} + +/********************************************************************** + * hb_libmpeg2_close + ********************************************************************** + * + *********************************************************************/ +void hb_libmpeg2_close( hb_libmpeg2_t ** _m ) +{ + hb_libmpeg2_t * m = *_m; + + mpeg2_close( m->libmpeg2 ); + + free( m ); + *_m = NULL; +} + +/********************************************************************** + * The decmpeg2 work object + ********************************************************************** + * + *********************************************************************/ +struct hb_work_private_s +{ + hb_libmpeg2_t * libmpeg2; + hb_list_t * list; +}; + +/********************************************************************** + * hb_work_decmpeg2_init + ********************************************************************** + * + *********************************************************************/ +int decmpeg2Init( hb_work_object_t * w, hb_job_t * job ) +{ + hb_work_private_t * pv; + + pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + + pv->libmpeg2 = hb_libmpeg2_init(); + pv->list = hb_list_init(); + + return 0; +} + +/********************************************************************** + * Work + ********************************************************************** + * + *********************************************************************/ +int decmpeg2Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * buf, * last = NULL; + + hb_libmpeg2_decode( pv->libmpeg2, *buf_in, pv->list ); + + *buf_out = NULL; + while( ( buf = hb_list_item( pv->list, 0 ) ) ) + { + hb_list_rem( pv->list, buf ); + if( last ) + { + last->next = buf; + last = buf; + } + else + { + *buf_out = buf; + last = buf; + } + } + + return HB_WORK_OK; +} + +/********************************************************************** + * Close + ********************************************************************** + * + *********************************************************************/ +void decmpeg2Close( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + hb_list_close( &pv->list ); + hb_libmpeg2_close( &pv->libmpeg2 ); + free( pv ); +} + +hb_work_object_t hb_decmpeg2 = +{ + WORK_DECMPEG2, + "MPEG-2 decoder (libmpeg2)", + decmpeg2Init, + decmpeg2Work, + decmpeg2Close +}; + diff --git a/libmediafork/decsub.c b/libmediafork/decsub.c new file mode 100644 index 000000000..f68e763e1 --- /dev/null +++ b/libmediafork/decsub.c @@ -0,0 +1,415 @@ +/* $Id: decsub.c,v 1.12 2005/04/14 17:37:54 titer Exp $ + + 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 "mediafork.h" + +struct hb_work_private_s +{ + hb_job_t * job; + + uint8_t buf[0xFFFF]; + int size_sub; + int size_got; + int size_rle; + int64_t pts; + int64_t pts_start; + int64_t pts_stop; + int x; + int y; + int width; + int height; + + int offsets[2]; + uint8_t lum[4]; + uint8_t alpha[4]; +}; + +static hb_buffer_t * Decode( hb_work_object_t * ); + +int decsubInit( hb_work_object_t * w, hb_job_t * job ) +{ + hb_work_private_t * pv; + + pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + + pv->job = job; + pv->pts = -1; + + return 0; +} + +int decsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * in = *buf_in; + + int size_sub, size_rle; + + size_sub = ( in->data[0] << 8 ) | in->data[1]; + size_rle = ( in->data[2] << 8 ) | in->data[3]; + + if( !pv->size_sub ) + { + /* We are looking for the start of a new subtitle */ + if( size_sub && size_rle && size_sub > size_rle && + in->size <= size_sub ) + { + /* Looks all right so far */ + pv->size_sub = size_sub; + pv->size_rle = size_rle; + + memcpy( pv->buf, in->data, in->size ); + pv->size_got = in->size; + pv->pts = in->start; + } + } + else + { + /* We are waiting for the end of the current subtitle */ + if( in->size <= pv->size_sub - pv->size_got ) + { + memcpy( pv->buf + pv->size_got, in->data, in->size ); + pv->size_got += in->size; + if( in->start >= 0 ) + { + pv->pts = in->start; + } + } + } + + *buf_out = NULL; + + if( pv->size_sub && pv->size_sub == pv->size_got ) + { + /* We got a complete subtitle, decode it */ + *buf_out = Decode( w ); + + /* Wait for the next one */ + pv->size_sub = 0; + pv->size_got = 0; + pv->size_rle = 0; + pv->pts = -1; + } + + return HB_WORK_OK; +} + +void decsubClose( hb_work_object_t * w ) +{ + free( w->private_data ); +} + +hb_work_object_t hb_decsub = +{ + WORK_DECSUB, + "Subtitle decoder", + decsubInit, + decsubWork, + decsubClose +}; + + +/*********************************************************************** + * ParseControls + *********************************************************************** + * Get the start and end dates (relative to the PTS from the PES + * header), the width and height of the subpicture and the colors and + * alphas used in it + **********************************************************************/ +static void ParseControls( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + hb_job_t * job = pv->job; + hb_title_t * title = job->title; + + int i; + int command; + int date, next; + + pv->pts_start = 0; + pv->pts_stop = 0; + + for( i = pv->size_rle; ; ) + { + date = ( pv->buf[i] << 8 ) | pv->buf[i+1]; i += 2; + next = ( pv->buf[i] << 8 ) | pv->buf[i+1]; i += 2; + + for( ;; ) + { + command = pv->buf[i++]; + + if( command == 0xFF ) + { + break; + } + + switch( command ) + { + case 0x00: + break; + + case 0x01: + pv->pts_start = pv->pts + date * 900; + break; + + case 0x02: + pv->pts_stop = pv->pts + date * 900; + break; + + case 0x03: + { + int colors[4]; + int j; + + colors[0] = (pv->buf[i+0]>>4)&0x0f; + colors[1] = (pv->buf[i+0])&0x0f; + colors[2] = (pv->buf[i+1]>>4)&0x0f; + colors[3] = (pv->buf[i+1])&0x0f; + + for( j = 0; j < 4; j++ ) + { + uint32_t color = title->palette[colors[j]]; + pv->lum[3-j] = (color>>16) & 0xff; + } + i += 2; + break; + } + case 0x04: + { + pv->alpha[3] = (pv->buf[i+0]>>4)&0x0f; + pv->alpha[2] = (pv->buf[i+0])&0x0f; + pv->alpha[1] = (pv->buf[i+1]>>4)&0x0f; + pv->alpha[0] = (pv->buf[i+1])&0x0f; + i += 2; + break; + } + case 0x05: + { + pv->x = (pv->buf[i+0]<<4) | ((pv->buf[i+1]>>4)&0x0f); + pv->width = (((pv->buf[i+1]&0x0f)<<8)| pv->buf[i+2]) - pv->x + 1; + pv->y = (pv->buf[i+3]<<4)| ((pv->buf[i+4]>>4)&0x0f); + pv->height = (((pv->buf[i+4]&0x0f)<<8)| pv->buf[i+5]) - pv->y + 1; + i += 6; + break; + } + case 0x06: + { + pv->offsets[0] = ( pv->buf[i] << 8 ) | pv->buf[i+1]; i += 2; + pv->offsets[1] = ( pv->buf[i] << 8 ) | pv->buf[i+1]; i += 2; + break; + } + } + } + + if( i > next ) + { + break; + } + i = next; + } + + if( !pv->pts_stop ) + { + /* Show it for 3 seconds */ + pv->pts_stop = pv->pts_start + 3 * 90000; + } +} + +/*********************************************************************** + * CropSubtitle + *********************************************************************** + * Given a raw decoded subtitle, detects transparent borders and + * returns a cropped subtitle in a hb_buffer_t ready to be used by + * the renderer, or NULL if the subtitle was completely transparent + **********************************************************************/ +static int LineIsTransparent( hb_work_object_t * w, uint8_t * p ) +{ + hb_work_private_t * pv = w->private_data; + int i; + for( i = 0; i < pv->width; i++ ) + { + if( p[i] ) + { + return 0; + } + } + return 1; +} +static int ColumnIsTransparent( hb_work_object_t * w, uint8_t * p ) +{ + hb_work_private_t * pv = w->private_data; + int i; + for( i = 0; i < pv->height; i++ ) + { + if( p[i*pv->width] ) + { + return 0; + } + } + return 1; +} +static hb_buffer_t * CropSubtitle( hb_work_object_t * w, uint8_t * raw ) +{ + hb_work_private_t * pv = w->private_data; + int i; + int crop[4] = { -1,-1,-1,-1 }; + uint8_t * alpha; + int realwidth, realheight; + hb_buffer_t * buf; + uint8_t * lum_in, * lum_out, * alpha_in, * alpha_out; + + alpha = raw + pv->width * pv->height; + + /* Top */ + for( i = 0; i < pv->height; i++ ) + { + if( !LineIsTransparent( w, &alpha[i*pv->width] ) ) + { + crop[0] = i; + break; + } + } + + if( crop[0] < 0 ) + { + /* Empty subtitle */ + return NULL; + } + + /* Bottom */ + for( i = pv->height - 1; i >= 0; i-- ) + { + if( !LineIsTransparent( w, &alpha[i*pv->width] ) ) + { + crop[1] = i; + break; + } + } + + /* Left */ + for( i = 0; i < pv->width; i++ ) + { + if( !ColumnIsTransparent( w, &alpha[i] ) ) + { + crop[2] = i; + break; + } + } + + /* Right */ + for( i = pv->width - 1; i >= 0; i-- ) + { + if( !ColumnIsTransparent( w, &alpha[i] ) ) + { + crop[3] = i; + break; + } + } + + realwidth = crop[3] - crop[2] + 1; + realheight = crop[1] - crop[0] + 1; + + buf = hb_buffer_init( realwidth * realheight * 2 ); + buf->start = pv->pts_start; + buf->stop = pv->pts_stop; + buf->x = pv->x + crop[2]; + buf->y = pv->y + crop[0]; + buf->width = realwidth; + buf->height = realheight; + + lum_in = raw + crop[0] * pv->width + crop[2]; + alpha_in = lum_in + pv->width * pv->height; + lum_out = buf->data; + alpha_out = lum_out + realwidth * realheight; + + for( i = 0; i < realheight; i++ ) + { + memcpy( lum_out, lum_in, realwidth ); + memcpy( alpha_out, alpha_in, realwidth ); + lum_in += pv->width; + alpha_in += pv->width; + lum_out += realwidth; + alpha_out += realwidth; + } + + return buf; +} + +static hb_buffer_t * Decode( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + int code, line, col; + int offsets[2]; + int * offset; + hb_buffer_t * buf; + uint8_t * buf_raw = NULL; + + /* Get infos about the subtitle */ + ParseControls( w ); + + /* Do the actual decoding now */ + buf_raw = malloc( pv->width * pv->height * 2 ); + +#define GET_NEXT_NIBBLE code = ( code << 4 ) | ( ( ( *offset & 1 ) ? \ +( pv->buf[((*offset)>>1)] & 0xF ) : ( pv->buf[((*offset)>>1)] >> 4 ) ) ); \ +(*offset)++ + + offsets[0] = pv->offsets[0] * 2; + offsets[1] = pv->offsets[1] * 2; + + for( line = 0; line < pv->height; line++ ) + { + /* Select even or odd field */ + offset = ( line & 1 ) ? &offsets[1] : &offsets[0]; + + for( col = 0; col < pv->width; col += code >> 2 ) + { + uint8_t * lum, * alpha; + + code = 0; + GET_NEXT_NIBBLE; + if( code < 0x4 ) + { + GET_NEXT_NIBBLE; + if( code < 0x10 ) + { + GET_NEXT_NIBBLE; + if( code < 0x40 ) + { + GET_NEXT_NIBBLE; + if( code < 0x100 ) + { + /* End of line */ + code |= ( pv->width - col ) << 2; + } + } + } + } + + lum = buf_raw; + alpha = lum + pv->width * pv->height; + memset( lum + line * pv->width + col, + pv->lum[code & 3], code >> 2 ); + memset( alpha + line * pv->width + col, + pv->alpha[code & 3], code >> 2 ); + } + + /* Byte-align */ + if( *offset & 1 ) + { + (*offset)++; + } + } + + /* Crop subtitle (remove transparent borders) */ + buf = CropSubtitle( w, buf_raw ); + + free( buf_raw ); + + return buf; +} diff --git a/libmediafork/demuxmpeg.c b/libmediafork/demuxmpeg.c new file mode 100644 index 000000000..54bef2b47 --- /dev/null +++ b/libmediafork/demuxmpeg.c @@ -0,0 +1,125 @@ +/* $Id: demuxmpeg.c,v 1.4 2004/10/19 23:11:36 titer Exp $ + + 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 "mediafork.h" + +/* Basic MPEG demuxer, only works with DVDs (2048 bytes packets) */ + +int hb_demux_ps( hb_buffer_t * buf_ps, hb_list_t * list_es ) +{ + hb_buffer_t * buf_es; + int pos; + + pos = 0; + +#define d (buf_ps->data) + + /* pack_header */ + if( d[pos] != 0 || d[pos+1] != 0 || + d[pos+2] != 0x1 || d[pos+3] != 0xBA ) + { + hb_log( "hb_demux_ps: not a PS packet (%02x%02x%02x%02x)", + d[pos], d[pos+1], d[pos+2], d[pos+3] ); + return 0; + } + pos += 4; /* pack_start_code */ + pos += 9; /* pack_header */ + pos += 1 + ( d[pos] & 0x7 ); /* stuffing bytes */ + + /* system_header */ + if( d[pos] == 0 && d[pos+1] == 0 && + d[pos+2] == 0x1 && d[pos+3] == 0xBB ) + { + int header_length; + + pos += 4; /* system_header_start_code */ + header_length = ( d[pos] << 8 ) + d[pos+1]; + pos += 2 + header_length; + } + + /* pes */ + while( pos + 6 < buf_ps->size && + d[pos] == 0 && d[pos+1] == 0 && d[pos+2] == 0x1 ) + { + int id; + int pes_packet_length; + int pes_packet_end; + int pes_header_d_length; + int pes_header_end; + int has_pts; + int64_t pts = -1; + + pos += 3; /* packet_start_code_prefix */ + id = d[pos]; + pos += 1; + + pes_packet_length = ( d[pos] << 8 ) + d[pos+1]; + pos += 2; /* pes_packet_length */ + pes_packet_end = pos + pes_packet_length; + + if( id != 0xE0 && id != 0xBD && + ( id & 0xC0 ) != 0xC0 ) + { + /* Not interesting */ + pos = pes_packet_end; + continue; + } + + has_pts = ( ( d[pos+1] >> 6 ) & 0x2 ) ? 1 : 0; + pos += 2; /* Required headers */ + + pes_header_d_length = d[pos]; + pos += 1; + pes_header_end = pos + pes_header_d_length; + + if( has_pts ) + { + pts = ( ( ( (uint64_t) d[pos] >> 1 ) & 0x7 ) << 30 ) + + ( d[pos+1] << 22 ) + + ( ( d[pos+2] >> 1 ) << 15 ) + + ( d[pos+3] << 7 ) + + ( d[pos+4] >> 1 ); + } + + pos = pes_header_end; + + if( id == 0xBD ) + { + id |= ( d[pos] << 8 ); + if( ( id & 0xF0FF ) == 0x80BD ) /* A52 */ + { + pos += 4; + } + else if( ( id & 0xE0FF ) == 0x20BD || /* SPU */ + ( id & 0xF0FF ) == 0xA0BD ) /* LPCM */ + { + pos += 1; + } + } + + /* Sanity check */ + if( pos >= pes_packet_end ) + { + pos = pes_packet_end; + continue; + } + + /* Here we hit we ES payload */ + buf_es = hb_buffer_init( pes_packet_end - pos ); + + buf_es->id = id; + buf_es->start = pts; + memcpy( buf_es->data, d + pos, pes_packet_end - pos ); + + hb_list_add( list_es, buf_es ); + + pos = pes_packet_end; + } + +#undef d + + return 1; +} diff --git a/libmediafork/dvd.c b/libmediafork/dvd.c new file mode 100644 index 000000000..04695a3e2 --- /dev/null +++ b/libmediafork/dvd.c @@ -0,0 +1,805 @@ +/* $Id: dvd.c,v 1.12 2005/11/25 15:05:25 titer Exp $ + + 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 "mediafork.h" +#include "lang.h" + +#include "dvdread/ifo_read.h" +#include "dvdread/nav_read.h" + +struct hb_dvd_s +{ + char * path; + + dvd_reader_t * reader; + ifo_handle_t * vmg; + + int vts; + int ttn; + ifo_handle_t * ifo; + dvd_file_t * file; + + pgc_t * pgc; + int cell_start; + int cell_end; + int title_start; + int title_end; + int title_block_count; + int cell_cur; + int cell_next; + int block; + int pack_len; + int next_vobu; +}; + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static void FindNextCell( hb_dvd_t * ); +static int dvdtime2msec( dvd_time_t * ); + +char * hb_dvd_name( char * path ) +{ + static char name[1024]; + unsigned char unused[1024]; + dvd_reader_t * reader; + + reader = DVDOpen( path ); + if( !reader ) + { + return NULL; + } + + if( DVDUDFVolumeInfo( reader, name, sizeof( name ), + unused, sizeof( unused ) ) ) + { + DVDClose( reader ); + return NULL; + } + + DVDClose( reader ); + return name; +} + +/*********************************************************************** + * hb_dvd_init + *********************************************************************** + * + **********************************************************************/ +hb_dvd_t * hb_dvd_init( char * path ) +{ + hb_dvd_t * d; + + d = calloc( sizeof( hb_dvd_t ), 1 ); + + /* Open device */ + if( !( d->reader = DVDOpen( path ) ) ) + { + hb_log( "dvd: DVDOpen failed (%s)", path ); + goto fail; + } + + /* Open main IFO */ + if( !( d->vmg = ifoOpen( d->reader, 0 ) ) ) + { + hb_log( "dvd: ifoOpen failed" ); + goto fail; + } + + d->path = strdup( path ); + + return d; + +fail: + if( d->vmg ) ifoClose( d->vmg ); + if( d->reader ) DVDClose( d->reader ); + free( d ); + return NULL; +} + +/*********************************************************************** + * hb_dvd_title_count + **********************************************************************/ +int hb_dvd_title_count( hb_dvd_t * d ) +{ + return d->vmg->tt_srpt->nr_of_srpts; +} + +/*********************************************************************** + * hb_dvd_title_scan + **********************************************************************/ +hb_title_t * hb_dvd_title_scan( hb_dvd_t * d, int t ) +{ + + hb_title_t * title; + ifo_handle_t * vts = NULL; + int pgc_id, pgn, i; + hb_chapter_t * chapter, * chapter_old; + int c; + uint64_t duration; + float duration_correction; + unsigned char unused[1024]; + + hb_log( "scan: scanning title %d", t ); + + title = hb_title_init( d->path, t ); + if( DVDUDFVolumeInfo( d->reader, title->name, sizeof( title->name ), + unused, sizeof( unused ) ) ) + { + char * p_cur, * p_last = d->path; + for( p_cur = d->path; *p_cur; p_cur++ ) + { + if( p_cur[0] == '/' && p_cur[1] ) + { + p_last = &p_cur[1]; + } + } + snprintf( title->name, sizeof( title->name ), "%s", p_last ); + } + + /* VTS which our title is in */ + title->vts = d->vmg->tt_srpt->title[t-1].title_set_nr; + + hb_log( "scan: opening IFO for VTS %d", title->vts ); + if( !( vts = ifoOpen( d->reader, title->vts ) ) ) + { + hb_log( "scan: ifoOpen failed" ); + goto fail; + } + + /* Position of the title in the VTS */ + title->ttn = d->vmg->tt_srpt->title[t-1].vts_ttn; + + /* Get pgc */ + pgc_id = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgcn; + pgn = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgn; + d->pgc = vts->vts_pgcit->pgci_srp[pgc_id-1].pgc; + + /* Start cell */ + title->cell_start = d->pgc->program_map[pgn-1] - 1; + title->block_start = d->pgc->cell_playback[title->cell_start].first_sector; + + /* End cell */ + title->cell_end = d->pgc->nr_of_cells - 1; + title->block_end = d->pgc->cell_playback[title->cell_end].last_sector; + + /* Block count */ + title->block_count = 0; + d->cell_cur = title->cell_start; + while( d->cell_cur <= title->cell_end ) + { +#define cp d->pgc->cell_playback[d->cell_cur] + title->block_count += cp.last_sector + 1 - cp.first_sector; +#undef cp + FindNextCell( d ); + d->cell_cur = d->cell_next; + } + + hb_log( "scan: vts=%d, ttn=%d, cells=%d->%d, blocks=%d->%d, " + "%d blocks", title->vts, title->ttn, title->cell_start, + title->cell_end, title->block_start, title->block_end, + title->block_count ); + + if( title->block_count < 2048 ) + { + hb_log( "scan: title too short (%d blocks), ignoring", + title->block_count ); + goto fail; + } + + + /* Get duration */ + title->duration = 90LL * dvdtime2msec( &d->pgc->playback_time ); + title->hours = title->duration / 90000 / 3600; + title->minutes = ( ( title->duration / 90000 ) % 3600 ) / 60; + title->seconds = ( title->duration / 90000 ) % 60; + hb_log( "scan: duration is %02d:%02d:%02d (%lld ms)", + title->hours, title->minutes, title->seconds, + title->duration / 90 ); + + /* Discard titles under 10 seconds */ + if( !( title->hours | title->minutes ) && title->seconds < 10 ) + { + hb_log( "scan: ignoring title (too short)" ); + goto fail; + } + + /* Detect languages */ + for( i = 0; i < vts->vtsi_mat->nr_of_vts_audio_streams; i++ ) + { + hb_audio_t * audio, * audio_tmp; + int audio_format, lang_code, audio_control, + position, j; + + hb_log( "scan: checking audio %d", i + 1 ); + + audio = calloc( sizeof( hb_audio_t ), 1 ); + + audio_format = vts->vtsi_mat->vts_audio_attr[i].audio_format; + lang_code = vts->vtsi_mat->vts_audio_attr[i].lang_code; + audio_control = + vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->audio_control[i]; + + if( !( audio_control & 0x8000 ) ) + { + hb_log( "scan: audio channel is not active" ); + free( audio ); + continue; + } + + position = ( audio_control & 0x7F00 ) >> 8; + + switch( audio_format ) + { + case 0x00: + audio->id = ( ( 0x80 + position ) << 8 ) | 0xbd; + audio->codec = HB_ACODEC_AC3; + break; + + case 0x02: + case 0x03: + audio->id = 0xc0 + position; + audio->codec = HB_ACODEC_MPGA; + break; + + case 0x04: + audio->id = ( ( 0xa0 + position ) << 8 ) | 0xbd; + audio->codec = HB_ACODEC_LPCM; + break; + + default: + audio->id = 0; + audio->codec = 0; + hb_log( "scan: unknown audio codec (%x)", + audio_format ); + break; + } + if( !audio->id ) + { + continue; + } + + /* Check for duplicate tracks */ + audio_tmp = NULL; + for( j = 0; j < hb_list_count( title->list_audio ); j++ ) + { + audio_tmp = hb_list_item( title->list_audio, j ); + if( audio->id == audio_tmp->id ) + { + break; + } + audio_tmp = NULL; + } + if( audio_tmp ) + { + hb_log( "scan: duplicate audio track" ); + free( audio ); + continue; + } + + snprintf( audio->lang, sizeof( audio->lang ), "%s (%s)", + lang_for_code( vts->vtsi_mat->vts_audio_attr[i].lang_code ), + audio->codec == HB_ACODEC_AC3 ? "AC3" : ( audio->codec == + HB_ACODEC_MPGA ? "MPEG" : "LPCM" ) ); + snprintf( audio->lang_simple, sizeof( audio->lang_simple ), "%s", + lang_for_code( vts->vtsi_mat->vts_audio_attr[i].lang_code ) ); + + hb_log( "scan: id=%x, lang=%s", audio->id, + audio->lang ); + + hb_list_add( title->list_audio, audio ); + } + + if( !hb_list_count( title->list_audio ) ) + { + hb_log( "scan: ignoring title (no audio track)" ); + goto fail; + } + + memcpy( title->palette, + vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->palette, + 16 * sizeof( uint32_t ) ); + + /* Check for subtitles */ + for( i = 0; i < vts->vtsi_mat->nr_of_vts_subp_streams; i++ ) + { + hb_subtitle_t * subtitle; + int spu_control; + int position; + + hb_log( "scan: checking subtitle %d", i + 1 ); + + spu_control = + vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->subp_control[i]; + + if( !( spu_control & 0x80000000 ) ) + { + hb_log( "scan: subtitle channel is not active" ); + continue; + } + + if( vts->vtsi_mat->vts_video_attr.display_aspect_ratio ) + { + switch( vts->vtsi_mat->vts_video_attr.permitted_df ) + { + case 1: + position = spu_control & 0xFF; + break; + case 2: + position = ( spu_control >> 8 ) & 0xFF; + break; + default: + position = ( spu_control >> 16 ) & 0xFF; + } + } + else + { + position = ( spu_control >> 24 ) & 0x7F; + } + + subtitle = calloc( sizeof( hb_subtitle_t ), 1 ); + subtitle->id = ( ( 0x20 + position ) << 8 ) | 0xbd; + snprintf( subtitle->lang, sizeof( subtitle->lang ), "%s", + lang_for_code( vts->vtsi_mat->vts_subp_attr[i].lang_code ) ); + + hb_log( "scan: id=%x, lang=%s", subtitle->id, + subtitle->lang ); + + hb_list_add( title->list_subtitle, subtitle ); + } + + /* Chapters */ + hb_log( "scan: title %d has %d chapters", t, + vts->vts_ptt_srpt->title[title->ttn-1].nr_of_ptts ); + for( i = 0, c = 1; + i < vts->vts_ptt_srpt->title[title->ttn-1].nr_of_ptts; i++ ) + { + int pgc_id_next, pgn_next; + pgc_t * pgc_next; + + chapter = calloc( sizeof( hb_chapter_t ), 1 ); + chapter->index = c; + + pgc_id = vts->vts_ptt_srpt->title[title->ttn-1].ptt[i].pgcn; + pgn = vts->vts_ptt_srpt->title[title->ttn-1].ptt[i].pgn; + d->pgc = vts->vts_pgcit->pgci_srp[pgc_id-1].pgc; + + /* Start cell */ + chapter->cell_start = d->pgc->program_map[pgn-1] - 1; + chapter->block_start = + d->pgc->cell_playback[chapter->cell_start].first_sector; + + /* End cell */ + if( i != vts->vts_ptt_srpt->title[title->ttn-1].nr_of_ptts - 1 ) + { + /* The cell before the starting cell of the next chapter, + or... */ + pgc_id_next = vts->vts_ptt_srpt->title[title->ttn-1].ptt[i+1].pgcn; + pgn_next = vts->vts_ptt_srpt->title[title->ttn-1].ptt[i+1].pgn; + pgc_next = vts->vts_pgcit->pgci_srp[pgc_id_next-1].pgc; + chapter->cell_end = pgc_next->program_map[pgn_next-1] - 2; + if( chapter->cell_end < 0 ) + { + /* Huh? */ + free( chapter ); + continue; + } + } + else + { + /* ... the last cell of the title */ + chapter->cell_end = title->cell_end; + } + chapter->block_end = + d->pgc->cell_playback[chapter->cell_end].last_sector; + + /* Block count, duration */ + pgc_id = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgcn; + pgn = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgn; + d->pgc = vts->vts_pgcit->pgci_srp[pgc_id-1].pgc; + chapter->block_count = 0; + chapter->duration = 0; + + d->cell_cur = chapter->cell_start; + while( d->cell_cur <= chapter->cell_end ) + { +#define cp d->pgc->cell_playback[d->cell_cur] + chapter->block_count += cp.last_sector + 1 - cp.first_sector; + chapter->duration += 90LL * dvdtime2msec( &cp.playback_time ); +#undef cp + FindNextCell( d ); + d->cell_cur = d->cell_next; + } + + if( chapter->block_count < 2048 && chapter->index > 1 ) + { + hb_log( "scan: chapter %d too short (%d blocks, " + "cells=%d->%d), merging", chapter->index, + chapter->block_count, chapter->cell_start, + chapter->cell_end ); + chapter_old = hb_list_item( title->list_chapter, + chapter->index - 2 ); + chapter_old->cell_end = chapter->cell_end; + chapter_old->block_end = chapter->block_end; + chapter_old->block_count += chapter->block_count; + free( chapter ); + chapter = chapter_old; + } + else + { + hb_list_add( title->list_chapter, chapter ); + c++; + } + } + + /* The durations we get for chapters aren't precise. Scale them so + the total matches the title duration */ + duration = 0; + for( i = 0; i < hb_list_count( title->list_chapter ); i++ ) + { + chapter = hb_list_item( title->list_chapter, i ); + duration += chapter->duration; + } + duration_correction = (float) title->duration / (float) duration; + for( i = 0; i < hb_list_count( title->list_chapter ); i++ ) + { + int seconds; + chapter = hb_list_item( title->list_chapter, i ); + chapter->duration = duration_correction * chapter->duration; + seconds = ( chapter->duration + 45000 ) / 90000; + chapter->hours = seconds / 3600; + chapter->minutes = ( seconds % 3600 ) / 60; + chapter->seconds = seconds % 60; + + hb_log( "scan: chap %d c=%d->%d, b=%d->%d (%d), %lld ms", + chapter->index, chapter->cell_start, chapter->cell_end, + chapter->block_start, chapter->block_end, + chapter->block_count, chapter->duration / 90 ); + } + + /* Get aspect. We don't get width/height/rate infos here as + they tend to be wrong */ + switch( vts->vtsi_mat->vts_video_attr.display_aspect_ratio ) + { + case 0: + title->aspect = HB_ASPECT_BASE * 4 / 3; + break; + case 3: + title->aspect = HB_ASPECT_BASE * 16 / 9; + break; + default: + hb_log( "scan: unknown aspect" ); + goto fail; + } + + hb_log( "scan: aspect = %d", title->aspect ); + + /* This title is ok so far */ + goto cleanup; + +fail: + hb_list_close( &title->list_audio ); + free( title ); + title = NULL; + +cleanup: + if( vts ) ifoClose( vts ); + + return title; +} + +/*********************************************************************** + * hb_dvd_start + *********************************************************************** + * Title and chapter start at 1 + **********************************************************************/ +int hb_dvd_start( hb_dvd_t * d, int title, int chapter ) +{ + int pgc_id, pgn; + int i; + + /* Open the IFO and the VOBs for this title */ + d->vts = d->vmg->tt_srpt->title[title-1].title_set_nr; + d->ttn = d->vmg->tt_srpt->title[title-1].vts_ttn; + if( !( d->ifo = ifoOpen( d->reader, d->vts ) ) ) + { + hb_log( "dvd: ifoOpen failed for VTS %d", d->vts ); + return 0; + } + if( !( d->file = DVDOpenFile( d->reader, d->vts, + DVD_READ_TITLE_VOBS ) ) ) + { + hb_log( "dvd: DVDOpenFile failed for VTS %d", d->vts ); + return 0; + } + + /* Get title first and last blocks */ + pgc_id = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[0].pgcn; + pgn = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[0].pgn; + d->pgc = d->ifo->vts_pgcit->pgci_srp[pgc_id-1].pgc; + d->cell_start = d->pgc->program_map[pgn - 1] - 1; + d->cell_end = d->pgc->nr_of_cells - 1; + d->title_start = d->pgc->cell_playback[d->cell_start].first_sector; + d->title_end = d->pgc->cell_playback[d->cell_end].last_sector; + + /* Block count */ + d->title_block_count = 0; + for( i = d->cell_start; i <= d->cell_end; i++ ) + { + d->title_block_count += d->pgc->cell_playback[i].last_sector + 1 - + d->pgc->cell_playback[i].first_sector; + } + + /* Get pgc for the current chapter */ + pgc_id = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[chapter-1].pgcn; + pgn = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[chapter-1].pgn; + d->pgc = d->ifo->vts_pgcit->pgci_srp[pgc_id-1].pgc; + + /* Get the two first cells */ + d->cell_cur = d->pgc->program_map[pgn-1] - 1; + FindNextCell( d ); + + d->block = d->pgc->cell_playback[d->cell_cur].first_sector; + d->next_vobu = d->block; + d->pack_len = 0; + + return 1; +} + +/*********************************************************************** + * hb_dvd_stop + *********************************************************************** + * + **********************************************************************/ +void hb_dvd_stop( hb_dvd_t * d ) +{ + if( d->ifo ) + { + ifoClose( d->ifo ); + d->ifo = NULL; + } + if( d->file ) + { + DVDCloseFile( d->file ); + d->file = NULL; + } +} + +/*********************************************************************** + * hb_dvd_seek + *********************************************************************** + * + **********************************************************************/ +int hb_dvd_seek( hb_dvd_t * d, float f ) +{ + int count, sizeCell; + int i; + + count = f * d->title_block_count; + + for( i = d->cell_start; i <= d->cell_end; i++ ) + { + sizeCell = d->pgc->cell_playback[i].last_sector + 1 - + d->pgc->cell_playback[i].first_sector; + + if( count < sizeCell ) + { + d->cell_cur = i; + FindNextCell( d ); + + /* Now let hb_dvd_read find the next VOBU */ + d->next_vobu = d->pgc->cell_playback[i].first_sector + count; + d->pack_len = 0; + break; + } + + count -= sizeCell; + } + + if( i > d->cell_end ) + { + return 0; + } + + return 1; +} + +/*********************************************************************** + * hb_dvd_read + *********************************************************************** + * + **********************************************************************/ +int hb_dvd_read( hb_dvd_t * d, hb_buffer_t * b ) +{ + if( !d->pack_len ) + { + /* New pack */ + dsi_t dsi_pack; + int error; + + error = 0; + + for( ;; ) + { + int block, pack_len, next_vobu; + + if( DVDReadBlocks( d->file, d->next_vobu, 1, b->data ) != 1 ) + { + hb_log( "dvd: DVDReadBlocks failed (%d)", d->next_vobu ); + return 0; + } + + navRead_DSI( &dsi_pack, &b->data[DSI_START_BYTE] ); + + block = dsi_pack.dsi_gi.nv_pck_lbn; + pack_len = dsi_pack.dsi_gi.vobu_ea; + next_vobu = block + ( dsi_pack.vobu_sri.next_vobu & 0x7fffffff ); + + if( pack_len > 0 && + pack_len < 1024 && + block >= d->next_vobu && + ( block <= d->title_start + d->title_block_count || + block <= d->title_end ) ) + { + /* XXX + This looks like a valid VOBU, but actually we are + just hoping */ + if( error ) + { +#if 0 + hb_log( "dvd: found VOBU at %d (b %d, l %d, n %d)", + d->next_vobu, block, pack_len, next_vobu ); +#endif + } + d->block = block; + d->pack_len = pack_len; + d->next_vobu = next_vobu; + break; + } + + /* Wasn't a valid VOBU, try next block */ + if( !error ) + { +#if 0 + hb_log( "dvd: looking for a VOBU (%d)", d->next_vobu ); +#endif + } + + if( ++error > 1024 ) + { + hb_log( "dvd: couldn't find a VOBU after 1024 blocks" ); + return 0; + } + + (d->next_vobu)++; + } + + if( dsi_pack.vobu_sri.next_vobu == SRI_END_OF_CELL ) + { + d->cell_cur = d->cell_next; + d->next_vobu = d->pgc->cell_playback[d->cell_cur].first_sector; + FindNextCell( d ); + } + } + else + { + if( DVDReadBlocks( d->file, d->block, 1, b->data ) != 1 ) + { + hb_log( "reader: DVDReadBlocks failed (%d)", d->block ); + return 0; + } + d->pack_len--; + } + + d->block++; + + return 1; +} + +/*********************************************************************** + * hb_dvd_chapter + *********************************************************************** + * Returns in which chapter the next block to be read is. + * Chapter numbers start at 1. + **********************************************************************/ +int hb_dvd_chapter( hb_dvd_t * d ) +{ + int i; + int pgc_id, pgn; + pgc_t * pgc; + + for( i = 0; + i < d->ifo->vts_ptt_srpt->title[d->ttn-1].nr_of_ptts; + i++ ) + { + /* Get pgc for chapter (i+1) */ + pgc_id = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[i].pgcn; + pgn = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[i].pgn; + pgc = d->ifo->vts_pgcit->pgci_srp[pgc_id-1].pgc; + + if( d->cell_cur >= pgc->program_map[pgn-1] - 1 && + d->cell_cur <= pgc->nr_of_cells - 1 ) + { + /* We are in this chapter */ + return i + 1; + } + } + + /* End of title */ + return -1; +} + +/*********************************************************************** + * hb_dvd_close + *********************************************************************** + * Closes and frees everything + **********************************************************************/ +void hb_dvd_close( hb_dvd_t ** _d ) +{ + hb_dvd_t * d = *_d; + + if( d->vmg ) + { + ifoClose( d->vmg ); + } + if( d->reader ) + { + DVDClose( d->reader ); + } + + free( d ); + *_d = NULL; +} + +/*********************************************************************** + * FindNextCell + *********************************************************************** + * Assumes pgc and cell_cur are correctly set, and sets cell_next to the + * cell to be read when we will be done with cell_cur. + **********************************************************************/ +static void FindNextCell( hb_dvd_t * d ) +{ + int i = 0; + + if( d->pgc->cell_playback[d->cell_cur].block_type == + BLOCK_TYPE_ANGLE_BLOCK ) + { + + while( d->pgc->cell_playback[d->cell_cur+i].block_mode != + BLOCK_MODE_LAST_CELL ) + { + i++; + } + d->cell_next = d->cell_cur + i + 1; + } + else + { + d->cell_next = d->cell_cur + 1; + } +} + +/*********************************************************************** + * dvdtime2msec + *********************************************************************** + * From lsdvd + **********************************************************************/ +static int dvdtime2msec(dvd_time_t * dt) +{ + double frames_per_s[4] = {-1.0, 25.00, -1.0, 29.97}; + double fps = frames_per_s[(dt->frame_u & 0xc0) >> 6]; + long ms; + ms = (((dt->hour & 0xf0) >> 3) * 5 + (dt->hour & 0x0f)) * 3600000; + ms += (((dt->minute & 0xf0) >> 3) * 5 + (dt->minute & 0x0f)) * 60000; + ms += (((dt->second & 0xf0) >> 3) * 5 + (dt->second & 0x0f)) * 1000; + + if( fps > 0 ) + { + ms += ((dt->frame_u & 0x30) >> 3) * 5 + + (dt->frame_u & 0x0f) * 1000.0 / fps; + } + + return ms; +} diff --git a/libmediafork/encavcodec.c b/libmediafork/encavcodec.c new file mode 100644 index 000000000..c1d7df9d2 --- /dev/null +++ b/libmediafork/encavcodec.c @@ -0,0 +1,204 @@ +/* $Id: encavcodec.c,v 1.23 2005/10/13 23:47:06 titer Exp $ + + 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 "mediafork.h" + +#include "ffmpeg/avcodec.h" + +struct hb_work_private_s +{ + hb_job_t * job; + AVCodecContext * context; + FILE * file; +}; + +int encavcodecInit( hb_work_object_t *, hb_job_t * ); +int encavcodecWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +void encavcodecClose( hb_work_object_t * ); + +hb_work_object_t hb_encavcodec = +{ + WORK_ENCAVCODEC, + "MPEG-4 encoder (libavcodec)", + encavcodecInit, + encavcodecWork, + encavcodecClose +}; + +int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) +{ + AVCodec * codec; + AVCodecContext * context; + + hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + + pv->job = job; + + codec = avcodec_find_encoder( CODEC_ID_MPEG4 ); + if( !codec ) + { + hb_log( "hb_work_encavcodec_init: avcodec_find_encoder " + "failed" ); + } + context = avcodec_alloc_context(); + if( job->vquality < 0.0 || job->vquality > 1.0 ) + { + /* Rate control */ + context->bit_rate = 1000 * job->vbitrate; + context->bit_rate_tolerance = 10 * context->bit_rate; + } + else + { + /* Constant quantizer */ + context->qmin = 31 - job->vquality * 30; + context->qmax = context->qmin; + hb_log( "encavcodec: encoding at constant quantizer %d", + context->qmin ); + } + context->width = job->width; + context->height = job->height; + context->time_base = (AVRational) { job->vrate_base, job->vrate }; + context->gop_size = 10 * job->vrate / job->vrate_base; + context->pix_fmt = PIX_FMT_YUV420P; + + if( job->pixel_ratio ) + { + context->sample_aspect_ratio.num = job->pixel_aspect_width; + context->sample_aspect_ratio.den = job->pixel_aspect_height; + + hb_log( "encavcodec: encoding with stored aspect %d/%d", + job->pixel_aspect_width, job->pixel_aspect_height ); + } + + if( job->mux & ( HB_MUX_MP4 | HB_MUX_PSP ) ) + { + context->flags |= CODEC_FLAG_GLOBAL_HEADER; + } + if( job->mux & HB_MUX_PSP ) + { + context->flags |= CODEC_FLAG_BITEXACT; + } + if( job->grayscale ) + { + context->flags |= CODEC_FLAG_GRAY; + } + + if( job->pass ) + { + char filename[1024]; memset( filename, 0, 1024 ); + hb_get_tempory_filename( job->h, filename, "ffmpeg.log" ); + + if( job->pass == 1 ) + { + pv->file = fopen( filename, "wb" ); + context->flags |= CODEC_FLAG_PASS1; + } + else + { + int size; + char * log; + + pv->file = fopen( filename, "rb" ); + fseek( pv->file, 0, SEEK_END ); + size = ftell( pv->file ); + fseek( pv->file, 0, SEEK_SET ); + log = malloc( size + 1 ); + log[size] = '\0'; + fread( log, size, 1, pv->file ); + fclose( pv->file ); + pv->file = NULL; + + context->flags |= CODEC_FLAG_PASS2; + context->stats_in = log; + } + } + + if( avcodec_open( context, codec ) ) + { + hb_log( "hb_work_encavcodec_init: avcodec_open failed" ); + } + pv->context = context; + + if( ( job->mux & ( HB_MUX_MP4 | HB_MUX_PSP ) ) && job->pass != 1 ) + { +#if 0 + /* Hem hem */ + w->config->mpeg4.length = 15; + memcpy( w->config->mpeg4.bytes, context->extradata + 15, 15 ); +#else + w->config->mpeg4.length = context->extradata_size; + memcpy( w->config->mpeg4.bytes, context->extradata, + context->extradata_size ); +#endif + } + + return 0; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +void encavcodecClose( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + + if( pv->context ) + { + hb_log( "encavcodec: closing libavcodec" ); + avcodec_close( pv->context ); + } + if( pv->file ) + { + fclose( pv->file ); + } +} + +/*********************************************************************** + * Work + *********************************************************************** + * + **********************************************************************/ +int encavcodecWork( 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; + AVFrame * frame; + hb_buffer_t * in = *buf_in, * buf; + + frame = avcodec_alloc_frame(); + frame->data[0] = in->data; + frame->data[1] = frame->data[0] + job->width * job->height; + frame->data[2] = frame->data[1] + job->width * job->height / 4; + frame->linesize[0] = job->width; + frame->linesize[1] = job->width / 2; + frame->linesize[2] = job->width / 2; + + /* Should be way too large */ + buf = hb_buffer_init( 3 * job->width * job->height / 2 ); + buf->size = avcodec_encode_video( pv->context, buf->data, buf->alloc, + frame ); + buf->start = in->start; + buf->stop = in->stop; + buf->key = pv->context->coded_frame->key_frame; + + av_free( frame ); + + if( job->pass == 1 ) + { + /* Write stats */ + fprintf( pv->file, "%s", pv->context->stats_out ); + } + + *buf_out = buf; + + return HB_WORK_OK; +} + + diff --git a/libmediafork/encfaac.c b/libmediafork/encfaac.c new file mode 100644 index 000000000..19787a560 --- /dev/null +++ b/libmediafork/encfaac.c @@ -0,0 +1,168 @@ +/* $Id: encfaac.c,v 1.13 2005/03/03 17:21:57 titer Exp $ + + 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 "mediafork.h" + +#include "faac.h" + +struct hb_work_private_s +{ + hb_job_t * job; + + faacEncHandle * faac; + unsigned long input_samples; + unsigned long output_bytes; + uint8_t * buf; + + hb_list_t * list; + int64_t pts; +}; + +int encfaacInit( hb_work_object_t *, hb_job_t * ); +int encfaacWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +void encfaacClose( hb_work_object_t * ); + +hb_work_object_t hb_encfaac = +{ + WORK_ENCFAAC, + "AAC encoder (libfaac)", + encfaacInit, + encfaacWork, + encfaacClose +}; + +/*********************************************************************** + * hb_work_encfaac_init + *********************************************************************** + * + **********************************************************************/ +int encfaacInit( hb_work_object_t * w, hb_job_t * job ) +{ + hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); + faacEncConfigurationPtr cfg; + uint8_t * bytes; + unsigned long length; + + w->private_data = pv; + + pv->job = job; + + pv->faac = faacEncOpen( job->arate, 2, &pv->input_samples, + &pv->output_bytes ); + pv->buf = malloc( pv->input_samples * sizeof( float ) ); + + cfg = faacEncGetCurrentConfiguration( pv->faac ); + cfg->mpegVersion = MPEG4; + cfg->aacObjectType = LOW; + cfg->allowMidside = 1; + cfg->useLfe = 0; + cfg->useTns = 0; + cfg->bitRate = job->abitrate * 500; /* Per channel */ + cfg->bandWidth = 0; + cfg->outputFormat = 0; + cfg->inputFormat = FAAC_INPUT_FLOAT; + if( !faacEncSetConfiguration( pv->faac, cfg ) ) + { + hb_log( "faacEncSetConfiguration failed" ); + } + + if( faacEncGetDecoderSpecificInfo( pv->faac, &bytes, &length ) < 0 ) + { + hb_log( "faacEncGetDecoderSpecificInfo failed" ); + } + memcpy( w->config->aac.bytes, bytes, length ); + w->config->aac.length = length; + free( bytes ); + + pv->list = hb_list_init(); + pv->pts = -1; + + return 0; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +void encfaacClose( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + faacEncClose( pv->faac ); + free( pv->buf ); + hb_list_empty( &pv->list ); +} + +/*********************************************************************** + * Encode + *********************************************************************** + * + **********************************************************************/ +static hb_buffer_t * Encode( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * buf; + uint64_t pts; + int pos; + + if( hb_list_bytes( pv->list ) < pv->input_samples * sizeof( float ) ) + { + /* Need more data */ + return NULL; + } + + hb_list_getbytes( pv->list, pv->buf, pv->input_samples * sizeof( float ), + &pts, &pos ); + + buf = hb_buffer_init( pv->output_bytes ); + buf->start = pts + 90000 * pos / 2 / sizeof( float ) / pv->job->arate; + buf->stop = buf->start + 90000 * pv->input_samples / pv->job->arate / 2; + buf->size = faacEncEncode( pv->faac, (int32_t *) pv->buf, + pv->input_samples, buf->data, pv->output_bytes ); + buf->key = 1; + + if( !buf->size ) + { + /* Encoding was successful but we got no data. Try to encode + more */ + hb_buffer_close( &buf ); + return Encode( w ); + } + else if( buf->size < 0 ) + { + hb_log( "faacEncEncode failed" ); + hb_buffer_close( &buf ); + return NULL; + } + + return buf; +} + +/*********************************************************************** + * Work + *********************************************************************** + * + **********************************************************************/ +int encfaacWork( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * buf; + + hb_list_add( pv->list, *buf_in ); + *buf_in = NULL; + + *buf_out = buf = Encode( w ); + + while( buf ) + { + buf->next = Encode( w ); + buf = buf->next; + } + + return HB_WORK_OK; +} + diff --git a/libmediafork/enclame.c b/libmediafork/enclame.c new file mode 100644 index 000000000..82bca1132 --- /dev/null +++ b/libmediafork/enclame.c @@ -0,0 +1,147 @@ +/* $Id: enclame.c,v 1.9 2005/03/05 14:27:05 titer Exp $ + + 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 "mediafork.h" + +#include "lame/lame.h" + +int enclameInit( hb_work_object_t *, hb_job_t * ); +int enclameWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +void enclameClose( hb_work_object_t * ); + +hb_work_object_t hb_enclame = +{ + WORK_ENCLAME, + "MP3 encoder (libmp3lame)", + enclameInit, + enclameWork, + enclameClose +}; + +struct hb_work_private_s +{ + hb_job_t * job; + + /* LAME handle */ + lame_global_flags * lame; + + unsigned long input_samples; + unsigned long output_bytes; + uint8_t * buf; + + hb_list_t * list; + int64_t pts; +}; + +int enclameInit( hb_work_object_t * w, hb_job_t * job ) +{ + hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + + pv->job = job; + + hb_log( "enclame: opening libmp3lame" ); + + pv->lame = lame_init(); + lame_set_brate( pv->lame, job->abitrate ); + lame_set_in_samplerate( pv->lame, job->arate ); + lame_set_out_samplerate( pv->lame, job->arate ); + lame_init_params( pv->lame ); + + pv->input_samples = 1152 * 2; + pv->output_bytes = LAME_MAXMP3BUFFER; + pv->buf = malloc( pv->input_samples * sizeof( float ) ); + + pv->list = hb_list_init(); + pv->pts = -1; + + return 0; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +void enclameClose( hb_work_object_t * w ) +{ +} + +/*********************************************************************** + * Encode + *********************************************************************** + * + **********************************************************************/ +static hb_buffer_t * Encode( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * buf; + int16_t samples_s16[1152 * 2]; + uint64_t pts; + int pos, i; + + if( hb_list_bytes( pv->list ) < pv->input_samples * sizeof( float ) ) + { + return NULL; + } + + hb_list_getbytes( pv->list, pv->buf, pv->input_samples * sizeof( float ), + &pts, &pos); + + for( i = 0; i < 1152 * 2; i++ ) + { + samples_s16[i] = ((float*) pv->buf)[i]; + } + + buf = hb_buffer_init( pv->output_bytes ); + buf->start = pts + 90000 * pos / 2 / sizeof( float ) / pv->job->arate; + buf->stop = buf->start + 90000 * 1152 / pv->job->arate; + buf->size = lame_encode_buffer_interleaved( pv->lame, samples_s16, + 1152, buf->data, LAME_MAXMP3BUFFER ); + buf->key = 1; + + if( !buf->size ) + { + /* Encoding was successful but we got no data. Try to encode + more */ + hb_buffer_close( &buf ); + return Encode( w ); + } + else if( buf->size < 0 ) + { + hb_log( "enclame: lame_encode_buffer failed" ); + hb_buffer_close( &buf ); + return NULL; + } + + return buf; +} + +/*********************************************************************** + * Work + *********************************************************************** + * + **********************************************************************/ +int enclameWork( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * buf; + + hb_list_add( pv->list, *buf_in ); + *buf_in = NULL; + + *buf_out = buf = Encode( w ); + + while( buf ) + { + buf->next = Encode( w ); + buf = buf->next; + } + + return HB_WORK_OK; +} + diff --git a/libmediafork/encvorbis.c b/libmediafork/encvorbis.c new file mode 100644 index 000000000..1e916a447 --- /dev/null +++ b/libmediafork/encvorbis.c @@ -0,0 +1,197 @@ +/* $Id: encvorbis.c,v 1.6 2005/03/05 15:08:32 titer Exp $ + + 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 "mediafork.h" + +#include "vorbis/vorbisenc.h" + +#define OGGVORBIS_FRAME_SIZE 1024 + +int encvorbisInit( hb_work_object_t *, hb_job_t * ); +int encvorbisWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +void encvorbisClose( hb_work_object_t * ); + +hb_work_object_t hb_encvorbis = +{ + WORK_ENCVORBIS, + "Vorbis encoder (libvorbis)", + encvorbisInit, + encvorbisWork, + encvorbisClose +}; + +struct hb_work_private_s +{ + hb_job_t * job; + + vorbis_info vi; + vorbis_comment vc; + vorbis_dsp_state vd; + vorbis_block vb; + + unsigned long input_samples; + uint8_t * buf; + uint64_t pts; + + hb_list_t * list; +}; + +int encvorbisInit( hb_work_object_t * w, hb_job_t * job ) +{ + int i; + ogg_packet header[3]; + + hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + + pv->job = job; + + hb_log( "encvorbis: opening libvorbis" ); + + /* init */ + vorbis_info_init( &pv->vi ); + if( vorbis_encode_setup_managed( &pv->vi, 2, + job->arate, -1, 1000 * job->abitrate, -1 ) || + vorbis_encode_ctl( &pv->vi, OV_ECTL_RATEMANAGE_AVG, NULL ) || + vorbis_encode_setup_init( &pv->vi ) ) + { + hb_log( "encvorbis: vorbis_encode_setup_managed failed" ); + } + + /* add a comment */ + vorbis_comment_init( &pv->vc ); + vorbis_comment_add_tag( &pv->vc, "Encoder", "HandBrake"); + + /* set up the analysis state and auxiliary encoding storage */ + vorbis_analysis_init( &pv->vd, &pv->vi); + vorbis_block_init( &pv->vd, &pv->vb); + + /* get the 3 headers */ + vorbis_analysis_headerout( &pv->vd, &pv->vc, + &header[0], &header[1], &header[2] ); + for( i = 0; i < 3; i++ ) + { + memcpy( w->config->vorbis.headers[i], &header[i], + sizeof( ogg_packet ) ); + memcpy( w->config->vorbis.headers[i] + sizeof( ogg_packet ), + header[i].packet, header[i].bytes ); + } + + pv->input_samples = 2 * OGGVORBIS_FRAME_SIZE; + pv->buf = malloc( pv->input_samples * sizeof( float ) ); + + pv->list = hb_list_init(); + + return 0; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +void encvorbisClose( hb_work_object_t * w ) +{ +} + +/*********************************************************************** + * Flush + *********************************************************************** + * + **********************************************************************/ +static hb_buffer_t * Flush( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * buf; + + if( vorbis_analysis_blockout( &pv->vd, &pv->vb ) == 1 ) + { + ogg_packet op; + + vorbis_analysis( &pv->vb, NULL ); + vorbis_bitrate_addblock( &pv->vb ); + + if( vorbis_bitrate_flushpacket( &pv->vd, &op ) ) + { + buf = hb_buffer_init( sizeof( ogg_packet ) + op.bytes ); + memcpy( buf->data, &op, sizeof( ogg_packet ) ); + memcpy( buf->data + sizeof( ogg_packet ), op.packet, + op.bytes ); + buf->key = 1; + buf->start = pv->pts; /* No exact, but who cares - the OGM + muxer doesn't use it */ + buf->stop = buf->start + + 90000 * OGGVORBIS_FRAME_SIZE + pv->job->arate; + + return buf; + } + } + + return NULL; +} + +/*********************************************************************** + * Encode + *********************************************************************** + * + **********************************************************************/ +static hb_buffer_t * Encode( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * buf; + float ** buffer; + int i; + + /* Try to extract more data */ + if( ( buf = Flush( w ) ) ) + { + return buf; + } + + if( hb_list_bytes( pv->list ) < pv->input_samples * sizeof( float ) ) + { + return NULL; + } + + /* Process more samples */ + hb_list_getbytes( pv->list, pv->buf, pv->input_samples * sizeof( float ), + &pv->pts, NULL ); + buffer = vorbis_analysis_buffer( &pv->vd, OGGVORBIS_FRAME_SIZE ); + for( i = 0; i < OGGVORBIS_FRAME_SIZE; i++ ) + { + buffer[0][i] = ((float *) pv->buf)[2*i] / 32768.f; + buffer[1][i] = ((float *) pv->buf)[2*i+1] / 32768.f; + } + vorbis_analysis_wrote( &pv->vd, OGGVORBIS_FRAME_SIZE ); + + /* Try to extract again */ + return Flush( w ); +} + +/*********************************************************************** + * Work + *********************************************************************** + * + **********************************************************************/ +int encvorbisWork( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * buf; + + hb_list_add( pv->list, *buf_in ); + *buf_in = NULL; + + *buf_out = buf = Encode( w ); + + while( buf ) + { + buf->next = Encode( w ); + buf = buf->next; + } + + return HB_WORK_OK; +} diff --git a/libmediafork/encx264.c b/libmediafork/encx264.c new file mode 100644 index 000000000..cdd8e9b69 --- /dev/null +++ b/libmediafork/encx264.c @@ -0,0 +1,241 @@ +/* $Id: encx264.c,v 1.21 2005/11/04 13:09:41 titer Exp $ + + 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 "mediafork.h" + +#include "x264.h" + +int encx264Init( hb_work_object_t *, hb_job_t * ); +int encx264Work( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +void encx264Close( hb_work_object_t * ); + +hb_work_object_t hb_encx264 = +{ + WORK_ENCX264, + "H.264/AVC encoder (libx264)", + encx264Init, + encx264Work, + encx264Close +}; + +struct hb_work_private_s +{ + hb_job_t * job; + x264_t * x264; + x264_picture_t pic_in; + x264_picture_t pic_out; + + char filename[1024]; +}; + +/*********************************************************************** + * hb_work_encx264_init + *********************************************************************** + * + **********************************************************************/ +int encx264Init( hb_work_object_t * w, hb_job_t * job ) +{ + x264_param_t param; + x264_nal_t * nal; + int nal_count; + + hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + + pv->job = job; + + memset( pv->filename, 0, 1024 ); + hb_get_tempory_filename( job->h, pv->filename, "x264.log" ); + + x264_param_default( ¶m ); + + param.i_threads = hb_get_cpu_count(); + param.i_width = job->width; + param.i_height = job->height; + param.i_fps_num = job->vrate; + param.i_fps_den = job->vrate_base; + param.i_keyint_max = 20 * job->vrate / job->vrate_base; + param.i_log_level = X264_LOG_NONE; + if( job->h264_level ) + { + param.i_threads = 1; + param.b_cabac = 0; + param.i_level_idc = job->h264_level; + hb_log( "encx264: encoding at level %i", + param.i_level_idc ); + } + + /* Slightly faster with minimal quality lost */ + param.analyse.i_subpel_refine = 4; + + if( job->pixel_ratio ) + { + param.vui.i_sar_width = job->pixel_aspect_width; + param.vui.i_sar_height = job->pixel_aspect_height; + + hb_log( "encx264: encoding with stored aspect %d/%d", + param.vui.i_sar_width, param.vui.i_sar_height ); + } + + + if( job->vquality >= 0.0 && job->vquality <= 1.0 ) + { + switch(job->crf) + { + case 1: + /*Constant RF*/ + param.rc.i_rc_method = X264_RC_CRF; + param.rc.f_rf_constant = 51 - job->vquality * 51; + hb_log( "encx264: Encoding at constant RF %f", + param.rc.f_rf_constant ); + break; + + case 0: + /*Constant QP*/ + param.rc.i_rc_method = X264_RC_CQP; + param.rc.i_qp_constant = 51 - job->vquality * 51; + hb_log( "encx264: encoding at constant QP %d", + param.rc.i_qp_constant ); + break; + } + } + else + { + /* Rate control */ + param.rc.i_rc_method = X264_RC_ABR; + param.rc.i_bitrate = job->vbitrate; + switch( job->pass ) + { + case 1: + param.rc.b_stat_write = 1; + param.rc.psz_stat_out = pv->filename; + break; + case 2: + param.rc.b_stat_read = 1; + param.rc.psz_stat_in = pv->filename; + break; + } + } + + hb_log( "encx264: opening libx264 (pass %d)", job->pass ); + pv->x264 = x264_encoder_open( ¶m ); + + x264_encoder_headers( pv->x264, &nal, &nal_count ); + + /* Sequence Parameter Set */ + w->config->h264.sps_length = 1 + nal[1].i_payload; + w->config->h264.sps[0] = 0x67; + memcpy( &w->config->h264.sps[1], nal[1].p_payload, nal[1].i_payload ); + + /* Picture Parameter Set */ + w->config->h264.pps_length = 1 + nal[2].i_payload; + w->config->h264.pps[0] = 0x68; + memcpy( &w->config->h264.pps[1], nal[2].p_payload, nal[2].i_payload ); + + x264_picture_alloc( &pv->pic_in, X264_CSP_I420, + job->width, job->height ); + + return 0; +} + +void encx264Close( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + x264_encoder_close( pv->x264 ); + + /* TODO */ +} + +int encx264Work( 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; + int i_nal; + x264_nal_t * nal; + int i; + + /* 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 ); + } + + pv->pic_in.i_type = X264_TYPE_AUTO; + pv->pic_in.i_qpplus1 = 0; + + x264_encoder_encode( pv->x264, &nal, &i_nal, + &pv->pic_in, &pv->pic_out ); + + /* 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->key = 0; + + 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->key = 1; + } + 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; + if( nal[i].i_ref_idc == NAL_PRIORITY_HIGHEST ) + { + buf->key = 1; + } + buf->size += size; + } + } + + *buf_out = buf; + + return HB_WORK_OK; +} + + diff --git a/libmediafork/encxvid.c b/libmediafork/encxvid.c new file mode 100644 index 000000000..64b38a405 --- /dev/null +++ b/libmediafork/encxvid.c @@ -0,0 +1,223 @@ +/* $Id: encxvid.c,v 1.10 2005/03/09 23:28:39 titer Exp $ + + 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 "mediafork.h" + +#include "xvid.h" + +int encxvidInit( hb_work_object_t *, hb_job_t * ); +int encxvidWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +void encxvidClose( hb_work_object_t * ); + +hb_work_object_t hb_encxvid = +{ + WORK_ENCXVID, + "MPEG-4 encoder (libxvidcore)", + encxvidInit, + encxvidWork, + encxvidClose +}; + +struct hb_work_private_s +{ + hb_job_t * job; + void * xvid; + char filename[1024]; + int quant; + int configDone; +}; + +int encxvidInit( hb_work_object_t * w, hb_job_t * job ) +{ + xvid_gbl_init_t xvid_gbl_init; + xvid_enc_create_t create; + xvid_plugin_single_t single; + xvid_plugin_2pass1_t rc2pass1; + xvid_plugin_2pass2_t rc2pass2; + xvid_enc_plugin_t plugins[1]; + + hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + + pv->job = job; + + memset( pv->filename, 0, 1024 ); + hb_get_tempory_filename( job->h, pv->filename, "xvid.log" ); + + memset( &xvid_gbl_init, 0, sizeof( xvid_gbl_init ) ); + xvid_gbl_init.version = XVID_VERSION; + xvid_global( NULL, XVID_GBL_INIT, &xvid_gbl_init, NULL ); + + memset( &create, 0, sizeof( create ) ); + create.version = XVID_VERSION; + create.width = job->width; + create.height = job->height; + create.zones = NULL; + create.num_zones = 0; + + switch( job->pass ) + { + case 0: + memset( &single, 0, sizeof( single ) ); + single.version = XVID_VERSION; + if( job->vquality < 0.0 || job->vquality > 1.0 ) + { + /* Rate control */ + single.bitrate = 1000 * job->vbitrate; + pv->quant = 0; + } + else + { + /* Constant quantizer */ + pv->quant = 31 - job->vquality * 30; + hb_log( "encxvid: encoding at constant quantizer %d", + pv->quant ); + } + plugins[0].func = xvid_plugin_single; + plugins[0].param = &single; + break; + + case 1: + memset( &rc2pass1, 0, sizeof( rc2pass1 ) ); + rc2pass1.version = XVID_VERSION; + rc2pass1.filename = pv->filename; + plugins[0].func = xvid_plugin_2pass1; + plugins[0].param = &rc2pass1; + break; + + case 2: + memset( &rc2pass2, 0, sizeof( rc2pass2 ) ); + rc2pass2.version = XVID_VERSION; + rc2pass2.filename = pv->filename; + rc2pass2.bitrate = 1000 * job->vbitrate; + plugins[0].func = xvid_plugin_2pass2; + plugins[0].param = &rc2pass2; + break; + } + + create.plugins = plugins; + create.num_plugins = 1; + + create.num_threads = 0; + create.fincr = job->vrate_base; + create.fbase = job->vrate; + create.max_key_interval = 10 * job->vrate / job->vrate_base; + create.max_bframes = 0; + create.bquant_ratio = 150; + create.bquant_offset = 100; + create.frame_drop_ratio = 0; + create.global = 0; + + xvid_encore( NULL, XVID_ENC_CREATE, &create, NULL ); + pv->xvid = create.handle; + + return 0; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +void encxvidClose( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + + if( pv->xvid ) + { + hb_log( "encxvid: closing libxvidcore" ); + xvid_encore( pv->xvid, XVID_ENC_DESTROY, NULL, NULL); + } +} + +/*********************************************************************** + * Work + *********************************************************************** + * + **********************************************************************/ +int encxvidWork( 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; + xvid_enc_frame_t frame; + hb_buffer_t * in = *buf_in, * buf; + + /* Should be way too large */ + buf = hb_buffer_init( 3 * job->width * job->height / 2 ); + buf->start = in->start; + buf->stop = in->stop; + + memset( &frame, 0, sizeof( frame ) ); + + frame.version = XVID_VERSION; + frame.bitstream = buf->data; + frame.length = -1; + frame.input.plane[0] = in->data; + frame.input.csp = XVID_CSP_I420; + frame.input.stride[0] = job->width; + frame.vol_flags = 0; + frame.vop_flags = XVID_VOP_HALFPEL | XVID_VOP_INTER4V | + XVID_VOP_TRELLISQUANT | XVID_VOP_HQACPRED; + if( job->pixel_ratio ) + { + frame.par = XVID_PAR_EXT; + frame.par_width = job->pixel_aspect_width; + frame.par_height = job->pixel_aspect_height; + } + + if( job->grayscale ) + { + frame.vop_flags |= XVID_VOP_GREYSCALE; + } + frame.type = XVID_TYPE_AUTO; + frame.quant = pv->quant; + frame.motion = XVID_ME_ADVANCEDDIAMOND16 | XVID_ME_HALFPELREFINE16 | + XVID_ME_EXTSEARCH16 | XVID_ME_ADVANCEDDIAMOND8 | + XVID_ME_HALFPELREFINE8 | XVID_ME_EXTSEARCH8 | + XVID_ME_CHROMA_PVOP | XVID_ME_CHROMA_BVOP; + frame.quant_intra_matrix = NULL; + frame.quant_inter_matrix = NULL; + + buf->size = xvid_encore( pv->xvid, XVID_ENC_ENCODE, &frame, NULL ); + buf->key = ( frame.out_flags & XVID_KEYFRAME ); + + if( !pv->configDone ) + { + int vol_start, vop_start; + for( vol_start = 0; ; vol_start++ ) + { + if( buf->data[vol_start] == 0x0 && + buf->data[vol_start+1] == 0x0 && + buf->data[vol_start+2] == 0x1 && + buf->data[vol_start+3] == 0x20 ) + { + break; + } + } + for( vop_start = vol_start + 4; ; vop_start++ ) + { + if( buf->data[vop_start] == 0x0 && + buf->data[vop_start+1] == 0x0 && + buf->data[vop_start+2] == 0x1 && + buf->data[vop_start+3] == 0xB6 ) + { + break; + } + } + + hb_log( "encxvid: VOL size is %d bytes", vop_start - vol_start ); + job->config.mpeg4.length = vop_start - vol_start; + memcpy( job->config.mpeg4.bytes, &buf->data[vol_start], + job->config.mpeg4.length ); + pv->configDone = 1; + } + + *buf_out = buf; + + return HB_WORK_OK; +} + diff --git a/libmediafork/fifo.c b/libmediafork/fifo.c new file mode 100644 index 000000000..9e5ea15f8 --- /dev/null +++ b/libmediafork/fifo.c @@ -0,0 +1,207 @@ +/* $Id: fifo.c,v 1.17 2005/10/15 18:05:03 titer Exp $ + + 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 "mediafork.h" + +#ifndef SYS_DARWIN +#include <malloc.h> +#endif + +hb_buffer_t * hb_buffer_init( int size ) +{ + hb_buffer_t * b; + + if( !( b = calloc( sizeof( hb_buffer_t ), 1 ) ) ) + { + hb_log( "out of memory" ); + return NULL; + } + + b->alloc = size; + b->size = size; +#if defined( SYS_DARWIN ) || defined( SYS_FREEBSD ) + b->data = malloc( size ); +#elif defined( SYS_CYGWIN ) + /* FIXME */ + b->data = malloc( size + 17 ); +#else + b->data = memalign( 16, size ); +#endif + + if( !b->data ) + { + hb_log( "out of memory" ); + free( b ); + return NULL; + } + return b; +} + +void hb_buffer_realloc( hb_buffer_t * b, int size ) +{ + /* No more alignment, but we don't care */ + b->data = realloc( b->data, size ); + b->alloc = size; +} + +void hb_buffer_close( hb_buffer_t ** _b ) +{ + hb_buffer_t * b = *_b; + + if( b->data ) + { + free( b->data ); + } + free( b ); + + *_b = NULL; +} + +/* Fifo */ +struct hb_fifo_s +{ + hb_lock_t * lock; + int capacity; + int size; + hb_buffer_t * first; + hb_buffer_t * last; +}; + +hb_fifo_t * hb_fifo_init( int capacity ) +{ + hb_fifo_t * f; + f = calloc( sizeof( hb_fifo_t ), 1 ); + f->lock = hb_lock_init(); + f->capacity = capacity; + return f; +} + +int hb_fifo_size( hb_fifo_t * f ) +{ + int ret; + + hb_lock( f->lock ); + ret = f->size; + hb_unlock( f->lock ); + + return ret; +} + +int hb_fifo_is_full( hb_fifo_t * f ) +{ + int ret; + + hb_lock( f->lock ); + ret = ( f->size >= f->capacity ); + hb_unlock( f->lock ); + + return ret; +} + +float hb_fifo_percent_full( hb_fifo_t * f ) +{ + float ret; + + hb_lock( f->lock ); + ret = f->size / f->capacity; + hb_unlock( f->lock ); + + return ret; +} + +hb_buffer_t * hb_fifo_get( hb_fifo_t * f ) +{ + hb_buffer_t * b; + + hb_lock( f->lock ); + if( f->size < 1 ) + { + hb_unlock( f->lock ); + return NULL; + } + b = f->first; + f->first = b->next; + b->next = NULL; + f->size -= 1; + hb_unlock( f->lock ); + + return b; +} + +hb_buffer_t * hb_fifo_see( hb_fifo_t * f ) +{ + hb_buffer_t * b; + + hb_lock( f->lock ); + if( f->size < 1 ) + { + hb_unlock( f->lock ); + return NULL; + } + b = f->first; + hb_unlock( f->lock ); + + return b; +} + +hb_buffer_t * hb_fifo_see2( hb_fifo_t * f ) +{ + hb_buffer_t * b; + + hb_lock( f->lock ); + if( f->size < 2 ) + { + hb_unlock( f->lock ); + return NULL; + } + b = f->first->next; + hb_unlock( f->lock ); + + return b; +} + +void hb_fifo_push( hb_fifo_t * f, hb_buffer_t * b ) +{ + if( !b ) + { + return; + } + + hb_lock( f->lock ); + if( f->size > 0 ) + { + f->last->next = b; + } + else + { + f->first = b; + } + f->last = b; + f->size += 1; + while( f->last->next ) + { + f->size += 1; + f->last = f->last->next; + } + hb_unlock( f->lock ); +} + +void hb_fifo_close( hb_fifo_t ** _f ) +{ + hb_fifo_t * f = *_f; + hb_buffer_t * b; + + hb_log( "fifo_close: trashing %d buffer(s)", hb_fifo_size( f ) ); + while( ( b = hb_fifo_get( f ) ) ) + { + hb_buffer_close( &b ); + } + + hb_lock_close( &f->lock ); + free( f ); + + *_f = NULL; +} diff --git a/libmediafork/internal.h b/libmediafork/internal.h new file mode 100644 index 000000000..3948d5dfd --- /dev/null +++ b/libmediafork/internal.h @@ -0,0 +1,187 @@ +/* $Id: internal.h,v 1.41 2005/11/25 15:05:25 titer Exp $ + + 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. */ + +/*********************************************************************** + * common.c + **********************************************************************/ +void hb_log( char * log, ... ); + +int hb_list_bytes( hb_list_t * ); +void hb_list_seebytes( hb_list_t * l, uint8_t * dst, int size ); +void hb_list_getbytes( hb_list_t * l, uint8_t * dst, int size, + uint64_t * pts, int * pos ); +void hb_list_empty( hb_list_t ** ); + +hb_title_t * hb_title_init( char * dvd, int index ); +void hb_title_close( hb_title_t ** ); + +/*********************************************************************** + * hb.c + **********************************************************************/ +int hb_get_pid( hb_handle_t * ); +void hb_set_state( hb_handle_t *, hb_state_t * ); + +/*********************************************************************** + * fifo.c + **********************************************************************/ +struct hb_buffer_s +{ + int size; + int alloc; + uint8_t * data; + int cur; + + int id; + int64_t start; + int64_t stop; + int key; + + int x; + int y; + int width; + int height; + + hb_buffer_t * sub; + + hb_buffer_t * next; +}; + +hb_buffer_t * hb_buffer_init( int size ); +void hb_buffer_realloc( hb_buffer_t *, int size ); +void hb_buffer_close( hb_buffer_t ** ); + + +hb_fifo_t * hb_fifo_init(); +int hb_fifo_size( hb_fifo_t * ); +int hb_fifo_is_full( hb_fifo_t * ); +float hb_fifo_percent_full( hb_fifo_t * f ); +hb_buffer_t * hb_fifo_get( hb_fifo_t * ); +hb_buffer_t * hb_fifo_see( hb_fifo_t * ); +hb_buffer_t * hb_fifo_see2( hb_fifo_t * ); +void hb_fifo_push( hb_fifo_t *, hb_buffer_t * ); +void hb_fifo_close( hb_fifo_t ** ); + +/*********************************************************************** + * Threads: update.c, scan.c, work.c, reader.c, muxcommon.c + **********************************************************************/ +hb_thread_t * hb_update_init( int * build, char * version ); +hb_thread_t * hb_scan_init( hb_handle_t *, const char * path, + int title_index, hb_list_t * list_title ); +hb_thread_t * hb_work_init( hb_list_t * jobs, int cpu_count, + volatile int * die, int * error ); +hb_thread_t * hb_reader_init( hb_job_t * ); +hb_thread_t * hb_muxer_init( hb_job_t * ); + +/*********************************************************************** + * libmpeg2 wrapper + *********************************************************************** + * It is exported here because it is used at several places + **********************************************************************/ +typedef struct hb_libmpeg2_s hb_libmpeg2_t; + +hb_libmpeg2_t * hb_libmpeg2_init(); +int hb_libmpeg2_decode( hb_libmpeg2_t *, + hb_buffer_t * es_buf, + hb_list_t * raw_list ); +void hb_libmpeg2_info( hb_libmpeg2_t * m, int * width, + int * height, int * rate ); +void hb_libmpeg2_close( hb_libmpeg2_t ** ); + +/*********************************************************************** + * mpegdemux.c + **********************************************************************/ +int hb_demux_ps( hb_buffer_t * ps_buf, hb_list_t * es_list ); + +/*********************************************************************** + * dvd.c + **********************************************************************/ +typedef struct hb_dvd_s hb_dvd_t; + +hb_dvd_t * hb_dvd_init( char * path ); +int hb_dvd_title_count( hb_dvd_t * ); +hb_title_t * hb_dvd_title_scan( hb_dvd_t *, int title ); +int hb_dvd_start( hb_dvd_t *, int title, int chapter ); +void hb_dvd_stop( hb_dvd_t * ); +int hb_dvd_seek( hb_dvd_t *, float ); +int hb_dvd_read( hb_dvd_t *, hb_buffer_t * ); +int hb_dvd_chapter( hb_dvd_t * ); +void hb_dvd_close( hb_dvd_t ** ); + +/*********************************************************************** + * Work objects + **********************************************************************/ +#define HB_CONFIG_MAX_SIZE 8192 +union hb_esconfig_u +{ + struct + { + uint8_t bytes[HB_CONFIG_MAX_SIZE]; + int length; + } mpeg4; + + struct + { + uint8_t sps[HB_CONFIG_MAX_SIZE]; + int sps_length; + uint8_t pps[HB_CONFIG_MAX_SIZE]; + int pps_length; + } h264; + + struct + { + uint8_t bytes[HB_CONFIG_MAX_SIZE]; + int length; + } aac; + + struct + { + uint8_t headers[3][HB_CONFIG_MAX_SIZE]; + } vorbis; +}; + +enum +{ + WORK_SYNC = 1, + WORK_DECMPEG2, + WORK_DECSUB, + WORK_RENDER, + WORK_ENCAVCODEC, + WORK_ENCXVID, + WORK_ENCX264, + WORK_DECA52, + WORK_DECAVCODEC, + WORK_DECLPCM, + WORK_ENCFAAC, + WORK_ENCLAME, + WORK_ENCVORBIS +}; + +extern hb_work_object_t * hb_objects; + +#define HB_WORK_IDLE 0 +#define HB_WORK_OK 1 +#define HB_WORK_ERROR 2 +#define HB_WORK_DONE 3 + +/*********************************************************************** + * Muxers + **********************************************************************/ +typedef struct hb_mux_object_s hb_mux_object_t; +typedef struct hb_mux_data_s hb_mux_data_t; + +#define HB_MUX_COMMON \ + int (*init) ( hb_mux_object_t * ); \ + int (*mux) ( hb_mux_object_t *, hb_mux_data_t *, \ + hb_buffer_t * ); \ + int (*end) ( hb_mux_object_t * ); + +#define DECLARE_MUX( a ) \ + hb_mux_object_t * hb_mux_##a##_init( hb_job_t * ); + +DECLARE_MUX( mp4 ); +DECLARE_MUX( avi ); +DECLARE_MUX( ogm ); + diff --git a/libmediafork/ipodutil.cpp b/libmediafork/ipodutil.cpp new file mode 100644 index 000000000..ba37e4768 --- /dev/null +++ b/libmediafork/ipodutil.cpp @@ -0,0 +1,34 @@ +/* + * MP4 library API functions + * + * These are wrapper functions that provide C linkage conventions + * to the library, and catch any internal errors, ensuring that + * a proper return value is given. + */ + +#include "mp4common.h" + +static u_int8_t ipod_magic[] = { + 0x6b, 0x68, 0x40, 0xf2, 0x5f, 0x24, 0x4f, 0xc5, + 0xba, 0x39, 0xa5, 0x1b, 0xcf, 0x03, 0x23, 0xf3 +}; + +class IPodUUIDAtom : public MP4Atom { +public: + IPodUUIDAtom() : MP4Atom("uuid") + { + SetExtendedType(ipod_magic); + + MP4Integer32Property* value = new MP4Integer32Property("value"); + value->SetValue(1); + AddProperty(value); + } +}; + +extern "C" void AddIPodUUID(MP4FileHandle hFile, MP4TrackId trackId) +{ + MP4Track* track = ((MP4File*)hFile)->GetTrack(trackId); + MP4Atom* avc1 = track->GetTrakAtom()->FindChildAtom("mdia.minf.stbl.stsd.avc1"); + avc1->AddChildAtom(new IPodUUIDAtom()); +} + diff --git a/libmediafork/lang.h b/libmediafork/lang.h new file mode 100644 index 000000000..f88f539ff --- /dev/null +++ b/libmediafork/lang.h @@ -0,0 +1,209 @@ +/* $Id: lang.h,v 1.1 2004/08/02 07:19:05 titer Exp $ + + 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. */ + +#ifndef HB_LANG_H +#define HB_LANG_H + +typedef struct iso639_lang_t +{ + char * eng_name; /* Description in English */ + char * native_name; /* Description in native language */ + char * iso639_1; /* ISO-639-1 (2 characters) code */ + +} iso639_lang_t; + +static const iso639_lang_t languages[] = +{ { "Afar", "", "aa" }, + { "Abkhazian", "", "ab" }, + { "Afrikaans", "", "af" }, + { "Albanian", "", "sq" }, + { "Amharic", "", "am" }, + { "Arabic", "", "ar" }, + { "Armenian", "", "hy" }, + { "Assamese", "", "as" }, + { "Avestan", "", "ae" }, + { "Aymara", "", "ay" }, + { "Azerbaijani", "", "az" }, + { "Bashkir", "", "ba" }, + { "Basque", "", "eu" }, + { "Belarusian", "", "be" }, + { "Bengali", "", "bn" }, + { "Bihari", "", "bh" }, + { "Bislama", "", "bi" }, + { "Bosnian", "", "bs" }, + { "Breton", "", "br" }, + { "Bulgarian", "", "bg" }, + { "Burmese", "", "my" }, + { "Catalan", "", "ca" }, + { "Chamorro", "", "ch" }, + { "Chechen", "", "ce" }, + { "Chinese", "", "zh" }, + { "Church Slavic", "", "cu" }, + { "Chuvash", "", "cv" }, + { "Cornish", "", "kw" }, + { "Corsican", "", "co" }, + { "Czech", "", "cs" }, + { "Danish", "Dansk", "da" }, + { "Dutch", "Nederlands", "nl" }, + { "Dzongkha", "", "dz" }, + { "English", "English", "en" }, + { "Esperanto", "", "eo" }, + { "Estonian", "", "et" }, + { "Faroese", "", "fo" }, + { "Fijian", "", "fj" }, + { "Finnish", "Suomi", "fi" }, + { "French", "Francais", "fr" }, + { "Frisian", "", "fy" }, + { "Georgian", "", "ka" }, + { "German", "Deutsch", "de" }, + { "Gaelic (Scots)", "", "gd" }, + { "Irish", "", "ga" }, + { "Gallegan", "", "gl" }, + { "Manx", "", "gv" }, + { "Greek, Modern ()", "", "el" }, + { "Guarani", "", "gn" }, + { "Gujarati", "", "gu" }, + { "Hebrew", "", "he" }, + { "Herero", "", "hz" }, + { "Hindi", "", "hi" }, + { "Hiri Motu", "", "ho" }, + { "Hungarian", "Magyar", "hu" }, + { "Icelandic", "Islenska", "is" }, + { "Inuktitut", "", "iu" }, + { "Interlingue", "", "ie" }, + { "Interlingua", "", "ia" }, + { "Indonesian", "", "id" }, + { "Inupiaq", "", "ik" }, + { "Italian", "Italiano", "it" }, + { "Javanese", "", "jv" }, + { "Japanese", "", "ja" }, + { "Kalaallisut (Greenlandic)", "", "kl" }, + { "Kannada", "", "kn" }, + { "Kashmiri", "", "ks" }, + { "Kazakh", "", "kk" }, + { "Khmer", "", "km" }, + { "Kikuyu", "", "ki" }, + { "Kinyarwanda", "", "rw" }, + { "Kirghiz", "", "ky" }, + { "Komi", "", "kv" }, + { "Korean", "", "ko" }, + { "Kuanyama", "", "kj" }, + { "Kurdish", "", "ku" }, + { "Lao", "", "lo" }, + { "Latin", "", "la" }, + { "Latvian", "", "lv" }, + { "Lingala", "", "ln" }, + { "Lithuanian", "", "lt" }, + { "Letzeburgesch", "", "lb" }, + { "Macedonian", "", "mk" }, + { "Marshall", "", "mh" }, + { "Malayalam", "", "ml" }, + { "Maori", "", "mi" }, + { "Marathi", "", "mr" }, + { "Malay", "", "ms" }, + { "Malagasy", "", "mg" }, + { "Maltese", "", "mt" }, + { "Moldavian", "", "mo" }, + { "Mongolian", "", "mn" }, + { "Nauru", "", "na" }, + { "Navajo", "", "nv" }, + { "Ndebele, South", "", "nr" }, + { "Ndebele, North", "", "nd" }, + { "Ndonga", "", "ng" }, + { "Nepali", "", "ne" }, + { "Norwegian", "Norsk", "no" }, + { "Norwegian Nynorsk", "", "nn" }, + { "Norwegian BokmÃ¥l", "", "nb" }, + { "Chichewa; Nyanja", "", "ny" }, + { "Occitan (post 1500); Provençal", "", "oc" }, + { "Oriya", "", "or" }, + { "Oromo", "", "om" }, + { "Ossetian; Ossetic", "", "os" }, + { "Panjabi", "", "pa" }, + { "Persian", "", "fa" }, + { "Pali", "", "pi" }, + { "Polish", "", "pl" }, + { "Portuguese", "Portugues", "pt" }, + { "Pushto", "", "ps" }, + { "Quechua", "", "qu" }, + { "Raeto-Romance", "", "rm" }, + { "Romanian", "", "ro" }, + { "Rundi", "", "rn" }, + { "Russian", "", "ru" }, + { "Sango", "", "sg" }, + { "Sanskrit", "", "sa" }, + { "Serbian", "", "sr" }, + { "Croatian", "Hrvatski", "hr" }, + { "Sinhalese", "", "si" }, + { "Slovak", "", "sk" }, + { "Slovenian", "", "sl" }, + { "Northern Sami", "", "se" }, + { "Samoan", "", "sm" }, + { "Shona", "", "sn" }, + { "Sindhi", "", "sd" }, + { "Somali", "", "so" }, + { "Sotho, Southern", "", "st" }, + { "Spanish", "Espanol", "es" }, + { "Sardinian", "", "sc" }, + { "Swati", "", "ss" }, + { "Sundanese", "", "su" }, + { "Swahili", "", "sw" }, + { "Swedish", "Svenska", "sv" }, + { "Tahitian", "", "ty" }, + { "Tamil", "", "ta" }, + { "Tatar", "", "tt" }, + { "Telugu", "", "te" }, + { "Tajik", "", "tg" }, + { "Tagalog", "", "tl" }, + { "Thai", "", "th" }, + { "Tibetan", "", "bo" }, + { "Tigrinya", "", "ti" }, + { "Tonga (Tonga Islands)", "", "to" }, + { "Tswana", "", "tn" }, + { "Tsonga", "", "ts" }, + { "Turkish", "", "tr" }, + { "Turkmen", "", "tk" }, + { "Twi", "", "tw" }, + { "Uighur", "", "ug" }, + { "Ukrainian", "", "uk" }, + { "Urdu", "", "ur" }, + { "Uzbek", "", "uz" }, + { "Vietnamese", "", "vi" }, + { "Volapük", "", "vo" }, + { "Welsh", "", "cy" }, + { "Wolof", "", "wo" }, + { "Xhosa", "", "xh" }, + { "Yiddish", "", "yi" }, + { "Yoruba", "", "yo" }, + { "Zhuang", "", "za" }, + { "Zulu", "", "zu" }, + { NULL, NULL, NULL } }; + +static char * lang_for_code( int code ) +{ + char code_string[2]; + iso639_lang_t * lang; + + code_string[0] = ( code >> 8 ) & 0xFF; + code_string[1] = code & 0xFF; + + for( lang = (iso639_lang_t*) languages; lang->eng_name; lang++ ) + { + if( !strncmp( lang->iso639_1, code_string, 2 ) ) + { + if( *lang->native_name ) + { + return lang->native_name; + } + + return lang->eng_name; + } + } + + return "Unknown"; +} + +#endif diff --git a/libmediafork/mediafork.c b/libmediafork/mediafork.c new file mode 100644 index 000000000..6eb115fd8 --- /dev/null +++ b/libmediafork/mediafork.c @@ -0,0 +1,806 @@ +#include "mediafork.h" + +#include "ffmpeg/avcodec.h" + +struct hb_handle_s +{ + /* The "Check for update" thread */ + int build; + char version[16]; + hb_thread_t * update_thread; + + /* This thread's only purpose is to check other threads' + states */ + volatile int die; + hb_thread_t * main_thread; + int pid; + + /* DVD/file scan thread */ + hb_list_t * list_title; + hb_thread_t * scan_thread; + + /* The thread which processes the jobs. Others threads are launched + from this one (see work.c) */ + hb_list_t * jobs; + int job_count; + volatile int work_die; + int work_error; + hb_thread_t * work_thread; + + int cpu_count; + + hb_lock_t * state_lock; + hb_state_t state; + + int paused; + hb_lock_t * pause_lock; +}; + +hb_work_object_t * hb_objects = NULL; + +static void thread_func( void * ); + +/** + * Registers work objects, by adding the work object to a liked list. + * @param w Handle to hb_work_object_t to register. + */ +void hb_register( hb_work_object_t * w ) +{ + w->next = hb_objects; + hb_objects = w; +} + +/** + * libhb initialization routine. + * @param verbose HB_DEBUG_NONE or HB_DEBUG_ALL. + * @param update_check signals libhb to check for updated version from HandBrake website. + * @return Handle to hb_handle_t for use on all subsequent calls to libhb. + */ +hb_handle_t * hb_init_real( int verbose, int update_check ) +{ + hb_handle_t * h = calloc( sizeof( hb_handle_t ), 1 ); + uint64_t date; + + /* See hb_log() in common.c */ + if( verbose > HB_DEBUG_NONE ) + { + putenv( "HB_DEBUG=1" ); + av_log_set_level(AV_LOG_DEBUG); + } + + /* Check for an update on the website if asked to */ + h->build = -1; + + if( update_check ) + { + hb_log( "hb_init: checking for updates" ); + date = hb_get_date(); + h->update_thread = hb_update_init( &h->build, h->version ); + + for( ;; ) + { + if( hb_thread_has_exited( h->update_thread ) ) + { + /* Immediate success or failure */ + hb_thread_close( &h->update_thread ); + break; + } + if( hb_get_date() > date + 1000 ) + { + /* Still nothing after one second. Connection problem, + let the thread die */ + hb_log( "hb_init: connection problem, not waiting for " + "update_thread" ); + break; + } + hb_snooze( 500 ); + } + } + + /* CPU count detection */ + hb_log( "hb_init: checking cpu count" ); + h->cpu_count = hb_get_cpu_count(); + + h->list_title = hb_list_init(); + h->jobs = hb_list_init(); + + h->state_lock = hb_lock_init(); + h->state.state = HB_STATE_IDLE; + + h->pause_lock = hb_lock_init(); + + /* libavcodec */ + avcodec_init(); + register_avcodec( &mpeg4_encoder ); + register_avcodec( &mp2_decoder ); + register_avcodec( &ac3_encoder ); + + /* Start library thread */ + hb_log( "hb_init: starting libhb thread" ); + h->die = 0; + h->main_thread = hb_thread_init( "libhb", thread_func, h, + HB_NORMAL_PRIORITY ); + + return h; +} + +/** + * libhb initialization routine. + * This version is to use when calling the dylib, the macro hb_init isn't available from a dylib call! + * @param verbose HB_DEBUG_NONE or HB_DEBUG_ALL. + * @param update_check signals libhb to check for updated version from HandBrake website. + * @return Handle to hb_handle_t for use on all subsequent calls to libhb. + */ +hb_handle_t * hb_init_dl( int verbose, int update_check ) +{ + hb_handle_t * h = calloc( sizeof( hb_handle_t ), 1 ); + uint64_t date; + + /* See hb_log() in common.c */ + if( verbose > HB_DEBUG_NONE ) + { + putenv( "HB_DEBUG=1" ); + av_log_set_level(AV_LOG_DEBUG); + } + + /* Check for an update on the website if asked to */ + h->build = -1; + + if( update_check ) + { + hb_log( "hb_init: checking for updates" ); + date = hb_get_date(); + h->update_thread = hb_update_init( &h->build, h->version ); + + for( ;; ) + { + if( hb_thread_has_exited( h->update_thread ) ) + { + /* Immediate success or failure */ + hb_thread_close( &h->update_thread ); + break; + } + if( hb_get_date() > date + 1000 ) + { + /* Still nothing after one second. Connection problem, + let the thread die */ + hb_log( "hb_init: connection problem, not waiting for " + "update_thread" ); + break; + } + hb_snooze( 500 ); + } + } + + /* CPU count detection */ + hb_log( "hb_init: checking cpu count" ); + h->cpu_count = hb_get_cpu_count(); + + h->list_title = hb_list_init(); + h->jobs = hb_list_init(); + + h->state_lock = hb_lock_init(); + h->state.state = HB_STATE_IDLE; + + h->pause_lock = hb_lock_init(); + + /* libavcodec */ + avcodec_init(); + register_avcodec( &mpeg4_encoder ); + register_avcodec( &mp2_decoder ); + register_avcodec( &ac3_encoder ); + + /* Start library thread */ + hb_log( "hb_init: starting libhb thread" ); + h->die = 0; + h->main_thread = hb_thread_init( "libhb", thread_func, h, + HB_NORMAL_PRIORITY ); + + hb_register( &hb_sync ); + hb_register( &hb_decmpeg2 ); + hb_register( &hb_decsub ); + hb_register( &hb_render ); + hb_register( &hb_encavcodec ); + hb_register( &hb_encxvid ); + hb_register( &hb_encx264 ); + hb_register( &hb_deca52 ); + hb_register( &hb_decavcodec ); + hb_register( &hb_declpcm ); + hb_register( &hb_encfaac ); + hb_register( &hb_enclame ); + hb_register( &hb_encvorbis ); + + return h; +} + + +/** + * Returns current version of libhb. + * @param h Handle to hb_handle_t. + * @return character array of version number. + */ +char * hb_get_version( hb_handle_t * h ) +{ + return HB_VERSION; +} + +/** + * Returns current build of libhb. + * @param h Handle to hb_handle_t. + * @return character array of build number. + */ +int hb_get_build( hb_handle_t * h ) +{ + return HB_BUILD; +} + +/** + * Checks for needed update. + * @param h Handle to hb_handle_t. + * @param version Pointer to handle where version will be copied. + * @return update indicator. + */ +int hb_check_update( hb_handle_t * h, char ** version ) +{ + *version = ( h->build < 0 ) ? NULL : h->version; + return h->build; +} + +/** + * Sets the cpu count to the desired value. + * @param h Handle to hb_handle_t + * @param cpu_count Number of CPUs to use. + */ +void hb_set_cpu_count( hb_handle_t * h, int cpu_count ) +{ + cpu_count = MAX( 1, cpu_count ); + cpu_count = MIN( cpu_count, 8 ); + h->cpu_count = cpu_count; +} + +/** + * Initializes a scan of the by calling hb_scan_init + * @param h Handle to hb_handle_t + * @param path location of VIDEO_TS folder. + * @param title_index Desired title to scan. 0 for all titles. + */ +void hb_scan( hb_handle_t * h, const char * path, int title_index ) +{ + hb_title_t * title; + + /* Clean up from previous scan */ + while( ( title = hb_list_item( h->list_title, 0 ) ) ) + { + hb_list_rem( h->list_title, title ); + hb_title_close( &title ); + } + + hb_log( "hb_scan: path=%s, title_index=%d", path, title_index ); + h->scan_thread = hb_scan_init( h, path, title_index, h->list_title ); +} + +/** + * Returns the list of titles found. + * @param h Handle to hb_handle_t + * @return Handle to hb_list_t of the title list. + */ +hb_list_t * hb_get_titles( hb_handle_t * h ) +{ + return h->list_title; +} + +/** + * Create preview image of desired title a index of picture. + * @param h Handle to hb_handle_t. + * @param title Handle to hb_title_t of desired title. + * @param picture Index in title. + * @param buffer Handle to buufer were inage will be drawn. + */ +void hb_get_preview( hb_handle_t * h, hb_title_t * title, int picture, + uint8_t * buffer ) +{ + hb_job_t * job = title->job; + char filename[1024]; + FILE * file; + uint8_t * buf1, * buf2, * buf3, * buf4, * pen; + uint32_t * p32; + AVPicture pic1, pic2, pic3, pic4; + ImgReSampleContext * context; + int i; + + buf1 = malloc( title->width * title->height * 3 / 2 ); + buf2 = malloc( title->width * title->height * 3 / 2 ); + buf3 = malloc( title->width * title->height * 3 / 2 ); + buf4 = malloc( title->width * title->height * 4 ); + avpicture_fill( &pic1, buf1, PIX_FMT_YUV420P, + title->width, title->height ); + avpicture_fill( &pic2, buf2, PIX_FMT_YUV420P, + title->width, title->height ); + avpicture_fill( &pic3, buf3, PIX_FMT_YUV420P, + job->width, job->height ); + avpicture_fill( &pic4, buf4, PIX_FMT_RGBA32, + job->width, job->height ); + + memset( filename, 0, 1024 ); + + hb_get_tempory_filename( h, filename, "%x%d", + (int) title, picture ); + + file = fopen( filename, "r" ); + if( !file ) + { + hb_log( "hb_get_preview: fopen failed" ); + return; + } + + fread( buf1, title->width * title->height * 3 / 2, 1, file ); + fclose( file ); + + context = img_resample_full_init( + job->width, job->height, title->width, title->height, + job->crop[0], job->crop[1], job->crop[2], job->crop[3], + 0, 0, 0, 0 ); + + if( job->deinterlace ) + { + avpicture_deinterlace( &pic2, &pic1, PIX_FMT_YUV420P, + title->width, title->height ); + img_resample( context, &pic3, &pic2 ); + } + else + { + img_resample( context, &pic3, &pic1 ); + } + img_convert( &pic4, PIX_FMT_RGBA32, &pic3, PIX_FMT_YUV420P, + job->width, job->height ); + + /* Gray background */ + p32 = (uint32_t *) buffer; + for( i = 0; i < ( title->width + 2 ) * ( title->height + 2 ); i++ ) + { + p32[i] = 0xFF808080; + } + + /* Draw the picture, centered, and draw the cropping zone */ + pen = buffer + ( title->height - job->height ) * + ( title->width + 2 ) * 2 + ( title->width - job->width ) * 2; + memset( pen, 0xFF, 4 * ( job->width + 2 ) ); + pen += 4 * ( title->width + 2 ); + for( i = 0; i < job->height; i++ ) + { + uint8_t * nextLine; + nextLine = pen + 4 * ( title->width + 2 ); + memset( pen, 0xFF, 4 ); + pen += 4; + memcpy( pen, buf4 + 4 * job->width * i, 4 * job->width ); + pen += 4 * job->width; + memset( pen, 0xFF, 4 ); + pen = nextLine; + } + memset( pen, 0xFF, 4 * ( job->width + 2 ) ); + + free( buf1 ); + free( buf2 ); + free( buf3 ); + free( buf4 ); +} + +/** + * Calculates job width, height, and cropping parameters. + * @param job Handle to hb_job_t. + * @param aspect Desired aspect ratio. Value of -1 uses title aspect. + * @param pixels Maximum desired pixel count. + */ +void hb_set_size( hb_job_t * job, int aspect, int pixels ) +{ + hb_title_t * title = job->title; + + int croppedWidth = title->width - title->crop[2] - title->crop[3]; + int croppedHeight = title->height - title->crop[0] - title->crop[1]; + int croppedAspect = title->aspect * title->height * croppedWidth / + croppedHeight / title->width; + int addCrop; + int i, w, h; + + if( aspect <= 0 ) + { + /* Keep the best possible aspect ratio */ + aspect = croppedAspect; + } + + /* Crop if necessary to obtain the desired ratio */ + memcpy( job->crop, title->crop, 4 * sizeof( int ) ); + if( aspect < croppedAspect ) + { + /* Need to crop on the left and right */ + addCrop = croppedWidth - aspect * croppedHeight * title->width / + title->aspect / title->height; + if( addCrop & 3 ) + { + addCrop = ( addCrop + 1 ) / 2; + job->crop[2] += addCrop; + job->crop[3] += addCrop; + } + else if( addCrop & 2 ) + { + addCrop /= 2; + job->crop[2] += addCrop - 1; + job->crop[3] += addCrop + 1; + } + else + { + addCrop /= 2; + job->crop[2] += addCrop; + job->crop[3] += addCrop; + } + } + else if( aspect > croppedAspect ) + { + /* Need to crop on the top and bottom */ + addCrop = croppedHeight - croppedWidth * title->aspect * + title->height / aspect / title->width; + if( addCrop & 3 ) + { + addCrop = ( addCrop + 1 ) / 2; + job->crop[0] += addCrop; + job->crop[1] += addCrop; + } + else if( addCrop & 2 ) + { + addCrop /= 2; + job->crop[0] += addCrop - 1; + job->crop[1] += addCrop + 1; + } + else + { + addCrop /= 2; + job->crop[0] += addCrop; + job->crop[1] += addCrop; + } + } + + /* Compute a resolution from the number of pixels and aspect */ + for( i = 0;; i++ ) + { + w = 16 * i; + h = MULTIPLE_16( w * HB_ASPECT_BASE / aspect ); + if( w * h > pixels ) + { + break; + } + } + i--; + job->width = 16 * i; + job->height = MULTIPLE_16( 16 * i * HB_ASPECT_BASE / aspect ); +} + +/** + * Returns the number of jobs in the queue. + * @param h Handle to hb_handle_t. + * @return Number of jobs. + */ +int hb_count( hb_handle_t * h ) +{ + return hb_list_count( h->jobs ); +} + +/** + * Returns handle to job at index i within the job list. + * @param h Handle to hb_handle_t. + * @param i Index of job. + * @returns Handle to hb_job_t of desired job. + */ +hb_job_t * hb_job( hb_handle_t * h, int i ) +{ + return hb_list_item( h->jobs, i ); +} + +/** + * Adds a job to the job list. + * @param h Handle to hb_handle_t. + * @param job Handle to hb_job_t. + */ +void hb_add( hb_handle_t * h, hb_job_t * job ) +{ + hb_job_t * job_copy; + hb_title_t * title, * title_copy; + hb_chapter_t * chapter, * chapter_copy; + hb_audio_t * audio, * audio_copy; + hb_subtitle_t * subtitle, * subtitle_copy; + int i; + + /* Copy the title */ + title = job->title; + title_copy = malloc( sizeof( hb_title_t ) ); + memcpy( title_copy, title, sizeof( hb_title_t ) ); + + title_copy->list_chapter = hb_list_init(); + for( i = 0; i < hb_list_count( title->list_chapter ); i++ ) + { + chapter = hb_list_item( title->list_chapter, i ); + chapter_copy = malloc( sizeof( hb_chapter_t ) ); + memcpy( chapter_copy, chapter, sizeof( hb_chapter_t ) ); + hb_list_add( title_copy->list_chapter, chapter_copy ); + } + + /* Copy the audio track(s) we want */ + title_copy->list_audio = hb_list_init(); + + /* Do nothing about audio during first pass */ + if( job->pass != 1 ) + { + for( i = 0; i < 8; i++ ) + { + if( job->audios[i] < 0 ) + { + break; + } + if( ( audio = hb_list_item( title->list_audio, job->audios[i] ) ) ) + { + audio_copy = malloc( sizeof( hb_audio_t ) ); + memcpy( audio_copy, audio, sizeof( hb_audio_t ) ); + hb_list_add( title_copy->list_audio, audio_copy ); + } + } + } + + /* Copy the subtitle we want (or not) */ + title_copy->list_subtitle = hb_list_init(); + if( ( subtitle = hb_list_item( title->list_subtitle, job->subtitle ) ) ) + { + subtitle_copy = malloc( sizeof( hb_subtitle_t ) ); + memcpy( subtitle_copy, subtitle, sizeof( hb_subtitle_t ) ); + hb_list_add( title_copy->list_subtitle, subtitle_copy ); + } + + /* Copy the job */ + job_copy = calloc( sizeof( hb_job_t ), 1 ); + memcpy( job_copy, job, sizeof( hb_job_t ) ); + job_copy->title = title_copy; + job_copy->file = strdup( job->file ); + job_copy->h = h; + job_copy->pause = h->pause_lock; + + /* Add the job to the list */ + hb_list_add( h->jobs, job_copy ); +} + +/** + * Removes a job from the job list. + * @param h Handle to hb_handle_t. + * @param job Handle to hb_job_t. + */ +void hb_rem( hb_handle_t * h, hb_job_t * job ) +{ + hb_list_rem( h->jobs, job ); + + /* XXX free everything XXX */ +} + +/** + * Starts the conversion process. + * Sets state to HB_STATE_WORKING. + * calls hb_work_init, to launch work thread. Stores handle to work thread. + * @param h Handle to hb_handle_t. + */ +void hb_start( hb_handle_t * h ) +{ + /* XXX Hack */ + h->job_count = hb_list_count( h->jobs ); + + hb_lock( h->state_lock ); + h->state.state = HB_STATE_WORKING; +#define p h->state.param.working + p.progress = 0.0; + p.job_cur = 1; + p.job_count = h->job_count; + p.rate_cur = 0.0; + p.rate_avg = 0.0; + p.hours = -1; + p.minutes = -1; + p.seconds = -1; +#undef p + hb_unlock( h->state_lock ); + + h->paused = 0; + + h->work_die = 0; + h->work_thread = hb_work_init( h->jobs, h->cpu_count, + &h->work_die, &h->work_error ); +} + +/** + * Pauses the conversion process. + * @param h Handle to hb_handle_t. + */ +void hb_pause( hb_handle_t * h ) +{ + if( !h->paused ) + { + hb_lock( h->pause_lock ); + h->paused = 1; + + hb_lock( h->state_lock ); + h->state.state = HB_STATE_PAUSED; + hb_unlock( h->state_lock ); + } +} + +/** + * Resumes the conversion process. + * @param h Handle to hb_handle_t. + */ +void hb_resume( hb_handle_t * h ) +{ + if( h->paused ) + { + hb_unlock( h->pause_lock ); + h->paused = 0; + } +} + +/** + * Stops the conversion process. + * @param h Handle to hb_handle_t. + */ +void hb_stop( hb_handle_t * h ) +{ + h->work_die = 1; + + hb_resume( h ); +} + +/** + * Returns the state of the conversion process. + * @param h Handle to hb_handle_t. + * @param s Handle to hb_state_t which to copy the state data. + */ +void hb_get_state( hb_handle_t * h, hb_state_t * s ) +{ + hb_lock( h->state_lock ); + + memcpy( s, &h->state, sizeof( hb_state_t ) ); + h->state.state = HB_STATE_IDLE; + + hb_unlock( h->state_lock ); +} + +/** + * Closes access to libhb by freeing the hb_handle_t handle ontained in hb_init_real. + * @param _h Pointer to handle to hb_handle_t. + */ +void hb_close( hb_handle_t ** _h ) +{ + hb_handle_t * h = *_h; + hb_title_t * title; + + h->die = 1; + hb_thread_close( &h->main_thread ); + + while( ( title = hb_list_item( h->list_title, 0 ) ) ) + { + hb_list_rem( h->list_title, title ); + hb_title_close( &title ); + } + hb_list_close( &h->list_title ); + + hb_list_close( &h->jobs ); + hb_lock_close( &h->state_lock ); + hb_lock_close( &h->pause_lock ); + free( h ); + *_h = NULL; +} + +/** + * Monitors the state of the update, scan, and work threads. + * Sets scan done state when scan thread exits. + * Sets work done state when work thread exits. + * @param _h Handle to hb_handle_t + */ +static void thread_func( void * _h ) +{ + hb_handle_t * h = (hb_handle_t *) _h; + char dirname[1024]; + DIR * dir; + struct dirent * entry; + + h->pid = getpid(); + + /* Create folder for temporary files */ + memset( dirname, 0, 1024 ); + hb_get_tempory_directory( h, dirname ); + + hb_mkdir( dirname ); + + while( !h->die ) + { + /* In case the check_update thread hangs, it'll die sooner or + later. Then, we join it here */ + if( h->update_thread && + hb_thread_has_exited( h->update_thread ) ) + { + hb_thread_close( &h->update_thread ); + } + + /* Check if the scan thread is done */ + if( h->scan_thread && + hb_thread_has_exited( h->scan_thread ) ) + { + hb_thread_close( &h->scan_thread ); + + hb_log( "libhb: scan thread found %d valid title(s)", + hb_list_count( h->list_title ) ); + hb_lock( h->state_lock ); + h->state.state = HB_STATE_SCANDONE; + hb_unlock( h->state_lock ); + } + + /* Check if the work thread is done */ + if( h->work_thread && + hb_thread_has_exited( h->work_thread ) ) + { + hb_thread_close( &h->work_thread ); + + hb_log( "libhb: work result = %d", + h->work_error ); + hb_lock( h->state_lock ); + h->state.state = HB_STATE_WORKDONE; + h->state.param.workdone.error = h->work_error; + hb_unlock( h->state_lock ); + } + + hb_snooze( 50 ); + } + + if( h->work_thread ) + { + hb_stop( h ); + hb_thread_close( &h->work_thread ); + } + + /* Remove temp folder */ + dir = opendir( dirname ); + while( ( entry = readdir( dir ) ) ) + { + char filename[1024]; + if( entry->d_name[0] == '.' ) + { + continue; + } + memset( filename, 0, 1024 ); + snprintf( filename, 1023, "%s/%s", dirname, entry->d_name ); + unlink( filename ); + } + closedir( dir ); + rmdir( dirname ); +} + +/** + * Returns the PID. + * @param h Handle to hb_handle_t + */ +int hb_get_pid( hb_handle_t * h ) +{ + return h->pid; +} + +/** + * Sets the current state. + * @param h Handle to hb_handle_t + * @param s Handle to new hb_state_t + */ +void hb_set_state( hb_handle_t * h, hb_state_t * s ) +{ + hb_lock( h->pause_lock ); + hb_lock( h->state_lock ); + memcpy( &h->state, s, sizeof( hb_state_t ) ); + if( h->state.state == HB_STATE_WORKING ) + { + /* XXX Hack */ + h->state.param.working.job_cur = + h->job_count - hb_list_count( h->jobs ); + h->state.param.working.job_count = h->job_count; + } + hb_unlock( h->state_lock ); + hb_unlock( h->pause_lock ); +} diff --git a/libmediafork/mediafork.h b/libmediafork/mediafork.h new file mode 100644 index 000000000..8b5d4696a --- /dev/null +++ b/libmediafork/mediafork.h @@ -0,0 +1,104 @@ +#ifndef HB_HB_H +#define HB_HB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common.h" + +/* hb_init() + Initializes a libhb session (launches his own thread, detects CPUs, + etc) */ +#define HB_DEBUG_NONE 0 +#define HB_DEBUG_ALL 1 +void hb_register( hb_work_object_t * ); +hb_handle_t * hb_init_real( int verbose, int update_check ); +hb_handle_t * hb_init_dl ( int verbose, int update_check ); // hb_init for use with dylib + +#define hb_init(v,u) \ +hb_init_real( v, u ); \ +hb_register( &hb_sync ); \ +hb_register( &hb_decmpeg2 ); \ +hb_register( &hb_decsub ); \ +hb_register( &hb_render ); \ +hb_register( &hb_encavcodec ); \ +hb_register( &hb_encxvid ); \ +hb_register( &hb_encx264 ); \ +hb_register( &hb_deca52 ); \ +hb_register( &hb_decavcodec ); \ +hb_register( &hb_declpcm ); \ +hb_register( &hb_encfaac ); \ +hb_register( &hb_enclame ); \ +hb_register( &hb_encvorbis ); \ + +#define hb_init_express(v,u) \ +hb_init_real( v, u ); \ +hb_register( &hb_sync ); \ +hb_register( &hb_decmpeg2 ); \ +hb_register( &hb_decsub ); \ +hb_register( &hb_render ); \ +hb_register( &hb_encavcodec ); \ +hb_register( &hb_encx264 ); \ +hb_register( &hb_deca52 ); \ +hb_register( &hb_decavcodec ); \ +hb_register( &hb_declpcm ); \ +hb_register( &hb_encfaac ); \ + +/* hb_get_version() */ +char * hb_get_version( hb_handle_t * ); +int hb_get_build( hb_handle_t * ); + +/* hb_check_update() + Checks for an update on the website. If there is, returns the build + number and points 'version' to a version description. Returns a + negative value otherwise. */ +int hb_check_update( hb_handle_t * h, char ** version ); + +/* hb_set_cpu_count() + Force libhb to act as if you had X CPU(s). + Default is to use the detected count (see also hb_get_cpu_count() in + ports.h) */ +void hb_set_cpu_count( hb_handle_t *, int ); + +char * hb_dvd_name( char * path ); + +/* hb_scan() + Scan the specified path. Can be a DVD device, a VIDEO_TS folder or + a VOB file. If title_index is 0, scan all titles. */ +void hb_scan( hb_handle_t *, const char * path, + int title_index ); + +/* hb_get_titles() + Returns the list of valid titles detected by the latest scan. */ +hb_list_t * hb_get_titles( hb_handle_t * ); + +void hb_get_preview( hb_handle_t *, hb_title_t *, int, + uint8_t * ); +void hb_set_size( hb_job_t *, int ratio, int pixels ); + +/* Handling jobs */ +int hb_count( hb_handle_t * ); +hb_job_t * hb_job( hb_handle_t *, int ); +void hb_add( hb_handle_t *, hb_job_t * ); +void hb_rem( hb_handle_t *, hb_job_t * ); + +void hb_start( hb_handle_t * ); +void hb_pause( hb_handle_t * ); +void hb_resume( hb_handle_t * ); +void hb_stop( hb_handle_t * ); + +/* hb_get_state() + Should be regularly called by the UI (like 5 or 10 times a second). + Look at test/test.c to see how to use it. */ +void hb_get_state( hb_handle_t *, hb_state_t * ); + +/* hb_close() + Aborts all current jobs if any, frees memory. */ +void hb_close( hb_handle_t ** ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libmediafork/muxavi.c b/libmediafork/muxavi.c new file mode 100644 index 000000000..4231d32df --- /dev/null +++ b/libmediafork/muxavi.c @@ -0,0 +1,551 @@ +/* $Id: muxavi.c,v 1.10 2005/03/30 18:17:29 titer Exp $ + + 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 "mediafork.h" + +#define AVIF_HASINDEX 0x10 +#define AVIIF_KEYFRAME 0x10 +#define FOURCC(a) ((a[3]<<24)|(a[2]<<16)|(a[1]<<8)|a[0]) + +/* Structures definitions */ +typedef struct __attribute__((__packed__)) +{ + uint32_t FourCC; + uint32_t BytesCount; + uint32_t MicroSecPerFrame; + uint32_t MaxBytesPerSec; + uint32_t PaddingGranularity; + uint32_t Flags; + uint32_t TotalFrames; + uint32_t InitialFrames; + uint32_t Streams; + uint32_t SuggestedBufferSize; + uint32_t Width; + uint32_t Height; + uint32_t Reserved[4]; + +} hb_avi_main_header_t; + +typedef struct __attribute__((__packed__)) +{ + uint32_t FourCC; + uint32_t BytesCount; + uint32_t Type; + uint32_t Handler; + uint32_t Flags; + uint16_t Priority; + uint16_t Language; + uint32_t InitialFrames; + uint32_t Scale; + uint32_t Rate; + uint32_t Start; + uint32_t Length; + uint32_t SuggestedBufferSize; + uint32_t Quality; + uint32_t SampleSize; + int16_t Left; + int16_t Top; + int16_t Right; + int16_t Bottom; + +} hb_avi_stream_header_t; + +typedef struct __attribute__((__packed__)) +{ + uint32_t FourCC; + uint32_t BytesCount; + uint32_t Size; + uint32_t Width; + uint32_t Height; + uint16_t Planes; + uint16_t BitCount; + uint32_t Compression; + uint32_t SizeImage; + uint32_t XPelsPerMeter; + uint32_t YPelsPerMeter; + uint32_t ClrUsed; + uint32_t ClrImportant; + +} hb_bitmap_info_t; + +typedef struct __attribute__((__packed__)) +{ + uint32_t FourCC; + uint32_t BytesCount; + uint16_t FormatTag; + uint16_t Channels; + uint32_t SamplesPerSec; + uint32_t AvgBytesPerSec; + uint16_t BlockAlign; + uint16_t BitsPerSample; + uint16_t Size; + +} hb_wave_formatex_t; + +typedef struct __attribute__((__packed__)) +{ + uint16_t Id; + uint32_t Flags; + uint16_t BlockSize; + uint16_t FramesPerBlock; + uint16_t CodecDelay; + +} hb_wave_mp3_t; + +static void WriteBuffer( FILE * file, hb_buffer_t * buf ) +{ + fwrite( buf->data, buf->size, 1, file ); +} + +/* Little-endian write routines */ + +static void WriteInt8( FILE * file, uint8_t val ) +{ + fputc( val, file ); +} + +static void WriteInt16( FILE * file, uint16_t val ) +{ + fputc( val & 0xFF, file ); + fputc( val >> 8, file ); +} + +static void WriteInt32( FILE * file, uint32_t val ) +{ + fputc( val & 0xFF, file ); + fputc( ( val >> 8 ) & 0xFF, file ); + fputc( ( val >> 16 ) & 0xFF, file ); + fputc( val >> 24, file ); +} + +static void WriteBitmapInfo( FILE * file, hb_bitmap_info_t * info ) +{ + WriteInt32( file, info->FourCC ); + WriteInt32( file, info->BytesCount ); + WriteInt32( file, info->Size ); + WriteInt32( file, info->Width ); + WriteInt32( file, info->Height ); + WriteInt16( file, info->Planes ); + WriteInt16( file, info->BitCount ); + WriteInt32( file, info->Compression ); + WriteInt32( file, info->SizeImage ); + WriteInt32( file, info->XPelsPerMeter ); + WriteInt32( file, info->YPelsPerMeter ); + WriteInt32( file, info->ClrUsed ); + WriteInt32( file, info->ClrImportant ); +} + +static void WriteWaveFormatEx( FILE * file, hb_wave_formatex_t * format ) +{ + WriteInt32( file, format->FourCC ); + WriteInt32( file, format->BytesCount ); + WriteInt16( file, format->FormatTag ); + WriteInt16( file, format->Channels ); + WriteInt32( file, format->SamplesPerSec ); + WriteInt32( file, format->AvgBytesPerSec ); + WriteInt16( file, format->BlockAlign ); + WriteInt16( file, format->BitsPerSample ); + WriteInt16( file, format->Size ); +} + +static void WriteWaveMp3( FILE * file, hb_wave_mp3_t * mp3 ) +{ + WriteInt16( file, mp3->Id ); + WriteInt32( file, mp3->Flags ); + WriteInt16( file, mp3->BlockSize ); + WriteInt16( file, mp3->FramesPerBlock ); + WriteInt16( file, mp3->CodecDelay ); +} + +static void WriteMainHeader( FILE * file, hb_avi_main_header_t * header ) +{ + WriteInt32( file, header->FourCC ); + WriteInt32( file, header->BytesCount ); + WriteInt32( file, header->MicroSecPerFrame ); + WriteInt32( file, header->MaxBytesPerSec ); + WriteInt32( file, header->PaddingGranularity ); + WriteInt32( file, header->Flags ); + WriteInt32( file, header->TotalFrames ); + WriteInt32( file, header->InitialFrames ); + WriteInt32( file, header->Streams ); + WriteInt32( file, header->SuggestedBufferSize ); + WriteInt32( file, header->Width ); + WriteInt32( file, header->Height ); + WriteInt32( file, header->Reserved[0] ); + WriteInt32( file, header->Reserved[1] ); + WriteInt32( file, header->Reserved[2] ); + WriteInt32( file, header->Reserved[3] ); +} + +static void WriteStreamHeader( FILE * file, hb_avi_stream_header_t * header ) +{ + WriteInt32( file, header->FourCC ); + WriteInt32( file, header->BytesCount ); + WriteInt32( file, header->Type ); + WriteInt32( file, header->Handler ); + WriteInt32( file, header->Flags ); + WriteInt16( file, header->Priority ); + WriteInt16( file, header->Language ); + WriteInt32( file, header->InitialFrames ); + WriteInt32( file, header->Scale ); + WriteInt32( file, header->Rate ); + WriteInt32( file, header->Start ); + WriteInt32( file, header->Length ); + WriteInt32( file, header->SuggestedBufferSize ); + WriteInt32( file, header->Quality ); + WriteInt32( file, header->SampleSize ); + WriteInt16( file, header->Left ); + WriteInt16( file, header->Top ); + WriteInt16( file, header->Right ); + WriteInt16( file, header->Bottom ); +} + +static void IndexAddInt32( hb_buffer_t * b, uint32_t val ) +{ + if( b->size + 16 > b->alloc ) + { + hb_log( "muxavi: reallocing index (%d MB)", + 1 + b->alloc / 1024 / 1024 ); + hb_buffer_realloc( b, b->alloc + 1024 * 1024 ); + } + + b->data[b->size++] = val & 0xFF; + b->data[b->size++] = ( val >> 8 ) & 0xFF; + b->data[b->size++] = ( val >> 16 ) & 0xFF; + b->data[b->size++] = val >> 24; +} + +struct hb_mux_object_s +{ + HB_MUX_COMMON; + + hb_job_t * job; + + /* Data size in bytes, not including headers */ + unsigned size; + FILE * file; + hb_buffer_t * index; + hb_avi_main_header_t main_header; +}; + +struct hb_mux_data_s +{ + uint32_t fourcc; + hb_avi_stream_header_t header; + union + { + hb_bitmap_info_t v; + struct + { + hb_wave_formatex_t f; + hb_wave_mp3_t m; + } a; + } format; +}; + +static void AddIndex( hb_mux_object_t * m ) +{ + fseek( m->file, 0, SEEK_END ); + + /* Write the index at the end of the file */ + WriteInt32( m->file, FOURCC( "idx1" ) ); + WriteInt32( m->file, m->index->size ); + WriteBuffer( m->file, m->index ); + + /* Update file size */ + m->size += 8 + m->index->size; + fseek( m->file, 4, SEEK_SET ); + WriteInt32( m->file, 2040 + m->size ); + + /* Update HASINDEX flag */ + m->main_header.Flags |= AVIF_HASINDEX; + fseek( m->file, 24, SEEK_SET ); + WriteMainHeader( m->file, &m->main_header ); +} + + +/********************************************************************** + * AVIInit + ********************************************************************** + * Allocates things, create file, initialize and write headers + *********************************************************************/ +static int AVIInit( hb_mux_object_t * m ) +{ + hb_job_t * job = m->job; + hb_title_t * title = job->title; + + hb_audio_t * audio; + hb_mux_data_t * mux_data; + + int audio_count = hb_list_count( title->list_audio ); + int is_ac3 = ( job->acodec & HB_ACODEC_AC3 ); + int hdrl_bytes; + int i; + + /* Allocate index */ + m->index = hb_buffer_init( 1024 * 1024 ); + m->index->size = 0; + + /* Open destination file */ + hb_log( "muxavi: opening %s", job->file ); + m->file = fopen( job->file, "wb" ); + +#define m m->main_header + /* AVI main header */ + m.FourCC = FOURCC( "avih" ); + m.BytesCount = sizeof( hb_avi_main_header_t ) - 8; + m.MicroSecPerFrame = (uint64_t) 1000000 * job->vrate_base / job->vrate; + m.Streams = 1 + audio_count; + m.Width = job->width; + m.Height = job->height; +#undef m + + /* Video track */ + mux_data = calloc( sizeof( hb_mux_data_t ), 1 ); + job->mux_data = mux_data; + +#define h mux_data->header + /* Video stream header */ + h.FourCC = FOURCC( "strh" ); + h.BytesCount = sizeof( hb_avi_stream_header_t ) - 8; + h.Type = FOURCC( "vids" ); + + if( job->vcodec == HB_VCODEC_FFMPEG ) + h.Handler = FOURCC( "divx" ); + else if( job->vcodec == HB_VCODEC_XVID ) + h.Handler = FOURCC( "xvid" ); + else if( job->vcodec == HB_VCODEC_X264 ) + h.Handler = FOURCC( "h264" ); + + h.Scale = job->vrate_base; + h.Rate = job->vrate; +#undef h + +#define f mux_data->format.v + /* Video stream format */ + f.FourCC = FOURCC( "strf" ); + f.BytesCount = sizeof( hb_bitmap_info_t ) - 8; + f.Size = f.BytesCount; + f.Width = job->width; + f.Height = job->height; + f.Planes = 1; + f.BitCount = 24; + if( job->vcodec == HB_VCODEC_FFMPEG ) + f.Compression = FOURCC( "DX50" ); + else if( job->vcodec == HB_VCODEC_XVID ) + f.Compression = FOURCC( "XVID" ); + else if( job->vcodec == HB_VCODEC_X264 ) + f.Compression = FOURCC( "H264" ); +#undef f + + /* Audio tracks */ + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + + mux_data = calloc( sizeof( hb_mux_data_t ), 1 ); + audio->mux_data = mux_data; + +#define h mux_data->header +#define f mux_data->format.a.f +#define m mux_data->format.a.m + /* Audio stream header */ + h.FourCC = FOURCC( "strh" ); + h.BytesCount = sizeof( hb_avi_stream_header_t ) - 8; + h.Type = FOURCC( "auds" ); + h.InitialFrames = 1; + h.Scale = 1; + h.Rate = is_ac3 ? ( audio->bitrate / 8 ) : + ( job->abitrate * 1000 / 8 ); + h.Quality = 0xFFFFFFFF; + h.SampleSize = 1; + + /* Audio stream format */ + f.FourCC = FOURCC( "strf" ); + if( is_ac3 ) + { + f.BytesCount = sizeof( hb_wave_formatex_t ) - 8; + f.FormatTag = 0x2000; + f.Channels = audio->channels; + f.SamplesPerSec = audio->rate; + } + else + { + f.BytesCount = sizeof( hb_wave_formatex_t ) + + sizeof( hb_wave_mp3_t ) - 8; + f.FormatTag = 0x55; + f.Channels = 2; + f.SamplesPerSec = job->arate; + } + f.AvgBytesPerSec = h.Rate; + f.BlockAlign = 1; + if( is_ac3 ) + { + f.Size = 0; + } + else + { + f.Size = sizeof( hb_wave_mp3_t ); + m.Id = 1; + m.Flags = 2; + m.BlockSize = 1152 * f.AvgBytesPerSec / job->arate; + m.FramesPerBlock = 1; + m.CodecDelay = 1393; + } +#undef h +#undef f +#undef m + } + + hdrl_bytes = + /* Main header */ + 4 + sizeof( hb_avi_main_header_t ) + + /* strh for video + audios */ + ( 1 + audio_count ) * ( 12 + sizeof( hb_avi_stream_header_t ) ) + + /* video strf */ + sizeof( hb_bitmap_info_t ) + + /* audios strf */ + audio_count * ( sizeof( hb_wave_formatex_t ) + + ( is_ac3 ? 0 : sizeof( hb_wave_mp3_t ) ) ); + + /* Here we really start to write into the file */ + + /* Main headers */ + WriteInt32( m->file, FOURCC( "RIFF" ) ); + WriteInt32( m->file, 2040 ); + WriteInt32( m->file, FOURCC( "AVI " ) ); + WriteInt32( m->file, FOURCC( "LIST" ) ); + WriteInt32( m->file, hdrl_bytes ); + WriteInt32( m->file, FOURCC( "hdrl" ) ); + WriteMainHeader( m->file, &m->main_header ); + + /* Video track */ + mux_data = job->mux_data; + mux_data->fourcc = FOURCC( "00dc" ); + + WriteInt32( m->file, FOURCC( "LIST" ) ); + WriteInt32( m->file, 4 + sizeof( hb_avi_stream_header_t ) + + sizeof( hb_bitmap_info_t ) ); + WriteInt32( m->file, FOURCC( "strl" ) ); + WriteStreamHeader( m->file, &mux_data->header ); + WriteBitmapInfo( m->file, &mux_data->format.v ); + + /* Audio tracks */ + for( i = 0; i < audio_count; i++ ) + { + char fourcc[4] = "00wb"; + + audio = hb_list_item( title->list_audio, i ); + mux_data = audio->mux_data; + + fourcc[1] = '1' + i; /* This is fine as we don't allow more + than 8 tracks */ + mux_data->fourcc = FOURCC( fourcc ); + + WriteInt32( m->file, FOURCC( "LIST" ) ); + WriteInt32( m->file, 4 + sizeof( hb_avi_stream_header_t ) + + sizeof( hb_wave_formatex_t ) + + ( is_ac3 ? 0 : sizeof( hb_wave_mp3_t ) ) ); + WriteInt32( m->file, FOURCC( "strl" ) ); + WriteStreamHeader( m->file, &mux_data->header ); + WriteWaveFormatEx( m->file, &mux_data->format.a.f ); + if( !is_ac3 ) + { + WriteWaveMp3( m->file, &mux_data->format.a.m ); + } + } + + WriteInt32( m->file, FOURCC( "JUNK" ) ); + WriteInt32( m->file, 2020 - hdrl_bytes ); + for( i = 0; i < 2020 - hdrl_bytes; i++ ) + { + WriteInt8( m->file, 0 ); + } + WriteInt32( m->file, FOURCC( "LIST" ) ); + WriteInt32( m->file, 4 ); + WriteInt32( m->file, FOURCC( "movi" ) ); + + return 0; +} + +static int AVIMux( hb_mux_object_t * m, hb_mux_data_t * mux_data, + hb_buffer_t * buf ) +{ + hb_job_t * job = m->job; + hb_title_t * title = job->title; + + hb_audio_t * audio; + int i; + + /* Update index */ + IndexAddInt32( m->index, mux_data->fourcc ); + IndexAddInt32( m->index, buf->key ? AVIIF_KEYFRAME : 0 ); + IndexAddInt32( m->index, 4 + m->size ); + IndexAddInt32( m->index, buf->size ); + + /* Write the chunk to the file */ + fseek( m->file, 0, SEEK_END ); + WriteInt32( m->file, mux_data->fourcc ); + WriteInt32( m->file, buf->size ); + WriteBuffer( m->file, buf ); + + /* Chunks must be 2-bytes aligned */ + if( buf->size & 1 ) + { + WriteInt8( m->file, 0 ); + } + + /* Update headers */ + m->size += 8 + EVEN( buf->size ); + mux_data->header.Length++; + + /* RIFF size */ + fseek( m->file, 4, SEEK_SET ); + WriteInt32( m->file, 2052 + m->size ); + + /* Mmmmh that's not nice */ + fseek( m->file, 140, SEEK_SET ); + WriteInt32( m->file, job->mux_data->header.Length ); + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + fseek( m->file, 264 + i * + ( 102 + ( ( job->acodec & HB_ACODEC_AC3 ) ? 0 : + sizeof( hb_wave_mp3_t ) ) ), SEEK_SET ); + WriteInt32( m->file, audio->mux_data->header.Length ); + } + + /* movi size */ + fseek( m->file, 2052, SEEK_SET ); + WriteInt32( m->file, 4 + m->size ); + return 0; +} + +static int AVIEnd( hb_mux_object_t * m ) +{ + hb_job_t * job = m->job; + + hb_log( "muxavi: writing index" ); + AddIndex( m ); + + hb_log( "muxavi: closing %s", job->file ); + fclose( m->file ); + + hb_buffer_close( &m->index ); + + return 0; +} + +hb_mux_object_t * hb_mux_avi_init( hb_job_t * job ) +{ + hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 ); + m->init = AVIInit; + m->mux = AVIMux; + m->end = AVIEnd; + m->job = job; + return m; +} + diff --git a/libmediafork/muxcommon.c b/libmediafork/muxcommon.c new file mode 100644 index 000000000..3f6613716 --- /dev/null +++ b/libmediafork/muxcommon.c @@ -0,0 +1,227 @@ +/* $Id: muxcommon.c,v 1.23 2005/03/30 17:27:19 titer Exp $ + + 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 "mediafork.h" + +struct hb_mux_object_s +{ + HB_MUX_COMMON; +}; + +typedef struct +{ + hb_job_t * job; + uint64_t pts; + +} hb_mux_t; + +typedef struct +{ + hb_fifo_t * fifo; + hb_mux_data_t * mux_data; + uint64_t frames; + uint64_t bytes; + +} hb_track_t; + +static hb_track_t * GetTrack( hb_list_t * list ) +{ + hb_buffer_t * buf; + hb_track_t * track = NULL, * track2; + int64_t pts = 0; + int i; + + for( i = 0; i < hb_list_count( list ); i++ ) + { + track2 = hb_list_item( list, i ); + buf = hb_fifo_see( track2->fifo ); + if( !buf ) + { + return NULL; + } + if( !track || buf->start < pts ) + { + track = track2; + pts = buf->start; + } + } + return track; +} + +static void MuxerFunc( void * _mux ) +{ + hb_mux_t * mux = _mux; + hb_job_t * job = mux->job; + hb_title_t * title = job->title; + hb_audio_t * audio; + hb_list_t * list; + hb_buffer_t * buf; + hb_track_t * track; + int i; + + hb_mux_object_t * m = NULL; + + /* Get a real muxer */ + if( job->pass != 1 ) + { + switch( job->mux ) + { + case HB_MUX_MP4: + case HB_MUX_PSP: + case HB_MUX_IPOD: + m = hb_mux_mp4_init( job ); + break; + case HB_MUX_AVI: + m = hb_mux_avi_init( job ); + break; + case HB_MUX_OGM: + m = hb_mux_ogm_init( job ); + break; + } + } + + /* Wait for one buffer for each track */ + while( !*job->die && !job->done ) + { + int ready; + + ready = 1; + if( !hb_fifo_size( job->fifo_mpeg4 ) ) + { + ready = 0; + } + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + if( !hb_fifo_size( audio->fifo_out ) ) + { + ready = 0; + break; + } + } + + if( ready ) + { + break; + } + + hb_snooze( 200 ); + } + + /* Create file, write headers */ + if( job->pass != 1 ) + { + m->init( m ); + } + + /* Build list of fifos we're interested in */ + list = hb_list_init(); + + track = calloc( sizeof( hb_track_t ), 1 ); + track->fifo = job->fifo_mpeg4; + track->mux_data = job->mux_data; + hb_list_add( list, track ); + + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + track = calloc( sizeof( hb_track_t ), 1 ); + track->fifo = audio->fifo_out; + track->mux_data = audio->mux_data; + hb_list_add( list, track ); + } + + int thread_sleep_interval = 50; + while( !*job->die && !job->done ) + { + if( !( track = GetTrack( list ) ) ) + { + hb_snooze( thread_sleep_interval ); +// thread_sleep_interval += 1; + continue; + } +// thread_sleep_interval = MAX(1, (thread_sleep_interval - 1)); + + buf = hb_fifo_get( track->fifo ); + if( job->pass != 1 ) + { + m->mux( m, track->mux_data, buf ); + track->frames += 1; + track->bytes += buf->size; + mux->pts = buf->stop; + } + hb_buffer_close( &buf ); + } + + if( job->pass != 1 ) + { + struct stat sb; + uint64_t bytes_total, frames_total; + +#define p state.param.muxing + /* Update the UI */ + hb_state_t state; + state.state = HB_STATE_MUXING; + p.progress = 0; + hb_set_state( job->h, &state ); +#undef p + m->end( m ); + + if( !stat( job->file, &sb ) ) + { + hb_log( "mux: file size, %lld bytes", (uint64_t) sb.st_size ); + + bytes_total = 0; + frames_total = 0; + for( i = 0; i < hb_list_count( list ); i++ ) + { + track = hb_list_item( list, i ); + hb_log( "mux: track %d, %lld bytes, %.2f kbps", + i, track->bytes, + 90000.0 * track->bytes / mux->pts / 125 ); + if( !i && ( job->vquality < 0.0 || job->vquality > 1.0 ) ) + { + /* Video */ + hb_log( "mux: video bitrate error, %+lld bytes", + track->bytes - mux->pts * job->vbitrate * + 125 / 90000 ); + } + bytes_total += track->bytes; + frames_total += track->frames; + } + + if( bytes_total && frames_total ) + { + hb_log( "mux: overhead, %.2f bytes per frame", + (float) ( sb.st_size - bytes_total ) / + frames_total ); + } + } + } + + free( m ); + + for( i = 0; i < hb_list_count( list ); i++ ) + { + track = hb_list_item( list, i ); + if( track->mux_data ) + { + free( track->mux_data ); + } + free( track ); + } + hb_list_close( &list ); + + free( mux ); +} + +hb_thread_t * hb_muxer_init( hb_job_t * job ) +{ + hb_mux_t * mux = calloc( sizeof( hb_mux_t ), 1 ); + mux->job = job; + return hb_thread_init( "muxer", MuxerFunc, mux, + HB_NORMAL_PRIORITY ); +} diff --git a/libmediafork/muxmp4.c b/libmediafork/muxmp4.c new file mode 100644 index 000000000..1fc201f4d --- /dev/null +++ b/libmediafork/muxmp4.c @@ -0,0 +1,167 @@ +/* $Id: muxmp4.c,v 1.24 2005/11/04 13:09:41 titer Exp $ + + 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. */ + +/* libmp4v2 header */ +#include "mp4.h" + +#include "mediafork.h" + +void AddIPodUUID(MP4FileHandle, MP4TrackId); + + +struct hb_mux_object_s +{ + HB_MUX_COMMON; + + hb_job_t * job; + + /* libmp4v2 handle */ + MP4FileHandle file; + + /* Cumulated durations so far, in timescale units (see MP4Mux) */ + uint64_t sum_dur; +}; + +struct hb_mux_data_s +{ + MP4TrackId track; +}; + +/********************************************************************** + * MP4Init + ********************************************************************** + * Allocates hb_mux_data_t structures, create file and write headers + *********************************************************************/ +static int MP4Init( hb_mux_object_t * m ) +{ + hb_job_t * job = m->job; + hb_title_t * title = job->title; + + hb_audio_t * audio; + hb_mux_data_t * mux_data; + int i; + + /* Create an empty mp4 file */ + m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 ); + + /* Video track */ + mux_data = malloc( sizeof( hb_mux_data_t ) ); + job->mux_data = mux_data; + + /* When using the standard 90000 timescale, QuickTime tends to have + synchronization issues (audio not playing at the correct speed). + To workaround this, we use the audio samplerate as the + timescale */ + MP4SetTimeScale( m->file, job->arate ); + + if( job->vcodec == HB_VCODEC_X264 ) + { + /* Stolen from mp4creator */ + MP4SetVideoProfileLevel( m->file, 0x7F ); + + mux_data->track = MP4AddH264VideoTrack( m->file, job->arate, + MP4_INVALID_DURATION, job->width, job->height, + job->config.h264.sps[1], /* AVCProfileIndication */ + job->config.h264.sps[2], /* profile_compat */ + job->config.h264.sps[3], /* AVCLevelIndication */ + 3 ); /* 4 bytes length before each NAL unit */ + + MP4AddH264SequenceParameterSet( m->file, mux_data->track, + job->config.h264.sps, job->config.h264.sps_length ); + MP4AddH264PictureParameterSet( m->file, mux_data->track, + job->config.h264.pps, job->config.h264.pps_length ); + + if( job->h264_level == 30) + { + hb_log("About to add iPod atom"); + AddIPodUUID(m->file, mux_data->track); + } + } + else /* FFmpeg or XviD */ + { + MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 ); + mux_data->track = MP4AddVideoTrack( m->file, job->arate, + MP4_INVALID_DURATION, job->width, job->height, + MP4_MPEG4_VIDEO_TYPE ); + + /* VOL from FFmpeg or XviD */ + MP4SetTrackESConfiguration( m->file, mux_data->track, + job->config.mpeg4.bytes, job->config.mpeg4.length ); + } + + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + mux_data = malloc( sizeof( hb_mux_data_t ) ); + audio->mux_data = mux_data; + + mux_data->track = MP4AddAudioTrack( m->file, + job->arate, 1024, MP4_MPEG4_AUDIO_TYPE ); + MP4SetAudioProfileLevel( m->file, 0x0F ); + MP4SetTrackESConfiguration( m->file, mux_data->track, + audio->config.aac.bytes, audio->config.aac.length ); + } + + return 0; +} + +static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, + hb_buffer_t * buf ) +{ + hb_job_t * job = m->job; + + uint64_t duration; + + if( mux_data == job->mux_data ) + { + /* Video */ + /* Because we use the audio samplerate as the timescale, + we have to use potentially variable durations so the video + doesn't go out of sync */ + duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur; + m->sum_dur += duration; + } + else + { + /* Audio */ + duration = MP4_INVALID_DURATION; + } + + MP4WriteSample( m->file, mux_data->track, buf->data, buf->size, + duration, 0, buf->key ); + return 0; +} + +static int MP4End( hb_mux_object_t * m ) +{ +#if 0 + hb_job_t * job = m->job; +#endif + char filename[1024]; memset( filename, 0, 1024 ); + + MP4Close( m->file ); + +#if 0 + hb_log( "muxmp4: optimizing file" ); + snprintf( filename, 1024, "%s.tmp", job->file ); + MP4Optimize( job->file, filename, MP4_DETAILS_ERROR ); + remove( job->file ); + rename( filename, job->file ); +#endif + + return 0; +} + +hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job ) +{ + hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 ); + m->init = MP4Init; + m->mux = MP4Mux; + m->end = MP4End; + m->job = job; + return m; +} + diff --git a/libmediafork/muxogm.c b/libmediafork/muxogm.c new file mode 100644 index 000000000..62ff4e7b8 --- /dev/null +++ b/libmediafork/muxogm.c @@ -0,0 +1,364 @@ +/* $Id: muxogm.c,v 1.4 2005/02/20 00:41:56 titer Exp $ + + 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 "mediafork.h" + +#include <ogg/ogg.h> + +struct hb_mux_object_s +{ + HB_MUX_COMMON; + + hb_job_t * job; + + FILE * file; +}; + +struct hb_mux_data_s +{ + int codec; + ogg_stream_state os; + int i_packet_no; +}; + +typedef struct __attribute__((__packed__)) +{ + uint8_t i_packet_type; + + char stream_type[8]; + char sub_type[4]; + + int32_t i_size; + + int64_t i_time_unit; + int64_t i_samples_per_unit; + int32_t i_default_len; + + int32_t i_buffer_size; + int16_t i_bits_per_sample; + int16_t i_padding_0; // hum hum + union + { + struct + { + int32_t i_width; + int32_t i_height; + + } video; + struct + { + int16_t i_channels; + int16_t i_block_align; + int32_t i_avgbytespersec; + } audio; + } header; + +} ogg_stream_header_t; + +#define SetWLE( p, v ) _SetWLE( (uint8_t*)p, v) +static void _SetWLE( uint8_t *p, uint16_t i_dw ) +{ + p[1] = ( i_dw >> 8 )&0xff; + p[0] = ( i_dw )&0xff; +} + +#define SetDWLE( p, v ) _SetDWLE( (uint8_t*)p, v) +static void _SetDWLE( uint8_t *p, uint32_t i_dw ) +{ + p[3] = ( i_dw >> 24 )&0xff; + p[2] = ( i_dw >> 16 )&0xff; + p[1] = ( i_dw >> 8 )&0xff; + p[0] = ( i_dw )&0xff; +} +#define SetQWLE( p, v ) _SetQWLE( (uint8_t*)p, v) +static void _SetQWLE( uint8_t *p, uint64_t i_qw ) +{ + SetDWLE( p, i_qw&0xffffffff ); + SetDWLE( p+4, ( i_qw >> 32)&0xffffffff ); +} + +static int OGMFlush( hb_mux_object_t * m, hb_mux_data_t * mux_data ) +{ + for( ;; ) + { + ogg_page og; + if( ogg_stream_flush( &mux_data->os, &og ) == 0 ) + { + break; + } + if( fwrite( og.header, og.header_len, 1, m->file ) <= 0 || + fwrite( og.body, og.body_len, 1, m->file ) <= 0 ) + { + return -1; + } + } + return 0; +} + +/********************************************************************** + * OGMInit + ********************************************************************** + * Allocates hb_mux_data_t structures, create file and write headers + *********************************************************************/ +static int OGMInit( hb_mux_object_t * m ) +{ + hb_job_t * job = m->job; + hb_title_t * title = job->title; + + hb_audio_t * audio; + hb_mux_data_t * mux_data; + int i; + + ogg_packet op; + ogg_stream_header_t h; + + /* Open output file */ + if( ( m->file = fopen( job->file, "wb" ) ) == NULL ) + { + hb_log( "muxogm: failed to open `%s'", job->file ); + return -1; + } + hb_log( "muxogm: `%s' opened", job->file ); + + /* Video track */ + mux_data = malloc( sizeof( hb_mux_data_t ) ); + mux_data->codec = job->vcodec; + mux_data->i_packet_no = 0; + job->mux_data = mux_data; + ogg_stream_init( &mux_data->os, 0 ); + + /* Audio */ + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + mux_data = malloc( sizeof( hb_mux_data_t ) ); + mux_data->codec = job->acodec; + mux_data->i_packet_no = 0; + audio->mux_data = mux_data; + ogg_stream_init( &mux_data->os, i + 1 ); + } + + + /* First pass: all b_o_s packets */ + + /* Video */ + mux_data = job->mux_data; + memset( &h, 0, sizeof( ogg_stream_header_t ) ); + h.i_packet_type = 0x01; + memcpy( h.stream_type, "video ", 8 ); + if( mux_data->codec == HB_VCODEC_X264 ) + { + memcpy( h.sub_type, "H264", 4 ); + } + else if( mux_data->codec == HB_VCODEC_XVID ) + { + memcpy( h.sub_type, "XVID", 4 ); + } + else + { + memcpy( h.sub_type, "DX50", 4 ); + } + SetDWLE( &h.i_size, sizeof( ogg_stream_header_t ) - 1); + SetQWLE( &h.i_time_unit, (int64_t) 10 * 1000 * 1000 * + (int64_t) job->vrate_base / (int64_t) job->vrate ); + SetQWLE( &h.i_samples_per_unit, 1 ); + SetDWLE( &h.i_default_len, 0 ); + SetDWLE( &h.i_buffer_size, 1024*1024 ); + SetWLE ( &h.i_bits_per_sample, 0 ); + SetDWLE( &h.header.video.i_width, job->width ); + SetDWLE( &h.header.video.i_height, job->height ); + op.packet = (unsigned char*)&h; + op.bytes = sizeof( ogg_stream_header_t ); + op.b_o_s = 1; + op.e_o_s = 0; + op.granulepos = 0; + op.packetno = mux_data->i_packet_no++; + ogg_stream_packetin( &mux_data->os, &op ); + OGMFlush( m, mux_data ); + + /* Audio */ + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + mux_data = audio->mux_data; + memset( &h, 0, sizeof( ogg_stream_header_t ) ); + switch( job->acodec ) + { + case HB_ACODEC_LAME: + { + h.i_packet_type = 0x01; + memcpy( h.stream_type, "audio ", 8 ); + memcpy( h.sub_type, "55 ", 4 ); + + SetDWLE( &h.i_size, sizeof( ogg_stream_header_t ) - 1); + SetQWLE( &h.i_time_unit, 0 ); + SetQWLE( &h.i_samples_per_unit, job->arate ); + SetDWLE( &h.i_default_len, 1 ); + SetDWLE( &h.i_buffer_size, 30 * 1024 ); + SetWLE ( &h.i_bits_per_sample, 0 ); + + SetDWLE( &h.header.audio.i_channels, 2 ); + SetDWLE( &h.header.audio.i_block_align, 0 ); + SetDWLE( &h.header.audio.i_avgbytespersec, + job->abitrate / 8 ); + + op.packet = (char*) &h; + op.bytes = sizeof( ogg_stream_header_t ); + op.b_o_s = 1; + op.e_o_s = 0; + op.granulepos = 0; + op.packetno = mux_data->i_packet_no++; + ogg_stream_packetin( &mux_data->os, &op ); + break; + } + case HB_ACODEC_VORBIS: + { + memcpy( &op, audio->config.vorbis.headers[0], + sizeof( ogg_packet ) ); + op.packet = audio->config.vorbis.headers[0] + + sizeof( ogg_packet ); + ogg_stream_packetin( &mux_data->os, &op ); + break; + } + default: + hb_log( "muxogm: unhandled codec" ); + break; + } + OGMFlush( m, mux_data ); + } + + /* second pass: all non b_o_s packets */ + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + if( job->acodec == HB_ACODEC_VORBIS ) + { + int j; + mux_data = audio->mux_data; + + for( j = 1; j < 3; j++ ) + { + memcpy( &op, audio->config.vorbis.headers[j], + sizeof( ogg_packet ) ); + op.packet = audio->config.vorbis.headers[j] + + sizeof( ogg_packet ); + ogg_stream_packetin( &mux_data->os, &op ); + + OGMFlush( m, mux_data ); + } + } + } + hb_log( "muxogm: headers written" ); + + return 0; +} + +static int OGMMux( hb_mux_object_t * m, hb_mux_data_t * mux_data, + hb_buffer_t * buf ) +{ + ogg_packet op; + + switch( mux_data->codec ) + { + case HB_VCODEC_FFMPEG: + case HB_VCODEC_XVID: + case HB_VCODEC_X264: + op.bytes = buf->size + 1; + op.packet = malloc( op.bytes ); + op.packet[0] = buf->key ? 0x08 : 0x00; + memcpy( &op.packet[1], buf->data, buf->size ); + op.b_o_s = 0; + op.e_o_s = 0; + op.granulepos = mux_data->i_packet_no; + op.packetno = mux_data->i_packet_no++; + break; + case HB_ACODEC_LAME: + op.bytes = buf->size + 1; + op.packet = malloc( op.bytes ); + op.packet[0] = 0x08; + memcpy( &op.packet[1], buf->data, buf->size ); + op.b_o_s = 0; + op.e_o_s = 0; + op.granulepos = mux_data->i_packet_no * 1152; + op.packetno = mux_data->i_packet_no++; + break; + case HB_ACODEC_VORBIS: + memcpy( &op, buf->data, sizeof( ogg_packet ) ); + op.packet = malloc( op.bytes ); + memcpy( op.packet, buf->data + sizeof( ogg_packet ), op.bytes ); + break; + + default: + hb_log( "muxogm: unhandled codec" ); + op.bytes = 0; + op.packet = NULL; + break; + } + + if( op.packet ) + { + ogg_stream_packetin( &mux_data->os, &op ); + + for( ;; ) + { + ogg_page og; + if( ogg_stream_pageout( &mux_data->os, &og ) == 0 ) + { + break; + } + + if( fwrite( og.header, og.header_len, 1, m->file ) <= 0 || + fwrite( og.body, og.body_len, 1, m->file ) <= 0 ) + { + hb_log( "muxogm: write failed" ); + break; + } + } + free( op.packet ); + } + return 0; +} + +static int OGMEnd( hb_mux_object_t * m ) +{ + hb_job_t * job = m->job; + + hb_title_t * title = job->title; + hb_audio_t * audio; + hb_mux_data_t * mux_data; + int i; + + mux_data = job->mux_data; + if( OGMFlush( m, mux_data ) < 0 ) + { + return -1; + } + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + mux_data = audio->mux_data; + if( OGMFlush( m, mux_data ) < 0 ) + { + return -1; + } + } + + fclose( m->file ); + hb_log( "muxogm: `%s' closed", job->file ); + + return 0; +} + +hb_mux_object_t * hb_mux_ogm_init( hb_job_t * job ) +{ + hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 ); + m->init = OGMInit; + m->mux = OGMMux; + m->end = OGMEnd; + m->job = job; + return m; +} + diff --git a/libmediafork/ports.c b/libmediafork/ports.c new file mode 100644 index 000000000..1e0b622b1 --- /dev/null +++ b/libmediafork/ports.c @@ -0,0 +1,597 @@ +/* $Id: ports.c,v 1.15 2005/10/15 18:05:03 titer Exp $ + + 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 <time.h> +#include <sys/time.h> + +#if defined( SYS_BEOS ) +#include <OS.h> +#include <signal.h> +#elif defined( SYS_CYGWIN ) +#include <windows.h> +#endif + +#if USE_PTHREAD +#include <pthread.h> +#endif + +//#ifdef SYS_CYGWIN +//#include <winsock2.h> +//#include <ws2tcpip.h> +//#else +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +//#endif + +#include "mediafork.h" + +/************************************************************************ + * hb_get_date() + ************************************************************************ + * Returns the current date in milliseconds. + * On Win32, we implement a gettimeofday emulation here because + * libdvdread and libmp4v2 use it without checking. + ************************************************************************/ +/* +#ifdef SYS_CYGWIN +struct timezone +{ +}; + +int gettimeofday( struct timeval * tv, struct timezone * tz ) +{ + int tick; + tick = GetTickCount(); + tv->tv_sec = tick / 1000; + tv->tv_usec = ( tick % 1000 ) * 1000; + return 0; +} +#endif +*/ + +uint64_t hb_get_date() +{ + struct timeval tv; + gettimeofday( &tv, NULL ); + return( (uint64_t) tv.tv_sec * 1000 + (uint64_t) tv.tv_usec / 1000 ); +} + +/************************************************************************ + * hb_snooze() + ************************************************************************ + * Waits <delay> milliseconds. + ************************************************************************/ +void hb_snooze( int delay ) +{ + if( delay < 1 ) + { + return; + } +#if defined( SYS_BEOS ) + snooze( 1000 * delay ); +#elif defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + usleep( 1000 * delay ); +#elif defined( SYS_CYGWIN ) + Sleep( delay ); +#endif +} + +/************************************************************************ + * hb_get_cpu_count() + ************************************************************************ + * Whenever possible, returns the number of CPUs on the current + * computer. Returns 1 otherwise. + * The detection is actually only performed on the first call. + ************************************************************************/ +int hb_get_cpu_count() +{ + static int cpu_count = 0; + + if( cpu_count ) + { + return cpu_count; + } + cpu_count = 1; + +#if defined( SYS_BEOS ) + { + system_info info; + get_system_info( &info ); + cpu_count = info.cpu_count; + } + +#elif defined( SYS_DARWIN ) || defined( SYS_FREEBSD ) + FILE * info; + char buffer[16]; + + if( ( info = popen( "/usr/sbin/sysctl hw.ncpu", "r" ) ) ) + { + memset( buffer, 0, 16 ); + if( fgets( buffer, 15, info ) ) + { + if( sscanf( buffer, "hw.ncpu: %d", &cpu_count ) != 1 ) + { + cpu_count = 1; + } + } + fclose( info ); + } + +#elif defined( SYS_LINUX ) + { + FILE * info; + char buffer[8]; + + if( ( info = popen( "grep -c '^processor' /proc/cpuinfo", + "r" ) ) ) + { + memset( buffer, 0, 8 ); + if( fgets( buffer, 7, info ) ) + { + if( sscanf( buffer, "%d", &cpu_count ) != 1 ) + { + cpu_count = 1; + } + } + fclose( info ); + } + } + +#elif defined( SYS_CYGWIN ) + SYSTEM_INFO cpuinfo; + GetSystemInfo( &cpuinfo ); + cpu_count = cpuinfo.dwNumberOfProcessors; +#endif + + cpu_count = MAX( 1, cpu_count ); + cpu_count = MIN( cpu_count, 8 ); + + return cpu_count; +} + +/************************************************************************ + * Get a tempory directory for HB + ***********************************************************************/ +void hb_get_tempory_directory( hb_handle_t * h, char path[512] ) +{ + char base[512]; + + /* Create the base */ +#ifdef SYS_CYGWIN + char *p; + int i_size = GetTempPath( 512, base ); + if( i_size <= 0 || i_size >= 512 ) + { + if( getcwd( base, 512 ) == NULL ) + strcpy( base, "c:" ); /* Bad fallback but ... */ + } + + /* c:/path/ works like a charm under cygwin(win32?) so use it */ + while( ( p = strchr( base, '\\' ) ) ) + *p = '/'; +#else + strcpy( base, "/tmp" ); +#endif + /* I prefer to remove evntual last '/' (for cygwin) */ + if( base[strlen(base)-1] == '/' ) + base[strlen(base)-1] = '\0'; + + snprintf( path, 512, "%s/hb.%d", base, hb_get_pid( h ) ); +} + +/************************************************************************ + * Get a tempory filename for HB + ***********************************************************************/ +void hb_get_tempory_filename( hb_handle_t * h, char name[1024], + char *fmt, ... ) +{ + va_list args; + + hb_get_tempory_directory( h, name ); + strcat( name, "/" ); + + va_start( args, fmt ); + vsnprintf( &name[strlen(name)], 1024 - strlen(name), fmt, args ); + va_end( args ); +} + +/************************************************************************ + * hb_mkdir + ************************************************************************ + * Wrapper to the real mkdir, needed only because it doesn't take a + * second argument on Win32. Grrr. + ***********************************************************************/ +void hb_mkdir( char * name ) +{ +//#ifdef SYS_CYGWIN +// mkdir( name ); +//#else + mkdir( name, 0755 ); +//#endif +} + +/************************************************************************ + * Portable thread implementation + ***********************************************************************/ +struct hb_thread_s +{ + char * name; + int priority; + void (* function) ( void * ); + void * arg; + + hb_lock_t * lock; + int exited; + +#if defined( SYS_BEOS ) + thread_id thread; +#elif USE_PTHREAD + pthread_t thread; +//#elif defined( SYS_CYGWIN ) +// HANDLE thread; +#endif +}; + +/************************************************************************ + * hb_thread_func() + ************************************************************************ + * We use it as the root routine for any thread, for two reasons: + * + To set the thread priority on OS X (pthread_setschedparam() could + * be called from hb_thread_init(), but it's nicer to do it as we + * are sure it is done before the real routine starts) + * + Get informed when the thread exits, so we know whether + * hb_thread_close() will block or not. + ***********************************************************************/ +static void hb_thread_func( void * _t ) +{ + hb_thread_t * t = (hb_thread_t *) _t; + +#if defined( SYS_DARWIN ) + /* Set the thread priority */ + struct sched_param param; + memset( ¶m, 0, sizeof( struct sched_param ) ); + param.sched_priority = t->priority; + pthread_setschedparam( pthread_self(), SCHED_OTHER, ¶m ); +#endif + +#if defined( SYS_BEOS ) + signal( SIGINT, SIG_IGN ); +#endif + + /* Start the actual routine */ + t->function( t->arg ); + + /* Inform that the thread can be joined now */ + hb_log( "thread %d exited (\"%s\")", t->thread, t->name ); + hb_lock( t->lock ); + t->exited = 1; + hb_unlock( t->lock ); +} + +/************************************************************************ + * hb_thread_init() + ************************************************************************ + * name: user-friendly name + * function: the thread routine + * arg: argument of the routine + * priority: HB_LOW_PRIORITY or HB_NORMAL_PRIORITY + ***********************************************************************/ +hb_thread_t * hb_thread_init( char * name, void (* function)(void *), + void * arg, int priority ) +{ + hb_thread_t * t = calloc( sizeof( hb_thread_t ), 1 ); + + t->name = strdup( name ); + t->function = function; + t->arg = arg; + t->priority = priority; + + t->lock = hb_lock_init(); + + /* Create and start the thread */ +#if defined( SYS_BEOS ) + t->thread = spawn_thread( (thread_func) hb_thread_func, + name, priority, t ); + resume_thread( t->thread ); + +#elif USE_PTHREAD + pthread_create( &t->thread, NULL, + (void * (*)( void * )) hb_thread_func, t ); + +//#elif defined( SYS_CYGWIN ) +// t->thread = CreateThread( NULL, 0, +// (LPTHREAD_START_ROUTINE) hb_thread_func, t, 0, NULL ); +// +// /* Maybe use THREAD_PRIORITY_LOWEST instead */ +// if( priority == HB_LOW_PRIORITY ) +// SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL ); +#endif + + hb_log( "thread %d started (\"%s\")", t->thread, t->name ); + return t; +} + +/************************************************************************ + * hb_thread_close() + ************************************************************************ + * Joins the thread and frees memory. + ***********************************************************************/ +void hb_thread_close( hb_thread_t ** _t ) +{ + hb_thread_t * t = *_t; + + /* Join the thread */ +#if defined( SYS_BEOS ) + long exit_value; + wait_for_thread( t->thread, &exit_value ); + +#elif USE_PTHREAD + pthread_join( t->thread, NULL ); + +//#elif defined( SYS_CYGWIN ) +// WaitForSingleObject( t->thread, INFINITE ); +#endif + + hb_log( "thread %d joined (\"%s\")", + t->thread, t->name ); + + hb_lock_close( &t->lock ); + free( t->name ); + free( t ); + *_t = NULL; +} + +/************************************************************************ + * hb_thread_has_exited() + ************************************************************************ + * Returns 1 if the thread can be joined right away, 0 otherwise. + ***********************************************************************/ +int hb_thread_has_exited( hb_thread_t * t ) +{ + int exited; + + hb_lock( t->lock ); + exited = t->exited; + hb_unlock( t->lock ); + + return exited; +} + +/************************************************************************ + * Portable mutex implementation + ***********************************************************************/ +struct hb_lock_s +{ +#if defined( SYS_BEOS ) + sem_id sem; +#elif USE_PTHREAD + pthread_mutex_t mutex; +//#elif defined( SYS_CYGWIN ) +// HANDLE mutex; +#endif +}; + +/************************************************************************ + * hb_lock_init() + * hb_lock_close() + * hb_lock() + * hb_unlock() + ************************************************************************ + * Basic wrappers to OS-specific semaphore or mutex functions. + ***********************************************************************/ +hb_lock_t * hb_lock_init() +{ + hb_lock_t * l = calloc( sizeof( hb_lock_t ), 1 ); + +#if defined( SYS_BEOS ) + l->sem = create_sem( 1, "sem" ); +#elif USE_PTHREAD + pthread_mutex_init( &l->mutex, NULL ); +//#elif defined( SYS_CYGWIN ) +// l->mutex = CreateMutex( 0, FALSE, 0 ); +#endif + + return l; +} + +void hb_lock_close( hb_lock_t ** _l ) +{ + hb_lock_t * l = *_l; + +#if defined( SYS_BEOS ) + delete_sem( l->sem ); +#elif USE_PTHREAD + pthread_mutex_destroy( &l->mutex ); +//#elif defined( SYS_CYGWIN ) +// CloseHandle( l->mutex ); +#endif + free( l ); + + *_l = NULL; +} + +void hb_lock( hb_lock_t * l ) +{ +#if defined( SYS_BEOS ) + acquire_sem( l->sem ); +#elif USE_PTHREAD + pthread_mutex_lock( &l->mutex ); +//#elif defined( SYS_CYGWIN ) +// WaitForSingleObject( l->mutex, INFINITE ); +#endif +} + +void hb_unlock( hb_lock_t * l ) +{ +#if defined( SYS_BEOS ) + release_sem( l->sem ); +#elif USE_PTHREAD + pthread_mutex_unlock( &l->mutex ); +//#elif defined( SYS_CYGWIN ) +// ReleaseMutex( l->mutex ); +#endif +} + +/************************************************************************ + * Portable condition variable implementation + ***********************************************************************/ +struct hb_cond_s +{ +#if defined( SYS_BEOS ) + int thread; +#elif USE_PTHREAD + pthread_cond_t cond; +//#elif defined( SYS_CYGWIN ) +// HANDLE event; +#endif +}; + +/************************************************************************ + * hb_cond_init() + * hb_cond_close() + * hb_cond_wait() + * hb_cond_signal() + ************************************************************************ + * Win9x is not supported by this implementation (SignalObjectAndWait() + * only available on Windows 2000/XP). + ***********************************************************************/ +hb_cond_t * hb_cond_init() +{ + hb_cond_t * c = calloc( sizeof( hb_cond_t ), 1 ); + +#if defined( SYS_BEOS ) + c->thread = -1; +#elif USE_PTHREAD + pthread_cond_init( &c->cond, NULL ); +//#elif defined( SYS_CYGWIN ) +// c->event = CreateEvent( NULL, FALSE, FALSE, NULL ); +#endif + + return c; +} + +void hb_cond_close( hb_cond_t ** _c ) +{ + hb_cond_t * c = *_c; + +#if defined( SYS_BEOS ) +#elif USE_PTHREAD + pthread_cond_destroy( &c->cond ); +//#elif defined( SYS_CYGWIN ) +// CloseHandle( c->event ); +#endif + free( c ); + + *_c = NULL; +} + +void hb_cond_wait( hb_cond_t * c, hb_lock_t * lock ) +{ +#if defined( SYS_BEOS ) + c->thread = find_thread( NULL ); + release_sem( lock->sem ); + suspend_thread( c->thread ); + acquire_sem( lock->sem ); + c->thread = -1; +#elif USE_PTHREAD + pthread_cond_wait( &c->cond, &lock->mutex ); +//#elif defined( SYS_CYGWIN ) +// SignalObjectAndWait( lock->mutex, c->event, INFINITE, FALSE ); +// WaitForSingleObject( lock->mutex, INFINITE ); +#endif +} + +void hb_cond_signal( hb_cond_t * c ) +{ +#if defined( SYS_BEOS ) + while( c->thread != -1 ) + { + thread_info info; + get_thread_info( c->thread, &info ); + if( info.state == B_THREAD_SUSPENDED ) + { + resume_thread( c->thread ); + break; + } + /* Looks like we have been called between hb_cond_wait's + release_sem() and suspend_thread() lines. Wait until the + thread is actually suspended before we resume it */ + snooze( 5000 ); + } +#elif USE_PTHREAD + pthread_cond_signal( &c->cond ); +//#elif defined( SYS_CYGWIN ) +// PulseEvent( c->event ); +#endif +} + +/************************************************************************ + * Network + ***********************************************************************/ + +struct hb_net_s +{ + int socket; +}; + +hb_net_t * hb_net_open( char * address, int port ) +{ + hb_net_t * n = calloc( sizeof( hb_net_t ), 1 ); + + struct sockaddr_in sock; + struct hostent * host; + + /* TODO: find out why this doesn't work on Win32 */ + if( !( host = gethostbyname( address ) ) ) + { + hb_log( "gethostbyname failed (%s)", address ); + free( n ); + return NULL; + } + + memset( &sock, 0, sizeof( struct sockaddr_in ) ); + sock.sin_family = host->h_addrtype; + sock.sin_port = htons( port ); + memcpy( &sock.sin_addr, host->h_addr, host->h_length ); + + if( ( n->socket = socket( host->h_addrtype, SOCK_STREAM, 0 ) ) < 0 ) + { + hb_log( "socket failed" ); + free( n ); + return NULL; + } + + if( connect( n->socket, (struct sockaddr *) &sock, + sizeof( struct sockaddr_in ) ) < 0 ) + { + hb_log( "connect failed" ); + free( n ); + return NULL; + } + + return n; +} + +int hb_net_send( hb_net_t * n, char * buffer ) +{ + return send( n->socket, buffer, strlen( buffer ), 0 ); +} + +int hb_net_recv( hb_net_t * n, char * buffer, int size ) +{ + return recv( n->socket, buffer, size - 1, 0 ); +} + +void hb_net_close( hb_net_t ** _n ) +{ + hb_net_t * n = (hb_net_t *) *_n; + close( n->socket ); + free( n ); + *_n = NULL; +} + diff --git a/libmediafork/ports.h b/libmediafork/ports.h new file mode 100644 index 000000000..740252e6b --- /dev/null +++ b/libmediafork/ports.h @@ -0,0 +1,85 @@ +/* $Id: ports.h,v 1.7 2005/10/15 18:05:03 titer Exp $ + + 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. */ + +#ifndef HB_PORTS_H +#define HB_PORTS_H + +/************************************************************************ + * Utils + ***********************************************************************/ +uint64_t hb_get_date(); +void hb_snooze( int delay ); +int hb_get_cpu_count(); + +#ifdef __LIBMEDIAFORK__ + +/* Everything from now is only used internally and hidden to the UI */ + +/************************************************************************ + * Files utils + ***********************************************************************/ +void hb_get_tempory_directory( hb_handle_t * h, char path[512] ); +void hb_get_tempory_filename( hb_handle_t *, char name[1024], + char * fmt, ... ); +void hb_mkdir( char * name ); + +/************************************************************************ + * Threads + ***********************************************************************/ +typedef struct hb_thread_s hb_thread_t; + +#if defined( SYS_BEOS ) +# define HB_LOW_PRIORITY 5 +# define HB_NORMAL_PRIORITY 10 +#elif defined( SYS_DARWIN ) +# define HB_LOW_PRIORITY 0 +# define HB_NORMAL_PRIORITY 31 +#elif defined( SYS_LINUX ) || defined( SYS_FREEBSD ) +# define HB_LOW_PRIORITY 0 +# define HB_NORMAL_PRIORITY 0 +#elif defined( SYS_CYGWIN ) +# define HB_LOW_PRIORITY 0 +# define HB_NORMAL_PRIORITY 1 +#endif + +hb_thread_t * hb_thread_init( char * name, void (* function)(void *), + void * arg, int priority ); +void hb_thread_close( hb_thread_t ** ); +int hb_thread_has_exited( hb_thread_t * ); + +/************************************************************************ + * Mutexes + ***********************************************************************/ + +hb_lock_t * hb_lock_init(); +void hb_lock_close( hb_lock_t ** ); +void hb_lock( hb_lock_t * ); +void hb_unlock( hb_lock_t * ); + +/************************************************************************ + * Condition variables + ***********************************************************************/ +typedef struct hb_cond_s hb_cond_t; + +hb_cond_t * hb_cond_init(); +void hb_cond_wait( hb_cond_t *, hb_lock_t * ); +void hb_cond_signal( hb_cond_t * ); +void hb_cond_close( hb_cond_t ** ); + +/************************************************************************ + * Network + ***********************************************************************/ +typedef struct hb_net_s hb_net_t; + +hb_net_t * hb_net_open( char * address, int port ); +int hb_net_send( hb_net_t *, char * ); +int hb_net_recv( hb_net_t *, char *, int ); +void hb_net_close( hb_net_t ** ); + +#endif /* __LIBMEDIAFORK__ */ + +#endif + diff --git a/libmediafork/reader.c b/libmediafork/reader.c new file mode 100644 index 000000000..7be28237d --- /dev/null +++ b/libmediafork/reader.c @@ -0,0 +1,156 @@ +/* $Id: reader.c,v 1.21 2005/11/25 15:05:25 titer Exp $ + + 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 "mediafork.h" + +typedef struct +{ + hb_job_t * job; + hb_title_t * title; + volatile int * die; + + hb_dvd_t * dvd; + hb_buffer_t * ps; + +} hb_reader_t; + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static void ReaderFunc( void * ); +static hb_fifo_t * GetFifoForId( hb_job_t * job, int id ); + +/*********************************************************************** + * hb_reader_init + *********************************************************************** + * + **********************************************************************/ +hb_thread_t * hb_reader_init( hb_job_t * job ) +{ + hb_reader_t * r; + + r = calloc( sizeof( hb_reader_t ), 1 ); + + r->job = job; + r->title = job->title; + r->die = job->die; + + return hb_thread_init( "reader", ReaderFunc, r, + HB_NORMAL_PRIORITY ); +} + +/*********************************************************************** + * ReaderFunc + *********************************************************************** + * + **********************************************************************/ +static void ReaderFunc( void * _r ) +{ + hb_reader_t * r = _r; + hb_fifo_t * fifo; + hb_buffer_t * buf; + hb_list_t * list; + int chapter; + + if( !( r->dvd = hb_dvd_init( r->title->dvd ) ) ) + { + return; + } + + if( !hb_dvd_start( r->dvd, r->title->index, r->job->chapter_start ) ) + { + hb_dvd_close( &r->dvd ); + return; + } + + list = hb_list_init(); + r->ps = hb_buffer_init( 2048 ); + + while( !*r->die && !r->job->done ) + { + chapter = hb_dvd_chapter( r->dvd ); + if( chapter < 0 ) + { + hb_log( "reader: end of the title reached" ); + break; + } + if( chapter > r->job->chapter_end ) + { + hb_log( "reader: end of chapter %d reached (%d)", + r->job->chapter_end, chapter ); + break; + } + + if( !hb_dvd_read( r->dvd, r->ps ) ) + { + break; + } + + hb_demux_ps( r->ps, list ); + + while( ( buf = hb_list_item( list, 0 ) ) ) + { + hb_list_rem( list, buf ); + fifo = GetFifoForId( r->job, buf->id ); + if( fifo ) + { + while( !*r->die && !r->job->done && + hb_fifo_is_full( fifo ) ) + { + hb_snooze( 50 ); + } + hb_fifo_push( fifo, buf ); + } + else + { + hb_buffer_close( &buf ); + } + } + } + + hb_list_empty( &list ); + hb_buffer_close( &r->ps ); + hb_dvd_stop( r->dvd ); + hb_dvd_close( &r->dvd ); + + hb_log( "reader: done" ); +} + +/*********************************************************************** + * GetFifoForId + *********************************************************************** + * + **********************************************************************/ +static hb_fifo_t * GetFifoForId( hb_job_t * job, int id ) +{ + hb_title_t * title = job->title; + hb_audio_t * audio; + hb_subtitle_t * subtitle; + int i; + + if( id == 0xE0 ) + { + return job->fifo_mpeg2; + } + + if( ( subtitle = hb_list_item( title->list_subtitle, 0 ) ) && + id == subtitle->id ) + { + return subtitle->fifo_in; + } + + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + if( id == audio->id ) + { + return audio->fifo_in; + } + } + + return NULL; +} + diff --git a/libmediafork/render.c b/libmediafork/render.c new file mode 100644 index 000000000..97117089a --- /dev/null +++ b/libmediafork/render.c @@ -0,0 +1,186 @@ +/* $Id: render.c,v 1.17 2005/04/14 17:37:54 titer Exp $ + + 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 "mediafork.h" + +#include "ffmpeg/avcodec.h" + +struct hb_work_private_s +{ + hb_job_t * job; + + ImgReSampleContext * context; + AVPicture pic_raw; + AVPicture pic_deint; + AVPicture pic_render; + hb_buffer_t * buf_deint; +}; + +int renderInit( hb_work_object_t *, hb_job_t * ); +int renderWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +void renderClose( hb_work_object_t * ); + +hb_work_object_t hb_render = +{ + WORK_RENDER, + "Renderer", + renderInit, + renderWork, + renderClose +}; + +static void ApplySub( hb_job_t * job, hb_buffer_t * buf, + hb_buffer_t ** _sub ) +{ + hb_buffer_t * sub = *_sub; + hb_title_t * title = job->title; + int i, j, offset_top, offset_left; + uint8_t * lum, * alpha, * out; + + if( !sub ) + { + return; + } + + /* If necessary, move the subtitle so it is not in a cropped zone. + When it won't fit, we center it so we loose as much on both ends. + Otherwise we try to leave a 20px margin around it. */ + + if( sub->height > title->height - job->crop[0] - job->crop[1] - 40 ) + offset_top = job->crop[0] + ( title->height - job->crop[0] - + job->crop[1] - sub->height ) / 2; + else if( sub->y < job->crop[0] + 20 ) + offset_top = job->crop[0] + 20; + else if( sub->y > title->height - job->crop[1] - 20 - sub->height ) + offset_top = title->height - job->crop[1] - 20 - sub->height; + else + offset_top = sub->y; + + if( sub->width > title->width - job->crop[2] - job->crop[3] - 40 ) + offset_left = job->crop[2] + ( title->width - job->crop[2] - + job->crop[3] - sub->width ) / 2; + else if( sub->x < job->crop[2] + 20 ) + offset_left = job->crop[2] + 20; + else if( sub->x > title->width - job->crop[3] - 20 - sub->width ) + offset_left = title->width - job->crop[3] - 20 - sub->width; + else + offset_left = sub->x; + + lum = sub->data; + alpha = lum + sub->width * sub->height; + out = buf->data + offset_top * title->width + offset_left; + + for( i = 0; i < sub->height; i++ ) + { + if( offset_top + i >= 0 && offset_top + i < title->height ) + { + for( j = 0; j < sub->width; j++ ) + { + if( offset_left + j >= 0 && offset_left + j < title->width ) + { + out[j] = ( (uint16_t) out[j] * ( 16 - (uint16_t) alpha[j] ) + + (uint16_t) lum[j] * (uint16_t) alpha[j] ) >> 4; + } + } + } + + lum += sub->width; + alpha += sub->width; + out += title->width; + } + + hb_buffer_close( _sub ); +} + +int renderWork( 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_title_t * title = job->title; + hb_buffer_t * in = *buf_in, * buf; + + avpicture_fill( &pv->pic_raw, in->data, PIX_FMT_YUV420P, + title->width, title->height ); + + buf = hb_buffer_init( 3 * job->width * job->height / 2 ); + buf->start = in->start; + buf->stop = in->stop; + + if( job->deinterlace && pv->context ) + { + avpicture_fill( &pv->pic_render, buf->data, PIX_FMT_YUV420P, + job->width, job->height ); + avpicture_deinterlace( &pv->pic_deint, &pv->pic_raw, + PIX_FMT_YUV420P, title->width, + title->height ); + ApplySub( job, pv->buf_deint, &in->sub ); + img_resample( pv->context, &pv->pic_render, &pv->pic_deint ); + } + else if( job->deinterlace ) + { + avpicture_fill( &pv->pic_deint, buf->data, PIX_FMT_YUV420P, + job->width, job->height ); + avpicture_deinterlace( &pv->pic_deint, &pv->pic_raw, + PIX_FMT_YUV420P, title->width, + title->height ); + ApplySub( job, buf, &in->sub ); + } + else if( pv->context ) + { + ApplySub( job, in, &in->sub ); + avpicture_fill( &pv->pic_render, buf->data, PIX_FMT_YUV420P, + job->width, job->height ); + img_resample( pv->context, &pv->pic_render, &pv->pic_raw ); + } + else + { + hb_buffer_close( &buf ); + ApplySub( job, in, &in->sub ); + buf = in; + *buf_in = NULL; + } + + (*buf_out) = buf; + + return HB_WORK_OK; +} + +void renderClose( hb_work_object_t * w ) +{ +} + +int renderInit( hb_work_object_t * w, hb_job_t * job ) +{ + hb_title_t * title; + + hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + + title = job->title; + + pv->job = job; + + if( job->crop[0] || job->crop[1] || job->crop[2] || job->crop[3] || + job->width != title->width || job->height != title->height ) + { + pv->context = img_resample_full_init( + job->width, job->height, title->width, title->height, + job->crop[0], job->crop[1], job->crop[2], job->crop[3], + 0, 0, 0, 0 ); + } + + if( job->deinterlace ) + { + /* Allocate a constant buffer used for deinterlacing */ + pv->buf_deint = hb_buffer_init( 3 * title->width * + title->height / 2 ); + avpicture_fill( &pv->pic_deint, pv->buf_deint->data, + PIX_FMT_YUV420P, title->width, title->height ); + } + + return 0; +} diff --git a/libmediafork/scan.c b/libmediafork/scan.c new file mode 100644 index 000000000..1f74f629d --- /dev/null +++ b/libmediafork/scan.c @@ -0,0 +1,511 @@ +/* $Id: scan.c,v 1.52 2005/11/25 15:05:25 titer Exp $ + + 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 "mediafork.h" +#include "a52dec/a52.h" + +typedef struct +{ + hb_handle_t * h; + + char * path; + int title_index; + hb_list_t * list_title; + + hb_dvd_t * dvd; + +} hb_scan_t; + +static void ScanFunc( void * ); +static int DecodePreviews( hb_scan_t *, hb_title_t * title ); +static void LookForAC3( hb_title_t * title, hb_buffer_t * b ); +static int AllAC3OK( hb_title_t * title ); + +hb_thread_t * hb_scan_init( hb_handle_t * handle, const char * path, + int title_index, hb_list_t * list_title ) +{ + hb_scan_t * data = calloc( sizeof( hb_scan_t ), 1 ); + + data->h = handle; + data->path = strdup( path ); + data->title_index = title_index; + data->list_title = list_title; + + return hb_thread_init( "scan", ScanFunc, data, HB_NORMAL_PRIORITY ); +} + +static void ScanFunc( void * _data ) +{ + hb_scan_t * data = (hb_scan_t *) _data; + hb_title_t * title; + int i; + + /* Try to open the path as a DVD. If it fails, try as a file */ + hb_log( "scan: trying to open with libdvdread" ); + if( ( data->dvd = hb_dvd_init( data->path ) ) ) + { + hb_log( "scan: DVD has %d title(s)", + hb_dvd_title_count( data->dvd ) ); + if( data->title_index ) + { + /* Scan this title only */ + hb_list_add( data->list_title, hb_dvd_title_scan( data->dvd, + data->title_index ) ); + } + else + { + /* Scan all titles */ + for( i = 0; i < hb_dvd_title_count( data->dvd ); i++ ) + { + hb_list_add( data->list_title, + hb_dvd_title_scan( data->dvd, i + 1 ) ); + } + } + } + else + { + /* Open as a VOB file */ + FILE * file; + hb_log( "scan: trying to open as VOB file" ); + file = fopen( data->path, "rb" ); + if( file ) + { + /* XXX */ + fclose( file ); + } + else + { + hb_log( "scan: fopen failed" ); + return; + } + } + + for( i = 0; i < hb_list_count( data->list_title ); ) + { + int j; + hb_state_t state; + hb_audio_t * audio; + hb_title_t * title_tmp = NULL; + + title = hb_list_item( data->list_title, i ); + + /* I've seen a DVD with strictly identical titles. Check this + here and ignore it if redundant */ + for( j = 0; j < i; j++ ) + { + title_tmp = hb_list_item( data->list_title, j ); + if( title->vts == title_tmp->vts && + title->block_start == title_tmp->block_start && + title->block_end == title_tmp->block_end && + title->block_count == title_tmp->block_count ) + { + break; + } + else + { + title_tmp = NULL; + } + } + if( title_tmp ) + { + hb_log( "scan: title %d is duplicate with title %d", + title->index, title_tmp->index ); + hb_list_rem( data->list_title, title ); + free( title ); + continue; + } + +#define p state.param.scanning + /* Update the UI */ + state.state = HB_STATE_SCANNING; + p.title_cur = title->index; + p.title_count = hb_dvd_title_count( data->dvd ); + hb_set_state( data->h, &state ); +#undef p + + /* Decode previews */ + if( !DecodePreviews( data, title ) ) + { + /* TODO: free things */ + hb_list_rem( data->list_title, title ); + continue; + } + + /* Make sure we found AC3 rates and bitrates */ + for( j = 0; j < hb_list_count( title->list_audio ); ) + { + audio = hb_list_item( title->list_audio, j ); + if( audio->codec == HB_ACODEC_AC3 && + !audio->bitrate ) + { + hb_list_rem( title->list_audio, audio ); + free( audio ); + continue; + } + j++; + } + + /* Do we still have audio */ + if( !hb_list_count( title->list_audio ) ) + { + hb_list_rem( data->list_title, title ); + free( title ); + continue; + } + + i++; + } + + /* Init jobs templates */ + for( i = 0; i < hb_list_count( data->list_title ); i++ ) + { + hb_job_t * job; + + title = hb_list_item( data->list_title, i ); + job = calloc( sizeof( hb_job_t ), 1 ); + title->job = job; + + job->title = title; + + /* Set defaults settings */ + job->chapter_start = 1; + job->chapter_end = hb_list_count( title->list_chapter ); + + /* Autocrop by default. Gnark gnark */ + memcpy( job->crop, title->crop, 4 * sizeof( int ) ); + + if( title->aspect == 16 ) + { + hb_reduce( &job->pixel_aspect_width, &job->pixel_aspect_height, + 16 * title->height, 9 * title->width ); + } + else + { + hb_reduce( &job->pixel_aspect_width, &job->pixel_aspect_height, + 4 * title->height, 3 * title->width ); + } + + job->width = title->width - job->crop[2] - job->crop[3]; +// job->height = title->height - job->crop[0] - job->crop[1]; + hb_fix_aspect( job, HB_KEEP_WIDTH ); + if( job->height > title->height - job->crop[0] - job->crop[1] ) + { + job->height = title->height - job->crop[0] - job->crop[1]; + hb_fix_aspect( job, HB_KEEP_HEIGHT ); + } + + hb_log( "scan: title (%d) job->width:%d, job->height:%d", + i,job->width, job->height ); + + job->keep_ratio = 1; + + job->vcodec = HB_VCODEC_FFMPEG; + job->vquality = -1.0; + job->vbitrate = 1000; + job->pass = 0; + job->vrate = title->rate; + job->vrate_base = title->rate_base; + + job->audios[0] = 0; + job->audios[1] = -1; + + job->acodec = HB_ACODEC_FAAC; + job->abitrate = 128; + job->arate = 44100; + + job->subtitle = -1; + + job->mux = HB_MUX_MP4; + } + + if( data->dvd ) + { + hb_dvd_close( &data->dvd ); + } +} + +/*********************************************************************** + * DecodePreviews + *********************************************************************** + * Decode 10 pictures for the given title. + * It assumes that data->reader and data->vts have successfully been + * DVDOpen()ed and ifoOpen()ed. + **********************************************************************/ +static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) +{ + int i, ret; + hb_buffer_t * buf_ps, * buf_es, * buf_raw; + hb_list_t * list_es, * list_raw; + hb_libmpeg2_t * mpeg2; + + buf_ps = hb_buffer_init( 2048 ); + list_es = hb_list_init(); + list_raw = hb_list_init(); + + hb_log( "scan: decoding previews for title %d", title->index ); + + hb_dvd_start( data->dvd, title->index, 1 ); + + for( i = 0; i < 10; i++ ) + { + int j, k; + FILE * file_preview; + char filename[1024]; + + if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / 11.0 ) ) + { + goto error; + } + + hb_log( "scan: preview %d", i + 1 ); + + mpeg2 = hb_libmpeg2_init(); + + for( j = 0; j < 10240 ; j++ ) + { + if( !hb_dvd_read( data->dvd, buf_ps ) ) + { + goto error; + } + hb_demux_ps( buf_ps, list_es ); + + while( ( buf_es = hb_list_item( list_es, 0 ) ) ) + { + hb_list_rem( list_es, buf_es ); + if( buf_es->id == 0xE0 && !hb_list_count( list_raw ) ) + { + hb_libmpeg2_decode( mpeg2, buf_es, list_raw ); + } + else if( !i ) + { + LookForAC3( title, buf_es ); + } + hb_buffer_close( &buf_es ); + + if( hb_list_count( list_raw ) && + ( i || AllAC3OK( title ) ) ) + { + /* We got a picture */ + break; + } + } + + if( hb_list_count( list_raw ) && + ( i || AllAC3OK( title ) ) ) + { + break; + } + } + + if( !hb_list_count( list_raw ) ) + { + hb_log( "scan: could not get a decoded picture" ); + goto error; + } + + if( !i ) + { + /* Get size and rate infos */ + title->rate = 27000000; + hb_libmpeg2_info( mpeg2, &title->width, &title->height, + &title->rate_base ); + title->crop[0] = title->crop[1] = title->height / 2; + title->crop[2] = title->crop[3] = title->width / 2; + } + + hb_libmpeg2_close( &mpeg2 ); + + while( ( buf_es = hb_list_item( list_es, 0 ) ) ) + { + hb_list_rem( list_es, buf_es ); + hb_buffer_close( &buf_es ); + } + + buf_raw = hb_list_item( list_raw, 0 ); + + hb_get_tempory_filename( data->h, filename, "%x%d", + (int) title, i ); + + file_preview = fopen( filename, "w" ); + if( file_preview ) + { + fwrite( buf_raw->data, title->width * title->height * 3 / 2, + 1, file_preview ); + fclose( file_preview ); + } + else + { + hb_log( "scan: fopen failed (%s)", filename ); + } + +#define Y buf_raw->data +#define DARK 64 + + /* Detect black borders */ + + for( j = 0; j < title->width; j++ ) + { + for( k = 0; k < title->crop[0]; k++ ) + if( Y[ k * title->width + j ] > DARK ) + { + title->crop[0] = k; + break; + } + for( k = 0; k < title->crop[1]; k++ ) + if( Y[ ( title->height - k - 1 ) * + title->width + j ] > DARK ) + { + title->crop[1] = k; + break; + } + } + for( j = 0; j < title->height; j++ ) + { + for( k = 0; k < title->crop[2]; k++ ) + if( Y[ j * title->width + k ] > DARK ) + { + title->crop[2] = k; + break; + } + for( k = 0; k < title->crop[3]; k++ ) + if( Y[ j * title->width + + title->width - k - 1 ] > DARK ) + { + title->crop[3] = k; + break; + } + } + + while( ( buf_raw = hb_list_item( list_raw, 0 ) ) ) + { + hb_list_rem( list_raw, buf_raw ); + hb_buffer_close( &buf_raw ); + } + } + + title->crop[0] = EVEN( title->crop[0] ); + title->crop[1] = EVEN( title->crop[1] ); + title->crop[2] = EVEN( title->crop[2] ); + title->crop[3] = EVEN( title->crop[3] ); + + hb_log( "scan: %dx%d, %.3f fps, autocrop = %d/%d/%d/%d", + title->width, title->height, (float) title->rate / + (float) title->rate_base, title->crop[0], title->crop[1], + title->crop[2], title->crop[3] ); + + ret = 1; + goto cleanup; + +error: + ret = 0; + +cleanup: + hb_buffer_close( &buf_ps ); + while( ( buf_es = hb_list_item( list_es, 0 ) ) ) + { + hb_list_rem( list_es, buf_es ); + hb_buffer_close( &buf_es ); + } + hb_list_close( &list_es ); + while( ( buf_raw = hb_list_item( list_raw, 0 ) ) ) + { + hb_list_rem( list_raw, buf_raw ); + hb_buffer_close( &buf_raw ); + } + hb_list_close( &list_raw ); + hb_dvd_stop( data->dvd ); + return ret; +} + +static void LookForAC3( hb_title_t * title, hb_buffer_t * b ) +{ + int i; + int flags; + int rate; + int bitrate; + + /* Figure out if this is a AC3 buffer for a known track */ + hb_audio_t * audio = NULL; + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + if( audio->codec == HB_ACODEC_AC3 && + audio->id == b->id ) + { + break; + } + else + { + audio = NULL; + } + } + if( !audio ) + { + return; + } + + if( audio->bitrate ) + { + /* Already done for this track */ + return; + } + + for( i = 0; i < b->size - 7; i++ ) + { + if( a52_syncinfo( &b->data[i], &flags, &rate, &bitrate ) ) + { + hb_log( "scan: rate=%dHz, bitrate=%d", rate, bitrate ); + audio->rate = rate; + audio->bitrate = bitrate; + switch( flags & A52_CHANNEL_MASK ) + { + case A52_MONO: + case A52_CHANNEL1: + case A52_CHANNEL2: + audio->channels = 1; + break; + case A52_STEREO: + case A52_DOLBY: + case A52_CHANNEL: + audio->channels = 2; + break; + case A52_3F: + case A52_2F1R: + audio->channels = 3; + break; + case A52_3F1R: + case A52_2F2R: + audio->channels = 4; + break; + case A52_3F2R: + audio->channels = 5; + break; + } + /* XXX */ + sprintf( audio->lang + strlen( audio->lang ), + " (%d ch)", audio->channels ); + break; + } + } +} + +static int AllAC3OK( hb_title_t * title ) +{ + int i; + hb_audio_t * audio; + + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + if( audio->codec == HB_ACODEC_AC3 && + !audio->bitrate ) + { + return 0; + } + } + + return 1; +} diff --git a/libmediafork/sync.c b/libmediafork/sync.c new file mode 100644 index 000000000..7053016e0 --- /dev/null +++ b/libmediafork/sync.c @@ -0,0 +1,675 @@ +/* $Id: sync.c,v 1.38 2005/04/14 21:57:58 titer Exp $ + + 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 "mediafork.h" + +#include "samplerate.h" +#include "ffmpeg/avcodec.h" + +#ifdef INT64_MIN +#undef INT64_MIN /* Because it isn't defined correctly in Zeta */ +#endif +#define INT64_MIN (-9223372036854775807LL-1) + +#define AC3_SAMPLES_PER_FRAME 1536 + +typedef struct +{ + hb_audio_t * audio; + int64_t count_frames; + + /* Raw */ + SRC_STATE * state; + SRC_DATA data; + + /* AC-3 */ + int ac3_size; + uint8_t * ac3_buf; + +} hb_sync_audio_t; + +struct hb_work_private_s +{ + hb_job_t * job; + int done; + + /* Video */ + hb_subtitle_t * subtitle; + int64_t pts_offset; + int64_t pts_offset_old; + int64_t count_frames; + int64_t count_frames_max; + hb_buffer_t * cur; /* The next picture to process */ + + /* Audio */ + hb_sync_audio_t sync_audio[8]; + + /* Statistics */ + uint64_t st_counts[4]; + uint64_t st_dates[4]; + uint64_t st_first; +}; + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static void InitAudio( hb_work_object_t * w, int i ); +static int SyncVideo( hb_work_object_t * w ); +static void SyncAudio( hb_work_object_t * w, int i ); +static int NeedSilence( hb_work_object_t * w, hb_audio_t * ); +static void InsertSilence( hb_work_object_t * w, int i ); +static void UpdateState( hb_work_object_t * w ); + +/*********************************************************************** + * hb_work_sync_init + *********************************************************************** + * Initialize the work object + **********************************************************************/ +int syncInit( hb_work_object_t * w, hb_job_t * job ) +{ + hb_title_t * title = job->title; + hb_chapter_t * chapter; + int i; + uint64_t duration; + hb_work_private_t * pv; + + pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + + pv->job = job; + pv->pts_offset = INT64_MIN; + pv->pts_offset_old = INT64_MIN; + pv->count_frames = 0; + + /* Calculate how many video frames we are expecting */ + duration = 0; + for( i = job->chapter_start; i <= job->chapter_end; i++ ) + { + chapter = hb_list_item( title->list_chapter, i - 1 ); + duration += chapter->duration; + } + duration += 90000; + /* 1 second safety so we're sure we won't miss anything */ + pv->count_frames_max = duration * job->vrate / job->vrate_base / 90000; + + hb_log( "sync: expecting %lld video frames", pv->count_frames_max ); + + /* Initialize libsamplerate for every audio track we have */ + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + InitAudio( w, i ); + } + + /* Get subtitle info, if any */ + pv->subtitle = hb_list_item( title->list_subtitle, 0 ); + + return 0; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +void syncClose( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + hb_job_t * job = pv->job; + hb_title_t * title = job->title; + + int i; + + if( pv->cur ) hb_buffer_close( &pv->cur ); + + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + if( job->acodec & HB_ACODEC_AC3 ) + { + free( pv->sync_audio[i].ac3_buf ); + } + else + { + src_delete( pv->sync_audio[i].state ); + } + } +} + +/*********************************************************************** + * Work + *********************************************************************** + * The root routine of this work abject + **********************************************************************/ +int syncWork( hb_work_object_t * w, hb_buffer_t ** unused1, + hb_buffer_t ** unused2 ) +{ + hb_work_private_t * pv = w->private_data; + int i; + + /* If we ever got a video frame, handle audio now */ + if( pv->pts_offset != INT64_MIN ) + { + for( i = 0; i < hb_list_count( pv->job->title->list_audio ); i++ ) + { + SyncAudio( w, i ); + } + } + + /* Handle video */ + return SyncVideo( w ); +} + +hb_work_object_t hb_sync = +{ + WORK_SYNC, + "Synchronization", + syncInit, + syncWork, + syncClose +}; + +static void InitAudio( hb_work_object_t * w, int i ) +{ + hb_work_private_t * pv = w->private_data; + hb_job_t * job = pv->job; + hb_title_t * title = job->title; + hb_sync_audio_t * sync; + + sync = &pv->sync_audio[i]; + sync->audio = hb_list_item( title->list_audio, i ); + + if( job->acodec & HB_ACODEC_AC3 ) + { + /* Have a silent AC-3 frame ready in case we have to fill a + gap */ + AVCodec * codec; + AVCodecContext * c; + short * zeros; + + codec = avcodec_find_encoder( CODEC_ID_AC3 ); + c = avcodec_alloc_context(); + + c->bit_rate = sync->audio->bitrate; + c->sample_rate = sync->audio->rate; + c->channels = sync->audio->channels; + + if( avcodec_open( c, codec ) < 0 ) + { + hb_log( "sync: avcodec_open failed" ); + return; + } + + zeros = calloc( AC3_SAMPLES_PER_FRAME * + sizeof( short ) * c->channels, 1 ); + sync->ac3_size = sync->audio->bitrate * AC3_SAMPLES_PER_FRAME / + sync->audio->rate / 8; + sync->ac3_buf = malloc( sync->ac3_size ); + + if( avcodec_encode_audio( c, sync->ac3_buf, sync->ac3_size, + zeros ) != sync->ac3_size ) + { + hb_log( "sync: avcodec_encode_audio failed" ); + } + + free( zeros ); + avcodec_close( c ); + av_free( c ); + } + else + { + /* Initialize libsamplerate */ + int error; + sync->state = src_new( SRC_LINEAR, 2, &error ); + sync->data.end_of_input = 0; + } +} + + + +#define PTS_DISCONTINUITY_TOLERANCE 90000 + +/*********************************************************************** + * SyncVideo + *********************************************************************** + * + **********************************************************************/ +static int SyncVideo( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * cur, * next, * sub = NULL; + hb_job_t * job = pv->job; + int64_t pts_expected; + + if( pv->done ) + { + return HB_WORK_DONE; + } + + if( hb_thread_has_exited( job->reader ) && + !hb_fifo_size( job->fifo_mpeg2 ) && + !hb_fifo_size( job->fifo_raw ) ) + { + /* All video data has been processed already, we won't get + more */ + hb_log( "sync: got %lld frames, %lld expected", + pv->count_frames, pv->count_frames_max ); + pv->done = 1; + return HB_WORK_DONE; + } + + if( !pv->cur && !( pv->cur = hb_fifo_get( job->fifo_raw ) ) ) + { + /* We haven't even got a frame yet */ + return HB_WORK_OK; + } + cur = pv->cur; + + /* At this point we have a frame to process. Let's check + 1) if we will be able to push into the fifo ahead + 2) if the next frame is there already, since we need it to + know whether we'll have to repeat the current frame or not */ + while( !hb_fifo_is_full( job->fifo_sync ) && + ( next = hb_fifo_see( job->fifo_raw ) ) ) + { + hb_buffer_t * buf_tmp; + + if( pv->pts_offset == INT64_MIN ) + { + /* This is our first frame */ + hb_log( "sync: first pts is %lld", cur->start ); + pv->pts_offset = cur->start; + } + + /* Check for PTS jumps over 0.5 second */ + if( next->start < cur->start - PTS_DISCONTINUITY_TOLERANCE || + next->start > cur->start + PTS_DISCONTINUITY_TOLERANCE ) + { + hb_log( "PTS discontinuity (%lld, %lld)", + cur->start, next->start ); + + /* Trash all subtitles */ + if( pv->subtitle ) + { + while( ( sub = hb_fifo_get( pv->subtitle->fifo_raw ) ) ) + { + hb_buffer_close( &sub ); + } + } + + /* Trash current picture */ + hb_buffer_close( &cur ); + pv->cur = cur = hb_fifo_get( job->fifo_raw ); + + /* Calculate new offset */ + pv->pts_offset_old = pv->pts_offset; + pv->pts_offset = cur->start - + pv->count_frames * pv->job->vrate_base / 300; + continue; + } + + /* Look for a subtitle for this frame */ + if( pv->subtitle ) + { + hb_buffer_t * sub2; + while( ( sub = hb_fifo_see( pv->subtitle->fifo_raw ) ) ) + { + /* If two subtitles overlap, make the first one stop + when the second one starts */ + sub2 = hb_fifo_see2( pv->subtitle->fifo_raw ); + if( sub2 && sub->stop > sub2->start ) + sub->stop = sub2->start; + + if( sub->stop > cur->start ) + break; + + /* The subtitle is older than this picture, trash it */ + sub = hb_fifo_get( pv->subtitle->fifo_raw ); + hb_buffer_close( &sub ); + } + + /* If we have subtitles left in the fifo, check if we should + apply the first one to the current frame or if we should + keep it for later */ + if( sub && sub->start > cur->start ) + { + sub = NULL; + } + } + + /* The PTS of the frame we are expecting now */ + pts_expected = pv->pts_offset + + pv->count_frames * pv->job->vrate_base / 300; + + if( cur->start < pts_expected - pv->job->vrate_base / 300 / 2 && + next->start < pts_expected + pv->job->vrate_base / 300 / 2 ) + { + /* The current frame is too old but the next one matches, + let's trash */ + hb_buffer_close( &cur ); + pv->cur = cur = hb_fifo_get( job->fifo_raw ); + continue; + } + + if( next->start > pts_expected + 3 * pv->job->vrate_base / 300 / 2 ) + { + /* We'll need the current frame more than one time. Make a + copy of it and keep it */ + buf_tmp = hb_buffer_init( cur->size ); + memcpy( buf_tmp->data, cur->data, cur->size ); + } + else + { + /* The frame has the expected date and won't have to be + duplicated, just put it through */ + buf_tmp = cur; + pv->cur = cur = hb_fifo_get( job->fifo_raw ); + } + + /* Replace those MPEG-2 dates with our dates */ + buf_tmp->start = (uint64_t) pv->count_frames * + pv->job->vrate_base / 300; + buf_tmp->stop = (uint64_t) ( pv->count_frames + 1 ) * + pv->job->vrate_base / 300; + + /* If we have a subtitle for this picture, copy it */ + /* FIXME: we should avoid this memcpy */ + if( sub ) + { + buf_tmp->sub = hb_buffer_init( sub->size ); + buf_tmp->sub->x = sub->x; + buf_tmp->sub->y = sub->y; + buf_tmp->sub->width = sub->width; + buf_tmp->sub->height = sub->height; + memcpy( buf_tmp->sub->data, sub->data, sub->size ); + } + + /* Push the frame to the renderer */ + hb_fifo_push( job->fifo_sync, buf_tmp ); + + /* Update UI */ + UpdateState( w ); + + /* Make sure we won't get more frames then expected */ + if( pv->count_frames >= pv->count_frames_max ) + { + hb_log( "sync: got %lld frames", pv->count_frames ); + pv->done = 1; + break; + } + } + + return HB_WORK_OK; +} + +/*********************************************************************** + * SyncAudio + *********************************************************************** + * + **********************************************************************/ +static void SyncAudio( hb_work_object_t * w, int i ) +{ + hb_work_private_t * pv = w->private_data; + hb_job_t * job; + hb_audio_t * audio; + hb_buffer_t * buf; + hb_sync_audio_t * sync; + + hb_fifo_t * fifo; + int rate; + + int64_t pts_expected; + int64_t start; + + job = pv->job; + sync = &pv->sync_audio[i]; + audio = sync->audio; + + if( job->acodec & HB_ACODEC_AC3 ) + { + fifo = audio->fifo_out; + rate = audio->rate; + } + else + { + fifo = audio->fifo_sync; + rate = job->arate; + } + + while( !hb_fifo_is_full( fifo ) && + ( buf = hb_fifo_see( audio->fifo_raw ) ) ) + { + /* The PTS of the samples we are expecting now */ + pts_expected = pv->pts_offset + sync->count_frames * 90000 / rate; + + if( ( buf->start > pts_expected + PTS_DISCONTINUITY_TOLERANCE || + buf->start < pts_expected - PTS_DISCONTINUITY_TOLERANCE ) && + pv->pts_offset_old > INT64_MIN ) + { + /* There has been a PTS discontinuity, and this frame might + be from before the discontinuity */ + pts_expected = pv->pts_offset_old + sync->count_frames * + 90000 / rate; + + if( buf->start > pts_expected + PTS_DISCONTINUITY_TOLERANCE || + buf->start < pts_expected - PTS_DISCONTINUITY_TOLERANCE ) + { + /* There is really nothing we can do with it */ + buf = hb_fifo_get( audio->fifo_raw ); + hb_buffer_close( &buf ); + continue; + } + + /* Use the older offset */ + start = pts_expected - pv->pts_offset_old; + } + else + { + start = pts_expected - pv->pts_offset; + } + + /* Tolerance: 100 ms */ + if( buf->start < pts_expected - 9000 ) + { + /* Late audio, trash it */ + hb_log( "sync: trashing late audio" ); + buf = hb_fifo_get( audio->fifo_raw ); + hb_buffer_close( &buf ); + continue; + } + else if( buf->start > pts_expected + 9000 ) + { + /* Missing audio, send a frame of silence */ + InsertSilence( w, i ); + continue; + } + + if( job->acodec & HB_ACODEC_AC3 ) + { + buf = hb_fifo_get( audio->fifo_raw ); + buf->start = start; + buf->stop = start + 90000 * AC3_SAMPLES_PER_FRAME / rate; + + sync->count_frames += AC3_SAMPLES_PER_FRAME; + } + else + { + hb_buffer_t * buf_raw = hb_fifo_get( audio->fifo_raw ); + + int count_in, count_out; + + count_in = buf_raw->size / 2 / sizeof( float ); + count_out = ( buf_raw->stop - buf_raw->start ) * job->arate / 90000; + if( buf->start < pts_expected - 1500 ) + count_out--; + else if( buf->start > pts_expected + 1500 ) + count_out++; + + sync->data.data_in = (float *) buf_raw->data; + sync->data.input_frames = count_in; + sync->data.output_frames = count_out; + + sync->data.src_ratio = (double) sync->data.output_frames / + (double) sync->data.input_frames; + + buf = hb_buffer_init( sync->data.output_frames * 2 * + sizeof( float ) ); + sync->data.data_out = (float *) buf->data; + if( src_process( sync->state, &sync->data ) ) + { + /* XXX If this happens, we're screwed */ + hb_log( "sync: src_process failed" ); + } + hb_buffer_close( &buf_raw ); + + buf->size = sync->data.output_frames_gen * 2 * sizeof( float ); + + /* Set dates for resampled data */ + buf->start = start; + buf->stop = start + sync->data.output_frames_gen * + 90000 / job->arate; + + sync->count_frames += sync->data.output_frames_gen; + } + + buf->key = 1; + hb_fifo_push( fifo, buf ); + } + + if( NeedSilence( w, audio ) ) + { + InsertSilence( w, i ); + } +} + +static int NeedSilence( hb_work_object_t * w, hb_audio_t * audio ) +{ + hb_work_private_t * pv = w->private_data; + hb_job_t * job = pv->job; + + if( hb_fifo_size( audio->fifo_in ) || + hb_fifo_size( audio->fifo_raw ) || + hb_fifo_size( audio->fifo_sync ) || + hb_fifo_size( audio->fifo_out ) ) + { + /* We have some audio, we are fine */ + return 0; + } + + /* No audio left in fifos */ + + if( hb_thread_has_exited( job->reader ) ) + { + /* We might miss some audio to complete encoding and muxing + the video track */ + return 1; + } + + if( hb_fifo_is_full( job->fifo_mpeg2 ) && + hb_fifo_is_full( job->fifo_raw ) && + hb_fifo_is_full( job->fifo_sync ) && + hb_fifo_is_full( job->fifo_render ) && + hb_fifo_is_full( job->fifo_mpeg4 ) ) + { + /* Too much video and no audio, oh-oh */ + return 1; + } + + return 0; +} + +static void InsertSilence( hb_work_object_t * w, int i ) +{ + hb_work_private_t * pv = w->private_data; + hb_job_t * job; + hb_sync_audio_t * sync; + hb_buffer_t * buf; + + job = pv->job; + sync = &pv->sync_audio[i]; + + if( job->acodec & HB_ACODEC_AC3 ) + { + buf = hb_buffer_init( sync->ac3_size ); + buf->start = sync->count_frames * 90000 / sync->audio->rate; + buf->stop = buf->start + 90000 * AC3_SAMPLES_PER_FRAME / + sync->audio->rate; + memcpy( buf->data, sync->ac3_buf, buf->size ); + + hb_log( "sync: adding a silent AC-3 frame for track %x", + sync->audio->id ); + hb_fifo_push( sync->audio->fifo_out, buf ); + + sync->count_frames += AC3_SAMPLES_PER_FRAME; + + } + else + { + buf = hb_buffer_init( 2 * job->arate / 20 * + sizeof( float ) ); + buf->start = sync->count_frames * 90000 / job->arate; + buf->stop = buf->start + 90000 / 20; + memset( buf->data, 0, buf->size ); + + hb_log( "sync: adding 50 ms of silence for track %x", + sync->audio->id ); + hb_fifo_push( sync->audio->fifo_sync, buf ); + + sync->count_frames += job->arate / 20; + } +} + +static void UpdateState( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + hb_state_t state; + + if( !pv->count_frames ) + { + pv->st_first = hb_get_date(); + } + pv->count_frames++; + + if( hb_get_date() > pv->st_dates[3] + 1000 ) + { + memmove( &pv->st_dates[0], &pv->st_dates[1], + 3 * sizeof( uint64_t ) ); + memmove( &pv->st_counts[0], &pv->st_counts[1], + 3 * sizeof( uint64_t ) ); + pv->st_dates[3] = hb_get_date(); + pv->st_counts[3] = pv->count_frames; + } + +#define p state.param.working + state.state = HB_STATE_WORKING; + p.progress = (float) pv->count_frames / (float) pv->count_frames_max; + if( p.progress > 1.0 ) + { + p.progress = 1.0; + } + p.rate_cur = 1000.0 * + (float) ( pv->st_counts[3] - pv->st_counts[0] ) / + (float) ( pv->st_dates[3] - pv->st_dates[0] ); + if( hb_get_date() > pv->st_first + 4000 ) + { + int eta; + p.rate_avg = 1000.0 * (float) pv->st_counts[3] / + (float) ( pv->st_dates[3] - pv->st_first ); + eta = (float) ( pv->count_frames_max - pv->st_counts[3] ) / + p.rate_avg; + p.hours = eta / 3600; + p.minutes = ( eta % 3600 ) / 60; + p.seconds = eta % 60; + } + else + { + p.rate_avg = 0.0; + p.hours = -1; + p.minutes = -1; + p.seconds = -1; + } +#undef p + + hb_set_state( pv->job->h, &state ); +} diff --git a/libmediafork/update.c b/libmediafork/update.c new file mode 100644 index 000000000..c12204fcc --- /dev/null +++ b/libmediafork/update.c @@ -0,0 +1,157 @@ +/* $Id: update.c,v 1.7 2005/03/26 23:04:14 titer Exp $ + + 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 "mediafork.h" + +#define HB_URL "handbrake.m0k.org" +#define HB_QUERY "GET /LATEST HTTP/1.0\r\nHost: " HB_URL "\r\n\r\n" + +typedef struct +{ + int * build; + char * version; + +} hb_update_t; + +static void UpdateFunc( void * ); + +hb_thread_t * hb_update_init( int * build, char * version ) +{ + hb_update_t * data = calloc( sizeof( hb_update_t ), 1 ); + data->build = build; + data->version = version; + + return hb_thread_init( "update", UpdateFunc, data, + HB_NORMAL_PRIORITY ); +} + +static void UpdateFunc( void * _data ) +{ + hb_update_t * data = (hb_update_t *) _data; + + hb_net_t * net; + int ret; + char buf[1024]; + char * cur, * end, * p; + int size; + int stable, unstable; + char stable_str[16], unstable_str[16]; + int i; + + if( !( net = hb_net_open( HB_URL, 80 ) ) ) + { + goto error; + } + + if( hb_net_send( net, HB_QUERY ) < 0 ) + { + hb_net_close( &net ); + goto error; + } + + size = 0; + memset( buf, 0, 1024 ); + for( ;; ) + { + ret = hb_net_recv( net, &buf[size], sizeof( buf ) - size ); + if( ret < 1 ) + { + hb_net_close( &net ); + break; + } + size += ret; + } + + cur = buf; + end = &buf[sizeof( buf )]; + + /* Make sure we got it */ + cur += 9; + if( size < 15 || strncmp( cur, "200 OK", 6 ) ) + { + /* Something went wrong */ + goto error; + } + cur += 6; + + /* Find the end of the headers and the beginning of the content */ + for( ; &cur[3] < end; cur++ ) + { + if( cur[0] == '\r' && cur[1] == '\n' && + cur[2] == '\r' && cur[3] == '\n' ) + { + cur += 4; + break; + } + } + + if( cur >= end ) + { + goto error; + } + + stable = strtol( cur, &p, 10 ); + if( cur == p ) + { + goto error; + } + cur = p + 1; + memset( stable_str, 0, sizeof( stable_str ) ); + for( i = 0; + i < sizeof( stable_str ) - 1 && cur < end && *cur != '\n'; + i++, cur++ ) + { + stable_str[i] = *cur; + } + + hb_log( "latest stable: %s, build %d", stable_str, stable ); + + cur++; + if( cur >= end ) + { + goto error; + } + + unstable = strtol( cur, &p, 10 ); + if( cur == p ) + { + goto error; + } + cur = p + 1; + memset( unstable_str, 0, sizeof( unstable_str ) ); + for( i = 0; + i < sizeof( unstable_str ) - 1 && cur < end && *cur != '\n'; + i++, cur++ ) + { + unstable_str[i] = *cur; + } + + hb_log( "latest unstable: %s, build %d", unstable_str, unstable ); + + if( HB_BUILD % 100 ) + { + /* We are runnning an unstable build */ + if( unstable > HB_BUILD ) + { + memcpy( data->version, unstable_str, sizeof( unstable_str ) ); + *(data->build) = unstable; + } + } + else + { + /* We are runnning an stable build */ + if( stable > HB_BUILD ) + { + memcpy( data->version, stable_str, sizeof( stable_str ) ); + *(data->build) = stable; + } + } + +error: + free( data ); + return; +} + diff --git a/libmediafork/work.c b/libmediafork/work.c new file mode 100644 index 000000000..b69275418 --- /dev/null +++ b/libmediafork/work.c @@ -0,0 +1,360 @@ +/* $Id: work.c,v 1.43 2005/03/17 16:38:49 titer Exp $ + + 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 "mediafork.h" + +typedef struct +{ + hb_list_t * jobs; + int cpu_count; + int * error; + volatile int * die; + +} hb_work_t; + +static void work_func(); +static void do_job( hb_job_t *, int cpu_count ); +static void work_loop( void * ); + +/** + * Allocates work object and launches work thread with work_func. + * @param jobs Handle to hb_list_t. + * @param cpu_count Humber of CPUs found in system. + * @param die Handle to user inititated exit indicator. + * @param error Handle to error indicator. + */ +hb_thread_t * hb_work_init( hb_list_t * jobs, int cpu_count, + volatile int * die, int * error ) +{ + hb_work_t * work = calloc( sizeof( hb_work_t ), 1 ); + + work->jobs = jobs; + work->cpu_count = cpu_count; + work->die = die; + work->error = error; + + return hb_thread_init( "work", work_func, work, HB_LOW_PRIORITY ); +} + +/** + * Iterates through job list and calls do_job for each job. + * @param _work Handle work object. + */ +static void work_func( void * _work ) +{ + hb_work_t * work = _work; + hb_job_t * job; + + hb_log( "%d job(s) to process", hb_list_count( work->jobs ) ); + + while( !*work->die && ( job = hb_list_item( work->jobs, 0 ) ) ) + { + hb_list_rem( work->jobs, job ); + job->die = work->die; + do_job( job, work->cpu_count ); + } + + *(work->error) = HB_ERROR_NONE; + + free( work ); +} + +static hb_work_object_t * getWork( int id ) +{ + hb_work_object_t * w; + for( w = hb_objects; w; w = w->next ) + { + if( w->id == id ) + { + return w; + } + } + return NULL; +} + +/** + * Job initialization rountine. + * Initializes fifos. + * Creates work objects for synchronizer, video decoder, video renderer, video decoder, audio decoder, audio encoder, reader, muxer. + * Launches thread for each work object with work_loop. + * Loops while monitoring status of work threads and fifos. + * Exits loop when conversion is done and fifos are empty. + * Closes threads and frees fifos. + * @param job Handle work hb_job_t. + * @param cpu_count number of CPUs found in system. + */ +static void do_job( hb_job_t * job, int cpu_count ) +{ + hb_title_t * title; + int i; + hb_work_object_t * w; + hb_audio_t * audio; + hb_subtitle_t * subtitle; + int done; + + title = job->title; + + job->list_work = hb_list_init(); + + hb_log( "starting job" ); + hb_log( " + device %s", title->dvd ); + hb_log( " + title %d, chapter(s) %d to %d", title->index, + job->chapter_start, job->chapter_end ); + if ( job->pixel_ratio == 1 ) + { + /* Correct the geometry of the output movie when using PixelRatio */ + job->height=title->height-job->crop[0]-job->crop[1]; + job->width=title->width-job->crop[2]-job->crop[3]; + } + else + { + hb_fix_aspect( job, HB_KEEP_WIDTH ); + } + hb_log( " + %dx%d -> %dx%d, crop %d/%d/%d/%d", + title->width, title->height, job->width, job->height, + job->crop[0], job->crop[1], job->crop[2], job->crop[3] ); + hb_log( " + deinterlace %s", job->deinterlace ? "on" : "off" ); + hb_log( " + grayscale %s", job->grayscale ? "on" : "off" ); + if( job->vquality >= 0.0 && job->vquality <= 1.0 ) + { + hb_log( " + %.3f fps, video quality %.2f", (float) job->vrate / + (float) job->vrate_base, job->vquality ); + } + else + { + hb_log( " + %.3f fps, video bitrate %d kbps, pass %d", + (float) job->vrate / (float) job->vrate_base, + job->vbitrate, job->pass ); + } + hb_log (" + PixelRatio: %d, width:%d, height: %d",job->pixel_ratio,job->width, job->height); + job->fifo_mpeg2 = hb_fifo_init( 2048 ); + job->fifo_raw = hb_fifo_init( 8 ); + job->fifo_sync = hb_fifo_init( 8 ); + job->fifo_render = hb_fifo_init( 8 ); + job->fifo_mpeg4 = hb_fifo_init( 8 ); + + /* Synchronization */ + hb_list_add( job->list_work, ( w = getWork( WORK_SYNC ) ) ); + w->fifo_in = NULL; + w->fifo_out = NULL; + + /* Video decoder */ + hb_list_add( job->list_work, ( w = getWork( WORK_DECMPEG2 ) ) ); + w->fifo_in = job->fifo_mpeg2; + w->fifo_out = job->fifo_raw; + + /* Video renderer */ + hb_list_add( job->list_work, ( w = getWork( WORK_RENDER ) ) ); + w->fifo_in = job->fifo_sync; + w->fifo_out = job->fifo_render; + + /* Video encoder */ + switch( job->vcodec ) + { + case HB_VCODEC_FFMPEG: + hb_log( " + encoder FFmpeg" ); + w = getWork( WORK_ENCAVCODEC ); + break; + case HB_VCODEC_XVID: + hb_log( " + encoder XviD" ); + w = getWork( WORK_ENCXVID ); + break; + case HB_VCODEC_X264: + hb_log( " + encoder x264" ); + w = getWork( WORK_ENCX264 ); + break; + } + w->fifo_in = job->fifo_render; + w->fifo_out = job->fifo_mpeg4; + w->config = &job->config; + hb_list_add( job->list_work, w ); + + subtitle = hb_list_item( title->list_subtitle, 0 ); + if( subtitle ) + { + hb_log( " + subtitle %x, %s", subtitle->id, subtitle->lang ); + + subtitle->fifo_in = hb_fifo_init( 8 ); + subtitle->fifo_raw = hb_fifo_init( 8 ); + + hb_list_add( job->list_work, ( w = getWork( WORK_DECSUB ) ) ); + w->fifo_in = subtitle->fifo_in; + w->fifo_out = subtitle->fifo_raw; + } + + if( job->acodec & HB_ACODEC_AC3 ) + { + hb_log( " + audio AC3 passthrough" ); + } + else + { + hb_log( " + audio %d kbps, %d Hz", job->abitrate, job->arate ); + hb_log( " + encoder %s", ( job->acodec & HB_ACODEC_FAAC ) ? + "faac" : ( ( job->acodec & HB_ACODEC_LAME ) ? "lame" : + "vorbis" ) ); + } + + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + hb_log( " + %x, %s", audio->id, audio->lang ); + + audio->fifo_in = hb_fifo_init( 2048 ); + audio->fifo_raw = hb_fifo_init( 8 ); + audio->fifo_sync = hb_fifo_init( 8 ); + audio->fifo_out = hb_fifo_init( 8 ); + + switch( audio->codec ) + { + case HB_ACODEC_AC3: + w = getWork( WORK_DECA52 ); + break; + case HB_ACODEC_MPGA: + w = getWork( WORK_DECAVCODEC ); + break; + case HB_ACODEC_LPCM: + w = getWork( WORK_DECLPCM ); + break; + } + w->fifo_in = audio->fifo_in; + w->fifo_out = audio->fifo_raw; + hb_list_add( job->list_work, w ); + + switch( job->acodec ) + { + case HB_ACODEC_FAAC: + w = getWork( WORK_ENCFAAC ); + break; + case HB_ACODEC_LAME: + w = getWork( WORK_ENCLAME ); + break; + case HB_ACODEC_VORBIS: + w = getWork( WORK_ENCVORBIS ); + break; + } + if( job->acodec != HB_ACODEC_AC3 ) + { + w->fifo_in = audio->fifo_sync; + w->fifo_out = audio->fifo_out; + w->config = &audio->config; + hb_list_add( job->list_work, w ); + } + } + + /* Init read & write threads */ + job->reader = hb_reader_init( job ); + + hb_log( " + output: %s", job->file ); + job->muxer = hb_muxer_init( job ); + + job->done = 0; + + /* Launch processing threads */ + for( i = 1; i < hb_list_count( job->list_work ); i++ ) + { + w = hb_list_item( job->list_work, i ); + w->done = &job->done; + w->thread_sleep_interval = 10; + w->init( w, job ); + w->thread = hb_thread_init( w->name, work_loop, w, + HB_LOW_PRIORITY ); + } + + done = 0; + w = hb_list_item( job->list_work, 0 ); + w->thread_sleep_interval = 50; + w->init( w, job ); + while( !*job->die ) + { + if( w->work( w, NULL, NULL ) == HB_WORK_DONE ) + { + done = 1; + } + if( done && + !hb_fifo_size( job->fifo_sync ) && + !hb_fifo_size( job->fifo_render ) && + hb_fifo_size( job->fifo_mpeg4 ) < 2 ) + { + break; + } + hb_snooze( w->thread_sleep_interval ); + } + hb_list_rem( job->list_work, w ); + w->close( w ); + job->done = 1; + + /* Close work objects */ + while( ( w = hb_list_item( job->list_work, 0 ) ) ) + { + hb_list_rem( job->list_work, w ); + hb_thread_close( &w->thread ); + w->close( w ); + } + + /* Stop read & write threads */ + hb_thread_close( &job->reader ); + hb_thread_close( &job->muxer ); + + /* Close fifos */ + hb_fifo_close( &job->fifo_mpeg2 ); + hb_fifo_close( &job->fifo_raw ); + hb_fifo_close( &job->fifo_sync ); + hb_fifo_close( &job->fifo_render ); + hb_fifo_close( &job->fifo_mpeg4 ); + if( subtitle ) + { + hb_fifo_close( &subtitle->fifo_in ); + hb_fifo_close( &subtitle->fifo_raw ); + } + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + hb_fifo_close( &audio->fifo_in ); + hb_fifo_close( &audio->fifo_raw ); + hb_fifo_close( &audio->fifo_sync ); + hb_fifo_close( &audio->fifo_out ); + } +} + +/** + * Performs the work objects specific work function. + * Loops calling work function for associated work object. Sleeps when fifo is full. + * Monitors work done indicator. + * Exits loop when work indiactor is set. + * @param _w Handle to work object. + */ +static void work_loop( void * _w ) +{ + hb_work_object_t * w = _w; + hb_buffer_t * buf_in, * buf_out; + + while( !*w->done ) + { +#if 0 + hb_lock( job->pause ); + hb_unlock( job->pause ); +#endif + if( hb_fifo_is_full( w->fifo_out ) || +// if( (hb_fifo_percent_full( w->fifo_out ) > 0.8) || + !( buf_in = hb_fifo_get( w->fifo_in ) ) ) + { + hb_snooze( w->thread_sleep_interval ); +// w->thread_sleep_interval += 1; + continue; + } +// w->thread_sleep_interval = MAX(1, (w->thread_sleep_interval - 1)); + + w->work( w, &buf_in, &buf_out ); + if( buf_in ) + { + hb_buffer_close( &buf_in ); + } + if( buf_out ) + { + hb_fifo_push( w->fifo_out, buf_out ); + } + } +} |