diff options
Diffstat (limited to 'libhb')
-rw-r--r-- | libhb/Jamfile | 18 | ||||
-rw-r--r-- | libhb/common.c | 502 | ||||
-rw-r--r-- | libhb/common.h | 349 | ||||
-rw-r--r-- | libhb/deca52.c | 200 | ||||
-rw-r--r-- | libhb/decavcodec.c | 137 | ||||
-rw-r--r-- | libhb/declpcm.c | 113 | ||||
-rw-r--r-- | libhb/decmpeg2.c | 253 | ||||
-rw-r--r-- | libhb/decsub.c | 420 | ||||
-rw-r--r-- | libhb/demuxmpeg.c | 125 | ||||
-rw-r--r-- | libhb/dvd.c | 735 | ||||
-rw-r--r-- | libhb/encavcodec.c | 201 | ||||
-rw-r--r-- | libhb/encfaac.c | 166 | ||||
-rw-r--r-- | libhb/enclame.c | 154 | ||||
-rw-r--r-- | libhb/encvorbis.c | 205 | ||||
-rw-r--r-- | libhb/encx264.c | 221 | ||||
-rw-r--r-- | libhb/encxvid.c | 222 | ||||
-rw-r--r-- | libhb/fifo.c | 180 | ||||
-rw-r--r-- | libhb/hb.c | 514 | ||||
-rw-r--r-- | libhb/hb.h | 76 | ||||
-rw-r--r-- | libhb/internal.h | 172 | ||||
-rw-r--r-- | libhb/lang.h | 209 | ||||
-rw-r--r-- | libhb/muxavi.c | 551 | ||||
-rw-r--r-- | libhb/muxcommon.c | 215 | ||||
-rw-r--r-- | libhb/muxmp4.c | 160 | ||||
-rw-r--r-- | libhb/muxogm.c | 364 | ||||
-rw-r--r-- | libhb/ports.c | 593 | ||||
-rw-r--r-- | libhb/ports.h | 86 | ||||
-rw-r--r-- | libhb/reader.c | 155 | ||||
-rw-r--r-- | libhb/render.c | 167 | ||||
-rw-r--r-- | libhb/scan.c | 491 | ||||
-rw-r--r-- | libhb/sync.c | 664 | ||||
-rw-r--r-- | libhb/update.c | 157 | ||||
-rw-r--r-- | libhb/work.c | 400 |
33 files changed, 9175 insertions, 0 deletions
diff --git a/libhb/Jamfile b/libhb/Jamfile new file mode 100644 index 000000000..29f9c7774 --- /dev/null +++ b/libhb/Jamfile @@ -0,0 +1,18 @@ +# $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 libhb ; + +LIBHB_SRC = +common.c hb.c ports.c scan.c work.c decmpeg2.c encavcodec.c update.c +demuxmpeg.c fifo.c render.c reader.c muxcommon.c 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 libhb : $(LIBHB_SRC) ; + +ObjectCcFlags $(LIBHB_SRC) : -I$(TOP)/contrib/include ; +ObjectDefines $(LIBHB_SRC) : __LIBHB__ ; diff --git a/libhb/common.c b/libhb/common.c new file mode 100644 index 000000000..7bd125c84 --- /dev/null +++ b/libhb/common.c @@ -0,0 +1,502 @@ +/* $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[] = +{ { "22050", 22050 }, { "24000", 24000 }, + { "44100", 44100 }, { "48000", 48000 } }; +int hb_audio_rates_count = sizeof( hb_audio_rates ) / + sizeof( hb_rate_t ); +int hb_audio_rates_default = 2; /* 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_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: + 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/libhb/common.h b/libhb/common.h new file mode 100644 index 000000000..402ae7f29 --- /dev/null +++ b/libhb/common.h @@ -0,0 +1,349 @@ +/* $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 <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; + +#include "ports.h" +#ifdef __LIBHB__ +#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 ** ); + +#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 */ + int crop[4]; + int deinterlace; + int width; + int height; + int keep_ratio; + int grayscale; + + /* 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; + + /* 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_AVI 0x020000 +#define HB_MUX_OGM 0x040000 + int mux; + char * file; + +#ifdef __LIBHB__ + /* 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; + + union + { + struct + { + uint8_t * config; + int config_length; + } mpeg4; + + struct + { + uint8_t * sps; + int sps_length; + uint8_t * pps; + int pps_length; + } h264; + + } config; + + /* MPEG-4 / AVC */ + uint8_t * es_config; + int es_config_length; + + hb_mux_data_t * mux_data; +#endif +}; + +struct hb_audio_s +{ + int id; + char lang[1024]; + int codec; + int rate; + int bitrate; + int channels; + +#ifdef __LIBHB__ + /* 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 */ + + union + { + struct + { + uint8_t * decinfo; + unsigned long size; + } faac; + + struct + { + uint8_t * headers[3]; + int sizes[3]; + } vorbis; + + } 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 __LIBHB__ + /* Internal data */ + hb_fifo_t * fifo_in; /* SPU ES */ + hb_fifo_t * fifo_raw; /* Decodec SPU */ +#endif +}; + +struct hb_title_s +{ + char dvd[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 + 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; + + } param; +}; + +#endif diff --git a/libhb/deca52.c b/libhb/deca52.c new file mode 100644 index 000000000..a4f7492b3 --- /dev/null +++ b/libhb/deca52.c @@ -0,0 +1,200 @@ +/* $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 "hb.h" + +#include "a52dec/a52.h" + +struct hb_work_object_s +{ + HB_WORK_COMMON; + + hb_job_t * job; + hb_audio_t * audio; + + /* 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; +}; + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static void Close( hb_work_object_t ** _w ); +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); +static hb_buffer_t * Decode( hb_work_object_t * w ); + +/*********************************************************************** + * hb_work_deca52_init + *********************************************************************** + * Allocate the work object, initialize liba52 + **********************************************************************/ +hb_work_object_t * hb_work_deca52_init( hb_job_t * job, hb_audio_t * audio ) +{ + hb_work_object_t * w = calloc( sizeof( hb_work_object_t ), 1 ); + w->name = strdup( "AC3 decoder" ); + w->work = Work; + w->close = Close; + + w->job = job; + w->audio = audio; + + w->list = hb_list_init(); + w->state = a52_init( 0 ); + w->flags_out = A52_STEREO; + w->level = 32768.0; + + return w; +} + +/*********************************************************************** + * Close + *********************************************************************** + * Free memory + **********************************************************************/ +static void Close( hb_work_object_t ** _w ) +{ + hb_work_object_t * w = *_w; + a52_free( w->state ); + free( w->name ); + free( w ); + *_w = NULL; +} + +/*********************************************************************** + * Work + *********************************************************************** + * Add the given buffer to the data we already have, and decode as much + * as we can + **********************************************************************/ +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_buffer_t * buf; + + hb_list_add( w->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_buffer_t * buf; + int i, j; + uint64_t pts; + int pos; + + /* Get a frame header if don't have one yet */ + if( !w->sync ) + { + while( hb_list_bytes( w->list ) >= 7 ) + { + /* We have 7 bytes, check if this is a correct header */ + hb_list_seebytes( w->list, w->frame, 7 ); + w->size = a52_syncinfo( w->frame, &w->flags_in, &w->rate, + &w->bitrate ); + if( w->size ) + { + /* It is. W00t. */ + if( w->error ) + { + hb_log( "a52_syncinfo ok" ); + } + w->error = 0; + w->sync = 1; + break; + } + + /* It is not */ + if( !w->error ) + { + hb_log( "a52_syncinfo failed" ); + w->error = 1; + } + + /* Try one byte later */ + hb_list_getbytes( w->list, w->frame, 1, NULL, NULL ); + } + } + + if( !w->sync || + hb_list_bytes( w->list ) < w->size ) + { + /* Need more data */ + return NULL; + } + + /* Get the whole frame */ + hb_list_getbytes( w->list, w->frame, w->size, &pts, &pos ); + + /* AC3 passthrough: don't decode the AC3 frame */ + if( w->job->acodec & HB_ACODEC_AC3 ) + { + buf = hb_buffer_init( w->size ); + memcpy( buf->data, w->frame, w->size ); + buf->start = pts + ( pos / w->size ) * 6 * 256 * 90000 / w->rate; + buf->stop = buf->start + 6 * 256 * 90000 / w->rate; + w->sync = 0; + return buf; + } + + /* Feed liba52 */ + a52_frame( w->state, w->frame, &w->flags_out, &w->level, 0 ); + + /* 6 blocks per frame, 256 samples per block, 2 channels */ + buf = hb_buffer_init( 3072 * sizeof( float ) ); + buf->start = pts + ( pos / w->size ) * 6 * 256 * 90000 / w->rate; + buf->stop = buf->start + 6 * 256 * 90000 / w->rate; + + for( i = 0; i < 6; i++ ) + { + sample_t * samples_in; + float * samples_out; + + a52_block( w->state ); + samples_in = a52_samples( w->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]; + } + } + + w->sync = 0; + return buf; +} + diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c new file mode 100644 index 000000000..f18579f36 --- /dev/null +++ b/libhb/decavcodec.c @@ -0,0 +1,137 @@ +/* $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 "hb.h" + +#include "ffmpeg/avcodec.h" + +struct hb_work_object_s +{ + HB_WORK_COMMON; + + hb_job_t * job; + hb_audio_t * audio; + + AVCodecContext * context; + int64_t pts_last; +}; + + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); +static void Close( hb_work_object_t ** _w ); + +/*********************************************************************** + * hb_work_decavcodec_init + *********************************************************************** + * + **********************************************************************/ +hb_work_object_t * hb_work_decavcodec_init( hb_job_t * job, + hb_audio_t * audio ) +{ + hb_work_object_t * w = calloc( sizeof( hb_work_object_t ), 1 ); + AVCodec * codec; + w->name = strdup( "MPGA decoder (libavcodec)" ); + w->work = Work; + w->close = Close; + + w->job = job; + w->audio = audio; + + codec = avcodec_find_decoder( CODEC_ID_MP2 ); + w->context = avcodec_alloc_context(); + avcodec_open( w->context, codec ); + w->pts_last = -1; + + return w; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +static void Close( hb_work_object_t ** _w ) +{ + hb_work_object_t * w = *_w; + avcodec_close( w->context ); + free( w->name ); + free( w ); + *_w = NULL; +} + +/*********************************************************************** + * Work + *********************************************************************** + * + **********************************************************************/ +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + 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 || + ( w->pts_last > 0 && + in->start > w->pts_last && + in->start - w->pts_last < 5000 ) ) /* Hacky */ + { + cur = w->pts_last; + } + else + { + cur = in->start; + } + + pos = 0; + while( pos < in->size ) + { + len = avcodec_decode_audio( w->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 ) / + w->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; + } + + w->pts_last = cur; + + return HB_WORK_OK; +} + diff --git a/libhb/declpcm.c b/libhb/declpcm.c new file mode 100644 index 000000000..59163c366 --- /dev/null +++ b/libhb/declpcm.c @@ -0,0 +1,113 @@ +/* $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 "hb.h" + +struct hb_work_object_s +{ + HB_WORK_COMMON; + + hb_job_t * job; + hb_audio_t * audio; + + int64_t pts_last; +}; + +static int Work( 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; + if( w->pts_last > 0 && + in->start < w->pts_last + duration / 6 && + in->start > w->pts_last - duration / 6 ) + { + /* Workaround for DVDs where dates aren't exact */ + out->start = w->pts_last; + } + else + { + out->start = in->start; + } + out->stop = out->start + duration; + w->pts_last = out->stop; + + 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; +} + +static void Close( hb_work_object_t ** _w ) +{ + hb_work_object_t * w = *_w; + free( w->name ); + free( w ); + *_w = NULL; +} + +hb_work_object_t * hb_work_declpcm_init( hb_job_t * job, hb_audio_t * audio ) +{ + hb_work_object_t * w = calloc( sizeof( hb_work_object_t ), 1 ); + w->name = strdup( "LPCM decoder" ); + w->work = Work; + w->close = Close; + + w->job = job; + w->audio = audio; + + w->pts_last = -1; + + return w; +} + diff --git a/libhb/decmpeg2.c b/libhb/decmpeg2.c new file mode 100644 index 000000000..d914c03a6 --- /dev/null +++ b/libhb/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 "hb.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_object_s +{ + HB_WORK_COMMON; + + hb_libmpeg2_t * libmpeg2; + hb_list_t * list; +}; + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); +static void Close( hb_work_object_t ** _w ); + +/********************************************************************** + * hb_work_decmpeg2_init + ********************************************************************** + * + *********************************************************************/ +hb_work_object_t * hb_work_decmpeg2_init( hb_job_t * job ) +{ + hb_work_object_t * w = calloc( sizeof( hb_work_object_t ), 1 ); + w->name = strdup( "MPEG-2 decoder (libmpeg2)" ); + w->work = Work; + w->close = Close; + + w->libmpeg2 = hb_libmpeg2_init(); + w->list = hb_list_init(); + return w; +} + +/********************************************************************** + * Work + ********************************************************************** + * + *********************************************************************/ +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_buffer_t * buf, * last = NULL; + + hb_libmpeg2_decode( w->libmpeg2, *buf_in, w->list ); + + *buf_out = NULL; + + while( ( buf = hb_list_item( w->list, 0 ) ) ) + { + hb_list_rem( w->list, buf ); + if( last ) + { + last->next = buf; + last = buf; + } + else + { + *buf_out = buf; + last = buf; + } + } + + return HB_WORK_OK; +} + +/********************************************************************** + * Close + ********************************************************************** + * + *********************************************************************/ +static void Close( hb_work_object_t ** _w ) +{ + hb_work_object_t * w = *_w; + hb_list_close( &w->list ); + hb_libmpeg2_close( &w->libmpeg2 ); + free( w->name ); + free( w ); + *_w = NULL; +} diff --git a/libhb/decsub.c b/libhb/decsub.c new file mode 100644 index 000000000..72f742bd9 --- /dev/null +++ b/libhb/decsub.c @@ -0,0 +1,420 @@ +/* $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 "hb.h" + +struct hb_work_object_s +{ + HB_WORK_COMMON; + + 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]; +}; + + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static void Close( hb_work_object_t ** _w ); +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); +static hb_buffer_t * Decode( hb_work_object_t * w ); +static void ParseControls( hb_work_object_t * w ); +static hb_buffer_t * CropSubtitle( hb_work_object_t * w, + uint8_t * raw ); + +/*********************************************************************** + * hb_work_decsub_init + *********************************************************************** + * + **********************************************************************/ +hb_work_object_t * hb_work_decsub_init( hb_job_t * job ) +{ + hb_work_object_t * w = calloc( sizeof( hb_work_object_t ), 1 ); + w->name = strdup( "Subtitle decoder" ); + w->work = Work; + w->close = Close; + + w->job = job; + w->pts = -1; + + return w; +} + +/*********************************************************************** + * Close + *********************************************************************** + * Free memory + **********************************************************************/ +static void Close( hb_work_object_t ** _w ) +{ + hb_work_object_t * w = *_w; + free( w->name ); + free( w ); + *_w = NULL; +} + +/*********************************************************************** + * Work + *********************************************************************** + * + **********************************************************************/ +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + 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( !w->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 */ + w->size_sub = size_sub; + w->size_rle = size_rle; + + memcpy( w->buf, in->data, in->size ); + w->size_got = in->size; + w->pts = in->start; + } + } + else + { + /* We are waiting for the end of the current subtitle */ + if( in->size <= w->size_sub - w->size_got ) + { + memcpy( w->buf + w->size_got, in->data, in->size ); + w->size_got += in->size; + if( in->start >= 0 ) + { + w->pts = in->start; + } + } + } + + *buf_out = NULL; + + if( w->size_sub && w->size_sub == w->size_got ) + { + /* We got a complete subtitle, decode it */ + *buf_out = Decode( w ); + + /* Wait for the next one */ + w->size_sub = 0; + w->size_got = 0; + w->size_rle = 0; + w->pts = -1; + } + + return HB_WORK_OK; +} + +static hb_buffer_t * Decode( hb_work_object_t * w ) +{ + 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( w->width * w->height * 2 ); + +#define GET_NEXT_NIBBLE code = ( code << 4 ) | ( ( ( *offset & 1 ) ? \ +( w->buf[((*offset)>>1)] & 0xF ) : ( w->buf[((*offset)>>1)] >> 4 ) ) ); \ +(*offset)++ + + offsets[0] = w->offsets[0] * 2; + offsets[1] = w->offsets[1] * 2; + + for( line = 0; line < w->height; line++ ) + { + /* Select even or odd field */ + offset = ( line & 1 ) ? &offsets[1] : &offsets[0]; + + for( col = 0; col < w->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 |= ( w->width - col ) << 2; + } + } + } + } + + lum = buf_raw; + alpha = lum + w->width * w->height; + memset( lum + line * w->width + col, + w->lum[code & 3], code >> 2 ); + memset( alpha + line * w->width + col, + w->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; +} + +/*********************************************************************** + * 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_job_t * job = w->job; + hb_title_t * title = job->title; + + int i; + int command; + int date, next; + + for( i = w->size_rle; ; ) + { + date = ( w->buf[i] << 8 ) | w->buf[i+1]; i += 2; + next = ( w->buf[i] << 8 ) | w->buf[i+1]; i += 2; + + for( ;; ) + { + command = w->buf[i++]; + + if( command == 0xFF ) + { + break; + } + + switch( command ) + { + case 0x00: + break; + + case 0x01: + w->pts_start = w->pts + date * 900; + break; + + case 0x02: + w->pts_stop = w->pts + date * 900; + break; + + case 0x03: + { + int colors[4]; + int j; + + colors[0] = (w->buf[i+0]>>4)&0x0f; + colors[1] = (w->buf[i+0])&0x0f; + colors[2] = (w->buf[i+1]>>4)&0x0f; + colors[3] = (w->buf[i+1])&0x0f; + + for( j = 0; j < 4; j++ ) + { + uint32_t color = title->palette[colors[j]]; + w->lum[3-j] = (color>>16) & 0xff; + } + i += 2; + break; + } + case 0x04: + { + w->alpha[3] = (w->buf[i+0]>>4)&0x0f; + w->alpha[2] = (w->buf[i+0])&0x0f; + w->alpha[1] = (w->buf[i+1]>>4)&0x0f; + w->alpha[0] = (w->buf[i+1])&0x0f; + i += 2; + break; + } + case 0x05: + { + w->x = (w->buf[i+0]<<4) | ((w->buf[i+1]>>4)&0x0f); + w->width = (((w->buf[i+1]&0x0f)<<8)| w->buf[i+2]) - w->x + 1; + w->y = (w->buf[i+3]<<4)| ((w->buf[i+4]>>4)&0x0f); + w->height = (((w->buf[i+4]&0x0f)<<8)| w->buf[i+5]) - w->y + 1; + i += 6; + break; + } + case 0x06: + { + w->offsets[0] = ( w->buf[i] << 8 ) | w->buf[i+1]; i += 2; + w->offsets[1] = ( w->buf[i] << 8 ) | w->buf[i+1]; i += 2; + break; + } + } + } + + if( i > next ) + { + break; + } + i = next; + } +} + +/*********************************************************************** + * 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 ) +{ + int i; + for( i = 0; i < w->width; i++ ) + { + if( p[i] ) + { + return 0; + } + } + return 1; +} +static int ColumnIsTransparent( hb_work_object_t * w, uint8_t * p ) +{ + int i; + for( i = 0; i < w->height; i++ ) + { + if( p[i*w->width] ) + { + return 0; + } + } + return 1; +} +static hb_buffer_t * CropSubtitle( hb_work_object_t * w, uint8_t * raw ) +{ + 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 + w->width * w->height; + + /* Top */ + for( i = 0; i < w->height; i++ ) + { + if( !LineIsTransparent( w, &alpha[i*w->width] ) ) + { + crop[0] = i; + break; + } + } + + if( crop[0] < 0 ) + { + /* Empty subtitle */ + return NULL; + } + + /* Bottom */ + for( i = w->height - 1; i >= 0; i-- ) + { + if( !LineIsTransparent( w, &alpha[i*w->width] ) ) + { + crop[1] = i; + break; + } + } + + /* Left */ + for( i = 0; i < w->width; i++ ) + { + if( !ColumnIsTransparent( w, &alpha[i] ) ) + { + crop[2] = i; + break; + } + } + + /* Right */ + for( i = w->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 = w->pts_start; + buf->stop = w->pts_stop; + buf->x = w->x + crop[2]; + buf->y = w->y + crop[0]; + buf->width = realwidth; + buf->height = realheight; + + lum_in = raw + crop[0] * w->width + crop[2]; + alpha_in = lum_in + w->width * w->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 += w->width; + alpha_in += w->width; + lum_out += realwidth; + alpha_out += realwidth; + } + + return buf; +} diff --git a/libhb/demuxmpeg.c b/libhb/demuxmpeg.c new file mode 100644 index 000000000..721478e80 --- /dev/null +++ b/libhb/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 "hb.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/libhb/dvd.c b/libhb/dvd.c new file mode 100644 index 000000000..9b2aef13b --- /dev/null +++ b/libhb/dvd.c @@ -0,0 +1,735 @@ +/* $Id: dvd.c,v 1.11 2005/11/04 15:30: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 "hb.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 * ); + +/*********************************************************************** + * 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; + + title = hb_title_init( d->path, t ); + + hb_log( "scan: scanning title %d", t ); + + /* 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" ) ); + + 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_seek + *********************************************************************** + * + **********************************************************************/ +int hb_dvd_seek( hb_dvd_t * d, float f ) +{ + int target; + + target = d->title_start + (int) ( f * d->title_block_count ); + + /* Find the cell we shall start in */ + d->cell_cur = d->cell_start; + FindNextCell( d ); + for( ;; ) + { + if( target < d->pgc->cell_playback[d->cell_cur].last_sector ) + { + break; + } + d->cell_cur = d->cell_next; + FindNextCell( d ); + } + + /* Now let hb_dvd_read find the next VOBU */ + d->next_vobu = target; + d->pack_len = 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->ifo ) ifoClose( d->ifo ); + if( d->vmg ) ifoClose( d->vmg ); + if( d->file ) DVDCloseFile( d->file ); + 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/libhb/encavcodec.c b/libhb/encavcodec.c new file mode 100644 index 000000000..deff7c527 --- /dev/null +++ b/libhb/encavcodec.c @@ -0,0 +1,201 @@ +/* $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 "hb.h" + +#include "ffmpeg/avcodec.h" + +struct hb_work_object_s +{ + HB_WORK_COMMON; + + hb_job_t * job; + AVCodecContext * context; + FILE * file; +}; + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static void Close( hb_work_object_t ** _w ); +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); + +/*********************************************************************** + * hb_work_encavcodec_init + *********************************************************************** + * + **********************************************************************/ +hb_work_object_t * hb_work_encavcodec_init( hb_job_t * job ) +{ + AVCodec * codec; + AVCodecContext * context; + + hb_work_object_t * w = calloc( sizeof( hb_work_object_t ), 1 ); + w->name = strdup( "MPEG-4 encoder (libavcodec)" ); + w->work = Work; + w->close = Close; + + w->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->mux & HB_MUX_MP4 ) + { + context->flags |= CODEC_FLAG_GLOBAL_HEADER; + } + 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 ) + { + w->file = fopen( filename, "wb" ); + context->flags |= CODEC_FLAG_PASS1; + } + else + { + int size; + char * log; + + w->file = fopen( filename, "rb" ); + fseek( w->file, 0, SEEK_END ); + size = ftell( w->file ); + fseek( w->file, 0, SEEK_SET ); + log = malloc( size + 1 ); + log[size] = '\0'; + fread( log, size, 1, w->file ); + fclose( w->file ); + w->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" ); + } + w->context = context; + + if( ( job->mux & HB_MUX_MP4 ) && job->pass != 1 ) + { +#define c job->config.mpeg4 + /* Hem hem */ + c.config = malloc( 15 ); + c.config_length = 15; + memcpy( c.config, context->extradata + 15, 15 ); +#undef c + } + + return w; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +static void Close( hb_work_object_t ** _w ) +{ + hb_work_object_t * w = *_w; + hb_job_t * job = w->job; + + if( w->context ) + { + hb_log( "encavcodec: closing libavcodec" ); + avcodec_close( w->context ); + } + if( w->file ) + { + fclose( w->file ); + } + if( job->es_config ) + { + free( job->es_config ); + job->es_config = NULL; + job->es_config_length = 0; + } + + free( w->name ); + free( w ); + *_w = NULL; +} + +/*********************************************************************** + * Work + *********************************************************************** + * + **********************************************************************/ +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_job_t * job = w->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( w->context, buf->data, buf->alloc, + frame ); + buf->start = in->start; + buf->stop = in->stop; + buf->key = w->context->coded_frame->key_frame; + + av_free( frame ); + + if( job->pass == 1 ) + { + /* Write stats */ + fprintf( w->file, "%s", w->context->stats_out ); + } + + *buf_out = buf; + + return HB_WORK_OK; +} + + diff --git a/libhb/encfaac.c b/libhb/encfaac.c new file mode 100644 index 000000000..a5b704f4c --- /dev/null +++ b/libhb/encfaac.c @@ -0,0 +1,166 @@ +/* $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 "hb.h" + +#include "faac.h" + +struct hb_work_object_s +{ + HB_WORK_COMMON; + + hb_job_t * job; + hb_audio_t * audio; + + faacEncHandle * faac; + unsigned long input_samples; + unsigned long output_bytes; + uint8_t * buf; + + hb_list_t * list; + int64_t pts; +}; + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static void Close( hb_work_object_t ** _w ); +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); + +/*********************************************************************** + * hb_work_encfaac_init + *********************************************************************** + * + **********************************************************************/ +hb_work_object_t * hb_work_encfaac_init( hb_job_t * job, hb_audio_t * audio ) +{ + hb_work_object_t * w = calloc( sizeof( hb_work_object_t ), 1 ); + faacEncConfigurationPtr cfg; + w->name = strdup( "AAC encoder (libfaac)" ); + w->work = Work; + w->close = Close; + + w->job = job; + w->audio = audio; + + w->faac = faacEncOpen( job->arate, 2, &w->input_samples, + &w->output_bytes ); + w->buf = malloc( w->input_samples * sizeof( float ) ); + + cfg = faacEncGetCurrentConfiguration( w->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( w->faac, cfg ) ) + { + hb_log( "faacEncSetConfiguration failed" ); + } + if( faacEncGetDecoderSpecificInfo( w->faac, &audio->config.faac.decinfo, + &audio->config.faac.size ) < 0 ) + { + hb_log( "faacEncGetDecoderSpecificInfo failed" ); + } + + w->list = hb_list_init(); + w->pts = -1; + + return w; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +static void Close( hb_work_object_t ** _w ) +{ + hb_work_object_t * w = *_w; + + faacEncClose( w->faac ); + free( w->buf ); + hb_list_empty( &w->list ); + free( w->audio->config.faac.decinfo ); + + free( w->name ); + free( w ); + *_w = NULL; +} + +/*********************************************************************** + * Encode + *********************************************************************** + * + **********************************************************************/ +static hb_buffer_t * Encode( hb_work_object_t * w ) +{ + hb_buffer_t * buf; + uint64_t pts; + int pos; + + if( hb_list_bytes( w->list ) < w->input_samples * sizeof( float ) ) + { + /* Need more data */ + return NULL; + } + + hb_list_getbytes( w->list, w->buf, w->input_samples * sizeof( float ), + &pts, &pos ); + + buf = hb_buffer_init( w->output_bytes ); + buf->start = pts + 90000 * pos / 2 / sizeof( float ) / w->job->arate; + buf->stop = buf->start + 90000 * w->input_samples / w->job->arate / 2; + buf->size = faacEncEncode( w->faac, (int32_t *) w->buf, + w->input_samples, buf->data, w->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 + *********************************************************************** + * + **********************************************************************/ +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_buffer_t * buf; + + hb_list_add( w->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/libhb/enclame.c b/libhb/enclame.c new file mode 100644 index 000000000..19014ac26 --- /dev/null +++ b/libhb/enclame.c @@ -0,0 +1,154 @@ +/* $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 "hb.h" + +#include "lame/lame.h" + +struct hb_work_object_s +{ + HB_WORK_COMMON; + + hb_job_t * job; + hb_audio_t * audio; + + /* LAME handle */ + lame_global_flags * lame; + + unsigned long input_samples; + unsigned long output_bytes; + uint8_t * buf; + + hb_list_t * list; + int64_t pts; +}; + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static void Close( hb_work_object_t ** _w ); +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); + +/*********************************************************************** + * hb_work_enclame_init + *********************************************************************** + * + **********************************************************************/ +hb_work_object_t * hb_work_enclame_init( hb_job_t * job, hb_audio_t * audio ) +{ + hb_work_object_t * w = calloc( sizeof( hb_work_object_t ), 1 ); + w->name = strdup( "MP3 encoder (libmp3lame)" ); + w->work = Work; + w->close = Close; + + w->job = job; + w->audio = audio; + + hb_log( "enclame: opening libmp3lame" ); + + w->lame = lame_init(); + lame_set_brate( w->lame, job->abitrate ); + lame_set_in_samplerate( w->lame, job->arate ); + lame_set_out_samplerate( w->lame, job->arate ); + lame_init_params( w->lame ); + + w->input_samples = 1152 * 2; + w->output_bytes = LAME_MAXMP3BUFFER; + w->buf = malloc( w->input_samples * sizeof( float ) ); + + w->list = hb_list_init(); + w->pts = -1; + + return w; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +static void Close( hb_work_object_t ** _w ) +{ + hb_work_object_t * w = *_w; + free( w->name ); + free( w ); + *_w = NULL; +} + +/*********************************************************************** + * Encode + *********************************************************************** + * + **********************************************************************/ +static hb_buffer_t * Encode( hb_work_object_t * w ) +{ + hb_buffer_t * buf; + int16_t samples_s16[1152 * 2]; + uint64_t pts; + int pos, i; + + if( hb_list_bytes( w->list ) < w->input_samples * sizeof( float ) ) + { + return NULL; + } + + hb_list_getbytes( w->list, w->buf, w->input_samples * sizeof( float ), + &pts, &pos); + + for( i = 0; i < 1152 * 2; i++ ) + { + samples_s16[i] = ((float*) w->buf)[i]; + } + + buf = hb_buffer_init( w->output_bytes ); + buf->start = pts + 90000 * pos / 2 / sizeof( float ) / w->job->arate; + buf->stop = buf->start + 90000 * 1152 / w->job->arate; + buf->size = lame_encode_buffer_interleaved( w->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 + *********************************************************************** + * + **********************************************************************/ +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_buffer_t * buf; + + hb_list_add( w->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/libhb/encvorbis.c b/libhb/encvorbis.c new file mode 100644 index 000000000..86b824372 --- /dev/null +++ b/libhb/encvorbis.c @@ -0,0 +1,205 @@ +/* $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 "hb.h" + +#include "vorbis/vorbisenc.h" + +#define OGGVORBIS_FRAME_SIZE 1024 + +struct hb_work_object_s +{ + HB_WORK_COMMON; + + hb_job_t * job; + hb_audio_t * audio; + + 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; +}; + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static void Close( hb_work_object_t ** _w ); +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); + +/*********************************************************************** + * hb_work_encvorbis_init + *********************************************************************** + * + **********************************************************************/ +hb_work_object_t * hb_work_encvorbis_init( hb_job_t * job, hb_audio_t * audio ) +{ + int i; + ogg_packet header[3]; + + hb_work_object_t * w = calloc( sizeof( hb_work_object_t ), 1 ); + w->name = strdup( "Vorbis encoder (libvorbis)" ); + w->work = Work; + w->close = Close; + + w->job = job; + w->audio = audio; + + hb_log( "encvorbis: opening libvorbis" ); + + /* init */ + vorbis_info_init( &w->vi ); + if( vorbis_encode_setup_managed( &w->vi, 2, + job->arate, -1, 1000 * job->abitrate, -1 ) || + vorbis_encode_ctl( &w->vi, OV_ECTL_RATEMANAGE_AVG, NULL ) || + vorbis_encode_setup_init( &w->vi ) ) + { + hb_log( "encvorbis: vorbis_encode_setup_managed failed" ); + } + + /* add a comment */ + vorbis_comment_init( &w->vc ); + vorbis_comment_add_tag( &w->vc, "Encoder", "HandBrake"); + + /* set up the analysis state and auxiliary encoding storage */ + vorbis_analysis_init( &w->vd, &w->vi); + vorbis_block_init( &w->vd, &w->vb); + + /* get the 3 headers */ + vorbis_analysis_headerout( &w->vd, &w->vc, + &header[0], &header[1], &header[2] ); + for( i = 0; i < 3; i++ ) + { + audio->config.vorbis.headers[i] = + malloc( sizeof( ogg_packet ) + header[i].bytes ); + memcpy( audio->config.vorbis.headers[i], &header[i], + sizeof( ogg_packet ) ); + memcpy( audio->config.vorbis.headers[i] + sizeof( ogg_packet ), + header[i].packet, header[i].bytes ); + } + + w->input_samples = 2 * OGGVORBIS_FRAME_SIZE; + w->buf = malloc( w->input_samples * sizeof( float ) ); + + w->list = hb_list_init(); + + return w; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +static void Close( hb_work_object_t ** _w ) +{ + hb_work_object_t * w = *_w; + free( w->name ); + free( w ); + *_w = NULL; +} + +/*********************************************************************** + * Flush + *********************************************************************** + * + **********************************************************************/ +static hb_buffer_t * Flush( hb_work_object_t * w ) +{ + hb_buffer_t * buf; + + if( vorbis_analysis_blockout( &w->vd, &w->vb ) == 1 ) + { + ogg_packet op; + + vorbis_analysis( &w->vb, NULL ); + vorbis_bitrate_addblock( &w->vb ); + + if( vorbis_bitrate_flushpacket( &w->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 = w->pts; /* No exact, but who cares - the OGM + muxer doesn't use it */ + buf->stop = buf->start + + 90000 * OGGVORBIS_FRAME_SIZE + w->job->arate; + + return buf; + } + } + + return NULL; +} + +/*********************************************************************** + * Encode + *********************************************************************** + * + **********************************************************************/ +static hb_buffer_t * Encode( hb_work_object_t * w ) +{ + hb_buffer_t * buf; + float ** buffer; + int i; + + /* Try to extract more data */ + if( ( buf = Flush( w ) ) ) + { + return buf; + } + + if( hb_list_bytes( w->list ) < w->input_samples * sizeof( float ) ) + { + return NULL; + } + + /* Process more samples */ + hb_list_getbytes( w->list, w->buf, w->input_samples * sizeof( float ), + &w->pts, NULL ); + buffer = vorbis_analysis_buffer( &w->vd, OGGVORBIS_FRAME_SIZE ); + for( i = 0; i < OGGVORBIS_FRAME_SIZE; i++ ) + { + buffer[0][i] = ((float *) w->buf)[2*i] / 32768.f; + buffer[1][i] = ((float *) w->buf)[2*i+1] / 32768.f; + } + vorbis_analysis_wrote( &w->vd, OGGVORBIS_FRAME_SIZE ); + + /* Try to extract again */ + return Flush( w ); +} + +/*********************************************************************** + * Work + *********************************************************************** + * + **********************************************************************/ +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_buffer_t * buf; + + hb_list_add( w->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/libhb/encx264.c b/libhb/encx264.c new file mode 100644 index 000000000..cf58452e7 --- /dev/null +++ b/libhb/encx264.c @@ -0,0 +1,221 @@ +/* $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 "hb.h" + +#include "x264.h" + +struct hb_work_object_s +{ + HB_WORK_COMMON; + + hb_job_t * job; + x264_t * x264; + x264_picture_t pic_in; + x264_picture_t pic_out; + + char filename[1024]; +}; + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); +static void Close( hb_work_object_t ** _w ); + +/*********************************************************************** + * hb_work_encx264_init + *********************************************************************** + * + **********************************************************************/ +hb_work_object_t * hb_work_encx264_init( hb_job_t * job ) +{ + hb_work_object_t * w; + x264_param_t param; + x264_nal_t * nal; + int nal_count; + + w = calloc( sizeof( hb_work_object_t ), 1 ); + w->name = strdup( "AVC encoder (libx264)" ); + w->work = Work; + w->close = Close; + + w->job = job; + + memset( w->filename, 0, 1024 ); + hb_get_tempory_filename( job->h, w->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_13 ) + { + param.b_cabac = 0; + param.i_level_idc = 13; + } + + /* Slightly faster with minimal quality lost */ + param.analyse.i_subpel_refine = 4; + + if( job->vquality >= 0.0 && job->vquality <= 1.0 ) + { + /* Constant QP */ + param.rc.i_qp_constant = 51 - job->vquality * 51; + hb_log( "encx264: encoding at constant QP %d", + param.rc.i_qp_constant ); + } + else + { + /* Rate control */ + param.rc.b_cbr = 1; + param.rc.i_bitrate = job->vbitrate; + switch( job->pass ) + { + case 1: + param.rc.b_stat_write = 1; + param.rc.psz_stat_out = w->filename; + break; + case 2: + param.rc.b_stat_read = 1; + param.rc.psz_stat_in = w->filename; + break; + } + } + + hb_log( "encx264: opening libx264 (pass %d)", job->pass ); + w->x264 = x264_encoder_open( ¶m ); + +#define c job->config.h264 + x264_encoder_headers( w->x264, &nal, &nal_count ); + + /* Sequence Parameter Set */ + c.sps_length = 1 + nal[1].i_payload; + c.sps = malloc( c.sps_length); + c.sps[0] = 0x67; + memcpy( &c.sps[1], nal[1].p_payload, nal[1].i_payload ); + + /* Picture Parameter Set */ + c.pps_length = 1 + nal[2].i_payload; + c.pps = malloc( c.pps_length ); + c.pps[0] = 0x68; + memcpy( &c.pps[1], nal[2].p_payload, nal[2].i_payload ); +#undef c + + x264_picture_alloc( &w->pic_in, X264_CSP_I420, + job->width, job->height ); + + return w; +} + +static void Close( hb_work_object_t ** _w ) +{ + hb_work_object_t * w = *_w; + + x264_encoder_close( w->x264 ); + + /* TODO */ + + free( w->name ); + free( w ); + *_w = NULL; +} + +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_job_t * job = w->job; + hb_buffer_t * in = *buf_in, * buf; + int i_nal; + x264_nal_t * nal; + int i; + + /* XXX avoid this memcpy ? */ + memcpy( w->pic_in.img.plane[0], in->data, job->width * job->height ); + if( job->grayscale ) + { + /* XXX x264 has currently no option for grayscale encoding */ + memset( w->pic_in.img.plane[1], 0x80, job->width * job->height / 4 ); + memset( w->pic_in.img.plane[2], 0x80, job->width * job->height / 4 ); + } + else + { + memcpy( w->pic_in.img.plane[1], in->data + job->width * job->height, + job->width * job->height / 4 ); + memcpy( w->pic_in.img.plane[2], in->data + 5 * job->width * + job->height / 4, job->width * job->height / 4 ); + } + + w->pic_in.i_type = X264_TYPE_AUTO; + w->pic_in.i_qpplus1 = 0; + + x264_encoder_encode( w->x264, &nal, &i_nal, + &w->pic_in, &w->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/libhb/encxvid.c b/libhb/encxvid.c new file mode 100644 index 000000000..a874d9f00 --- /dev/null +++ b/libhb/encxvid.c @@ -0,0 +1,222 @@ +/* $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 "hb.h" + +#include "xvid.h" + +struct hb_work_object_s +{ + HB_WORK_COMMON; + + hb_job_t * job; + void * xvid; + char filename[1024]; + int quant; +}; + +/*********************************************************************** + * Local prototypes + **********************************************************************/ +static void Close( hb_work_object_t ** _w ); +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); + +/*********************************************************************** + * hb_work_encxvid_init + *********************************************************************** + * + **********************************************************************/ +hb_work_object_t * hb_work_encxvid_init( 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_object_t * w = calloc( sizeof( hb_work_object_t ), 1 ); + w->name = strdup( "MPEG-4 encoder (libxvidcore)" ); + w->work = Work; + w->close = Close; + + w->job = job; + + memset( w->filename, 0, 1024 ); + hb_get_tempory_filename( job->h, w->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; + w->quant = 0; + } + else + { + /* Constant quantizer */ + w->quant = 31 - job->vquality * 30; + hb_log( "encxvid: encoding at constant quantizer %d", + w->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 = w->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 = w->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 ); + w->xvid = create.handle; + + return w; +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +static void Close( hb_work_object_t ** _w ) +{ + hb_work_object_t * w = *_w; + + if( w->xvid ) + { + hb_log( "encxvid: closing libxvidcore" ); + xvid_encore( w->xvid, XVID_ENC_DESTROY, NULL, NULL); + } + + free( w->name ); + free( w ); + *_w = NULL; +} + +/*********************************************************************** + * Work + *********************************************************************** + * + **********************************************************************/ +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_job_t * job = w->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->grayscale ) + { + frame.vop_flags |= XVID_VOP_GREYSCALE; + } + frame.type = XVID_TYPE_AUTO; + frame.quant = w->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( w->xvid, XVID_ENC_ENCODE, &frame, NULL ); + buf->key = ( frame.out_flags & XVID_KEYFRAME ); + +#define c job->config.mpeg4 + if( !c.config ) + { + 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 ); + c.config = malloc( vop_start - vol_start ); + c.config_length = vop_start - vol_start; + memcpy( c.config, &buf->data[vol_start], c.config_length ); + } +#undef c + + *buf_out = buf; + + return HB_WORK_OK; +} + diff --git a/libhb/fifo.c b/libhb/fifo.c new file mode 100644 index 000000000..81ca2a184 --- /dev/null +++ b/libhb/fifo.c @@ -0,0 +1,180 @@ +/* $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 "hb.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; +} + +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; +} + +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/libhb/hb.c b/libhb/hb.c new file mode 100644 index 000000000..f27a353bc --- /dev/null +++ b/libhb/hb.c @@ -0,0 +1,514 @@ +/* $Id: hb.c,v 1.43 2005/04/27 21:05:24 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 "hb.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; +}; + +static void thread_func( void * ); + +hb_handle_t * hb_init( 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" ); + } + + /* 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( 50 ); + } + } + + /* 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; +} + +char * hb_get_version( hb_handle_t * h ) +{ + return HB_VERSION; +} + +int hb_get_build( hb_handle_t * h ) +{ + return HB_BUILD; +} + +int hb_check_update( hb_handle_t * h, char ** version ) +{ + *version = ( h->build < 0 ) ? NULL : h->version; + return h->build; +} + +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; +} + +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 ); +} + +hb_list_t * hb_get_titles( hb_handle_t * h ) +{ + return h->list_title; +} + +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 ); +} + +int hb_count( hb_handle_t * h ) +{ + return hb_list_count( h->jobs ); +} + +hb_job_t * hb_job( hb_handle_t * h, int i ) +{ + return hb_list_item( h->jobs, i ); +} + +/* hb_add: memcpy() party. That's ugly, for if someone has a better + idea... */ +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 ); +} + +void hb_rem( hb_handle_t * h, hb_job_t * job ) +{ + hb_list_rem( h->jobs, job ); + + /* XXX free everything XXX */ +} + +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 ); +} + +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 ); + } +} + +void hb_resume( hb_handle_t * h ) +{ + if( h->paused ) + { + hb_unlock( h->pause_lock ); + h->paused = 0; + } +} + +void hb_stop( hb_handle_t * h ) +{ + h->work_die = 1; + + hb_resume( h ); +} + +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 ); +} + +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; +} + +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 ); +} + +int hb_get_pid( hb_handle_t * h ) +{ + return h->pid; +} + +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/libhb/hb.h b/libhb/hb.h new file mode 100644 index 000000000..4a64c9047 --- /dev/null +++ b/libhb/hb.h @@ -0,0 +1,76 @@ +/* $Id: hb.h,v 1.12 2005/03/29 09:40:28 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_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 +hb_handle_t * hb_init( int verbose, int update_check ); + +/* 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 ); + +/* 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 * ); + +/* 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/libhb/internal.h b/libhb/internal.h new file mode 100644 index 000000000..e22da82ee --- /dev/null +++ b/libhb/internal.h @@ -0,0 +1,172 @@ +/* $Id: internal.h,v 1.40 2005/04/27 10:14:02 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 + **********************************************************************/ +typedef struct hb_buffer_s hb_buffer_t; +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 ** ); + +typedef struct hb_fifo_s hb_fifo_t; + +hb_fifo_t * hb_fifo_init(); +int hb_fifo_size( hb_fifo_t * ); +int hb_fifo_is_full( hb_fifo_t * ); +hb_buffer_t * hb_fifo_get( hb_fifo_t * ); +hb_buffer_t * hb_fifo_see( 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 ); +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 + **********************************************************************/ +typedef struct hb_work_object_s hb_work_object_t; + +#define HB_WORK_COMMON \ + hb_lock_t * lock; \ + int used; \ + uint64_t time; \ + char * name; \ + hb_fifo_t * fifo_in; \ + hb_fifo_t * fifo_out; \ + int (* work) ( hb_work_object_t *, hb_buffer_t **, \ + hb_buffer_t ** ); \ + void (* close) ( hb_work_object_t ** ) + +#define HB_WORK_IDLE 0 +#define HB_WORK_OK 1 +#define HB_WORK_ERROR 2 +#define HB_WORK_DONE 3 + + +#define DECLARE_WORK_NORMAL( a ) \ + hb_work_object_t * hb_work_##a##_init( hb_job_t * ); + +#define DECLARE_WORK_AUDIO( a ) \ + hb_work_object_t * hb_work_##a##_init( hb_job_t *, hb_audio_t * ); + +DECLARE_WORK_NORMAL( sync ); +DECLARE_WORK_NORMAL( decmpeg2 ); +DECLARE_WORK_NORMAL( decsub ); +DECLARE_WORK_NORMAL( render ); +DECLARE_WORK_NORMAL( encavcodec ); +DECLARE_WORK_NORMAL( encxvid ); +DECLARE_WORK_NORMAL( encx264 ); +DECLARE_WORK_AUDIO( deca52 ); +DECLARE_WORK_AUDIO( decavcodec ); +DECLARE_WORK_AUDIO( declpcm ); +DECLARE_WORK_AUDIO( encfaac ); +DECLARE_WORK_AUDIO( enclame ); +DECLARE_WORK_AUDIO( encvorbis ); + +/*********************************************************************** + * 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/libhb/lang.h b/libhb/lang.h new file mode 100644 index 000000000..f88f539ff --- /dev/null +++ b/libhb/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/libhb/muxavi.c b/libhb/muxavi.c new file mode 100644 index 000000000..8746a3cdd --- /dev/null +++ b/libhb/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 "hb.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/libhb/muxcommon.c b/libhb/muxcommon.c new file mode 100644 index 000000000..090f332b2 --- /dev/null +++ b/libhb/muxcommon.c @@ -0,0 +1,215 @@ +/* $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 "hb.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: + 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( 50 ); + } + + /* 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 ); + } + + while( !*job->die && !job->done ) + { + if( !( track = GetTrack( list ) ) ) + { + hb_snooze( 50 ); + continue; + } + + 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; + + 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/libhb/muxmp4.c b/libhb/muxmp4.c new file mode 100644 index 000000000..a02a05232 --- /dev/null +++ b/libhb/muxmp4.c @@ -0,0 +1,160 @@ +/* $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 "hb.h" + +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 ) + { +#define c job->config.h264 + /* Stolen from mp4creator */ + MP4SetVideoProfileLevel( m->file, 0x7F ); + + mux_data->track = MP4AddH264VideoTrack( m->file, job->arate, + MP4_INVALID_DURATION, job->width, job->height, + c.sps[1], /* AVCProfileIndication */ + c.sps[2], /* profile_compat */ + c.sps[3], /* AVCLevelIndication */ + 3 ); /* 4 bytes length before each NAL unit */ + + MP4AddH264SequenceParameterSet( m->file, mux_data->track, + c.sps, c.sps_length ); + MP4AddH264PictureParameterSet( m->file, mux_data->track, + c.pps, c.pps_length ); +#undef c + } + else /* FFmpeg or XviD */ + { +#define c job->config.mpeg4 + 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, + c.config, c.config_length ); +#undef c + } + + 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.faac.decinfo, audio->config.faac.size ); + } + + 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 ) +{ + hb_job_t * job = m->job; + 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/libhb/muxogm.c b/libhb/muxogm.c new file mode 100644 index 000000000..d326c9828 --- /dev/null +++ b/libhb/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 "hb.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 = (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/libhb/ports.c b/libhb/ports.c new file mode 100644 index 000000000..20c2cae94 --- /dev/null +++ b/libhb/ports.c @@ -0,0 +1,593 @@ +/* $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_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) +#include <pthread.h> +#elif defined( SYS_CYGWIN ) +#include <windows.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 "hb.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 defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + 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 defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + 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 defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + 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 defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + 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 defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + 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 defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + 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 defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + 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 defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + 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 defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + 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 defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + 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 defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + 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 defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + 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 defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD ) + 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/libhb/ports.h b/libhb/ports.h new file mode 100644 index 000000000..4639c6b93 --- /dev/null +++ b/libhb/ports.h @@ -0,0 +1,86 @@ +/* $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 __LIBHB__ + +/* 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 + ***********************************************************************/ +typedef struct hb_lock_s hb_lock_t; + +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 /* __LIBHB__ */ + +#endif + diff --git a/libhb/reader.c b/libhb/reader.c new file mode 100644 index 000000000..e57467d8d --- /dev/null +++ b/libhb/reader.c @@ -0,0 +1,155 @@ +/* $Id: reader.c,v 1.20 2005/04/29 19:55: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 "hb.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_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/libhb/render.c b/libhb/render.c new file mode 100644 index 000000000..914fa3995 --- /dev/null +++ b/libhb/render.c @@ -0,0 +1,167 @@ +/* $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 "hb.h" + +#include "ffmpeg/avcodec.h" + +struct hb_work_object_s +{ + HB_WORK_COMMON; + + hb_job_t * job; + + ImgReSampleContext * context; + AVPicture pic_raw; + AVPicture pic_deint; + AVPicture pic_render; + hb_buffer_t * buf_deint; +}; + +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( sub->width > title->width - job->crop[0] - job->crop[1] - 40 || + sub->height > title->height - job->crop[2] - job->crop[3] - 40 ) + { + /* The subtitle won't fit */ + hb_buffer_close( _sub ); + return; + } + + /* If necessary, move the subtitle so it is 20 pixels far from each + border of the cropped picture */ + offset_top = sub->y; + offset_top = MAX( offset_top, job->crop[0] + 20 ); + offset_top = MIN( offset_top, + title->height - job->crop[1] - 20 - sub->height ); + offset_left = sub->x; + offset_left = MAX( offset_left, job->crop[2] + 20 ); + offset_left = MIN( offset_left, + title->width - job->crop[3] - 20 - sub->width ); + + 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++ ) + { + for( j = 0; j < sub->width; j++ ) + { + 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 ); +} + +static int Work( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_job_t * job = w->job; + hb_title_t * title = job->title; + hb_buffer_t * in = *buf_in, * buf; + + avpicture_fill( &w->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 && w->context ) + { + avpicture_fill( &w->pic_render, buf->data, PIX_FMT_YUV420P, + job->width, job->height ); + avpicture_deinterlace( &w->pic_deint, &w->pic_raw, + PIX_FMT_YUV420P, title->width, + title->height ); + ApplySub( job, w->buf_deint, &in->sub ); + img_resample( w->context, &w->pic_render, &w->pic_deint ); + } + else if( job->deinterlace ) + { + avpicture_fill( &w->pic_deint, buf->data, PIX_FMT_YUV420P, + job->width, job->height ); + avpicture_deinterlace( &w->pic_deint, &w->pic_raw, + PIX_FMT_YUV420P, title->width, + title->height ); + ApplySub( job, buf, &in->sub ); + } + else if( w->context ) + { + ApplySub( job, in, &in->sub ); + avpicture_fill( &w->pic_render, buf->data, PIX_FMT_YUV420P, + job->width, job->height ); + img_resample( w->context, &w->pic_render, &w->pic_raw ); + } + else + { + hb_buffer_close( &buf ); + ApplySub( job, in, &in->sub ); + buf = in; + *buf_in = NULL; + } + + (*buf_out) = buf; + + return HB_WORK_OK; +} + +static void Close( hb_work_object_t ** _w ) +{ + hb_work_object_t * w = *_w; + free( w->name ); + free( w ); + *_w = NULL; +} + +hb_work_object_t * hb_work_render_init( hb_job_t * job ) +{ + hb_title_t * title; + + hb_work_object_t * w = calloc( sizeof( hb_work_object_t ), 1 ); + w->name = strdup( "Renderer" ); + w->work = Work; + w->close = Close; + + title = job->title; + + w->job = job; + + if( job->crop[0] || job->crop[1] || job->crop[2] || job->crop[3] || + job->width != title->width || job->height != title->height ) + { + w->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 */ + w->buf_deint = hb_buffer_init( 3 * title->width * + title->height / 2 ); + avpicture_fill( &w->pic_deint, w->buf_deint->data, + PIX_FMT_YUV420P, title->width, title->height ); + } + return w; +} diff --git a/libhb/scan.c b/libhb/scan.c new file mode 100644 index 000000000..812e84026 --- /dev/null +++ b/libhb/scan.c @@ -0,0 +1,491 @@ +/* $Id: scan.c,v 1.51 2005/04/27 21:05:24 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 "hb.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 ) ); + + job->width = title->width - job->crop[2] - job->crop[3]; + 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 ); + } + 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]; + + hb_dvd_seek( data->dvd, (float) ( i + 1 ) / 11.0 ); + + 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 ); + 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/libhb/sync.c b/libhb/sync.c new file mode 100644 index 000000000..e0530a03a --- /dev/null +++ b/libhb/sync.c @@ -0,0 +1,664 @@ +/* $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 "hb.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_object_s +{ + HB_WORK_COMMON; + + 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 void Close( hb_work_object_t ** _w ); +static int Work( hb_work_object_t * w, hb_buffer_t ** unused1, + hb_buffer_t ** unused2 ); +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 + **********************************************************************/ +hb_work_object_t * hb_work_sync_init( hb_job_t * job ) +{ + hb_work_object_t * w; + hb_title_t * title = job->title; + hb_chapter_t * chapter; + int i; + uint64_t duration; + + w = calloc( sizeof( hb_work_object_t ), 1 ); + w->name = strdup( "Synchronization" ); + w->work = Work; + w->close = Close; + + w->job = job; + w->pts_offset = INT64_MIN; + w->pts_offset_old = INT64_MIN; + w->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 */ + w->count_frames_max = duration * job->vrate / job->vrate_base / 90000; + + hb_log( "sync: expecting %lld video frames", w->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 */ + w->subtitle = hb_list_item( title->list_subtitle, 0 ); + + return w; +} + +static void InitAudio( hb_work_object_t * w, int i ) +{ + hb_job_t * job = w->job; + hb_title_t * title = job->title; + hb_sync_audio_t * sync; + + sync = &w->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; + } +} + +/*********************************************************************** + * Close + *********************************************************************** + * + **********************************************************************/ +static void Close( hb_work_object_t ** _w ) +{ + hb_work_object_t * w = *_w; + hb_job_t * job = w->job; + hb_title_t * title = job->title; + + int i; + + if( w->cur ) hb_buffer_close( &w->cur ); + + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + if( job->acodec & HB_ACODEC_AC3 ) + { + free( w->sync_audio[i].ac3_buf ); + } + else + { + src_delete( w->sync_audio[i].state ); + } + } + + free( w->name ); + free( w ); + *_w = NULL; +} + +/*********************************************************************** + * Work + *********************************************************************** + * The root routine of this work abject + **********************************************************************/ +static int Work( hb_work_object_t * w, hb_buffer_t ** unused1, + hb_buffer_t ** unused2 ) +{ + int i; + + /* If we ever got a video frame, handle audio now */ + if( w->pts_offset != INT64_MIN ) + { + for( i = 0; i < hb_list_count( w->job->title->list_audio ); i++ ) + { + SyncAudio( w, i ); + } + } + + /* Handle video */ + return SyncVideo( w ); +} + +/*********************************************************************** + * SyncVideo + *********************************************************************** + * + **********************************************************************/ +static int SyncVideo( hb_work_object_t * w ) +{ + hb_buffer_t * cur, * next, * sub = NULL; + hb_job_t * job = w->job; + int64_t pts_expected; + + if( w->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", + w->count_frames, w->count_frames_max ); + w->done = 1; + return HB_WORK_DONE; + } + + if( !w->cur && !( w->cur = hb_fifo_get( job->fifo_raw ) ) ) + { + /* We haven't even got a frame yet */ + return HB_WORK_OK; + } + cur = w->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( w->pts_offset == INT64_MIN ) + { + /* This is our first frame */ + hb_log( "sync: first pts is %lld", cur->start ); + w->pts_offset = cur->start; + } + + /* Check for PTS jumps over 0.5 second */ + if( next->start < cur->start - 45000 || + next->start > cur->start + 45000 ) + { + hb_log( "PTS discontinuity (%lld, %lld)", + cur->start, next->start ); + + /* Trash all subtitles */ + if( w->subtitle ) + { + while( ( sub = hb_fifo_get( w->subtitle->fifo_raw ) ) ) + { + hb_buffer_close( &sub ); + } + } + + /* Trash current picture */ + hb_buffer_close( &cur ); + w->cur = cur = hb_fifo_get( job->fifo_raw ); + + /* Calculate new offset */ + w->pts_offset_old = w->pts_offset; + w->pts_offset = cur->start - + w->count_frames * w->job->vrate_base / 300; + continue; + } + + /* Look for a subtitle for this frame */ + if( w->subtitle ) + { + /* Trash subtitles older than this picture */ + while( ( sub = hb_fifo_see( w->subtitle->fifo_raw ) ) && + sub->stop < cur->start ) + { + sub = hb_fifo_get( w->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 = w->pts_offset + + w->count_frames * w->job->vrate_base / 300; + + if( cur->start < pts_expected - w->job->vrate_base / 300 / 2 && + next->start < pts_expected + w->job->vrate_base / 300 / 2 ) + { + /* The current frame is too old but the next one matches, + let's trash */ + hb_buffer_close( &cur ); + w->cur = cur = hb_fifo_get( job->fifo_raw ); + continue; + } + + if( next->start > pts_expected + 3 * w->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; + w->cur = cur = hb_fifo_get( job->fifo_raw ); + } + + /* Replace those MPEG-2 dates with our dates */ + buf_tmp->start = (uint64_t) w->count_frames * + w->job->vrate_base / 300; + buf_tmp->stop = (uint64_t) ( w->count_frames + 1 ) * + w->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( w->count_frames >= w->count_frames_max ) + { + hb_log( "sync: got %lld frames", w->count_frames ); + w->done = 1; + break; + } + } + + return HB_WORK_OK; +} + +/*********************************************************************** + * SyncAudio + *********************************************************************** + * + **********************************************************************/ +static void SyncAudio( hb_work_object_t * w, int i ) +{ + 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 = w->job; + sync = &w->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 = w->pts_offset + sync->count_frames * 90000 / rate; + + if( ( buf->start > pts_expected + 45000 || + buf->start < pts_expected - 45000 ) && + w->pts_offset_old > INT64_MIN ) + { + /* There has been a PTS discontinuity, and this frame might + be from before the discontinuity */ + pts_expected = w->pts_offset_old + sync->count_frames * + 90000 / rate; + + if( buf->start > pts_expected + 45000 || + buf->start < pts_expected - 45000 ) + { + /* 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 - w->pts_offset_old; + } + else + { + start = pts_expected - w->pts_offset; + } + + if( ( buf->start + buf->stop ) / 2 < pts_expected ) + { + /* Late audio, trash it */ + buf = hb_fifo_get( audio->fifo_raw ); + hb_buffer_close( &buf ); + continue; + } + + if( buf->start > pts_expected + ( buf->stop - buf->start ) / 2 ) + { + /* Audio push, 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->stop - pts_expected ) * job->arate / 90000; + + sync->data.data_in = (float *) buf_raw->data; + sync->data.input_frames = count_in; + + if( buf->start < pts_expected - ( buf->stop - buf->start ) / 5 ) + { + /* Avoid too heavy downsampling, trash the beginning of + the buffer instead */ + int drop; + drop = count_in * ( pts_expected - buf->start ) / + ( buf->stop - buf->start ); + sync->data.data_in += 2 * drop; + sync->data.input_frames -= drop; + hb_log( "dropping %d of %d samples", drop, 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_job_t * job = w->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_job_t * job; + hb_sync_audio_t * sync; + hb_buffer_t * buf; + + job = w->job; + sync = &w->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_state_t state; + + if( !w->count_frames ) + { + w->st_first = hb_get_date(); + } + w->count_frames++; + + if( hb_get_date() > w->st_dates[3] + 1000 ) + { + memmove( &w->st_dates[0], &w->st_dates[1], + 3 * sizeof( uint64_t ) ); + memmove( &w->st_counts[0], &w->st_counts[1], + 3 * sizeof( uint64_t ) ); + w->st_dates[3] = hb_get_date(); + w->st_counts[3] = w->count_frames; + } + +#define p state.param.working + state.state = HB_STATE_WORKING; + p.progress = (float) w->count_frames / (float) w->count_frames_max; + if( p.progress > 1.0 ) + { + p.progress = 1.0; + } + p.rate_cur = 1000.0 * + (float) ( w->st_counts[3] - w->st_counts[0] ) / + (float) ( w->st_dates[3] - w->st_dates[0] ); + if( hb_get_date() > w->st_first + 4000 ) + { + int eta; + p.rate_avg = 1000.0 * (float) w->st_counts[3] / + (float) ( w->st_dates[3] - w->st_first ); + eta = (float) ( w->count_frames_max - w->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( w->job->h, &state ); +} diff --git a/libhb/update.c b/libhb/update.c new file mode 100644 index 000000000..767807038 --- /dev/null +++ b/libhb/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 "hb.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/libhb/work.c b/libhb/work.c new file mode 100644 index 000000000..6c0be24e3 --- /dev/null +++ b/libhb/work.c @@ -0,0 +1,400 @@ +/* $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 "hb.h" + +struct hb_work_object_s +{ + HB_WORK_COMMON; +}; + +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 job_loop( void * ); + +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 ); +} + +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 void do_job( hb_job_t * job, int cpu_count ) +{ + hb_title_t * title; + int i; + hb_thread_t * threads[8]; + hb_work_object_t * w; + uint64_t time_total; + hb_audio_t * audio; + hb_subtitle_t * subtitle; + + 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 ); + 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 ); + } + + 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 */ + w = hb_work_sync_init( job ); + w->fifo_in = NULL; + w->fifo_out = NULL; + hb_list_add( job->list_work, w ); + + /* Video decoder */ + w = hb_work_decmpeg2_init( job ); + w->fifo_in = job->fifo_mpeg2; + w->fifo_out = job->fifo_raw; + hb_list_add( job->list_work, w ); + + /* Video renderer */ + w = hb_work_render_init( job ); + w->fifo_in = job->fifo_sync; + w->fifo_out = job->fifo_render; + hb_list_add( job->list_work, w ); + + /* Video encoder */ + switch( job->vcodec ) + { + case HB_VCODEC_FFMPEG: + hb_log( " + encoder FFmpeg" ); + w = hb_work_encavcodec_init( job ); + break; + case HB_VCODEC_XVID: + hb_log( " + encoder XviD" ); + w = hb_work_encxvid_init( job ); + break; + case HB_VCODEC_X264: + hb_log( " + encoder x264" ); + w = hb_work_encx264_init( job ); + break; + } + w->fifo_in = job->fifo_render; + w->fifo_out = job->fifo_mpeg4; + 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 ); + + w = hb_work_decsub_init( job ); + w->fifo_in = subtitle->fifo_in; + w->fifo_out = subtitle->fifo_raw; + hb_list_add( job->list_work, w ); + } + + 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 = hb_work_deca52_init( job, audio ); + break; + case HB_ACODEC_MPGA: + w = hb_work_decavcodec_init( job, audio ); + break; + case HB_ACODEC_LPCM: + w = hb_work_declpcm_init( job, audio ); + 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 = hb_work_encfaac_init( job, audio ); + break; + case HB_ACODEC_LAME: + w = hb_work_enclame_init( job, audio ); + break; + case HB_ACODEC_VORBIS: + w = hb_work_encvorbis_init( job, audio ); + break; + } + if( job->acodec != HB_ACODEC_AC3 ) + { + w->fifo_in = audio->fifo_sync; + w->fifo_out = audio->fifo_out; + 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 ); + + for( i = 0; i < hb_list_count( job->list_work ); i++ ) + { + w = hb_list_item( job->list_work, i ); + w->lock = hb_lock_init(); + w->used = 0; + w->time = 0; + } + + job->done = 0; + + /* Launch processing threads */ + for( i = 0; i < cpu_count; i++ ) + { + char thread_name[16]; + if( cpu_count - 1 ) + { + snprintf( thread_name, 16, "cpu killer %d", i + 1 ); + } + else + { + snprintf( thread_name, 16, "cpu killer" ); + } + threads[i] = hb_thread_init( thread_name, job_loop, job, + HB_LOW_PRIORITY ); + } + + while( !*job->die && !job->done ) + { + hb_snooze( 500 ); + } + + for( i = 0; i < cpu_count; i++ ) + { + hb_thread_close( &threads[i] ); + } + + /* Stop read & write threads */ + hb_thread_close( &job->reader ); + hb_thread_close( &job->muxer ); + + /* Stats */ + time_total = 0; + for( i = 0; i < hb_list_count( job->list_work ); i++ ) + { + w = hb_list_item( job->list_work, i ); + time_total += w->time; + } + for( i = 0; i < hb_list_count( job->list_work ); i++ ) + { + w = hb_list_item( job->list_work, i ); + hb_log( "%s: %.2f %%", w->name, + 100.0 * (float) w->time / (float) time_total ); + } + + /* Close work objects */ + while( ( w = hb_list_item( job->list_work, 0 ) ) ) + { + hb_list_rem( job->list_work, w ); + hb_lock_close( &w->lock ); + w->close( &w ); + } + + /* 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 ); + } +} + +static int lock( hb_work_object_t * w ) +{ + hb_lock( w->lock ); + if( w->used ) + { + hb_unlock( w->lock ); + return 0; + } + w->used = 1; + hb_unlock( w->lock ); + return 1; +} + +static void unlock( hb_work_object_t * w ) +{ + hb_lock( w->lock ); + w->used = 0; + hb_unlock( w->lock ); +} + +static void job_loop( void * _job ) +{ + hb_job_t * job = _job; + hb_buffer_t * buf_in, * buf_out; + hb_work_object_t * w; + int work_count; + int act; + int i; + uint64_t date; + int done; + + work_count = hb_list_count( job->list_work ); + act = 0; + done = 0; + + while( !*job->die && !job->done ) + { + /* Handle synchronization, resampling, framerate change, + etc */ + w = hb_list_item( job->list_work, 0 ); + if( lock( w ) ) + { + date = hb_get_date(); + if( w->work( w, NULL, NULL ) == HB_WORK_DONE ) + { + done = 1; + } + w->time += hb_get_date() - date; + unlock( w ); + } + + for( i = 1; !*job->die && !job->done && i < work_count; i++ ) + { + w = hb_list_item( job->list_work, i ); + if( !lock( w ) ) + continue; + + for( ;; ) + { + hb_lock( job->pause ); + hb_unlock( job->pause ); + + if( hb_fifo_is_full( w->fifo_out ) || + !( buf_in = hb_fifo_get( w->fifo_in ) ) ) + { + break; + } + + date = hb_get_date(); + w->work( w, &buf_in, &buf_out ); + w->time += hb_get_date() - date; + if( buf_in ) + { + hb_buffer_close( &buf_in ); + } + if( buf_out ) + { + act = 1; + hb_fifo_push( w->fifo_out, buf_out ); + } + } + + unlock( w ); + } + + if( done && + !hb_fifo_size( job->fifo_sync ) && + !hb_fifo_size( job->fifo_render ) && + hb_fifo_size( job->fifo_mpeg4 ) < 2 ) + { + job->done = 1; + break; + } + + /* If we did nothing, wait a bit before trying again */ + if( !act ) + { + hb_snooze( 50 ); + } + act = 0; + } +} |