summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
Diffstat (limited to 'libhb')
-rw-r--r--libhb/Jamfile18
-rw-r--r--libhb/common.c502
-rw-r--r--libhb/common.h349
-rw-r--r--libhb/deca52.c200
-rw-r--r--libhb/decavcodec.c137
-rw-r--r--libhb/declpcm.c113
-rw-r--r--libhb/decmpeg2.c253
-rw-r--r--libhb/decsub.c420
-rw-r--r--libhb/demuxmpeg.c125
-rw-r--r--libhb/dvd.c735
-rw-r--r--libhb/encavcodec.c201
-rw-r--r--libhb/encfaac.c166
-rw-r--r--libhb/enclame.c154
-rw-r--r--libhb/encvorbis.c205
-rw-r--r--libhb/encx264.c221
-rw-r--r--libhb/encxvid.c222
-rw-r--r--libhb/fifo.c180
-rw-r--r--libhb/hb.c514
-rw-r--r--libhb/hb.h76
-rw-r--r--libhb/internal.h172
-rw-r--r--libhb/lang.h209
-rw-r--r--libhb/muxavi.c551
-rw-r--r--libhb/muxcommon.c215
-rw-r--r--libhb/muxmp4.c160
-rw-r--r--libhb/muxogm.c364
-rw-r--r--libhb/ports.c593
-rw-r--r--libhb/ports.h86
-rw-r--r--libhb/reader.c155
-rw-r--r--libhb/render.c167
-rw-r--r--libhb/scan.c491
-rw-r--r--libhb/sync.c664
-rw-r--r--libhb/update.c157
-rw-r--r--libhb/work.c400
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( &param );
+
+ 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( &param );
+
+#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( &param, 0, sizeof( struct sched_param ) );
+ param.sched_priority = t->priority;
+ pthread_setschedparam( pthread_self(), SCHED_OTHER, &param );
+#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;
+ }
+}