summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authorawk <[email protected]>2007-07-04 02:14:42 +0000
committerawk <[email protected]>2007-07-04 02:14:42 +0000
commit2e0106d686bb69faa1182dabbceb58969a93956f (patch)
treee00e2090e8a526e8e2baf205cdc4bbcda8825df4 /libhb
parent5ceeadd648a9952506bd3e166d9fdba1ae2b4da4 (diff)
Implement transport and program stream support. With these changes it's now possible to open a .ts or .mpg files and transcode to standard Handbrake Output files. This fixes Ticket #21.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@648 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb')
-rw-r--r--libhb/Jamfile2
-rw-r--r--libhb/Makefile2
-rw-r--r--libhb/common.h4
-rw-r--r--libhb/deca52.c11
-rw-r--r--libhb/decmpeg2.c26
-rw-r--r--libhb/internal.h12
-rw-r--r--libhb/lang.h2
-rw-r--r--libhb/reader.c61
-rw-r--r--libhb/scan.c99
-rwxr-xr-xlibhb/stream.c1985
10 files changed, 2166 insertions, 38 deletions
diff --git a/libhb/Jamfile b/libhb/Jamfile
index 7765d01c4..f3014518a 100644
--- a/libhb/Jamfile
+++ b/libhb/Jamfile
@@ -8,7 +8,7 @@ SubDir TOP libhb ;
LIBHB_SRC =
ipodutil.cpp 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
+demuxmpeg.c fifo.c render.c reader.c muxcommon.c muxmp4.c sync.c stream.c
decsub.c deca52.c decdca.c encfaac.c declpcm.c encx264.c decavcodec.c encxvid.c
muxavi.c enclame.c muxogm.c encvorbis.c dvd.c ;
diff --git a/libhb/Makefile b/libhb/Makefile
index 6604ec428..cf92b481a 100644
--- a/libhb/Makefile
+++ b/libhb/Makefile
@@ -22,7 +22,7 @@ ifeq ($(SYSTEM),Linux)
endif
SRCS = common.c hb.c ports.c scan.c work.c decmpeg2.c encavcodec.c \
- update.c demuxmpeg.c fifo.c render.c reader.c muxcommon.c \
+ update.c demuxmpeg.c fifo.c render.c reader.c muxcommon.c stream.c \
muxmp4.c sync.c decsub.c deca52.c decdca.c encfaac.c declpcm.c encx264.c \
decavcodec.c encxvid.c muxavi.c enclame.c muxogm.c encvorbis.c \
dvd.c ipodutil.cpp
diff --git a/libhb/common.h b/libhb/common.h
index a6ea8ac95..123e528c2 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -28,6 +28,8 @@
#define EVEN( a ) ( (a) + ( (a) & 1 ) )
#define MULTIPLE_16( a ) ( 16 * ( ( (a) + 8 ) / 16 ) )
+#define HB_DVD_READ_BUFFER_SIZE 2048
+
typedef struct hb_handle_s hb_handle_t;
typedef struct hb_list_s hb_list_t;
typedef struct hb_rate_s hb_rate_t;
@@ -338,6 +340,8 @@ struct hb_audio_s
/* amixdown is the mixdown format to be used for this audio track */
int amixdown;
+ /* Source PID is only valid for MPEG Transport Streams */
+ int source_pid;
#endif
};
diff --git a/libhb/deca52.c b/libhb/deca52.c
index 2a8c53c31..88974d55d 100644
--- a/libhb/deca52.c
+++ b/libhb/deca52.c
@@ -25,6 +25,8 @@ struct hb_work_private_s
int sync;
int size;
+ int64_t next_expected_pts;
+
uint8_t frame[3840];
hb_list_t * list;
@@ -76,7 +78,8 @@ int deca52Init( hb_work_object_t * w, hb_job_t * job )
pv->out_discrete_channels = HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(w->amixdown);
pv->level = 32768.0;
-
+ pv->next_expected_pts = 0;
+
return 0;
}
@@ -191,9 +194,15 @@ static hb_buffer_t * Decode( hb_work_object_t * w )
/* 6 blocks per frame, 256 samples per block, channelsused channels */
buf = hb_buffer_init( 6 * 256 * pv->out_discrete_channels * sizeof( float ) );
+ if (pts == -1)
+ {
+ pts = pv->next_expected_pts;
+ }
buf->start = pts + ( pos / pv->size ) * 6 * 256 * 90000 / pv->rate;
buf->stop = buf->start + 6 * 256 * 90000 / pv->rate;
+ pv->next_expected_pts = buf->stop;
+
for( i = 0; i < 6; i++ )
{
sample_t * samples_in;
diff --git a/libhb/decmpeg2.c b/libhb/decmpeg2.c
index 4fb6ad76b..65293c681 100644
--- a/libhb/decmpeg2.c
+++ b/libhb/decmpeg2.c
@@ -20,6 +20,7 @@ struct hb_libmpeg2_s
int width;
int height;
int rate;
+ int aspect_ratio;
int got_iframe;
int look_for_break;
int64_t last_pts;
@@ -88,6 +89,28 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es,
m->rate = 1126125;
}
}
+ if ( m->aspect_ratio <= 0 )
+ {
+ // We can parse out the aspect ratio from the Sequence Start Header data in buf_es->data
+
+ // Make sure we have the correct data in the buffer
+ if ((buf_es->data[0] == 0x00) && (buf_es->data[1] == 0x00) && (buf_es->data[2] == 0x01) && (buf_es->data[3] == 0xb3))
+ {
+ unsigned char ar_fr = buf_es->data[7]; // Top 4 bits == aspect ratio flag - bottom 4 bits == rate flags
+ switch ((ar_fr & 0xf0) >> 4)
+ {
+ case 2:
+ m->aspect_ratio = HB_ASPECT_BASE * 4 / 3; // 4:3
+ break;
+ case 3:
+ m->aspect_ratio = HB_ASPECT_BASE * 16 / 9; // 16:9
+ break;
+ default:
+ hb_log("hb_libmpeg2_decode - STATE_SEQUENCE unexpected aspect ratio/frame rate 0x%x\n", ar_fr);
+ break;
+ }
+ }
+ }
}
else if( state == STATE_GOP && m->look_for_break == 2)
{
@@ -170,11 +193,12 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es,
*
*********************************************************************/
void hb_libmpeg2_info( hb_libmpeg2_t * m, int * width, int * height,
- int * rate )
+ int * rate, int *aspect_ratio )
{
*width = m->width;
*height = m->height;
*rate = m->rate;
+ *aspect_ratio = m->aspect_ratio;
}
/**********************************************************************
diff --git a/libhb/internal.h b/libhb/internal.h
index 34bca3d65..60fb80cc8 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -100,7 +100,7 @@ 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 );
+ int * height, int * rate, int * aspect_ratio );
void hb_libmpeg2_close( hb_libmpeg2_t ** );
/***********************************************************************
@@ -112,6 +112,7 @@ int hb_demux_ps( hb_buffer_t * ps_buf, hb_list_t * es_list );
* dvd.c
**********************************************************************/
typedef struct hb_dvd_s hb_dvd_t;
+typedef struct hb_stream_s hb_stream_t;
hb_dvd_t * hb_dvd_init( char * path );
int hb_dvd_title_count( hb_dvd_t * );
@@ -124,6 +125,15 @@ int hb_dvd_chapter( hb_dvd_t * );
int hb_dvd_is_break( hb_dvd_t * d );
void hb_dvd_close( hb_dvd_t ** );
+int hb_stream_is_stream_type( char * path);
+hb_stream_t * hb_stream_open( char * path );
+void hb_stream_close( hb_stream_t ** );
+hb_title_t * hb_stream_title_scan( hb_stream_t *);
+int hb_stream_read( hb_stream_t *, hb_buffer_t *);
+int hb_stream_seek( hb_stream_t *, float );
+void hb_stream_update_audio( hb_stream_t *, hb_audio_t *);
+void hb_stream_set_selected_audio_pid_index(hb_stream_t *, int);
+
/***********************************************************************
* Work objects
**********************************************************************/
diff --git a/libhb/lang.h b/libhb/lang.h
index ac734a7b1..a1cab4d32 100644
--- a/libhb/lang.h
+++ b/libhb/lang.h
@@ -205,7 +205,7 @@ static const iso639_lang_t languages[] =
{ "Zulu", "", "zu", "zul" },
{ NULL, NULL, NULL } };
-iso639_lang_t * lang_for_code( int code )
+static iso639_lang_t * lang_for_code( int code )
{
char code_string[2];
iso639_lang_t * lang;
diff --git a/libhb/reader.c b/libhb/reader.c
index 4767f87a7..335dfb80d 100644
--- a/libhb/reader.c
+++ b/libhb/reader.c
@@ -14,6 +14,7 @@ typedef struct
hb_dvd_t * dvd;
hb_buffer_t * ps;
+ hb_stream_t * stream;
} hb_reader_t;
@@ -53,25 +54,43 @@ static void ReaderFunc( void * _r )
hb_fifo_t * fifo;
hb_buffer_t * buf;
hb_list_t * list;
- int chapter;
+ int chapter = -1;
if( !( r->dvd = hb_dvd_init( r->title->dvd ) ) )
{
- return;
+ if ( !(r->stream = hb_stream_open(r->title->dvd) ) )
+ {
+ return;
+ }
}
- if( !hb_dvd_start( r->dvd, r->title->index, r->job->chapter_start ) )
+ if (r->dvd)
{
- hb_dvd_close( &r->dvd );
- return;
+ if( !hb_dvd_start( r->dvd, r->title->index, r->job->chapter_start ) )
+ {
+ hb_dvd_close( &r->dvd );
+ return;
+ }
}
-
+
+ if (r->stream)
+ {
+ // At this point r->audios[0] gives us the index of the selected audio track for output track 0
+ // we cannot effectively demux multiple PID's into the seperate output tracks unfortunately
+ // so we'll just specifiy things here for a single track.
+ hb_stream_set_selected_audio_pid_index(r->stream, r->job->audios[0]);
+ }
+
list = hb_list_init();
- r->ps = hb_buffer_init( 2048 );
+ r->ps = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE );
while( !*r->die && !r->job->done )
{
- chapter = hb_dvd_chapter( r->dvd );
+ if (r->dvd)
+ chapter = hb_dvd_chapter( r->dvd );
+ else if (r->stream)
+ chapter = 1;
+
if( chapter < 0 )
{
hb_log( "reader: end of the title reached" );
@@ -84,11 +103,21 @@ static void ReaderFunc( void * _r )
break;
}
- if( !hb_dvd_read( r->dvd, r->ps ) )
+ if (r->dvd)
+ {
+ if( !hb_dvd_read( r->dvd, r->ps ) )
+ {
+ break;
+ }
+ }
+ else if (r->stream)
{
+ if ( !hb_stream_read( r->stream, r->ps ) )
+ {
break;
+ }
}
-
+
hb_demux_ps( r->ps, list );
while( ( buf = hb_list_item( list, 0 ) ) )
@@ -113,8 +142,16 @@ static void ReaderFunc( void * _r )
hb_list_empty( &list );
hb_buffer_close( &r->ps );
- hb_dvd_stop( r->dvd );
- hb_dvd_close( &r->dvd );
+ if (r->dvd)
+ {
+ hb_dvd_stop( r->dvd );
+ hb_dvd_close( &r->dvd );
+ }
+ else if (r->stream)
+ {
+ hb_stream_close(&r->stream);
+ }
+
free( r );
_r = NULL;
diff --git a/libhb/scan.c b/libhb/scan.c
index 45da1b7d1..97a437008 100644
--- a/libhb/scan.c
+++ b/libhb/scan.c
@@ -17,7 +17,8 @@ typedef struct
hb_list_t * list_title;
hb_dvd_t * dvd;
-
+ hb_stream_t * stream;
+
} hb_scan_t;
static void ScanFunc( void * );
@@ -44,6 +45,9 @@ static void ScanFunc( void * _data )
hb_title_t * title;
int i;
+ data->dvd = NULL;
+ data->stream = NULL;
+
/* 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 ) ) )
@@ -68,18 +72,15 @@ static void ScanFunc( void * _data )
}
else
{
- /* Open as a VOB file */
- FILE * file;
- hb_log( "scan: trying to open as VOB file" );
- file = fopen( data->path, "rb" );
- if( file )
+ if ( hb_stream_is_stream_type(data->path) )
{
- /* XXX */
- fclose( file );
+ hb_log( "scan: trying to open as MPEG-2 Stream");
+ data->stream = hb_stream_open (data->path);
+ hb_list_add( data->list_title, hb_stream_title_scan( data->stream ) );
}
else
{
- hb_log( "scan: fopen failed" );
+ hb_log( "scan: unrecognized file type" );
return;
}
}
@@ -123,7 +124,7 @@ static void ScanFunc( void * _data )
/* Update the UI */
state.state = HB_STATE_SCANNING;
p.title_cur = title->index;
- p.title_count = hb_dvd_title_count( data->dvd );
+ p.title_count = data->dvd ? hb_dvd_title_count( data->dvd ) : hb_list_count(data->list_title);
hb_set_state( data->h, &state );
#undef p
@@ -135,7 +136,33 @@ static void ScanFunc( void * _data )
hb_list_rem( data->list_title, title );
continue;
}
-
+
+ if (data->stream)
+ {
+ // Stream based processing uses PID's to handle the different audio options for a given title
+ for( j = 0; j < hb_list_count( title->list_audio ); j++ )
+ {
+ audio = hb_list_item( title->list_audio, j );
+ hb_stream_update_audio(data->stream, audio);
+ }
+ }
+ else if (data->dvd)
+ {
+ /* 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++;
+ }
+ }
+
/* Make sure we found AC3 / DCA rates and bitrates */
for( j = 0; j < hb_list_count( title->list_audio ); )
{
@@ -241,6 +268,10 @@ static void ScanFunc( void * _data )
{
hb_dvd_close( &data->dvd );
}
+ if (data->stream)
+ {
+ hb_stream_close(&data->stream);
+ }
free( data->path );
free( data );
_data = NULL;
@@ -259,35 +290,56 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
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 );
+
+ buf_ps = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE );
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 );
-
+ if (data->dvd)
+ hb_dvd_start( data->dvd, title->index, 1 );
+
for( i = 0; i < 10; i++ )
{
int j, k;
FILE * file_preview;
char filename[1024];
- if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / 11.0 ) )
+ if (data->dvd)
{
+ if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / 11.0 ) )
+ {
+ goto error;
+ }
+ }
+ else if (data->stream)
+ {
+ if (!hb_stream_seek(data->stream, (float) ( i + 1 ) / 11.0 ) )
+ {
goto error;
+ }
}
-
+
hb_log( "scan: preview %d", i + 1 );
mpeg2 = hb_libmpeg2_init();
for( j = 0; j < 10240 ; j++ )
{
- if( !hb_dvd_read( data->dvd, buf_ps ) )
+ if (data->dvd)
{
+ if( !hb_dvd_read( data->dvd, buf_ps ) )
+ {
+ goto error;
+ }
+ }
+ else if (data->stream)
+ {
+ if ( !hb_stream_read(data->stream,buf_ps) )
+ {
goto error;
+ }
}
hb_demux_ps( buf_ps, list_es );
@@ -329,8 +381,13 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
{
/* Get size and rate infos */
title->rate = 27000000;
+ int ar;
hb_libmpeg2_info( mpeg2, &title->width, &title->height,
- &title->rate_base );
+ &title->rate_base, &ar );
+ // The aspect ratio may have already been set by parsing the VOB/IFO details on a DVD, however
+ // if we're working with program/transport streams that data needs to come from within the stream.
+ if (title->aspect <= 0)
+ title->aspect = ar;
title->crop[0] = title->crop[1] = title->height / 2;
title->crop[2] = title->crop[3] = title->width / 2;
}
@@ -435,7 +492,9 @@ cleanup:
hb_buffer_close( &buf_raw );
}
hb_list_close( &list_raw );
- hb_dvd_stop( data->dvd );
+ if (data->dvd)
+ hb_dvd_stop( data->dvd );
+
return ret;
}
diff --git a/libhb/stream.c b/libhb/stream.c
new file mode 100755
index 000000000..2cfc0e9bf
--- /dev/null
+++ b/libhb/stream.c
@@ -0,0 +1,1985 @@
+/* $Id$
+
+ 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 "a52dec/a52.h"
+
+#include <string.h>
+
+#define min(a, b) a < b ? a : b
+
+typedef enum { hb_stream_type_unknown = 0, hb_stream_type_transport, hb_stream_type_program } hb_stream_type_t;
+
+#define kMaxNumberDecodeStreams 8
+#define kMaxNumberVideoPIDS 16
+#define kMaxNumberAudioPIDS 32
+//#define kVideoStream 0
+//#define kAudioStream 1
+#define kNumDecodeBuffers 2
+
+#define CLOCKRATE ((int64_t)27000000) // MPEG System clock rate
+#define STREAMRATE ((int64_t)2401587) // Original HD stream rate 19.2 Mbps
+#define DEMUX (((int)STREAMRATE * 8) / 50)// Demux value for HD content STREAMRATE / 50
+
+struct hb_stream_s
+{
+ char * path;
+ FILE * file_handle;
+ hb_stream_type_t stream_type;
+
+ int ps_current_write_buffer_index;
+ int ps_current_read_buffer_index;
+
+ struct {
+ int size;
+ int len;
+ int read_pos;
+ int write_pos;
+ unsigned char * data;
+ } ps_decode_buffer[kNumDecodeBuffers];
+
+ struct {
+ int lang_code;
+ int flags;
+ int rate;
+ int bitrate;
+ } a52_info[kMaxNumberAudioPIDS];
+
+ int ts_video_pids[kMaxNumberVideoPIDS];
+ int ts_audio_pids[kMaxNumberAudioPIDS];
+
+ int ts_number_video_pids;
+ int ts_number_audio_pids;
+ int ts_selected_audio_pid_index;
+
+ unsigned char* ts_packetbuf[kMaxNumberDecodeStreams];
+ int ts_packetpos[kMaxNumberDecodeStreams];
+// int ts_bufpackets[kMaxNumberDecodeStreams];
+ int ts_foundfirst[kMaxNumberDecodeStreams];
+ int ts_skipbad[kMaxNumberDecodeStreams];
+ int ts_streamcont[kMaxNumberDecodeStreams];
+ int ts_streamid[kMaxNumberDecodeStreams];
+ int ts_audio_stream_type[kMaxNumberAudioPIDS];
+
+ FILE *debug_output;
+};
+
+/***********************************************************************
+ * Local prototypes
+ **********************************************************************/
+static void hb_stream_duration(hb_stream_t *stream, hb_title_t *inTitle);
+static void hb_ts_stream_init(hb_stream_t *stream);
+static void hb_ts_stream_find_pids(hb_stream_t *stream);
+static void hb_ts_stream_decode(hb_stream_t *stream);
+static void hb_ts_stream_reset(hb_stream_t *stream);
+static void hb_stream_put_back(hb_stream_t *stream, int i);
+static void hb_stream_set_audio_id_and_codec(hb_stream_t *stream, hb_audio_t *audio);
+
+int hb_stream_is_stream_type( char * path )
+{
+ if ((strstr(path,".mpg") != NULL) ||
+ (strstr(path,".vob") != NULL) ||
+ (strstr(path,".ts") != NULL) ||
+ (strstr(path, ".m2t") != NULL))
+ {
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/***********************************************************************
+ * hb_stream_open
+ ***********************************************************************
+ *
+ **********************************************************************/
+hb_stream_t * hb_stream_open( char * path )
+{
+ hb_stream_t * d;
+
+ d = calloc( sizeof( hb_stream_t ), 1 );
+
+ /* Open device */
+ if( !( d->file_handle = fopen( path, "rb" ) ) )
+ {
+ hb_log( "hb_stream_open: fopen failed (%s)", path );
+ goto fail;
+ }
+
+ d->path = strdup( path );
+
+ if ( ( strstr(d->path,".ts") != NULL) || ( strstr(d->path,".m2t") != NULL))
+ {
+ d->stream_type = hb_stream_type_transport;
+ hb_ts_stream_init(d);
+ }
+ else if (( strstr(d->path,".mpg") != NULL) || ( strstr(d->path,".vob") != NULL))
+ {
+ d->stream_type = hb_stream_type_program;
+ }
+
+ return d;
+
+fail:
+ free( d );
+ return NULL;
+}
+
+/***********************************************************************
+ * hb_stream_close
+ ***********************************************************************
+ * Closes and frees everything
+ **********************************************************************/
+void hb_stream_close( hb_stream_t ** _d )
+{
+ hb_stream_t * d = *_d;
+
+ if( d->file_handle )
+ {
+ fclose( d->file_handle );
+ d->file_handle = NULL;
+ }
+
+ if (d->debug_output)
+ {
+ fclose(d->debug_output);
+ d->debug_output = NULL;
+ }
+
+ int i=0;
+ for (i = 0; i < kNumDecodeBuffers; i++)
+ {
+ if (d->ps_decode_buffer[i].data)
+ {
+ free(d->ps_decode_buffer[i].data);
+ d->ps_decode_buffer[i].data = NULL;
+ }
+ }
+
+ for (i = 0; i < kMaxNumberDecodeStreams; i++)
+ {
+ if (d->ts_packetbuf[i])
+ {
+ free(d->ts_packetbuf[i]);
+ d->ts_packetbuf[i] = NULL;
+ }
+ }
+
+ free( d );
+ *_d = NULL;
+}
+
+/***********************************************************************
+ * hb_ps_stream_title_scan
+ ***********************************************************************
+ *
+ **********************************************************************/
+hb_title_t * hb_stream_title_scan(hb_stream_t *stream)
+{
+ // 'Barebones Title'
+ hb_title_t *aTitle = hb_title_init( stream->path, 0 );
+
+ // Copy part of the stream path to the title name
+ char *sep = strrchr(stream->path, '/');
+ if (sep)
+ strcpy(aTitle->name, sep+1);
+ char *dot_term = strrchr(aTitle->name, '.');
+ if (dot_term)
+ *dot_term = '\0';
+
+ // Height, width, rate and aspect ratio information is filled in when the previews are built
+
+ hb_stream_duration(stream, aTitle);
+
+ // One Chapter
+ hb_chapter_t * chapter;
+ chapter = calloc( sizeof( hb_chapter_t ), 1 );
+ chapter->index = 1;
+ chapter->duration = aTitle->duration;
+ chapter->hours = aTitle->hours;
+ chapter->minutes = aTitle->minutes;
+ chapter->seconds = aTitle->seconds;
+ hb_list_add( aTitle->list_chapter, chapter );
+
+ int i=0, num_audio_tracks = 1;
+
+ if (stream->stream_type == hb_stream_type_transport)
+ {
+ num_audio_tracks = stream->ts_number_audio_pids;
+ }
+
+ for (i=0; i < num_audio_tracks ; i++)
+ {
+ // Basic AC-3 Audio track
+ hb_audio_t * audio;
+ audio = calloc( sizeof( hb_audio_t ), 1 );
+
+ audio->source_pid = stream->ts_audio_pids[i];
+
+ hb_stream_set_audio_id_and_codec(stream, audio);
+
+ hb_list_add( aTitle->list_audio, audio );
+ }
+
+ return aTitle;
+}
+
+/***********************************************************************
+ * hb_stream_duration
+ ***********************************************************************
+ *
+ **********************************************************************/
+void hb_stream_duration(hb_stream_t *stream, hb_title_t *inTitle)
+{
+ // VOB Files often have exceedingly unusual PTS values in them - they will progress for a while
+ // and then reset without warning !
+ if (strstr(stream->path,".vob") != NULL)
+ {
+ // So we'll use a 'fake duration' that should give enough time !
+ int64_t duration = 4 * 3600 * 90000;
+ inTitle->duration = duration; //90LL * dvdtime2msec( &d->pgc->playback_time );
+ inTitle->hours = inTitle->duration / 90000 / 3600;
+ inTitle->minutes = ( ( inTitle->duration / 90000 ) % 3600 ) / 60;
+ inTitle->seconds = ( inTitle->duration / 90000 ) % 60;
+ return;
+ }
+
+ unsigned char *buf = (unsigned char *) malloc(4096);
+ int done = 0;
+ off_t cur_pos;
+ int64_t first_pts = 0, last_pts = 0;
+
+ // To calculate the duration we look for the first and last presentation time stamps in the stream for video data
+ // and then use the delta
+ while (!done)
+ {
+ cur_pos = ftello(stream->file_handle);
+ if (fread(buf, 4096, 1, stream->file_handle) == 1)
+ {
+ int i=0;
+ for (i=0; (i <= 4092) && !done; i++)
+ {
+ if ((buf[i] == 0x00) && (buf[i+1] == 0x00) && (buf[i+2] == 0x01) && (buf[i+3] == 0xe0)) // Found a Video Stream
+ {
+ // Now look for a PTS field - we need to make sure we have enough space so we back up a little and read
+ // some more data
+ fseeko(stream->file_handle, cur_pos + i, SEEK_SET);
+ if (fread(buf, 4096, 1, stream->file_handle) == 1)
+ {
+ int has_pts = ( ( buf[7] >> 6 ) & 0x2 ) ? 1 : 0;
+ if (has_pts)
+ {
+ first_pts = ( ( ( (uint64_t) buf[9] >> 1 ) & 0x7 ) << 30 ) +
+ ( buf[10] << 22 ) +
+ ( ( buf[11] >> 1 ) << 15 ) +
+ ( buf[12] << 7 ) +
+ ( buf[13] >> 1 );
+ done = 1;
+ }
+ else
+ {
+ fseeko(stream->file_handle, cur_pos, SEEK_SET);
+ fread(buf, 4096, 1, stream->file_handle);
+ }
+ }
+ }
+ }
+ }
+ else
+ done = 1; // End of data;
+ }
+
+ // Now work back from the end of the stream
+ fseeko(stream->file_handle,0 ,SEEK_END);
+
+ done = 0;
+ while (!done)
+ {
+ // Back up a little
+ if (fseeko(stream->file_handle, -4096, SEEK_CUR) < 0)
+ {
+ done = 1;
+ break;
+ }
+
+ cur_pos = ftello(stream->file_handle);
+ if (fread(buf, 4096, 1, stream->file_handle) == 1)
+ {
+ int i=0;
+ for (i=4092; (i >= 0) && !done; i--)
+ {
+ if ((buf[i] == 0x00) && (buf[i+1] == 0x00) && (buf[i+2] == 0x01) && (buf[i+3] == 0xe0)) // Found a Video Stream
+ {
+ // Now look for a PTS field - we need to make sure we have enough space so we back up a little and read
+ // some more data
+ fseeko(stream->file_handle, cur_pos + i, SEEK_SET);
+ fread(buf, 1, 4096, stream->file_handle);
+
+ unsigned char pts_dts_flag = buf[7];
+
+ int has_pts = ( ( buf[7] >> 6 ) & 0x2 ) ? 1 : 0;
+ if (has_pts)
+ {
+ last_pts = ( ( ( (uint64_t) buf[9] >> 1 ) & 0x7 ) << 30 ) +
+ ( buf[10] << 22 ) +
+ ( ( buf[11] >> 1 ) << 15 ) +
+ ( buf[12] << 7 ) +
+ ( buf[13] >> 1 );
+
+ done = 1;
+ }
+ else
+ {
+ // Re Read the original data and carry on (i is still valid in the old buffer)
+ fseeko(stream->file_handle, cur_pos, SEEK_SET);
+ fread(buf, 4096, 1, stream->file_handle);
+ }
+ }
+ }
+ fseeko(stream->file_handle, -4096, SEEK_CUR); // 'Undo' the last read
+ }
+ else
+ done = 1; // End of data;
+ }
+ free(buf);
+
+ int64_t duration = last_pts - first_pts;
+ inTitle->duration = duration; //90LL * dvdtime2msec( &d->pgc->playback_time );
+ inTitle->hours = inTitle->duration / 90000 / 3600;
+ inTitle->minutes = ( ( inTitle->duration / 90000 ) % 3600 ) / 60;
+ inTitle->seconds = ( inTitle->duration / 90000 ) % 60;
+
+}
+
+/***********************************************************************
+ * hb_stream_read
+ ***********************************************************************
+ *
+ **********************************************************************/
+int hb_stream_read( hb_stream_t * src_stream, hb_buffer_t * b )
+{
+ if (src_stream->stream_type == hb_stream_type_program)
+ {
+ size_t amt_read;
+ amt_read = fread(b->data, HB_DVD_READ_BUFFER_SIZE, 1, src_stream->file_handle);
+ if (amt_read > 0)
+ return 1;
+ else
+ return 0;
+ }
+ else if (src_stream->stream_type == hb_stream_type_transport)
+ {
+ int read_buffer_index = src_stream->ps_current_read_buffer_index;
+
+ // Transport streams are a little more complex - we might be able to just
+ // read from the transport stream conversion buffer (if there's enough data)
+ // or we may need to transfer what's left and fill it again.
+ if (src_stream->ps_decode_buffer[read_buffer_index].len - src_stream->ps_decode_buffer[read_buffer_index].read_pos > HB_DVD_READ_BUFFER_SIZE)
+ {
+ memcpy(b->data, src_stream->ps_decode_buffer[read_buffer_index].data + src_stream->ps_decode_buffer[read_buffer_index].read_pos,HB_DVD_READ_BUFFER_SIZE);
+ src_stream->ps_decode_buffer[read_buffer_index].read_pos += HB_DVD_READ_BUFFER_SIZE;
+ return 1;
+ }
+ else
+ {
+ // Not quite enough data in the buffer - transfer what is present, fill the buffer and then
+ // transfer what's still needed.
+ int transfer_size = HB_DVD_READ_BUFFER_SIZE;
+ int amt_avail_to_transfer = src_stream->ps_decode_buffer[read_buffer_index].len - src_stream->ps_decode_buffer[read_buffer_index].read_pos;
+ memcpy(b->data, src_stream->ps_decode_buffer[read_buffer_index].data + src_stream->ps_decode_buffer[read_buffer_index].read_pos, amt_avail_to_transfer);
+ transfer_size -= amt_avail_to_transfer;
+ src_stream->ps_decode_buffer[read_buffer_index].read_pos += amt_avail_to_transfer;
+
+ // Give up this buffer - decoding may well need it, and we're done
+ src_stream->ps_decode_buffer[read_buffer_index].write_pos = 0;
+ src_stream->ps_decode_buffer[read_buffer_index].len = 0;
+
+ // Fill the buffer
+ hb_ts_stream_decode(src_stream);
+
+ // Decoding will almost certainly have changed the current read buffer index
+ read_buffer_index = src_stream->ps_current_read_buffer_index;
+
+ if (src_stream->ps_decode_buffer[read_buffer_index].len == 0)
+ {
+ hb_log("hb_stream_read - buffer after decode has zero length data");
+ return 0;
+ }
+
+ // Read the bit we still need
+ memcpy(b->data+amt_avail_to_transfer, src_stream->ps_decode_buffer[read_buffer_index].data + src_stream->ps_decode_buffer[read_buffer_index].read_pos,transfer_size);
+ src_stream->ps_decode_buffer[read_buffer_index].read_pos += transfer_size;
+
+ return 1;
+ }
+ }
+ else
+ return 0;
+}
+
+/***********************************************************************
+ * hb_stream_seek
+ ***********************************************************************
+ *
+ **********************************************************************/
+int hb_stream_seek( hb_stream_t * src_stream, float f )
+{
+ off_t stream_size, cur_pos, new_pos;
+ double pos_ratio = f;
+ cur_pos = ftello(src_stream->file_handle);
+ fseeko(src_stream->file_handle,0 ,SEEK_END);
+ stream_size = ftello(src_stream->file_handle);
+ new_pos = (off_t) ((double) (stream_size) * pos_ratio);
+ int r = fseeko(src_stream->file_handle, new_pos, SEEK_SET);
+
+ if (r == -1)
+ {
+ fseeko(src_stream->file_handle, cur_pos, SEEK_SET);
+ return 0;
+ }
+
+ if (src_stream->stream_type == hb_stream_type_transport)
+ {
+ // We need to drop the current decoder output and move
+ // forwards to the next transport stream packet.
+ hb_ts_stream_reset(src_stream);
+ }
+
+ // Now we must scan forwards for a valid start code (0x000001BA)
+ int done = 0;
+ hb_buffer_t *buf = hb_buffer_init(HB_DVD_READ_BUFFER_SIZE);
+ while (!done)
+ {
+ if (hb_stream_read(src_stream,buf) == 1)
+ {
+ int i=0;
+ for (i=0; (i <= HB_DVD_READ_BUFFER_SIZE-4) && (!done); i++)
+ {
+ if ((buf->data[i] == 0x00) && (buf->data[i+1] == 0x00) && (buf->data[i+2] == 0x01) && (buf->data[i+3] == 0xba))
+ {
+ done = 1;
+ // 'Put Back' the data we've just read (up to this point)
+ hb_stream_put_back(src_stream, i);
+ }
+ }
+ }
+ else
+ done = 1; // End of data;
+ }
+ hb_buffer_close(&buf);
+ return 1;
+}
+
+/***********************************************************************
+ * hb_stream_set_audio_id_and_codec
+ ***********************************************************************
+ *
+ **********************************************************************/
+void hb_stream_set_audio_id_and_codec(hb_stream_t *stream, hb_audio_t *audio)
+{
+ off_t cur_pos;
+ cur_pos = ftello(stream->file_handle);
+ int done = 0;
+ hb_buffer_t *buf = NULL;
+
+ int cur_audio_pid_index = stream->ts_selected_audio_pid_index;
+
+ if (stream->stream_type == hb_stream_type_transport)
+ {
+ int i=0;
+ for (i=0; i < stream->ts_number_audio_pids; i++)
+ {
+ if (stream->ts_audio_pids[i] == audio->source_pid)
+ {
+ stream->ts_selected_audio_pid_index = i;
+ break;
+ }
+ }
+ }
+
+ //Start at the beginning of the stream
+ hb_stream_seek(stream, 0.0f);
+// fseeko(stream->file_handle,0 ,SEEK_SET);
+
+ // Now we must scan forwards for a valid audio start code (0x000001xx)
+ buf = hb_buffer_init(HB_DVD_READ_BUFFER_SIZE);
+ while (!done)
+ {
+// if (fread(buf->data,4096,1,stream->file_handle) == 1)
+ if (hb_stream_read(stream, buf) == 1)
+ {
+ int i=0;
+ for (i=0; (i <= HB_DVD_READ_BUFFER_SIZE-4) && (!done); i++)
+ {
+ if ((buf->data[i] == 0x00) && (buf->data[i+1] == 0x00) && (buf->data[i+2] == 0x01))
+ {
+ if (buf->data[i+3] == 0xbd)
+ {
+ audio->id = 0x80bd;
+ audio->codec = HB_ACODEC_AC3;
+ done = 1;
+ }
+ else if ((buf->data[i+3] & 0xe0) == 0xc0)
+ {
+ audio->id = buf->data[i+3];
+ audio->codec = HB_ACODEC_MPGA;
+ done = 1;
+ }
+ }
+ }
+ }
+ else
+ done = 1; // End of data;
+ }
+ hb_buffer_close(&buf);
+
+ fseeko(stream->file_handle, cur_pos, SEEK_SET);
+
+ stream->ts_selected_audio_pid_index = cur_audio_pid_index;
+}
+
+/***********************************************************************
+ * hb_stream_update_audio
+ ***********************************************************************
+ *
+ **********************************************************************/
+void hb_stream_update_audio(hb_stream_t *stream, hb_audio_t *audio)
+{
+ iso639_lang_t *lang;
+
+ if (stream->stream_type == hb_stream_type_program)
+ {
+ lang = lang_for_code('e' << 8 | 'n');
+ }
+ else if (stream->stream_type == hb_stream_type_transport)
+ {
+ // Find the audio stream info for this PID
+ int i=0;
+ for (i=0; i < stream->ts_number_audio_pids; i++)
+ {
+ if (stream->ts_audio_pids[i] == audio->source_pid)
+ break;
+ }
+ if (i == stream->ts_number_audio_pids)
+ {
+ hb_log("hb_stream_update_audio - cannot find PID 0x%x (%d) in ts_audio_pids list !", audio->source_pid, audio->source_pid);
+ return;
+ }
+
+ lang = lang_for_code(stream->a52_info[i].lang_code);
+ audio->rate = stream->a52_info[i].rate;
+ audio->bitrate = stream->a52_info[i].bitrate;
+ audio->config.a52.ac3flags = audio->ac3flags = stream->a52_info[i].flags;
+
+ }
+
+ switch( audio->ac3flags & A52_CHANNEL_MASK )
+ {
+ /* mono sources */
+ case A52_MONO:
+ case A52_CHANNEL1:
+ case A52_CHANNEL2:
+ audio->input_channel_layout = HB_INPUT_CH_LAYOUT_MONO;
+ break;
+ /* stereo input */
+ case A52_CHANNEL:
+ case A52_STEREO:
+ audio->input_channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
+ break;
+ /* dolby (DPL1 aka Dolby Surround = 4.0 matrix-encoded) input */
+ case A52_DOLBY:
+ audio->input_channel_layout = HB_INPUT_CH_LAYOUT_DOLBY;
+ break;
+ /* 3F/2R input */
+ case A52_3F2R:
+ audio->input_channel_layout = HB_INPUT_CH_LAYOUT_3F2R;
+ break;
+ /* 3F/1R input */
+ case A52_3F1R:
+ audio->input_channel_layout = HB_INPUT_CH_LAYOUT_3F1R;
+ break;
+ /* other inputs */
+ case A52_3F:
+ audio->input_channel_layout = HB_INPUT_CH_LAYOUT_3F;
+ break;
+ case A52_2F1R:
+ audio->input_channel_layout = HB_INPUT_CH_LAYOUT_2F1R;
+ break;
+ case A52_2F2R:
+ audio->input_channel_layout = HB_INPUT_CH_LAYOUT_2F2R;
+ break;
+ /* unknown */
+ default:
+ audio->input_channel_layout = HB_INPUT_CH_LAYOUT_STEREO;
+ }
+
+ /* add in our own LFE flag if the source has LFE */
+ if (audio->ac3flags & A52_LFE)
+ {
+ audio->input_channel_layout = audio->input_channel_layout | HB_INPUT_CH_LAYOUT_HAS_LFE;
+ }
+
+ snprintf( audio->lang, sizeof( audio->lang ), "%s (%s)", strlen(lang->native_name) ? lang->native_name : lang->eng_name,
+ audio->codec == HB_ACODEC_AC3 ? "AC3" : ( audio->codec == HB_ACODEC_MPGA ? "MPEG" : "LPCM" ) );
+ snprintf( audio->lang_simple, sizeof( audio->lang_simple ), "%s", strlen(lang->native_name) ? lang->native_name : lang->eng_name );
+ snprintf( audio->iso639_2, sizeof( audio->iso639_2 ), "%s", lang->iso639_2);
+
+ if ( (audio->ac3flags & A52_CHANNEL_MASK) == A52_DOLBY ) {
+ sprintf( audio->lang + strlen( audio->lang ),
+ " (Dolby Surround)" );
+ } else {
+ sprintf( audio->lang + strlen( audio->lang ),
+ " (%d.%d ch)",
+ HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT(audio->input_channel_layout) +
+ HB_INPUT_CH_LAYOUT_GET_DISCRETE_REAR_COUNT(audio->input_channel_layout),
+ HB_INPUT_CH_LAYOUT_GET_DISCRETE_LFE_COUNT(audio->input_channel_layout));
+ }
+
+ hb_log( "hb_stream_update_audio: id=%x, lang=%s, 3cc=%s, rate = %d, bitrate = %d, flags = 0x%x (%d)", audio->id, audio->lang, audio->iso639_2, audio->rate, audio->bitrate, audio->ac3flags, audio->ac3flags );
+
+}
+
+void hb_stream_set_selected_audio_pid_index(hb_stream_t *stream, int i)
+{
+ stream->ts_selected_audio_pid_index = i;
+}
+
+/***********************************************************************
+ * hb_stream_put_back
+ ***********************************************************************
+ *
+ **********************************************************************/
+static void hb_stream_put_back(hb_stream_t *stream, int i)
+{
+ if (stream->stream_type == hb_stream_type_program)
+ {
+ // Program streams are pretty easy - we just reposition the source file
+ // pointer
+ fseeko(stream->file_handle, -(HB_DVD_READ_BUFFER_SIZE-i), SEEK_CUR);
+ }
+ else if (stream->stream_type == hb_stream_type_transport)
+ {
+ int read_buffer_index = stream->ps_current_read_buffer_index;
+
+ // Transport streams are a little more tricky - so long as the
+ // amount to back up is still within the current decode buffer
+ // we can just adjust the read pos.
+ if (stream->ps_decode_buffer[read_buffer_index].read_pos - i > 0)
+ {
+ stream->ps_decode_buffer[read_buffer_index].read_pos -= i;
+ }
+ else
+ fprintf(stderr, "hb_stream_put_back - trying to step beyond the start of the buffer, read_pos = %d amt to put back = %d\n", stream->ps_decode_buffer[read_buffer_index].read_pos, i);
+ }
+}
+
+
+/***********************************************************************
+ * hb_ts_stream_init
+ ***********************************************************************
+ *
+ **********************************************************************/
+ #define PS_DECODE_BUFFER_SIZE ( 1024 * 1024 * 4)
+
+static void hb_ts_stream_init(hb_stream_t *stream)
+{
+ // Output Program Stream
+ int i=0;
+ for (i=0; i < kNumDecodeBuffers; i++)
+ {
+ stream->ps_decode_buffer[i].data = (unsigned char *) malloc(PS_DECODE_BUFFER_SIZE);
+ stream->ps_decode_buffer[i].read_pos = 0;
+ stream->ps_decode_buffer[i].size = PS_DECODE_BUFFER_SIZE;
+ stream->ps_decode_buffer[i].len = 0;
+ stream->ps_decode_buffer[i].write_pos = 0;
+ }
+
+ for (i=0; i < kMaxNumberDecodeStreams; i++)
+ {
+ stream->ts_streamcont[i] = -1;
+ }
+
+ stream->ps_current_write_buffer_index = 0;
+ stream->ps_current_read_buffer_index = 1;
+
+ // This is the index (in ts_audio_pids) of the selected
+ // output stream. It should not be set until after all the
+ // pids in the stream have been discovered.
+ stream->ts_selected_audio_pid_index = -1;
+
+ stream->debug_output = fopen("/Users/awk/Desktop/hb_debug.mpg", "wb");
+
+ // Find the audio and video pids in the stream
+ hb_ts_stream_find_pids(stream);
+
+ for (i=0; i < stream->ts_number_video_pids; i++)
+ {
+ // In progress audio/video data during the transport stream -> program stream processing
+ stream->ts_packetbuf[i] = (unsigned char *) malloc(1024 * 1024);
+ stream->ts_streamid[i] = 0xE0; // Stream is Video
+ }
+
+ for (i = stream->ts_number_video_pids; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
+ {
+ stream->ts_packetbuf[i] = (unsigned char *) malloc(1024 * 1024);
+ stream->ts_streamid[i] = 0xBD; // Stream 1 is AC-3 Audio
+ }
+}
+
+// ------------------------------------------------------------------------------------
+
+off_t align_to_next_packet(FILE* f)
+{
+ unsigned char buf[188*20];
+
+ off_t start = ftello(f);
+ off_t pos = 0;
+
+ if (fread(buf, 188*20, 1, f) == 1)
+ {
+ int found = 0;
+ while (!found && (pos < 188))
+ {
+ found = 1;
+ int i = 0;
+ for (i = 0; i < 188*20; i += 188)
+ {
+ unsigned char c = buf[pos+i];
+ // Check sync byte
+ if ((c != 0x47) && (c != 0x72) && (c != 0x29))
+ {
+ // this offset failed, try next
+ found = 0;
+ pos++;
+ break;
+ }
+ }
+ }
+ }
+
+ if (pos == 188)
+ pos = 0; // failed to find anything!!!!!?
+
+ fseek(f, start+pos, SEEK_SET);
+
+ return pos;
+}
+
+// ------------------------------------------------------------------------------------
+
+int bitpos = 0;
+unsigned int bitval = 0;
+unsigned char* bitbuf = NULL;
+unsigned int bitmask[] = {
+ 0x0,0x1,0x3,0x7,0xf,0x1f,0x3f,0x7f,0xff,
+ 0x1ff,0x3ff,0x7ff,0xfff,0x1fff,0x3fff,0x7fff,0xffff,
+ 0x1ffff,0x3ffff,0x7ffff,0xfffff,0x1fffff,0x3fffff,0x7fffff,0xffffff,
+ 0x1ffffff,0x3ffffff,0x7ffffff,0xfffffff,0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff};
+
+static inline void set_buf(unsigned char* buf, int bufsize, int clear)
+{
+ bitpos = 0;
+ bitbuf = buf;
+ bitval = (bitbuf[0] << 24) | (bitbuf[1] << 16) | (bitbuf[2] << 8) | bitbuf[3];
+ if (clear)
+ memset(bitbuf, 0, bufsize);
+}
+
+static inline int buf_size()
+{
+ return bitpos >> 3;
+}
+
+static inline void set_bits(unsigned int val, int bits)
+{
+ val &= bitmask[bits];
+
+ while (bits > 0)
+ {
+ int bitsleft = (8 - (bitpos & 7));
+ if (bits >= bitsleft)
+ {
+ bitbuf[bitpos >> 3] |= val >> (bits - bitsleft);
+ bitpos += bitsleft;
+ bits -= bitsleft;
+ val &= bitmask[bits];
+ }
+ else
+ {
+ bitbuf[bitpos >> 3] |= val << (bitsleft - bits);
+ bitpos += bits;
+ bits = 0;
+ }
+ }
+}
+
+static inline unsigned int get_bits(int bits)
+{
+ unsigned int val;
+ int left = 32 - (bitpos & 31);
+
+ if (bits < left)
+ {
+ val = (bitval >> (left - bits)) & bitmask[bits];
+ bitpos += bits;
+ }
+ else
+ {
+ val = (bitval & bitmask[left]) << (bits - left);
+ bitpos += left;
+ bits -= left;
+
+ int pos = bitpos >> 3;
+ bitval = (bitbuf[pos] << 24) | (bitbuf[pos + 1] << 16) | (bitbuf[pos + 2] << 8) | bitbuf[pos + 3];
+
+ if (bits > 0)
+ {
+ val |= (bitval >> (32 - bits)) & bitmask[bits];
+ bitpos += bits;
+ }
+ }
+
+ return val;
+}
+
+// ------------------------------------------------------------------------------------
+
+int decode_program_map(unsigned char *buf, hb_stream_t *stream)
+{
+ unsigned char tablebuf[1024];
+ unsigned int tablepos = 0;
+
+ int reading = 0;
+
+
+ // Get adaption header info
+ int adapt_len = 0;
+ int adaption = (buf[3] & 0x30) >> 4;
+ if (adaption == 0)
+ return 0;
+ else if (adaption == 0x2)
+ adapt_len = 184;
+ else if (adaption == 0x3)
+ adapt_len = buf[4] + 1;
+ if (adapt_len > 184)
+ return 0;
+
+ // Get pointer length
+ int pointer_len = buf[4 + adapt_len] + 1;
+
+ // Get payload start indicator
+ int start;
+ start = (buf[1] & 0x40) != 0;
+
+ if (start)
+ reading = 1;
+
+ // Add the payload for this packet to the current buffer
+ if (reading && (184 - adapt_len) > 0)
+ {
+ if (tablepos + 184 - adapt_len - pointer_len > 1024)
+ {
+ hb_log("decode_program_map - Bad program section length (> 1024)");
+ return 0;
+ }
+ memcpy(tablebuf + tablepos, buf + 4 + adapt_len + pointer_len, 184 - adapt_len - pointer_len);
+ tablepos += 184 - adapt_len - pointer_len;
+ }
+
+ if (start && reading)
+ {
+ int done_reading_stream_types = 0;
+
+ memcpy(tablebuf + tablepos, buf + 4 + adapt_len + 1, pointer_len - 1);
+
+ unsigned int pos = 0;
+ set_buf(tablebuf + pos, tablepos - pos, 0);
+
+ unsigned char section_id = get_bits(8);
+ get_bits(4);
+ unsigned int section_length = get_bits(12);
+ unsigned int program_number = get_bits(16);
+ get_bits(2);
+ unsigned char version_number = get_bits(5);
+ get_bits(1);
+ unsigned char section_number = get_bits(8);
+ unsigned char last_section_number = get_bits(8);
+ get_bits(3);
+ unsigned int PCR_PID = get_bits(13);
+ get_bits(4);
+ unsigned int program_info_length = get_bits(12);
+ int i=0;
+ unsigned char *descriptor_buf = (unsigned char *) malloc(program_info_length);
+ for (i = 0; i < program_info_length; i++)
+ {
+ descriptor_buf[i] = get_bits(8);
+ }
+
+ int cur_pos = 9 /* data so far */ + program_info_length;
+ done_reading_stream_types = 0;
+ while (!done_reading_stream_types)
+ {
+ unsigned char stream_type = get_bits(8);
+ get_bits(3);
+ unsigned int elementary_PID = get_bits(13);
+ get_bits(4);
+ unsigned int ES_info_length = get_bits(12);
+
+ int i=0;
+ unsigned char *ES_info_buf = (unsigned char *) malloc(ES_info_length);
+ for (i=0; i < ES_info_length; i++)
+ {
+ ES_info_buf[i] = get_bits(8);
+ }
+
+ if (stream_type == 0x02)
+ {
+ if (stream->ts_number_video_pids <= kMaxNumberVideoPIDS)
+ stream->ts_number_video_pids++;
+ stream->ts_video_pids[stream->ts_number_video_pids-1] = elementary_PID;
+ }
+ if ((stream_type == 0x04) || (stream_type == 0x81) || (stream_type == 0x03) || (stream_type == 0x06)) // ATSC Defines stream type 0x81 for AC-3/A52 audio, there's also some evidence of streams using type 6 for AC-3 audio too
+ {
+ if (stream->ts_number_audio_pids <= kMaxNumberAudioPIDS)
+ stream->ts_number_audio_pids++;
+ stream->ts_audio_pids[stream->ts_number_audio_pids-1] = elementary_PID;
+
+ stream->a52_info[stream->ts_number_audio_pids-1].lang_code = 'e' << 8 | 'n';
+ stream->ts_audio_stream_type[stream->ts_number_audio_pids-1] = stream_type;
+
+ if (ES_info_length > 0)
+ {
+ hb_log("decode_program_map - Elementary Stream Info Present, decode language codes ?");
+ }
+
+ }
+
+ cur_pos += 5 /* stream header */ + ES_info_length;
+
+ free(ES_info_buf);
+
+ if (cur_pos >= section_length - 4 /* stop before the CRC */)
+ done_reading_stream_types = 1;
+ }
+
+ free(descriptor_buf);
+ }
+
+ return 1;
+}
+
+int decode_PAT(unsigned char *buf, unsigned int *program_num, unsigned int *network_PID, unsigned int *program_map_PID)
+{
+// int maxchannels = 8;
+// static ATSC_CHANNEL_INFO* channels;
+//
+// if (channels == NULL)
+// channels = (ATSC_CHANNEL_INFO*) malloc(maxchannels * sizeof(ATSC_CHANNEL_INFO));
+//
+// int numchannels;
+
+ unsigned char tablebuf[1024];
+ unsigned int tablepos = 0;
+
+ int reading = 0;
+
+
+ // Get adaption header info
+ int adapt_len = 0;
+ int adaption = (buf[3] & 0x30) >> 4;
+ if (adaption == 0)
+ return 0;
+ else if (adaption == 0x2)
+ adapt_len = 184;
+ else if (adaption == 0x3)
+ adapt_len = buf[4] + 1;
+ if (adapt_len > 184)
+ return 0;
+
+ // Get pointer length
+ int pointer_len = buf[4 + adapt_len] + 1;
+
+ // Get payload start indicator
+ int start;
+ start = (buf[1] & 0x40) != 0;
+
+ if (start)
+ reading = 1;
+
+ // Add the payload for this packet to the current buffer
+ if (reading && (184 - adapt_len) > 0)
+ {
+ if (tablepos + 184 - adapt_len - pointer_len > 1024)
+ {
+ hb_log("decode_PAT - Bad program section length (> 1024)");
+ return 0;
+ }
+ memcpy(tablebuf + tablepos, buf + 4 + adapt_len + pointer_len, 184 - adapt_len - pointer_len);
+ tablepos += 184 - adapt_len - pointer_len;
+ }
+
+ if (start && reading)
+ {
+ memcpy(tablebuf + tablepos, buf + 4 + adapt_len + 1, pointer_len - 1);
+
+ unsigned int pos = 0;
+ //while (pos < tablepos)
+ {
+ set_buf(tablebuf + pos, tablepos - pos, 0);
+
+ unsigned char section_id = get_bits(8);
+ get_bits(4);
+ unsigned int section_len = get_bits(12);
+ unsigned int transport_id = get_bits(16);
+ get_bits(2);
+ unsigned int version_num = get_bits(5);
+ unsigned int current_next = get_bits(1);
+ unsigned int section_num = get_bits(8);
+ unsigned int last_section = get_bits(8);
+// unsigned int protocol_ver = get_bits(8);
+
+ switch (section_id)
+ {
+ case 0x00:
+ {
+ // Program Association Section
+ section_len -= 5; // Already read transport stream ID, version num, section num, and last section num
+ section_len -= 4; // Ignore the CRC
+ int curr_pos = 0;
+ while (curr_pos < section_len)
+ {
+ unsigned int pkt_program_num = get_bits(16);
+ if (program_num)
+ *program_num = pkt_program_num;
+
+ get_bits(3); // Reserved
+ if (pkt_program_num == 0)
+ {
+ unsigned int pkt_network_PID = get_bits(13);
+// printf("PAT - Transport ID = 0x%x (%d) program_num 0x%x (%d) network_PID = 0x%x (%d)\n", transport_id, transport_id, pkt_program_num, pkt_program_num, pkt_network_PID, pkt_network_PID);
+ if (network_PID)
+ *network_PID = pkt_network_PID;
+
+ }
+ else
+ {
+ unsigned int pkt_program_map_PID = get_bits(13);
+// printf("PAT - Transport ID = 0x%x (%d) program_num 0x%x (%d) program_map_PID = 0x%x (%d)\n", transport_id, transport_id, pkt_program_num, pkt_program_num, pkt_program_map_PID, pkt_program_map_PID);
+ if (program_map_PID)
+ *program_map_PID = pkt_program_map_PID;
+ }
+ curr_pos += 4;
+ }
+ }
+ break;
+ case 0xC7:
+ {
+ break;
+ }
+ case 0xC8:
+ {
+ break;
+ }
+ }
+
+ pos += 3 + section_len;
+ }
+
+ tablepos = 0;
+ }
+ return 1;
+}
+
+static int flushbuf(hb_stream_t *stream)
+{
+ int old_write_index = stream->ps_current_write_buffer_index;
+
+ if (stream->debug_output)
+ {
+ fwrite(stream->ps_decode_buffer[stream->ps_current_write_buffer_index].data, stream->ps_decode_buffer[stream->ps_current_write_buffer_index].len, 1, stream->debug_output);
+ }
+
+ // Flip the buffers and start moving on to the next
+ stream->ps_current_write_buffer_index++;
+ if (stream->ps_current_write_buffer_index > kNumDecodeBuffers-1)
+ stream->ps_current_write_buffer_index = 0;
+
+ if ( (stream->ps_decode_buffer[stream->ps_current_write_buffer_index].len != 0) || (stream->ps_decode_buffer[stream->ps_current_write_buffer_index].write_pos != 0) )
+ {
+ hb_log("flushbuf - new buffer (index %d) has non zero length and write position !", stream->ps_current_write_buffer_index);
+ return 0;
+ }
+
+ stream->ps_current_read_buffer_index = old_write_index;
+ stream->ps_decode_buffer[stream->ps_current_read_buffer_index].read_pos = 0;
+
+ return 1;
+}
+
+static int fwrite64(void* buf, int elsize, int elnum, hb_stream_t* stream)
+{
+ int size = elsize;
+ if (elnum > 1)
+ size *= elnum;
+
+ int written = 0;
+ int current_write_index = stream->ps_current_write_buffer_index;
+
+ if (size <= stream->ps_decode_buffer[current_write_index].size - stream->ps_decode_buffer[current_write_index].write_pos)
+ {
+ memcpy(stream->ps_decode_buffer[current_write_index].data + stream->ps_decode_buffer[current_write_index].write_pos, buf, size);
+ stream->ps_decode_buffer[current_write_index].write_pos += size;
+ stream->ps_decode_buffer[current_write_index].len = stream->ps_decode_buffer[current_write_index].write_pos;
+ written = size;
+ }
+ else
+ {
+ memcpy(stream->ps_decode_buffer[current_write_index].data + stream->ps_decode_buffer[current_write_index].write_pos, buf, stream->ps_decode_buffer[current_write_index].size - stream->ps_decode_buffer[current_write_index].write_pos);
+ written += stream->ps_decode_buffer[current_write_index].size - stream->ps_decode_buffer[current_write_index].write_pos;
+ stream->ps_decode_buffer[current_write_index].write_pos += stream->ps_decode_buffer[current_write_index].size - stream->ps_decode_buffer[current_write_index].write_pos;
+ stream->ps_decode_buffer[current_write_index].len = stream->ps_decode_buffer[current_write_index].write_pos;
+
+ if (flushbuf(stream))
+ {
+ // FLushing the buffer will have change the current write buffer
+ current_write_index = stream->ps_current_write_buffer_index;
+
+ memcpy(stream->ps_decode_buffer[current_write_index].data, (unsigned char*)buf + written, size - written);
+ stream->ps_decode_buffer[current_write_index].write_pos += size - written;
+ stream->ps_decode_buffer[current_write_index].len = stream->ps_decode_buffer[current_write_index].write_pos;
+ written += size - written;
+ }
+ }
+
+
+ if (elnum == 1 && written == size)
+ return 1;
+ else
+ return written / elsize;
+}
+
+static int write_pack(hb_stream_t* stream, int64_t time)
+{
+ unsigned char buf[64];
+ set_buf(buf, 64, 1); // clear buffer
+
+ int64_t ext_time = time % 300;
+ time = time / 300;
+
+ set_bits(0x000001ba, 32); // pack id 32
+ set_bits(1, 2); // 0x01 2
+ set_bits((unsigned int)(time >> 30), 3); // system_clock_reference_base 3
+ set_bits(1, 1); // marker_bit 1
+ set_bits((unsigned int)(time >> 15), 15); // system_clock_reference_base 15
+ set_bits(1, 1); // marker_bit 1
+ set_bits((unsigned int)time, 15); // system_clock_reference_base1 15
+ set_bits(1, 1); // marker_bit 1
+ set_bits((unsigned int)ext_time, 9); // system_clock_reference_extension 9
+ set_bits(1, 1); // marker_bit 1
+ set_bits(DEMUX, 22); // program_mux_rate 22
+ set_bits(1, 1); // marker_bit 1
+ set_bits(1, 1); // marker_bit 1
+ set_bits(31, 5); // reserved 5
+ set_bits(0, 3); // pack_stuffing_length 3
+
+ return fwrite64(buf, buf_size(), 1, stream) == 1;
+}
+
+static int pad_buffer(hb_stream_t *stream, int pad)
+{
+ pad -= 6;
+
+ char buf[6];
+ buf[0] = '\x0'; buf[1] = '\x0'; buf[2] = '\x1'; buf[3] = '\xbe';
+ buf[4] = pad >> 8; buf[5] = pad & 0xff;
+
+ if (fwrite64(buf, 6, 1, stream) != 1)
+ return 0;
+
+ unsigned char padbyte = 0xff;
+ int i=0;
+ for (i = 0; i < pad; i++)
+ {
+ if (fwrite64(&padbyte, 1, 1, stream) != 1)
+ return 0;
+ }
+
+ return 1;
+}
+
+int make_pes_header(unsigned char* buf, int streamid, int len, int64_t PTS, int64_t DTS)
+{
+ int hdrlen = 0;
+ int PTS_DTS_flags = 0;
+ if (PTS != -1)
+ {
+ if (DTS != -1)
+ {
+ PTS_DTS_flags = 3;
+ hdrlen += 10;
+ }
+ else
+ {
+ PTS_DTS_flags = 2;
+ hdrlen += 5;
+ }
+ }
+
+ set_buf(buf, 9 + hdrlen, 1); // clear the buffer
+
+ set_bits(0x000001, 24); // packet_start_code_prefix 24
+ set_bits((unsigned int)streamid, 8); // directory_stream_id 8
+ set_bits(len, 16); // PES_packet_length 16
+ set_bits(0x2, 2); // '10' 2
+ set_bits(0, 2); // PES_scrambling_control 2
+ set_bits(1, 1); // PES_priority 1
+ set_bits(0, 1); // data_alignment_indicator 1
+ set_bits(0, 1); // copyright 1
+ set_bits(0, 1); // original_or_copy 1
+ set_bits(PTS_DTS_flags, 2); // PTS_DTS_flags 2
+ set_bits(0, 1); // ESCR_flag 1
+ set_bits(0, 1); // ES_rate_flag 1
+ set_bits(0, 1); // DSM_trick_mode_flag 1
+ set_bits(0, 1); // additional_copy_info_flag 1
+ set_bits(0, 1); // PES_CRC_flag 1
+ set_bits(0, 1); // PES_extension_flag 1
+ set_bits(hdrlen, 8); // PES_header_data_length 8
+
+ if (PTS_DTS_flags == 2)
+ {
+ set_bits(2, 4); // '0010' 4
+ set_bits((unsigned int)(PTS >> 30), 3); // PTS [32..30] 3
+ set_bits(1, 1); // marker bit 1
+ set_bits((unsigned int)(PTS >> 15), 15); // PTS [29..15] 15
+ set_bits(1, 1); // marker bit 1
+ set_bits((unsigned int)PTS, 15); // PTS [14..0] 15
+ set_bits(1, 1); // marker bit 1
+ }
+ else if (PTS_DTS_flags == 3)
+ {
+ set_bits(3, 4); // '0011' 4
+ set_bits((unsigned int)(PTS >> 30), 3); // PTS [32..30] 3
+ set_bits(1, 1); // marker bit 1
+ set_bits((unsigned int)(PTS >> 15), 15); // PTS [29..15] 15
+ set_bits(1, 1); // marker bit 1
+ set_bits((unsigned int)PTS, 15); // PTS [14..0] 15
+ set_bits(1, 1); // marker bit 1
+ set_bits(1, 4); // '0001' 4
+ set_bits((unsigned int)(DTS >> 30), 3); // DTS [32..30] 3
+ set_bits(1, 1); // marker bit 1
+ set_bits((unsigned int)(DTS >> 15), 15); // DTS [29..15] 15
+ set_bits(1, 1); // marker bit 1
+ set_bits((unsigned int)DTS, 15); // DTS [14..0] 15
+ set_bits(1, 1); // marker bit 1
+ }
+
+ return buf_size();
+}
+
+int generate_output_data(hb_stream_t *stream, int write_ac3, int curstream, int pid)
+{
+ unsigned char ac3_substream_id[4];
+ int ac3len = 0;
+
+ if (write_ac3)
+ {
+ // Make a four byte ac3 streamid
+ ac3_substream_id[0] = 0x80; // Four byte AC3 CODE??
+ ac3_substream_id[1] = 0x01;
+ ac3_substream_id[2] = 0x00; // WHY??? OH WHY??
+ ac3_substream_id[3] = 0x02;
+ ac3len = 4;
+ }
+
+ int written = 0; // Bytes we've written to output file
+ int pos = 0; // Position in PES packet buffer
+
+ for (;;)
+ {
+// int64_t fpos = ftell64(fout);
+ if ((stream->ps_decode_buffer[stream->ps_current_write_buffer_index].len % HB_DVD_READ_BUFFER_SIZE) != 0)
+ {
+ hb_log("write_output_stream - Packet's not falling on read buffer size boundries!");
+ return 1;
+ }
+
+ // Get total length of this pack
+ int len = min(14 + ac3len + stream->ts_packetpos[curstream] - pos, HB_DVD_READ_BUFFER_SIZE);
+
+ // Figure out stuffing (if we have less than 16 bytes left)
+ int stuffing = 0;
+ if (len < HB_DVD_READ_BUFFER_SIZE && HB_DVD_READ_BUFFER_SIZE - len < 16)
+ {
+ stuffing = HB_DVD_READ_BUFFER_SIZE - len;
+ len += stuffing;
+ }
+
+ // Write out pack header
+ off_t file_offset = ftello(stream->file_handle);
+ int64_t packet_time = (file_offset * CLOCKRATE / STREAMRATE) + 0 /*file_time*/;
+ if (!write_pack(stream, packet_time))
+ {
+ hb_log("write_output_stream - Couldn't write pack header!");
+ return 1;
+ }
+
+// if (pid == stream->ts_audio_pids[0])
+// stream->ts_packetbuf[curstream][pos + 3] = stream->ts_streamid[kAudioStream];
+// else
+// stream->ts_packetbuf[curstream][pos + 3] = stream->ts_streamid[kVideoStream];
+ int index_of_selected_pid = -1;
+ if ((index_of_selected_pid = index_of_video_pid(pid,stream)) < 0)
+ {
+ if ((index_of_selected_pid = index_of_audio_pid(pid,stream)) < 0)
+ {
+ hb_log("generate_output_data - cannot find pid 0x%x (%d) in selected audio or video pids", pid, pid);
+ return 0;
+ }
+ else
+ {
+ stream->ts_packetbuf[curstream][pos + 3] = stream->ts_streamid[stream->ts_number_video_pids + index_of_selected_pid];
+ }
+ }
+ else
+ stream->ts_packetbuf[curstream][pos + 3] = stream->ts_streamid[index_of_selected_pid];
+
+ // Packet length..
+ // Subtract pack size (14) and pes id and len (6) from lenth
+ stream->ts_packetbuf[curstream][pos + 4] = (len - 6 - 14) >> 8; stream->ts_packetbuf[curstream][pos + 5] = (len - 6 - 14) & 0xFF;
+
+ // Add any stuffing bytes to header extra len
+ int hdrsize = 9 + stream->ts_packetbuf[curstream][pos + 8];
+ stream->ts_packetbuf[curstream][pos + 8] += stuffing; // Add stuffing to header bytes
+
+ // Write out id, streamid, len
+ if (fwrite64(stream->ts_packetbuf[curstream] + pos, hdrsize, 1, stream) != 1) // Write pes id, streamid, and len
+ {
+ hb_log("write_output_stream - Failed to write output file!");
+ return 1;
+ }
+
+ // Write stuffing
+ int i=0;
+ for (i = 0; i < stuffing; i++) // Write any stuffing bytes
+ {
+ unsigned char stuff = 0xff;
+ if (fwrite64(&stuff, 1, 1, stream) != 1)
+ {
+ hb_log("write_output_stream - Failed to write output file!");
+ return 1;
+ }
+ }
+
+ // Write ac3 streamid
+ if (ac3len != 0)
+ {
+ if (fwrite64(ac3_substream_id, ac3len, 1, stream) != 1)
+ {
+ hb_log("write_output_stream - Failed to write output file!");
+ return 1;
+ }
+ }
+
+ // Write rest of data len minus headersize (9) stuffing, and pack size (14)
+ if (fwrite64(stream->ts_packetbuf[curstream] + pos + hdrsize, len - hdrsize - 14 - stuffing - ac3len, 1, stream) != 1) // Write data bytes
+ {
+ hb_log("write_output_stream - Failed to write output file!");
+ return 1;
+ }
+ written += len;
+
+ // Add len minus stuff we added like the pack (14) and the stuffing.
+ pos += len - 14 - stuffing - ac3len;
+ if (pos == stream->ts_packetpos[curstream])
+ break;
+
+ // Add pes header for next packet
+ pos -= 9;
+// make_pes_header(stream->ts_packetbuf[curstream] + pos, (pid == stream->ts_video_pids[0] ? stream->ts_streamid[kVideoStream] : stream->ts_streamid[kAudioStream]), 0, -1, -1);
+ make_pes_header(stream->ts_packetbuf[curstream] + pos, stream->ts_streamid[curstream], 0, -1, -1);
+ }
+
+ // Write padding
+ if ((written % HB_DVD_READ_BUFFER_SIZE) != 0)
+ {
+ int left = HB_DVD_READ_BUFFER_SIZE - (written % HB_DVD_READ_BUFFER_SIZE);
+
+ // Pad out to HB_DVD_READ_BUFFER_SIZE bytes
+ if (!pad_buffer(stream, left))
+ {
+ hb_log("write_output_stream - Couldn't write pad buffer!");
+ return 1;
+ }
+ }
+
+ stream->ts_packetpos[curstream] = 0;
+ stream->ts_streamcont[curstream] = -1;
+
+ return 0;
+}
+
+static void hb_ts_handle_mpeg_audio(hb_stream_t *stream, int curstream, unsigned char* buf, int adapt_len )
+{
+ // Although we don't have AC3/A52 audio here we can still use the same structure to record this useful information.
+
+ stream->a52_info[curstream - stream->ts_number_video_pids].flags = A52_STEREO;
+ stream->a52_info[curstream - stream->ts_number_video_pids].rate = 48000 /*Hz*/;
+ stream->a52_info[curstream - stream->ts_number_video_pids].bitrate = 384000 /*Bps*/;
+}
+
+static int hb_ts_handle_ac3_audio(hb_stream_t *stream, int curstream, unsigned char* buf, int adapt_len )
+{
+ int spos, dpos;
+
+ // Make sure we start with 0x0b77
+ if (stream->ts_packetbuf[curstream][9 + stream->ts_packetbuf[curstream][8]] != 0x0b || stream->ts_packetbuf[curstream][9 + stream->ts_packetbuf[curstream][8] + 1] != 0x77)
+ {
+ spos = 9 + stream->ts_packetbuf[curstream][8];
+ dpos = 9 + stream->ts_packetbuf[curstream][8];
+ while (spos <= stream->ts_packetpos[curstream] - 2 && !(stream->ts_packetbuf[curstream][spos] == 0x0b && stream->ts_packetbuf[curstream][spos + 1] == 0x77))
+ spos++;
+
+ if (!(stream->ts_packetbuf[curstream][spos] == 0x0b && stream->ts_packetbuf[curstream][spos + 1] == 0x77))
+ {
+ hb_log("hb_ts_stream_decode - Couldn't sync AC3 packet!");
+ stream->ts_skipbad[curstream] = 1;
+ return 0;
+ }
+
+ while (spos < stream->ts_packetpos[curstream])
+ {
+ stream->ts_packetbuf[curstream][dpos] = stream->ts_packetbuf[curstream][spos];
+ spos++;
+ dpos++;
+ }
+ stream->ts_packetpos[curstream] = dpos;
+ }
+
+ // Check the next packet to make sure IT starts with a 0x0b77
+ int plen = 0;
+// if (buf[4 + adapt_len] == 0 && buf[4 + adapt_len + 1] == 0 && // Starting with an mpeg header?
+// buf[4 + adapt_len + 2] == 1 && buf[4 + adapt_len + 3] == 0xBD)
+ plen = 9 + buf[4 + adapt_len + 8];
+ int pstart = 4 + adapt_len + plen;
+ if (buf[pstart] != 0x0b || buf[pstart + 1] != 0x77)
+ {
+ spos = pstart;
+ while (spos < 188 - 2 && !(buf[spos] == 0x0b && buf[spos + 1] == 0x77))
+ {
+ stream->ts_packetbuf[curstream][stream->ts_packetpos[curstream]] = buf[spos];
+ stream->ts_packetpos[curstream]++;
+ spos++;
+ }
+
+ if (!(buf[spos] == 0x0b && buf[spos + 1] == 0x77))
+ {
+ hb_log("hb_ts_stream_decode - Couldn't sync AC3 packet!");
+ stream->ts_skipbad[curstream] = 1;
+ return 0;
+ }
+
+ adapt_len = spos - 4 - plen;
+
+ dpos = spos - 1;
+ spos = pstart - 1;
+ while (spos >= pstart - plen)
+ {
+ buf[dpos] = buf[spos];
+ spos--;
+ dpos--;
+ }
+ }
+
+ int flags, rate, bitrate;
+ if( a52_syncinfo( &buf[pstart], &flags, &rate, &bitrate ) )
+ {
+ stream->a52_info[curstream - stream->ts_number_video_pids].flags = flags;
+ stream->a52_info[curstream - stream->ts_number_video_pids].rate = rate;
+ stream->a52_info[curstream - stream->ts_number_video_pids].bitrate = bitrate;
+ }
+ return 1;
+}
+
+static void hb_ts_stream_find_pids(hb_stream_t *stream)
+{
+ unsigned char buf[188];
+ int curstream = 0;
+
+ // Stream ID info
+ unsigned int program_num = 0;
+ unsigned int network_PID = 0;
+ unsigned int program_map_PID = 0;
+
+ // align to first packet
+ align_to_next_packet(stream->file_handle);
+
+ // Read the Transport Stream Packets (188 bytes each) looking at first for PID 0 (the PAT PID), then decode that
+ // to find the program map PID and then decode that to get the list of audio and video PIDs
+
+ int bytesReadInPacket = 0;
+ for (;;)
+ {
+ // Try to read packet..
+ int bytesRead;
+ if ((bytesRead = fread(buf+bytesReadInPacket, 1, 188-bytesReadInPacket, stream->file_handle)) != 188-bytesReadInPacket)
+ {
+ if (bytesRead < 0)
+ bytesRead = 0;
+ bytesReadInPacket += bytesRead;
+
+ hb_log("hb_ts_stream_find_pids - end of file");
+ break;
+ }
+ else
+ {
+// curfilepos += bytesRead;
+ bytesReadInPacket = 0;
+ }
+
+ // Check sync byte
+ if ((buf[0] != 0x47) && (buf[0] != 0x72) && (buf[0] != 0x29))
+ {
+// __int64 pos = ftell64(fin);
+ hb_log("hb_ts_stream_find_pids - Bad transport packet (no sync byte 0x47)!");
+ int i = 0;
+ for (i=0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
+ stream->ts_skipbad[i] = 1;
+// stream->ts_skipbad[kAudioStream] = stream->ts_skipbad[kVideoStream] = 1;
+ continue;
+ }
+
+ // Get pid
+ int pid = (((buf[1] & 0x1F) << 8) | buf[2]) & 0x1FFF;
+
+ if ((pid == 0x0000) && (program_num == 0))
+ {
+ decode_PAT(buf, &program_num, &network_PID, &program_map_PID);
+ continue;
+ }
+
+ if (pid == 0x1ffb)
+ {
+ printf("Need to decode PSIP data !\n");
+ continue;
+ }
+
+ if ((network_PID > 0) && (pid == network_PID))
+ {
+ printf("Need to Decode network PID section !\n");
+ continue;
+ }
+
+ if ((program_map_PID > 0) && (pid == program_map_PID))
+ {
+ decode_program_map(buf, stream);
+ break;;
+ }
+
+ // Skip until we have a complete set of PIDs
+ if ((stream->ts_number_video_pids == 0) || (stream->ts_number_audio_pids == 0))
+ continue;
+ }
+
+ hb_log("hb_ts_stream_find_pids - found the following PIDS");
+ hb_log(" Video PIDS : ");
+ int i=0;
+ for (i=0; i < stream->ts_number_video_pids; i++)
+ {
+ hb_log(" 0x%x (%d)", stream->ts_video_pids[i], stream->ts_video_pids[i]);
+ }
+ hb_log(" Audio PIDS : ");
+ for (i = 0; i < stream->ts_number_audio_pids; i++)
+ {
+ hb_log(" 0x%x (%d)", stream->ts_audio_pids[i], stream->ts_audio_pids[i]);
+ }
+ }
+
+int index_of_video_pid(int pid, hb_stream_t *stream)
+{
+ int found_pid = -1, i = 0;
+
+ for (i = 0; (i < stream->ts_number_video_pids) && (found_pid < 0); i++)
+ {
+ if (pid == stream->ts_video_pids[i])
+ found_pid = i;
+ }
+ return found_pid;
+}
+
+int index_of_audio_pid(int pid, hb_stream_t *stream)
+{
+ int i = 0, found_pid = -1;
+
+ // If a selected audio pid index has been set it indicates
+ // which of the potential pids we need to output so only return
+ // that index for the appropriate pid. Other pids should just
+ // be ignored.
+ if (stream->ts_selected_audio_pid_index >= 0)
+ {
+ if (pid == stream->ts_audio_pids[stream->ts_selected_audio_pid_index])
+ return stream->ts_selected_audio_pid_index;
+ else
+ return -1;
+ }
+
+ // If no specific pid index is set then we're probably just gathering
+ // pid and/or stream information (during DecodePreviews for example)
+ // so return the appropriate index
+ for (i = 0; (i < stream->ts_number_audio_pids) && (found_pid < 0); i++)
+ {
+ if (pid == stream->ts_audio_pids[i])
+ found_pid = i;
+ }
+ return found_pid;
+}
+
+int index_of_pid(int pid, hb_stream_t *stream)
+{
+ int found_pid = -1;
+
+ if ((found_pid = index_of_video_pid(pid, stream)) >= 0)
+ return found_pid;
+
+ if ((found_pid = index_of_audio_pid(pid, stream)) >= 0)
+ return found_pid;
+
+ return found_pid;
+}
+
+/***********************************************************************
+ * hb_ts_stream_decode
+ ***********************************************************************
+ *
+ **********************************************************************/
+static void hb_ts_stream_decode(hb_stream_t *stream)
+{
+ unsigned char buf[188];
+ int curstream;
+ int doing_iframe;
+
+ int i = 0;
+ for (i=0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
+ {
+// stream->ts_skipbad[kAudioStream] = stream->ts_skipbad[kVideoStream] = 0;
+ stream->ts_skipbad[i] = 0;
+ }
+
+ doing_iframe = 0;
+
+ if ((stream->ts_number_video_pids == 0) || (stream->ts_number_audio_pids == 0))
+ {
+ hb_log("hb_ts_stream_decode - no Video or Audio PID selected, cannot decode transport stream");
+ return;
+ }
+
+ int bytesReadInPacket = 0;
+ int curr_write_buffer_index = stream->ps_current_write_buffer_index;
+
+ // Write output data until a buffer switch occurs.
+ while (curr_write_buffer_index == stream->ps_current_write_buffer_index)
+ {
+ // Try to read packet..
+ int bytesRead;
+ if ((bytesRead = fread(buf+bytesReadInPacket, 1, 188-bytesReadInPacket, stream->file_handle)) != 188-bytesReadInPacket)
+ {
+ if (bytesRead < 0)
+ bytesRead = 0;
+ bytesReadInPacket += bytesRead;
+
+ // Flush any outstanding output data - we're done here.
+ flushbuf(stream);
+ break;
+ }
+ else
+ {
+// curfilepos += bytesRead;
+ bytesReadInPacket = 0;
+ }
+
+ // Check sync byte
+ if ((buf[0] != 0x47) && (buf[0] != 0x72) && (buf[0] != 0x29))
+ {
+// __int64 pos = ftell64(fin);
+ hb_log("hb_ts_stream_decode - Bad transport packet (no sync byte 0x47)!");
+ for (i=0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
+ {
+ // stream->ts_skipbad[kAudioStream] = stream->ts_skipbad[kVideoStream] = 1;
+ stream->ts_skipbad[i] = 1;
+ }
+ continue;
+ }
+
+ // Get pid
+ int pid = (((buf[1] & 0x1F) << 8) | buf[2]) & 0x1FFF;
+
+ // Skip this block
+ if (index_of_pid(pid, stream) < 0)
+ continue;
+// if (pid != stream->ts_audio_pids[0] && pid != stream->ts_video_pids[0])
+// continue;
+
+ // Get the pos and buf - we organize our streams as 'n' video streams then 'm' audio streams
+ int index_of_selected_pid = -1;
+ if ((index_of_selected_pid = index_of_video_pid(pid,stream)) < 0)
+ {
+ // Not a video PID perhaps audio ?
+ if ((index_of_selected_pid = index_of_audio_pid(pid,stream)) < 0)
+ {
+ hb_log("hb_ts_stream_decode - Unknown pid 0x%x (%d)", pid, pid);
+ continue;
+ }
+ else
+ {
+ curstream = stream->ts_number_video_pids + index_of_selected_pid;
+ if (curstream > kMaxNumberDecodeStreams)
+ {
+ hb_log("hb_ts_stream_decode - Too many streams %d", curstream);
+ continue;
+ }
+ }
+ }
+ else
+ curstream = index_of_selected_pid;
+
+// if (pid == stream->ts_video_pids[0])
+// curstream = 0;
+// else
+// curstream = 1;
+
+ // Get start code
+ int start;
+ start = (buf[1] & 0x40) != 0;
+
+ if (!start && stream->ts_skipbad[curstream])
+ continue;
+
+ // Get error
+ int errorbit = (buf[1] & 0x80) != 0;
+ if (errorbit)
+ {
+ hb_log("hb_ts_stream_decode - Error bit set in packet");
+ stream->ts_skipbad[curstream] = 1;
+ continue;
+ }
+
+ // Get adaption header info
+ int adaption = (buf[3] & 0x30) >> 4;
+ int adapt_len = 0;
+
+ // Get continuity
+ int continuity = (buf[3] & 0xF);
+ if ((stream->ts_streamcont[curstream] != -1) && (adaption & 0x01 == 0x01)) // Continuity only increments for adaption values of 0x3 or 0x01
+ {
+ if (continuity != ((stream->ts_streamcont[curstream] + 1) & 0xF))
+ {
+ hb_log("hb_ts_stream_decode - Bad continuity code in packet");
+ stream->ts_skipbad[curstream] = 1;
+ continue;
+ }
+ stream->ts_streamcont[curstream] = continuity;
+ }
+
+ // Get adaption header size
+ if (adaption == 0)
+ {
+ hb_log("hb_ts_stream_decode - Bad adaption code (code was 0)!");
+ for (i=0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
+ {
+ stream->ts_skipbad[i] = 1;
+ }
+ // stream->ts_skipbad[kAudioStream] = stream->ts_skipbad[kVideoStream] = 1;
+ continue;
+ }
+ else if (adaption == 0x2)
+ adapt_len = 184;
+ else if (adaption == 0x3)
+ {
+ adapt_len = buf[4] + 1;
+ if (adapt_len > 184)
+ {
+ hb_log("hb_ts_stream_decode - Invalid adapt len (was > 183)!");
+ for (i=0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
+ {
+ stream->ts_skipbad[i] = 1;
+ }
+// stream->ts_skipbad[kAudioStream] = stream->ts_skipbad[kVideoStream] = 1;
+ }
+ }
+
+ // HBO is slick, it doesn't bother to sync AC3 packets with PES elementary stream packets.. so
+ // we have to swizzle them together! (ARGHH!)
+// if (pid == stream->ts_audio_pids[0] && start)
+ if ((index_of_audio_pid(pid, stream) >= 0) && start)
+ {
+ // Is there an AC3 packet start 0b77 code in this packet??
+ int sync_found = 0;
+ unsigned char *p = buf + 4 + adapt_len;
+ while (p <= buf + 186)
+ {
+ if (p[0] == 0x0b && p[1] == 0x77)
+ {
+ sync_found = 1;
+ break;
+ }
+ p++;
+ }
+
+ // Couldn't find an AC3 sync start in this packet.. don't make a PES packet!
+ if (!sync_found)
+ {
+// int pos = ftell(fin);
+// error("AC3 packet sync not found in start frame");
+// return 1;
+ adapt_len += 9 + buf[4 + adapt_len + 8];
+ start = 0;
+ }
+ }
+
+ // Get PCR
+ if (start && (adaption & 0x2) && (buf[5] & 0x10))
+ {
+ int64_t PCR_base = ((int64_t)buf[6] << 25) | ((int64_t)buf[7] << 17) |
+ ((int64_t)buf[8] << 9) | ((int64_t)buf[9] << 1) | ((int64_t)buf[10] >> 7);
+ int64_t PCR_ext = ((int64_t)(buf[10] & 0x1) << 8) | ((int64_t)buf[11]);
+ int64_t PCR = PCR_base * 300 + PCR_ext;
+ }
+
+ // Get random
+// bool random = false;
+// if (start && (adaption & 0x2))
+// random = (buf[5] & 0x40) != 0; // BUG: SOME TS STREAMS DON'T HAVE THE RANDOM BIT (ABC!! ALIAS)
+
+ // Found a random access point (now we can start a frame/audio packet..)
+ if (start)
+ {
+ // Check to see if this is an i_frame (group of picture start)
+ if (pid == stream->ts_video_pids[0])
+ {
+// printf("Found Video Start for pid 0x%x\n", pid);
+ // Look for the Group of Pictures packet.. indicates this is an I-Frame packet..
+ doing_iframe = 0;
+ unsigned int strid = 0;
+ int i = 4;
+ for (i = 4 + adapt_len; i < 188; i++)
+ {
+ strid = (strid << 8) | buf[i];
+ if (strid == 0x000001B8) // group_start_code
+ {
+ // found a Group of Pictures header, subsequent picture must be an I-frame
+ doing_iframe = 1;
+ }
+ else if (strid == 0x000001B3) // sequence_header code
+ {
+ doing_iframe = 1;
+ }
+ else if (strid == 0x00000100) // picture_start_code
+ {
+ // picture_header, let's see if it's an I-frame
+ if (i<187)
+ {
+// int pic_start_code = (buf[i+2] >> 3) & 0x07;
+// hb_log("hb_ts_stream_decode - picture_start_code header value = 0x%x (%d)", pic_start_code, pic_start_code);
+ // check if picture_coding_type == 1
+ if ((buf[i+2] & (0x7 << 3)) == (1 << 3))
+ {
+ // found an I-frame picture
+ doing_iframe = 1;
+ }
+ }
+ }
+
+ if (doing_iframe)
+ {
+ if (!stream->ts_foundfirst[curstream])
+ {
+ stream->ts_foundfirst[curstream] = 1;
+// first_video_PCR = PCR;
+ }
+ break;
+ }
+ }
+ }
+ else if (index_of_audio_pid(pid, stream) >= 0)
+ {
+ if (stream->ts_foundfirst[0]) // Set audio found first ONLY after first video frame found. There's an assumption here that stream '0' is a video stream
+ {
+ stream->ts_foundfirst[curstream] |= 1;
+ }
+ }
+
+ // If we were skipping a bad packet, start fresh on this new PES packet..
+ if (stream->ts_skipbad[curstream] == 1)
+ {
+ stream->ts_skipbad[curstream] = 0;
+ stream->ts_packetpos[curstream] = 0;
+ }
+
+ // Get the continuity code of this packet
+ stream->ts_streamcont[curstream] = continuity;
+ }
+
+ // Write a 2048 byte program stream packet..
+ if (start && stream->ts_packetpos[curstream] > 0 && stream->ts_foundfirst[curstream] && !stream->ts_skipbad[curstream])
+ {
+ // Save the substream id block so we can added it to subsequent blocks
+ int write_ac3 = 0;
+// if (pid == stream->ts_audio_pids[0] /*&& audstreamid == 0xBD*/)
+ if (index_of_audio_pid(pid, stream) >= 0)
+ {
+ if ((stream->ts_audio_stream_type[curstream] == 0x04) || (stream->ts_audio_stream_type[curstream] == 0x81))
+ {
+ write_ac3 = hb_ts_handle_ac3_audio(stream, curstream, buf, adapt_len);
+ }
+ else if (stream->ts_audio_stream_type[curstream] == 0x03)
+ {
+ hb_ts_handle_mpeg_audio(stream, curstream, buf, adapt_len);
+ }
+ else
+ {
+ hb_log("hb_ts_stream_decode - Unknown Audio Stream type ! 0x%x (%d)", stream->ts_audio_stream_type[curstream], stream->ts_audio_stream_type[curstream]);
+ }
+ }
+
+ if (generate_output_data(stream, write_ac3, curstream, pid) != 0)
+ return ;
+ }
+
+ // Add the payload for this packet to the current buffer
+ if (stream->ts_foundfirst[curstream] && (184 - adapt_len) > 0)
+ {
+ memcpy(stream->ts_packetbuf[curstream] + stream->ts_packetpos[curstream], buf + 4 + adapt_len, 184 - adapt_len);
+ stream->ts_packetpos[curstream] += 184 - adapt_len;
+ }
+ }
+}
+
+/***********************************************************************
+ * hb_ts_stream_reset
+ ***********************************************************************
+ *
+ **********************************************************************/
+static void hb_ts_stream_reset(hb_stream_t *stream)
+{
+ int i=0;
+ for (i=0; i < kNumDecodeBuffers; i++)
+ {
+ stream->ps_decode_buffer[i].read_pos = 0;
+ stream->ps_decode_buffer[i].write_pos = 0;
+ stream->ps_decode_buffer[i].len = 0;
+ }
+
+ for (i=0; i < kMaxNumberDecodeStreams; i++)
+ {
+ stream->ts_streamcont[i] = -1;
+ }
+
+ stream->ps_current_write_buffer_index = 0;
+ stream->ps_current_read_buffer_index = 1;
+
+ align_to_next_packet(stream->file_handle);
+}
+