summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authorjstebbins <[email protected]>2010-09-08 16:51:59 +0000
committerjstebbins <[email protected]>2010-09-08 16:51:59 +0000
commita007891bf9f7e53d3705c51b5b41f9e56a144031 (patch)
treeb7a532c24a458d549afb0174c374e631b896a46e /libhb
parent772c3574758e9bc29cb76bbc86343cf9ad5ddd71 (diff)
Add Bluray support
Unencrypted BD directory trees only. Doesn't support iso images. Also, no PGS subtitle support yet. Chapters and angles are supported. Adds a new contrib libbluray. Adds new option to hb_scan() for duration of short titles to filter. This applies to BD and DVD multi-title scans only. Does not apply to any single title scans. Fixes memory leak during scan. hb_buffer_close() was not freeing all buffers in a chain of buffers passed to it. git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@3510 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb')
-rw-r--r--libhb/bd.c651
-rw-r--r--libhb/common.h18
-rw-r--r--libhb/decavcodec.c20
-rw-r--r--libhb/demuxmpeg.c4
-rw-r--r--libhb/dvd.c16
-rw-r--r--libhb/dvd.h2
-rw-r--r--libhb/dvdnav.c16
-rw-r--r--libhb/fifo.c17
-rw-r--r--libhb/hb.c4
-rw-r--r--libhb/hb.h2
-rw-r--r--libhb/internal.h30
-rw-r--r--libhb/module.defs4
-rw-r--r--libhb/reader.c160
-rw-r--r--libhb/scan.c114
-rw-r--r--libhb/stream.c1125
15 files changed, 1578 insertions, 605 deletions
diff --git a/libhb/bd.c b/libhb/bd.c
new file mode 100644
index 000000000..ca09ce733
--- /dev/null
+++ b/libhb/bd.c
@@ -0,0 +1,651 @@
+/* $Id: dvd.c,v 1.12 2005/11/25 15:05:25 titer Exp $
+
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License. */
+
+#include "hb.h"
+#include "lang.h"
+#include "hbffmpeg.h"
+
+#include "libbluray/bluray.h"
+
+struct hb_bd_s
+{
+ char * path;
+ BLURAY * bd;
+ int title_count;
+ uint64_t pkt_count;
+ hb_stream_t * stream;
+ int chapter;
+ int next_chap;
+};
+
+/***********************************************************************
+ * Local prototypes
+ **********************************************************************/
+static int next_packet( BLURAY *bd, uint8_t *pkt );
+
+/***********************************************************************
+ * hb_bd_init
+ ***********************************************************************
+ *
+ **********************************************************************/
+hb_bd_t * hb_bd_init( char * path )
+{
+ hb_bd_t * d;
+
+ d = calloc( sizeof( hb_bd_t ), 1 );
+
+ /* Open device */
+ d->bd = bd_open( path, NULL );
+ if( d->bd == NULL )
+ {
+ /*
+ * Not an error, may be a stream - which we'll try in a moment.
+ */
+ hb_log( "bd: not a bd - trying as a stream/file instead" );
+ goto fail;
+ }
+
+ d->title_count = bd_get_titles( d->bd, TITLES_RELEVANT );
+ if ( d->title_count == 0 )
+ {
+ hb_log( "bd: not a bd - trying as a stream/file instead" );
+ goto fail;
+ }
+ d->path = strdup( path );
+
+ return d;
+
+fail:
+ if( d->bd ) bd_close( d->bd );
+ free( d );
+ return NULL;
+}
+
+/***********************************************************************
+ * hb_bd_title_count
+ **********************************************************************/
+int hb_bd_title_count( hb_bd_t * d )
+{
+ return d->title_count;
+}
+
+/***********************************************************************
+ * hb_bd_title_scan
+ **********************************************************************/
+hb_title_t * hb_bd_title_scan( hb_bd_t * d, int tt, uint64_t min_duration )
+{
+
+ hb_title_t * title;
+ hb_chapter_t * chapter;
+ int ii;
+ BLURAY_TITLE_INFO * ti = NULL;
+
+ hb_log( "bd: scanning title %d", tt );
+
+ title = hb_title_init( d->path, tt );
+ title->demuxer = HB_MPEG2_TS_DEMUXER;
+ title->type = HB_BD_TYPE;
+ title->reg_desc = STR4_TO_UINT32("HDMV");
+
+ char * p_cur, * p_last = d->path;
+ for( p_cur = d->path; *p_cur; p_cur++ )
+ {
+ if( p_cur[0] == '/' && p_cur[1] )
+ {
+ p_last = &p_cur[1];
+ }
+ }
+ snprintf( title->name, sizeof( title->name ), "%s", p_last );
+ strncpy( title->path, d->path, 1024 );
+ title->path[1023] = 0;
+
+ title->vts = 0;
+ title->ttn = 0;
+
+ ti = bd_get_title_info( d->bd, tt - 1 );
+ if ( ti == NULL )
+ {
+ hb_log( "bd: invalid title" );
+ goto fail;
+ }
+ if ( ti->clip_count == 0 )
+ {
+ hb_log( "bd: stream has no clips" );
+ goto fail;
+ }
+ if ( ti->clips[0].video_stream_count == 0 )
+ {
+ hb_log( "bd: stream has no video" );
+ goto fail;
+ }
+
+ uint64_t pkt_count = 0;
+ for ( ii = 0; ii < ti->clip_count; ii++ )
+ {
+ pkt_count += ti->clips[ii].pkt_count;
+ }
+ title->block_start = 0;
+ title->block_end = pkt_count;
+ title->block_count = pkt_count;
+
+ title->angle_count = ti->angle_count;
+
+ /* ignore short titles because they're often stills */
+ if( ti->duration < min_duration )
+ {
+ hb_log( "bd: ignoring title (too short)" );
+ goto fail;
+ }
+
+ /* Get duration */
+ title->duration = ti->duration;
+ title->hours = title->duration / 90000 / 3600;
+ title->minutes = ( ( title->duration / 90000 ) % 3600 ) / 60;
+ title->seconds = ( title->duration / 90000 ) % 60;
+ hb_log( "bd: duration is %02d:%02d:%02d (%"PRId64" ms)",
+ title->hours, title->minutes, title->seconds,
+ title->duration / 90 );
+
+ BLURAY_STREAM_INFO * bdvideo = &ti->clips[0].video_streams[0];
+
+ title->video_id = bdvideo->pid;
+ title->video_stream_type = bdvideo->coding_type;
+
+ hb_log( "bd: video id=%x, stream type=%s, format %s", title->video_id,
+ bdvideo->coding_type == BLURAY_STREAM_TYPE_VIDEO_MPEG1 ? "MPEG1" :
+ bdvideo->coding_type == BLURAY_STREAM_TYPE_VIDEO_MPEG2 ? "MPEG2" :
+ bdvideo->coding_type == BLURAY_STREAM_TYPE_VIDEO_VC1 ? "VC-1" :
+ bdvideo->coding_type == BLURAY_STREAM_TYPE_VIDEO_H264 ? "H264" :
+ "Unknown",
+ bdvideo->format == BLURAY_VIDEO_FORMAT_480I ? "480i" :
+ bdvideo->format == BLURAY_VIDEO_FORMAT_576I ? "576i" :
+ bdvideo->format == BLURAY_VIDEO_FORMAT_480P ? "480p" :
+ bdvideo->format == BLURAY_VIDEO_FORMAT_1080I ? "1080i" :
+ bdvideo->format == BLURAY_VIDEO_FORMAT_720P ? "720p" :
+ bdvideo->format == BLURAY_VIDEO_FORMAT_1080P ? "1080p" :
+ bdvideo->format == BLURAY_VIDEO_FORMAT_576P ? "576p" :
+ "Unknown"
+ );
+
+ if ( bdvideo->coding_type == BLURAY_STREAM_TYPE_VIDEO_VC1 &&
+ ( bdvideo->format == BLURAY_VIDEO_FORMAT_480I ||
+ bdvideo->format == BLURAY_VIDEO_FORMAT_576I ||
+ bdvideo->format == BLURAY_VIDEO_FORMAT_1080I ) )
+ {
+ hb_log( "bd: Interlaced VC-1 not supported" );
+ goto fail;
+ }
+
+ switch( bdvideo->coding_type )
+ {
+ case BLURAY_STREAM_TYPE_VIDEO_MPEG1:
+ case BLURAY_STREAM_TYPE_VIDEO_MPEG2:
+ title->video_codec = WORK_DECMPEG2;
+ title->video_codec_param = 0;
+ break;
+
+ case BLURAY_STREAM_TYPE_VIDEO_VC1:
+ title->video_codec = WORK_DECAVCODECV;
+ title->video_codec_param = CODEC_ID_VC1;
+ break;
+
+ case BLURAY_STREAM_TYPE_VIDEO_H264:
+ title->video_codec = WORK_DECAVCODECV;
+ title->video_codec_param = CODEC_ID_H264;
+ title->flags |= HBTF_NO_IDR;
+ break;
+
+ default:
+ hb_log( "scan: unknown video codec (%x)",
+ bdvideo->coding_type );
+ goto fail;
+ }
+
+ switch ( bdvideo->aspect )
+ {
+ case BLURAY_ASPECT_RATIO_4_3:
+ title->container_aspect = 4. / 3.;
+ break;
+ case BLURAY_ASPECT_RATIO_16_9:
+ title->container_aspect = 16. / 9.;
+ break;
+ default:
+ hb_log( "bd: unknown aspect" );
+ goto fail;
+ }
+ hb_log( "bd: aspect = %g", title->container_aspect );
+
+ /* Detect audio */
+ // The BD may have clips that have no audio tracks, so scan
+ // the list of clips for one that has audio.
+ int most_audio = 0;
+ int audio_clip_index = 0;
+ for ( ii = 0; ii < ti->clip_count; ii++ )
+ {
+ if ( most_audio < ti->clips[ii].audio_stream_count )
+ {
+ most_audio = ti->clips[ii].audio_stream_count;
+ audio_clip_index = ii;
+ }
+ }
+ // Add all the audios found in the above clip.
+ for ( ii = 0; ii < ti->clips[audio_clip_index].audio_stream_count; ii++ )
+ {
+ hb_audio_t * audio;
+ iso639_lang_t * lang;
+ BLURAY_STREAM_INFO * bdaudio;
+
+ bdaudio = &ti->clips[audio_clip_index].audio_streams[ii];
+
+ hb_log( "bd: checking audio %d", ii + 1 );
+
+ audio = calloc( sizeof( hb_audio_t ), 1 );
+
+ audio->id = bdaudio->pid;
+
+ audio->config.in.stream_type = bdaudio->coding_type;
+ switch( bdaudio->coding_type )
+ {
+ case BLURAY_STREAM_TYPE_AUDIO_AC3:
+ case BLURAY_STREAM_TYPE_AUDIO_TRUHD:
+ audio->config.in.codec = HB_ACODEC_AC3;
+ audio->config.in.codec_param = 0;
+ break;
+
+ case BLURAY_STREAM_TYPE_AUDIO_LPCM:
+ audio->config.in.codec = HB_ACODEC_MPGA;
+ audio->config.in.codec_param = CODEC_ID_PCM_BLURAY;
+ break;
+
+ case BLURAY_STREAM_TYPE_AUDIO_AC3PLUS:
+ audio->config.in.codec = HB_ACODEC_MPGA;
+ audio->config.in.codec_param = CODEC_ID_AC3;
+ break;
+
+ case BLURAY_STREAM_TYPE_AUDIO_MPEG1:
+ case BLURAY_STREAM_TYPE_AUDIO_MPEG2:
+ audio->config.in.codec = HB_ACODEC_MPGA;
+ audio->config.in.codec_param = CODEC_ID_MP2;
+ break;
+
+ case BLURAY_STREAM_TYPE_AUDIO_DTS:
+ case BLURAY_STREAM_TYPE_AUDIO_DTSHD:
+ case BLURAY_STREAM_TYPE_AUDIO_DTSHD_MASTER:
+ audio->config.in.codec = HB_ACODEC_DCA;
+ audio->config.in.codec_param = 0;
+ break;
+
+ default:
+ audio->config.in.codec = 0;
+ hb_log( "scan: unknown audio codec (%x)",
+ bdaudio->coding_type );
+ break;
+ }
+
+ audio->config.lang.type = 0;
+ lang = lang_for_code2( (char*)bdaudio->lang );
+
+ snprintf( audio->config.lang.description,
+ sizeof( audio->config.lang.description ), "%s (%s)",
+ strlen(lang->native_name) ? lang->native_name :
+ lang->eng_name,
+ audio->config.in.codec == HB_ACODEC_AC3 ? "AC3" :
+ ( audio->config.in.codec == HB_ACODEC_DCA ? "DTS" :
+ ( audio->config.in.codec == HB_ACODEC_MPGA ? "MPEG" :
+ "LPCM" ) ) );
+
+ snprintf( audio->config.lang.simple,
+ sizeof( audio->config.lang.simple ), "%s",
+ strlen(lang->native_name) ? lang->native_name :
+ lang->eng_name );
+
+ snprintf( audio->config.lang.iso639_2,
+ sizeof( audio->config.lang.iso639_2 ), "%s", lang->iso639_2);
+
+ hb_log( "bd: audio id=%x, lang=%s, 3cc=%s", audio->id,
+ audio->config.lang.description, audio->config.lang.iso639_2 );
+
+ audio->config.in.track = ii;
+ hb_list_add( title->list_audio, audio );
+ }
+
+ /* Chapters */
+ for ( ii = 0; ii < ti->chapter_count; ii++ )
+ {
+ chapter = calloc( sizeof( hb_chapter_t ), 1 );
+
+ chapter->index = ii + 1;
+ chapter->duration = ti->chapters[ii].duration;
+ chapter->block_start = ti->chapters[ii].offset;
+
+ int seconds;
+ seconds = ( chapter->duration + 45000 ) / 90000;
+ chapter->hours = seconds / 3600;
+ chapter->minutes = ( seconds % 3600 ) / 60;
+ chapter->seconds = seconds % 60;
+
+ hb_log( "bd: chap %d packet=%"PRIu64", %"PRId64" ms",
+ chapter->index,
+ chapter->block_start,
+ chapter->duration / 90 );
+
+ hb_list_add( title->list_chapter, chapter );
+ }
+ hb_log( "bd: title %d has %d chapters", tt, ti->chapter_count );
+
+ /* This title is ok so far */
+ goto cleanup;
+
+fail:
+ hb_list_close( &title->list_audio );
+ free( title );
+ title = NULL;
+
+cleanup:
+ if ( ti ) bd_free_title_info( ti );
+
+ return title;
+}
+
+/***********************************************************************
+ * hb_bd_main_feature
+ **********************************************************************/
+int hb_bd_main_feature( hb_bd_t * d, hb_list_t * list_title )
+{
+ int longest = 0;
+ int ii;
+ uint64_t longest_duration = 0;
+ int highest_rank = 0;
+ int rank[8] = {0, 1, 3, 2, 6, 5, 7, 4};
+ BLURAY_TITLE_INFO * ti;
+
+ for ( ii = 0; ii < hb_list_count( list_title ); ii++ )
+ {
+ hb_title_t * title = hb_list_item( list_title, ii );
+ ti = bd_get_title_info( d->bd, title->index - 1 );
+ if ( ti )
+ {
+ BLURAY_STREAM_INFO * bdvideo = &ti->clips[0].video_streams[0];
+ if ( title->duration > longest_duration * 0.7 && bdvideo->format < 8 )
+ {
+ if (highest_rank < rank[bdvideo->format] ||
+ ( title->duration > longest_duration &&
+ highest_rank == rank[bdvideo->format]))
+ {
+ longest = title->index;
+ longest_duration = title->duration;
+ highest_rank = rank[bdvideo->format];
+ }
+ }
+ bd_free_title_info( ti );
+ }
+ else if ( title->duration > longest_duration )
+ {
+ longest_duration = title->duration;
+ longest = title->index;
+ }
+ }
+ return longest;
+}
+
+/***********************************************************************
+ * hb_bd_start
+ ***********************************************************************
+ * Title and chapter start at 1
+ **********************************************************************/
+int hb_bd_start( hb_bd_t * d, hb_title_t *title )
+{
+ BD_EVENT event;
+
+ d->pkt_count = title->block_count;
+
+ // Calling bd_get_event initializes libbluray event queue.
+ bd_select_title( d->bd, title->index - 1 );
+ bd_get_event( d->bd, &event );
+ d->chapter = 1;
+ d->stream = hb_bd_stream_open( title );
+ if ( d->stream == NULL )
+ {
+ return 0;
+ }
+ return 1;
+}
+
+/***********************************************************************
+ * hb_bd_stop
+ ***********************************************************************
+ *
+ **********************************************************************/
+void hb_bd_stop( hb_bd_t * d )
+{
+ if( d->stream ) hb_stream_close( &d->stream );
+}
+
+/***********************************************************************
+ * hb_bd_seek
+ ***********************************************************************
+ *
+ **********************************************************************/
+int hb_bd_seek( hb_bd_t * d, float f )
+{
+ uint64_t packet = f * d->pkt_count;
+
+ bd_seek(d->bd, packet * 192);
+ return 1;
+}
+
+int hb_bd_seek_pts( hb_bd_t * d, uint64_t pts )
+{
+ bd_seek_time(d->bd, pts);
+ return 1;
+}
+
+int hb_bd_seek_chapter( hb_bd_t * d, int c )
+{
+ int64_t pos;
+ d->next_chap = c;
+ pos = bd_seek_chapter( d->bd, c - 1 );
+ return 1;
+}
+
+/***********************************************************************
+ * hb_bd_read
+ ***********************************************************************
+ *
+ **********************************************************************/
+int hb_bd_read( hb_bd_t * d, hb_buffer_t * b )
+{
+ int result;
+ int error_count = 0;
+ uint8_t buf[192];
+ BD_EVENT event;
+ uint64_t pos;
+
+ while ( 1 )
+ {
+ if ( d->next_chap != d->chapter )
+ {
+ b->new_chap = d->chapter = d->next_chap;
+ }
+ result = next_packet( d->bd, buf );
+ if ( result < 0 )
+ {
+ hb_error("bd: Read Error");
+ pos = bd_tell( d->bd );
+ bd_seek( d->bd, pos + 192 );
+ error_count++;
+ if (error_count > 10)
+ {
+ hb_error("bd: Error, too many consecutive read errors");
+ return 0;
+ }
+ continue;
+ }
+ else if ( result == 0 )
+ {
+ return 0;
+ }
+
+ error_count = 0;
+ while ( bd_get_event( d->bd, &event ) )
+ {
+ switch ( event.event )
+ {
+ case BD_EVENT_CHAPTER:
+ // The muxers expect to only get chapter 2 and above
+ // They write chapter 1 when chapter 2 is detected.
+ d->next_chap = event.param;
+ break;
+
+ default:
+ break;
+ }
+ }
+ // buf+4 to skip the BD timestamp at start of packet
+ result = hb_ts_decode_pkt( d->stream, buf+4, b );
+ if ( result )
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/***********************************************************************
+ * hb_bd_chapter
+ ***********************************************************************
+ * Returns in which chapter the next block to be read is.
+ * Chapter numbers start at 1.
+ **********************************************************************/
+int hb_bd_chapter( hb_bd_t * d )
+{
+ return d->next_chap;
+}
+
+/***********************************************************************
+ * hb_bd_close
+ ***********************************************************************
+ * Closes and frees everything
+ **********************************************************************/
+void hb_bd_close( hb_bd_t ** _d )
+{
+ hb_bd_t * d = *_d;
+
+ if( d->stream ) hb_stream_close( &d->stream );
+ if( d->bd ) bd_close( d->bd );
+
+ free( d );
+ *_d = NULL;
+}
+
+/***********************************************************************
+ * hb_bd_set_angle
+ ***********************************************************************
+ * Sets the angle to read
+ **********************************************************************/
+void hb_bd_set_angle( hb_bd_t * d, int angle )
+{
+
+ if ( !bd_select_angle( d->bd, angle) )
+ {
+ hb_log("bd_select_angle failed");
+ }
+}
+
+static int check_ts_sync(const uint8_t *buf)
+{
+ // must have initial sync byte, no scrambling & a legal adaptation ctrl
+ return (buf[0] == 0x47) && ((buf[3] >> 6) == 0) && ((buf[3] >> 4) > 0);
+}
+
+static int have_ts_sync(const uint8_t *buf, int psize)
+{
+ return check_ts_sync(&buf[0*psize]) && check_ts_sync(&buf[1*psize]) &&
+ check_ts_sync(&buf[2*psize]) && check_ts_sync(&buf[3*psize]) &&
+ check_ts_sync(&buf[4*psize]) && check_ts_sync(&buf[5*psize]) &&
+ check_ts_sync(&buf[6*psize]) && check_ts_sync(&buf[7*psize]);
+}
+
+#define MAX_HOLE 192*80
+
+static uint64_t align_to_next_packet(BLURAY *bd)
+{
+ uint8_t buf[MAX_HOLE];
+ uint64_t pos = 0;
+ uint64_t start = bd_tell(bd);
+ uint64_t orig;
+
+ if ( start >= 192 ) {
+ start -= 192;
+ bd_seek(bd, start);
+ }
+ orig = start;
+
+ while (1)
+ {
+ if (bd_read(bd, buf, sizeof(buf)) == sizeof(buf))
+ {
+ const uint8_t *bp = buf;
+ int i;
+
+ for ( i = sizeof(buf) - 8 * 192; --i >= 0; ++bp )
+ {
+ if ( have_ts_sync( bp, 192 ) )
+ {
+ break;
+ }
+ }
+ if ( i >= 0 )
+ {
+ pos = ( bp - buf );
+ break;
+ }
+ uint64_t next = start + sizeof(buf) - 8 * 192;
+ bd_seek(bd, next);
+ start = bd_tell(bd);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ bd_seek(bd, start+pos);
+ return start - orig + pos;
+}
+
+static int next_packet( BLURAY *bd, uint8_t *pkt )
+{
+ int result;
+
+ while ( 1 )
+ {
+ result = bd_read( bd, pkt, 192 );
+ if ( result < 0 )
+ {
+ return -1;
+ }
+ if ( result < 192 )
+ {
+ return 0;
+ }
+ // Sync byte is byte 4. 0-3 are timestamp.
+ if (pkt[4] == 0x47)
+ {
+ return 1;
+ }
+ // lost sync - back up to where we started then try to re-establish.
+ uint64_t pos = bd_tell(bd) - 192;
+ uint64_t pos2 = align_to_next_packet(bd);
+ if ( pos2 == 0 )
+ {
+ hb_log( "next_packet: eof while re-establishing sync @ %"PRId64, pos );
+ return 0;
+ }
+ hb_log( "next_packet: sync lost @ %"PRId64", regained after %"PRId64" bytes",
+ pos, pos2 );
+ }
+}
+
diff --git a/libhb/common.h b/libhb/common.h
index 278c5f8d1..b59cba515 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -397,6 +397,7 @@ struct hb_audio_config_s
{
int track; /* Input track number */
PRIVATE uint32_t codec; /* Input audio codec */
+ PRIVATE uint32_t stream_type; /* stream type from source stream */
PRIVATE uint32_t codec_param; /* per-codec config info */
PRIVATE uint32_t version; /* Bitsream version */
PRIVATE uint32_t mode; /* Bitstream mode, codec dependent encoding */
@@ -452,9 +453,9 @@ struct hb_chapter_s
int pgn;
int cell_start;
int cell_end;
- int block_start;
- int block_end;
- int block_count;
+ uint64_t block_start;
+ uint64_t block_end;
+ uint64_t block_count;
/* Visual-friendly duration */
int hours;
@@ -544,7 +545,8 @@ struct hb_metadata_s
struct hb_title_s
{
- enum { HB_DVD_TYPE, HB_STREAM_TYPE } type;
+ enum { HB_DVD_TYPE, HB_BD_TYPE, HB_STREAM_TYPE } type;
+ uint32_t reg_desc;
char path[1024];
char name[1024];
int index;
@@ -552,9 +554,9 @@ struct hb_title_s
int ttn;
int cell_start;
int cell_end;
- int block_start;
- int block_end;
- int block_count;
+ uint64_t block_start;
+ uint64_t block_end;
+ uint64_t block_count;
int angle_count;
/* Visual-friendly duration */
@@ -576,8 +578,10 @@ struct hb_title_s
int crop[4];
enum { HB_MPEG2_PS_DEMUXER = 0, HB_MPEG2_TS_DEMUXER, HB_NULL_DEMUXER } demuxer;
int detected_interlacing;
+ int pcr_pid; /* PCR PID for TS streams */
int video_id; /* demuxer stream id for video */
int video_codec; /* worker object id of video codec */
+ uint32_t video_stream_type; /* stream type from source stream */
int video_codec_param; /* codec specific config */
const char *video_codec_name;
int video_bitrate;
diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c
index 09f15ecd8..f5c46699e 100644
--- a/libhb/decavcodec.c
+++ b/libhb/decavcodec.c
@@ -533,7 +533,12 @@ static int reget_frame_buf( AVCodecContext *context, AVFrame *frame )
static void log_chapter( hb_work_private_t *pv, int chap_num, int64_t pts )
{
- hb_chapter_t *c = hb_list_item( pv->job->title->list_chapter, chap_num - 1 );
+ hb_chapter_t *c;
+
+ if ( !pv->job )
+ return;
+
+ c = hb_list_item( pv->job->title->list_chapter, chap_num - 1 );
if ( c && c->title )
{
hb_log( "%s: \"%s\" (%d) at frame %u time %"PRId64,
@@ -648,6 +653,17 @@ static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size, int sequ
buf = copy_frame( pv, &frame );
buf->start = pts;
buf->sequence = sequence;
+ if ( pv->new_chap && buf->start >= pv->chap_time )
+ {
+ buf->new_chap = pv->new_chap;
+ pv->new_chap = 0;
+ pv->chap_time = 0;
+ log_chapter( pv, buf->new_chap, buf->start );
+ }
+ else if ( pv->nframes == 0 && pv->job )
+ {
+ log_chapter( pv, pv->job->chapter_start, buf->start );
+ }
hb_list_add( pv->list, buf );
++pv->nframes;
return got_picture;
@@ -687,7 +703,7 @@ static int decodeFrame( hb_work_private_t *pv, uint8_t *data, int size, int sequ
pv->chap_time = 0;
log_chapter( pv, buf->new_chap, buf->start );
}
- else if ( pv->nframes == 0 )
+ else if ( pv->nframes == 0 && pv->job )
{
log_chapter( pv, pv->job->chapter_start, buf->start );
}
diff --git a/libhb/demuxmpeg.c b/libhb/demuxmpeg.c
index a0c010208..4185cb503 100644
--- a/libhb/demuxmpeg.c
+++ b/libhb/demuxmpeg.c
@@ -263,6 +263,10 @@ int hb_demux_ts( hb_buffer_t *buf_ps, hb_list_t *list_es, hb_psdemux_t *state )
hb_buffer_t *buf = hb_buffer_init( buf_ps->alloc );
hb_buffer_swap_copy( buf_ps, buf );
+ if (buf->type == VIDEO_BUF) {
+ // Consume a chapter break
+ buf_ps->new_chap = 0;
+ }
hb_list_add( list_es, buf );
return 1;
diff --git a/libhb/dvd.c b/libhb/dvd.c
index ee10dd507..5fd0a4ad9 100644
--- a/libhb/dvd.c
+++ b/libhb/dvd.c
@@ -16,7 +16,7 @@ static hb_dvd_t * hb_dvdread_init( char * path );
static void hb_dvdread_close( hb_dvd_t ** _d );
static char * hb_dvdread_name( char * path );
static int hb_dvdread_title_count( hb_dvd_t * d );
-static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * d, int t );
+static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * d, int t, uint64_t min_duration );
static int hb_dvdread_start( hb_dvd_t * d, hb_title_t *title, int chapter );
static void hb_dvdread_stop( hb_dvd_t * d );
static int hb_dvdread_seek( hb_dvd_t * d, float f );
@@ -162,7 +162,7 @@ static int hb_dvdread_title_count( hb_dvd_t * e )
/***********************************************************************
* hb_dvdread_title_scan
**********************************************************************/
-static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * e, int t )
+static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * e, int t, uint64_t min_duration )
{
hb_dvdread_t *d = &(e->dvdread);
@@ -299,8 +299,8 @@ static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * e, int t )
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,
+ hb_log( "scan: vts=%d, ttn=%d, cells=%d->%d, blocks=%"PRIu64"->%"PRIu64", "
+ "%"PRIu64" blocks", title->vts, title->ttn, title->cell_start,
title->cell_end, title->block_start, title->block_end,
title->block_count );
@@ -316,7 +316,7 @@ static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * e, int t )
/* ignore titles under 10 seconds because they're often stills or
* clips with no audio & our preview code doesn't currently handle
* either of these. */
- if( title->duration < 900000LL )
+ if( title->duration < min_duration )
{
hb_log( "scan: ignoring title (too short)" );
goto fail;
@@ -624,7 +624,7 @@ static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * e, int t )
chapter->minutes = ( seconds % 3600 ) / 60;
chapter->seconds = seconds % 60;
- hb_log( "scan: chap %d c=%d->%d, b=%d->%d (%d), %"PRId64" ms",
+ hb_log( "scan: chap %d c=%d->%d, b=%"PRIu64"->%"PRIu64" (%"PRIu64"), %"PRId64" ms",
chapter->index, chapter->cell_start, chapter->cell_end,
chapter->block_start, chapter->block_end,
chapter->block_count, chapter->duration / 90 );
@@ -1268,9 +1268,9 @@ int hb_dvd_title_count( hb_dvd_t * d )
return dvd_methods->title_count(d);
}
-hb_title_t * hb_dvd_title_scan( hb_dvd_t * d, int t )
+hb_title_t * hb_dvd_title_scan( hb_dvd_t * d, int t, uint64_t min_duration )
{
- return dvd_methods->title_scan(d, t);
+ return dvd_methods->title_scan(d, t, min_duration);
}
int hb_dvd_start( hb_dvd_t * d, hb_title_t *title, int chapter )
diff --git a/libhb/dvd.h b/libhb/dvd.h
index 79cf4e888..95f85b1f8 100644
--- a/libhb/dvd.h
+++ b/libhb/dvd.h
@@ -71,7 +71,7 @@ struct hb_dvd_func_s
void (* close) ( hb_dvd_t ** );
char * (* name) ( char * );
int (* title_count) ( hb_dvd_t * );
- hb_title_t * (* title_scan) ( hb_dvd_t *, int );
+ hb_title_t * (* title_scan) ( hb_dvd_t *, int, uint64_t );
int (* start) ( hb_dvd_t *, hb_title_t *, int );
void (* stop) ( hb_dvd_t * );
int (* seek) ( hb_dvd_t *, float );
diff --git a/libhb/dvdnav.c b/libhb/dvdnav.c
index 67e7b8b18..5f30dedf0 100644
--- a/libhb/dvdnav.c
+++ b/libhb/dvdnav.c
@@ -18,7 +18,7 @@
static char * hb_dvdnav_name( char * path );
static hb_dvd_t * hb_dvdnav_init( char * path );
static int hb_dvdnav_title_count( hb_dvd_t * d );
-static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * d, int t );
+static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * d, int t, uint64_t min_duration );
static int hb_dvdnav_start( hb_dvd_t * d, hb_title_t *title, int chapter );
static void hb_dvdnav_stop( hb_dvd_t * d );
static int hb_dvdnav_seek( hb_dvd_t * d, float f );
@@ -289,7 +289,7 @@ PttDuration(ifo_handle_t *ifo, int ttn, int pttn, int *blocks, int *last_pgcn)
/***********************************************************************
* hb_dvdnav_title_scan
**********************************************************************/
-static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t )
+static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t, uint64_t min_duration )
{
hb_dvdnav_t * d = &(e->dvdnav);
@@ -418,7 +418,7 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t )
/* ignore titles under 10 seconds because they're often stills or
* clips with no audio & our preview code doesn't currently handle
* either of these. */
- if( longest < 900000LL )
+ if( longest < min_duration )
{
hb_log( "scan: ignoring title (too short)" );
goto fail;
@@ -456,8 +456,8 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t )
title->cell_end = pgc->nr_of_cells - 1;
title->block_end = pgc->cell_playback[title->cell_end].last_sector;
- hb_log( "scan: vts=%d, ttn=%d, cells=%d->%d, blocks=%d->%d, "
- "%d blocks", title->vts, title->ttn, title->cell_start,
+ hb_log( "scan: vts=%d, ttn=%d, cells=%d->%d, blocks=%"PRIu64"->%"PRIu64", "
+ "%"PRIu64" blocks", title->vts, title->ttn, title->cell_start,
title->cell_end, title->block_start, title->block_end,
title->block_count );
@@ -782,7 +782,7 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t )
chapter->minutes = ( seconds % 3600 ) / 60;
chapter->seconds = seconds % 60;
- hb_log( "scan: chap %d c=%d->%d, b=%d->%d (%d), %"PRId64" ms",
+ hb_log( "scan: chap %d c=%d->%d, b=%"PRIu64"->%"PRIu64" (%"PRIu64"), %"PRId64" ms",
chapter->index, chapter->cell_start, chapter->cell_end,
chapter->block_start, chapter->block_end,
chapter->block_count, chapter->duration / 90 );
@@ -1246,8 +1246,8 @@ static int hb_dvdnav_main_feature( hb_dvd_t * e, hb_list_t * list_title )
{
hb_dvdnav_t * d = &(e->dvdnav);
int longest_root;
- int longest_title;
- int longest_fallback;
+ int longest_title = 0;
+ int longest_fallback = 0;
int ii;
uint64_t longest_duration_root = 0;
uint64_t longest_duration_title = 0;
diff --git a/libhb/fifo.c b/libhb/fifo.c
index 3aeeea273..a29e84696 100644
--- a/libhb/fifo.c
+++ b/libhb/fifo.c
@@ -202,14 +202,19 @@ void hb_buffer_close( hb_buffer_t ** _b )
return;
}
/* either the pool is full or this size doesn't use a pool - free the buf */
- if( b->data )
+ while( b )
{
- free( b->data );
- hb_lock(buffers.lock);
- buffers.allocated -= b->alloc;
- hb_unlock(buffers.lock);
+ hb_buffer_t * next = b->next;
+ if( b->data )
+ {
+ free( b->data );
+ hb_lock(buffers.lock);
+ buffers.allocated -= b->alloc;
+ hb_unlock(buffers.lock);
+ }
+ free( b );
+ b = next;
}
- free( b );
*_b = NULL;
}
diff --git a/libhb/hb.c b/libhb/hb.c
index d466d83c7..11b80357e 100644
--- a/libhb/hb.c
+++ b/libhb/hb.c
@@ -539,7 +539,7 @@ void hb_remove_previews( hb_handle_t * h )
* @param store_previews Whether or not to write previews to disk.
*/
void hb_scan( hb_handle_t * h, const char * path, int title_index,
- int preview_count, int store_previews )
+ int preview_count, int store_previews, uint64_t min_duration )
{
hb_title_t * title;
@@ -556,7 +556,7 @@ void hb_scan( hb_handle_t * h, const char * path, int title_index,
hb_log( "hb_scan: path=%s, title_index=%d", path, title_index );
h->scan_thread = hb_scan_init( h, &h->scan_die, path, title_index,
h->list_title, preview_count,
- store_previews );
+ store_previews, min_duration );
}
/**
diff --git a/libhb/hb.h b/libhb/hb.h
index d3e2b5d8a..6de2fcb74 100644
--- a/libhb/hb.h
+++ b/libhb/hb.h
@@ -42,7 +42,7 @@ void hb_dvd_set_dvdnav( int enable );
a VOB file. If title_index is 0, scan all titles. */
void hb_scan( hb_handle_t *, const char * path,
int title_index, int preview_count,
- int store_previews );
+ int store_previews, uint64_t min_duration );
void hb_scan_stop( hb_handle_t * );
hb_filter_object_t * hb_get_filter_object(int filter_id, const char * settings);
uint64_t hb_first_duration( hb_handle_t * );
diff --git a/libhb/internal.h b/libhb/internal.h
index 135d73fcc..fe1cdca26 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -64,6 +64,8 @@ struct hb_buffer_s
*/
int64_t sequence;
+ enum { AUDIO_BUF, VIDEO_BUF, SUBTITLE_BUF, OTHER_BUF } type;
+
int id; // ID of the track that the packet comes from
int64_t start; // Video and subtitle packets: start time of frame/subtitle
int64_t stop; // Video and subtitle packets: stop time of frame/subtitle
@@ -164,7 +166,7 @@ hb_thread_t * hb_update_init( int * build, char * version );
hb_thread_t * hb_scan_init( hb_handle_t *, volatile int * die,
const char * path, int title_index,
hb_list_t * list_title, int preview_count,
- int store_previews );
+ int store_previews, uint64_t min_duration );
hb_thread_t * hb_work_init( hb_list_t * jobs, int cpu_count,
volatile int * die, int * error, hb_job_t ** job );
hb_thread_t * hb_reader_init( hb_job_t * );
@@ -214,12 +216,13 @@ hb_title_t * hb_batch_title_scan( hb_batch_t * d, int t );
/***********************************************************************
* dvd.c
**********************************************************************/
+typedef struct hb_bd_s hb_bd_t;
typedef union 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 * );
-hb_title_t * hb_dvd_title_scan( hb_dvd_t *, int title );
+hb_title_t * hb_dvd_title_scan( hb_dvd_t *, int title, uint64_t min_duration );
int hb_dvd_start( hb_dvd_t *, hb_title_t *title, int chapter );
void hb_dvd_stop( hb_dvd_t * );
int hb_dvd_seek( hb_dvd_t *, float );
@@ -231,6 +234,21 @@ int hb_dvd_angle_count( hb_dvd_t * d );
void hb_dvd_set_angle( hb_dvd_t * d, int angle );
int hb_dvd_main_feature( hb_dvd_t * d, hb_list_t * list_title );
+hb_bd_t * hb_bd_init( char * path );
+int hb_bd_title_count( hb_bd_t * d );
+hb_title_t * hb_bd_title_scan( hb_bd_t * d, int t, uint64_t min_duration );
+int hb_bd_start( hb_bd_t * d, hb_title_t *title );
+void hb_bd_stop( hb_bd_t * d );
+int hb_bd_seek( hb_bd_t * d, float f );
+int hb_bd_seek_pts( hb_bd_t * d, uint64_t pts );
+int hb_bd_seek_chapter( hb_bd_t * d, int chapter );
+int hb_bd_read( hb_bd_t * d, hb_buffer_t * b );
+int hb_bd_chapter( hb_bd_t * d );
+void hb_bd_close( hb_bd_t ** _d );
+void hb_bd_set_angle( hb_bd_t * d, int angle );
+int hb_bd_main_feature( hb_bd_t * d, hb_list_t * list_title );
+
+hb_stream_t * hb_bd_stream_open( hb_title_t *title );
hb_stream_t * hb_stream_open( char * path, hb_title_t *title );
void hb_stream_close( hb_stream_t ** );
hb_title_t * hb_stream_title_scan( hb_stream_t *);
@@ -240,10 +258,18 @@ int hb_stream_seek_ts( hb_stream_t * stream, int64_t ts );
int hb_stream_seek_chapter( hb_stream_t *, int );
int hb_stream_chapter( hb_stream_t * );
+int hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt, hb_buffer_t *obuf );
+
void * hb_ffmpeg_context( int codec_param );
void * hb_ffmpeg_avstream( int codec_param );
+#define STR4_TO_UINT32(p) \
+ ((((const uint8_t*)(p))[0] << 24) | \
+ (((const uint8_t*)(p))[1] << 16) | \
+ (((const uint8_t*)(p))[2] << 8) | \
+ ((const uint8_t*)(p))[3])
+
/***********************************************************************
* Work objects
**********************************************************************/
diff --git a/libhb/module.defs b/libhb/module.defs
index 8ea22c3af..b4b1964c9 100644
--- a/libhb/module.defs
+++ b/libhb/module.defs
@@ -1,6 +1,6 @@
__deps__ := A52DEC BZIP2 FAAC FAAD2 FFMPEG LAME LIBDCA \
LIBDVDREAD LIBDVDNAV LIBICONV LIBMKV LIBOGG LIBSAMPLERATE LIBTHEORA LIBVORBIS \
- MP4V2 MPEG2DEC PTHREADW32 X264 ZLIB
+ MP4V2 MPEG2DEC PTHREADW32 X264 ZLIB LIBBLURAY
$(eval $(call import.MODULE.defs,LIBHB,libhb,$(__deps__)))
$(eval $(call import.GCC,LIBHB))
@@ -90,7 +90,7 @@ LIBHB.lib = $(LIBHB.build/)hb.lib
LIBHB.dll.libs = $(foreach n, \
a52 avcodec avformat avutil dca dvdnav dvdread faac faad mkv mpeg2 mp3lame mp4v2 \
- ogg samplerate swscale theora vorbis vorbisenc x264, \
+ ogg samplerate swscale theora vorbis vorbisenc x264 bluray, \
$(CONTRIB.build/)lib/lib$(n).a )
ifneq ($(HAS.iconv),1)
diff --git a/libhb/reader.c b/libhb/reader.c
index fd08b6ae3..f6c334172 100644
--- a/libhb/reader.c
+++ b/libhb/reader.c
@@ -12,6 +12,7 @@ typedef struct
int64_t last; // last timestamp seen on this stream
int id; // stream id
int is_audio; // != 0 if this is an audio stream
+ int valid; // Stream timing is not valid until next scr.
} stream_timing_t;
typedef struct
@@ -20,6 +21,7 @@ typedef struct
hb_title_t * title;
volatile int * die;
+ hb_bd_t * bd;
hb_dvd_t * dvd;
hb_stream_t * stream;
@@ -65,6 +67,7 @@ hb_thread_t * hb_reader_init( hb_job_t * job )
r->stream_timing[0].average = 90000. * (double)job->vrate_base /
(double)job->vrate;
r->stream_timing[0].last = -r->stream_timing[0].average;
+ r->stream_timing[0].valid = 1;
r->stream_timing[1].id = -1;
if ( !job->pts_to_start )
@@ -124,7 +127,7 @@ static stream_timing_t *find_st( hb_reader_t *r, const hb_buffer_t *buf )
// find or create the per-stream timing state for 'buf'
-static stream_timing_t *id_to_st( hb_reader_t *r, const hb_buffer_t *buf )
+static stream_timing_t *id_to_st( hb_reader_t *r, const hb_buffer_t *buf, int valid )
{
stream_timing_t *st = r->stream_timing;
while ( st->id != buf->id && st->id != -1)
@@ -147,15 +150,13 @@ static stream_timing_t *id_to_st( hb_reader_t *r, const hb_buffer_t *buf )
}
st->id = buf->id;
st->average = 30.*90.;
- if ( r->saw_video )
- st->last = buf->renderOffset - st->average;
- else
- st->last = -st->average;
+ st->last = -st->average;
if ( ( st->is_audio = is_audio( r, buf->id ) ) != 0 )
{
r->saw_audio = 1;
}
st[1].id = -1;
+ st->valid = valid;
}
return st;
}
@@ -165,7 +166,7 @@ static stream_timing_t *id_to_st( hb_reader_t *r, const hb_buffer_t *buf )
static void update_ipt( hb_reader_t *r, const hb_buffer_t *buf )
{
- stream_timing_t *st = id_to_st( r, buf );
+ stream_timing_t *st = id_to_st( r, buf, 1 );
double dt = buf->renderOffset - st->last;
// Protect against spurious bad timestamps
if ( dt > -5 * 90000LL && dt < 5 * 90000LL )
@@ -173,6 +174,7 @@ static void update_ipt( hb_reader_t *r, const hb_buffer_t *buf )
st->average += ( dt - st->average ) * (1./32.);
st->last = buf->renderOffset;
}
+ st->valid = 1;
}
// use the per-stream state associated with 'buf' to compute a new scr_offset
@@ -181,9 +183,25 @@ static void update_ipt( hb_reader_t *r, const hb_buffer_t *buf )
static void new_scr_offset( hb_reader_t *r, hb_buffer_t *buf )
{
- stream_timing_t *st = id_to_st( r, buf );
- int64_t nxt = st->last + st->average;
+ stream_timing_t *st = id_to_st( r, buf, 1 );
+ int64_t last;
+ if ( !st->valid )
+ {
+ // !valid means we've not received any previous data
+ // for this stream. There is no 'last' packet time.
+ // So approximate it with video's last time.
+ last = r->stream_timing[0].last;
+ st->valid = 1;
+ }
+ else
+ {
+ last = st->last;
+ }
+ int64_t nxt = last + st->average;
r->scr_offset = buf->renderOffset - nxt;
+ // This log is handy when you need to debug timing problems...
+ //hb_log("id %x last %ld avg %g nxt %ld renderOffset %ld scr_offset %ld",
+ // buf->id, last, st->average, nxt, buf->renderOffset, r->scr_offset);
r->scr_changes = r->demux.scr_changes;
st->last = nxt;
}
@@ -203,7 +221,12 @@ static void ReaderFunc( void * _r )
int chapter = -1;
int chapter_end = r->job->chapter_end;
- if ( r->title->type == HB_DVD_TYPE )
+ if ( r->title->type == HB_BD_TYPE )
+ {
+ if ( !( r->bd = hb_bd_init( r->title->path ) ) )
+ return;
+ }
+ else if ( r->title->type == HB_DVD_TYPE )
{
if ( !( r->dvd = hb_dvd_init( r->title->path ) ) )
return;
@@ -220,7 +243,34 @@ static void ReaderFunc( void * _r )
}
hb_buffer_t *ps = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE );
- if (r->dvd)
+ if (r->bd)
+ {
+ if( !hb_bd_start( r->bd, r->title ) )
+ {
+ hb_bd_close( &r->bd );
+ hb_buffer_close( &ps );
+ return;
+ }
+ if ( r->job->start_at_preview )
+ {
+ // XXX code from DecodePreviews - should go into its own routine
+ hb_bd_seek( r->bd, (float)r->job->start_at_preview /
+ ( r->job->seek_points ? ( r->job->seek_points + 1.0 ) : 11.0 ) );
+ }
+ else if ( r->job->pts_to_start )
+ {
+ hb_bd_seek_pts( r->bd, r->job->pts_to_start );
+ }
+ else
+ {
+ hb_bd_seek_chapter( r->bd, r->job->chapter_start );
+ }
+ if (r->job->angle > 1)
+ {
+ hb_bd_set_angle( r->bd, r->job->angle - 1 );
+ }
+ }
+ else if (r->dvd)
{
/*
* XXX this code is a temporary hack that should go away if/when
@@ -315,7 +365,9 @@ static void ReaderFunc( void * _r )
while( !*r->die && !r->job->done )
{
- if (r->dvd)
+ if (r->bd)
+ chapter = hb_bd_chapter( r->bd );
+ else if (r->dvd)
chapter = hb_dvd_chapter( r->dvd );
else if (r->stream)
chapter = hb_stream_chapter( r->stream );
@@ -332,7 +384,14 @@ static void ReaderFunc( void * _r )
break;
}
- if (r->dvd)
+ if (r->bd)
+ {
+ if( !hb_bd_read( r->bd, ps ) )
+ {
+ break;
+ }
+ }
+ else if (r->dvd)
{
if( !hb_dvd_read( r->dvd, ps ) )
{
@@ -399,7 +458,7 @@ static void ReaderFunc( void * _r )
r->scr_changes = r->demux.scr_changes - 1;
// create a stream state if we don't have one so the
// offset will get computed correctly.
- id_to_st( r, buf );
+ id_to_st( r, buf, 1 );
r->saw_video = 1;
hb_log( "reader: first SCR %"PRId64" id %d DTS %"PRId64,
r->demux.last_scr, buf->id, buf->renderOffset );
@@ -417,52 +476,38 @@ static void ReaderFunc( void * _r )
{
// This is the first audio or video packet after an SCR
// change. Compute a new scr offset that would make this
- // packet follow the last of this stream with the correct
- // average spacing.
- stream_timing_t *st = find_st( r, buf );
-
- if ( st )
+ // packet follow the last of this stream with the
+ // correct average spacing.
+ stream_timing_t *st = id_to_st( r, buf, 0 );
+
+ // if this is the video stream and we don't have
+ // audio yet or this is an audio stream
+ // generate a new scr
+ if ( st->is_audio ||
+ ( st == r->stream_timing && !r->saw_audio ) )
+ {
+ new_scr_offset( r, buf );
+ }
+ else
{
- // if this is the video stream and we don't have
- // audio yet or this is an audio stream
- // generate a new scr
- if ( st->is_audio ||
- ( st == r->stream_timing && !r->saw_audio ) )
+ // defer the scr change until we get some
+ // audio since audio has a timestamp per
+ // frame but video & subtitles don't. Clear
+ // the timestamps so the decoder will generate
+ // them from the frame durations.
+ if ( st != r->stream_timing )
{
- new_scr_offset( r, buf );
+ // not a video stream so it's probably
+ // subtitles - the best we can do is to
+ // line it up with the last video packet.
+ buf->start = r->stream_timing->last;
}
else
{
- // defer the scr change until we get some
- // audio since audio has a timestamp per
- // frame but video & subtitles don't. Clear
- // the timestamps so the decoder will generate
- // them from the frame durations.
- if ( st != r->stream_timing )
- {
- // not a video stream so it's probably
- // subtitles - the best we can do is to
- // line it up with the last video packet.
- buf->start = r->stream_timing->last;
- }
- else
- {
- buf->start = -1;
- buf->renderOffset = -1;
- }
+ buf->start = -1;
+ buf->renderOffset = -1;
}
}
- else
- {
- // we got a new scr at the same time as the first
- // packet of a stream we've never seen before. We
- // have no idea what the timing should be so toss
- // this buffer & wait for a stream we've already seen.
- // add stream to list of streams we have seen
- id_to_st( r, buf );
- hb_buffer_close( &buf );
- continue;
- }
}
}
if ( buf->start != -1 )
@@ -490,6 +535,10 @@ static void ReaderFunc( void * _r )
r->job->pts_to_start = 0;
}
}
+ // This log is handy when you need to debug timing problems
+ //hb_log("id %x scr_offset %ld start %ld --> %ld",
+ // buf->id, r->scr_offset, buf->start,
+ // buf->start - r->scr_offset);
buf->start -= r->scr_offset;
}
if ( buf->renderOffset != -1 )
@@ -553,7 +602,12 @@ static void ReaderFunc( void * _r )
hb_list_empty( &list );
hb_buffer_close( &ps );
- if (r->dvd)
+ if (r->bd)
+ {
+ hb_bd_stop( r->bd );
+ hb_bd_close( &r->bd );
+ }
+ else if (r->dvd)
{
hb_dvd_stop( r->dvd );
hb_dvd_close( &r->dvd );
diff --git a/libhb/scan.c b/libhb/scan.c
index 68188a253..1bbb241e9 100644
--- a/libhb/scan.c
+++ b/libhb/scan.c
@@ -19,13 +19,16 @@ typedef struct
int title_index;
hb_list_t * list_title;
+ hb_bd_t * bd;
hb_dvd_t * dvd;
hb_stream_t * stream;
hb_batch_t * batch;
-
+
int preview_count;
int store_previews;
+ uint64_t min_title_duration;
+
} hb_scan_t;
static void ScanFunc( void * );
@@ -48,7 +51,7 @@ static const char *aspect_to_string( double aspect )
hb_thread_t * hb_scan_init( hb_handle_t * handle, volatile int * die,
const char * path, int title_index,
hb_list_t * list_title, int preview_count,
- int store_previews )
+ int store_previews, uint64_t min_duration )
{
hb_scan_t * data = calloc( sizeof( hb_scan_t ), 1 );
@@ -60,6 +63,7 @@ hb_thread_t * hb_scan_init( hb_handle_t * handle, volatile int * die,
data->preview_count = preview_count;
data->store_previews = store_previews;
+ data->min_title_duration = min_duration;
return hb_thread_init( "scan", ScanFunc, data, HB_NORMAL_PRIORITY );
}
@@ -71,12 +75,33 @@ static void ScanFunc( void * _data )
int i;
int feature = 0;
- data->dvd = NULL;
- data->stream = NULL;
+ data->bd = NULL;
+ 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 ) ) )
+ if( ( data->bd = hb_bd_init( data->path ) ) )
+ {
+ hb_log( "scan: BD has %d title(s)",
+ hb_bd_title_count( data->bd ) );
+ if( data->title_index )
+ {
+ /* Scan this title only */
+ hb_list_add( data->list_title, hb_bd_title_scan( data->bd,
+ data->title_index, 0 ) );
+ }
+ else
+ {
+ /* Scan all titles */
+ for( i = 0; i < hb_bd_title_count( data->bd ); i++ )
+ {
+ hb_list_add( data->list_title, hb_bd_title_scan( data->bd,
+ i + 1, data->min_title_duration ) );
+ }
+ feature = hb_bd_main_feature( data->bd, data->list_title );
+ }
+ }
+ else if( ( data->dvd = hb_dvd_init( data->path ) ) )
{
hb_log( "scan: DVD has %d title(s)",
hb_dvd_title_count( data->dvd ) );
@@ -84,15 +109,15 @@ static void ScanFunc( void * _data )
{
/* Scan this title only */
hb_list_add( data->list_title, hb_dvd_title_scan( data->dvd,
- data->title_index ) );
+ data->title_index, 0 ) );
}
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 ) );
+ hb_list_add( data->list_title, hb_dvd_title_scan( data->dvd,
+ i + 1, data->min_title_duration ) );
}
feature = hb_dvd_main_feature( data->dvd, data->list_title );
}
@@ -132,7 +157,7 @@ static void ScanFunc( void * _data )
if ( *data->die )
{
- goto finish;
+ goto finish;
}
title = hb_list_item( data->list_title, i );
@@ -140,7 +165,9 @@ static void ScanFunc( void * _data )
/* Update the UI */
state.state = HB_STATE_SCANNING;
p.title_cur = title->index;
- p.title_count = data->dvd ? hb_dvd_title_count( data->dvd ) : hb_list_count(data->list_title);
+ p.title_count = data->dvd ? hb_dvd_title_count( data->dvd ) :
+ data->bd ? hb_bd_title_count( data->bd ) :
+ hb_list_count(data->list_title);
hb_set_state( data->h, &state );
#undef p
@@ -173,7 +200,7 @@ static void ScanFunc( void * _data )
j++;
}
- if ( data->dvd )
+ if ( data->dvd || data->bd )
{
// The subtitle width and height needs to be set to the
// title widht and height for DVDs. title width and
@@ -253,14 +280,18 @@ static void ScanFunc( void * _data )
finish:
+ if( data->bd )
+ {
+ hb_bd_close( &data->bd );
+ }
if( data->dvd )
{
hb_dvd_close( &data->dvd );
}
- if (data->stream)
- {
- hb_stream_close(&data->stream);
- }
+ if (data->stream)
+ {
+ hb_stream_close(&data->stream);
+ }
if( data->batch )
{
hb_batch_close( &data->batch );
@@ -432,7 +463,12 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
hb_log( "scan: decoding previews for title %d", title->index );
- if (data->dvd)
+ if (data->bd)
+ {
+ hb_bd_start( data->bd, title );
+ hb_log( "scan: title angle(s) %d", title->angle_count );
+ }
+ else if (data->dvd)
{
hb_dvd_start( data->dvd, title, 1 );
title->angle_count = hb_dvd_angle_count( data->dvd );
@@ -453,9 +489,16 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
{
return 0;
}
+ if (data->bd)
+ {
+ if( !hb_bd_seek( data->bd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) )
+ {
+ continue;
+ }
+ }
if (data->dvd)
{
- if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) )
+ if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) )
{
continue;
}
@@ -493,10 +536,26 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
{
vidskip = (double)title->rate / (double)title->rate_base + 0.5;
}
+ // If it's a BD, we can relax this a bit. Since seeks will
+ // at least get us to a recovery point.
+ if (data->bd)
+ vidskip = 4;
}
for( j = 0; j < 10240 ; j++ )
{
+ if (data->bd)
+ {
+ if( !hb_bd_read( data->bd, buf_ps ) )
+ {
+ if ( vid_buf )
+ {
+ break;
+ }
+ hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 );
+ goto skip_preview;
+ }
+ }
if (data->dvd)
{
if( !hb_dvd_read( data->dvd, buf_ps ) )
@@ -533,8 +592,13 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
{
// we're dropping frames to get the video decoder in sync
// when the video stream doesn't contain IDR frames
- hb_buffer_close( &vid_buf );
- vid_buf = NULL;
+ while (vid_buf && --vidskip >= 0)
+ {
+ hb_buffer_t * next = vid_buf->next;
+ vid_buf->next = NULL;
+ hb_buffer_close( &vid_buf );
+ vid_buf = next;
+ }
}
}
else if( ! AllAudioOK( title ) )
@@ -565,6 +629,10 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title )
* Could not fill vid_info, don't continue and try to use vid_info
* in this case.
*/
+ if (vid_buf)
+ {
+ hb_buffer_close( &vid_buf );
+ }
vid_decoder->close( vid_decoder );
free( vid_decoder );
continue;
@@ -732,8 +800,10 @@ skip_preview:
hb_fifo_flush( audio->priv.scan_cache );
}
}
- if ( vid_buf )
+ if (vid_buf)
+ {
hb_buffer_close( &vid_buf );
+ }
}
if ( data->batch && data->stream )
@@ -823,6 +893,8 @@ skip_preview:
hb_buffer_close( &buf_es );
}
hb_list_close( &list_es );
+ if (data->bd)
+ hb_bd_stop( data->bd );
if (data->dvd)
hb_dvd_stop( data->dvd );
diff --git a/libhb/stream.c b/libhb/stream.c
index 613651c45..733727c08 100644
--- a/libhb/stream.c
+++ b/libhb/stream.c
@@ -15,11 +15,6 @@
#include "mp4v2/mp4v2.h"
#define min(a, b) a < b ? a : b
-#define STR4_TO_UINT32(p) \
- ((((const uint8_t*)(p))[0] << 24) | \
- (((const uint8_t*)(p))[1] << 16) | \
- (((const uint8_t*)(p))[2] << 8) | \
- ((const uint8_t*)(p))[3])
/*
* This table defines how ISO MPEG stream type codes map to HandBrake
@@ -29,8 +24,13 @@
*
* Entries with a worker proc id of 0 or a kind of 'U' indicate that HB
* doesn't handle the stream type.
+ * N - Not used
+ * U - Unknown (to be determined by further processing)
+ * A - Audio
+ * V - Video
+ * P - PCR
*/
-typedef enum { N, U, A, V } kind_t;
+typedef enum { N, U, A, V, P } kind_t;
typedef struct {
kind_t kind; /* not handled / unknown / audio / video */
int codec; /* HB worker object id of codec */
@@ -94,9 +94,7 @@ typedef enum {
ffmpeg
} hb_stream_type_t;
-#define kMaxNumberVideoPIDS 1
-#define kMaxNumberAudioPIDS 31
-#define kMaxNumberDecodeStreams (kMaxNumberVideoPIDS+kMaxNumberAudioPIDS)
+#define kMaxNumberDecodeStreams 32
#define kMaxNumberPMTStreams 32
@@ -133,20 +131,19 @@ struct hb_stream_s
* we learn during the initial scan but cache so it can be
* reused during the conversion read.
*/
- uint8_t ts_number_video_pids;
- uint8_t ts_number_audio_pids;
+ uint8_t ts_number_pids;
uint8_t ts_flags; // stream characteristics:
#define TS_HAS_PCR (1 << 0) // at least one PCR seen
#define TS_HAS_RAP (1 << 1) // Random Access Point bit seen
#define TS_HAS_RSEI (1 << 2) // "Restart point" SEI seen
uint8_t ts_IDRs; // # IDRs found during duration scan
- int16_t ts_video_pids[kMaxNumberVideoPIDS];
- int16_t ts_audio_pids[kMaxNumberAudioPIDS];
+ int16_t ts_pids[kMaxNumberDecodeStreams];
uint32_t ts_format_id[kMaxNumberDecodeStreams];
#define TS_FORMAT_ID_AC3 (('A' << 24) | ('C' << 16) | ('-' << 8) | '3')
uint8_t ts_stream_type[kMaxNumberDecodeStreams];
+ kind_t ts_stream_kind[kMaxNumberDecodeStreams];
uint8_t ts_multiplexed[kMaxNumberDecodeStreams];
char *path;
@@ -164,7 +161,7 @@ struct hb_stream_s
int flags;
int rate;
int bitrate;
- } a52_info[kMaxNumberAudioPIDS];
+ } a52_info[kMaxNumberDecodeStreams];
struct
{
@@ -204,7 +201,7 @@ static void hb_ts_stream_find_pids(hb_stream_t *stream);
static int hb_ts_stream_decode(hb_stream_t *stream, hb_buffer_t *obuf);
static void hb_ts_stream_reset(hb_stream_t *stream);
static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
- int aud_pid_index);
+ int idx);
static void hb_ps_stream_find_audio_ids(hb_stream_t *stream, hb_title_t *title);
static off_t align_to_next_packet(hb_stream_t *stream);
@@ -294,6 +291,33 @@ static void ts_warn( hb_stream_t *stream, char *log, ... )
va_end( args );
}
+static kind_t ts_stream_kind( hb_stream_t *stream, int curstream )
+{
+ return st2codec[stream->ts_stream_type[curstream]].kind;
+}
+
+static int index_of_pid(hb_stream_t *stream, int pid)
+{
+ int i;
+
+ for ( i = 0; i < stream->ts_number_pids; ++i )
+ if ( pid == stream->ts_pids[i] )
+ return i;
+
+ return -1;
+}
+
+static int index_of_video(hb_stream_t *stream)
+{
+ int i;
+
+ for ( i = 0; i < stream->ts_number_pids; ++i )
+ if ( V == stream->ts_stream_kind[i] )
+ return i;
+
+ return -1;
+}
+
static void ts_err( hb_stream_t *stream, int curstream, char *log, ... )
{
va_list args;
@@ -434,7 +458,7 @@ static int hb_stream_get_type(hb_stream_t *stream)
stream->packetsize = psize;
stream->hb_stream_type = transport;
hb_ts_stream_init(stream);
- if ( !stream->ts_number_video_pids || !stream->ts_number_audio_pids )
+ if ( index_of_video( stream ) < 0 )
{
return 0;
}
@@ -461,24 +485,24 @@ static void hb_stream_delete_dynamic( hb_stream_t *d )
if( d->file_handle )
{
fclose( d->file_handle );
- d->file_handle = NULL;
+ d->file_handle = NULL;
}
- int i=0;
+ int i=0;
if ( d->ts_packet )
{
free( d->ts_packet );
d->ts_packet = NULL;
}
- for (i = 0; i < kMaxNumberDecodeStreams; i++)
- {
- if (d->ts_buf[i])
- {
- hb_buffer_close(&(d->ts_buf[i]));
- d->ts_buf[i] = NULL;
- }
- }
+ for (i = 0; i < kMaxNumberDecodeStreams; i++)
+ {
+ if (d->ts_buf[i])
+ {
+ hb_buffer_close(&(d->ts_buf[i]));
+ d->ts_buf[i] = NULL;
+ }
+ }
}
static void hb_stream_delete( hb_stream_t *d )
@@ -488,16 +512,16 @@ static void hb_stream_delete( hb_stream_t *d )
free( d );
}
-static int audio_inactive( hb_stream_t *stream, int indx )
+static int audio_inactive( hb_stream_t *stream, int idx )
{
- int aud_indx = indx - 1;
+ int pid = stream->ts_pids[idx];
- if ( stream->ts_audio_pids[aud_indx] < 0 )
+ if ( pid < 0 )
{
// PID declared inactive by hb_stream_title_scan
return 1;
}
- if ( stream->ts_audio_pids[aud_indx] == stream->pmt_info.PCR_PID )
+ if ( pid == stream->pmt_info.PCR_PID )
{
// PCR PID is always active
return 0;
@@ -509,13 +533,14 @@ static int audio_inactive( hb_stream_t *stream, int indx )
for ( i = 0; i < hb_list_count( stream->title->list_audio ); ++i )
{
hb_audio_t *audio = hb_list_item( stream->title->list_audio, i );
- if ( audio->id == indx )
+ if ( audio->id == pid )
{
return 0;
}
}
+
// not in the title's audio list - declare the PID inactive
- stream->ts_audio_pids[aud_indx] = -stream->ts_audio_pids[aud_indx];
+ stream->ts_pids[idx] = -stream->ts_pids[idx];
return 1;
}
@@ -563,17 +588,18 @@ hb_stream_t * hb_stream_open( char *path, hb_title_t *title )
{
d->ts_packet = malloc( d->packetsize );
- int i = 0;
- for ( ; i < d->ts_number_video_pids + d->ts_number_audio_pids; i++)
+ int i;
+ for ( i = 0; i < d->ts_number_pids; i++)
{
- if ( i && audio_inactive( d, i ) )
+ if ( d->ts_stream_kind[i] == A &&
+ audio_inactive( d, i ) )
{
// this PID isn't wanted (we don't have a codec for it
// or scan didn't find audio parameters)
continue;
}
d->ts_buf[i] = hb_buffer_init(d->packetsize);
- d->ts_buf[i]->size = 0;
+ d->ts_buf[i]->size = 0;
}
hb_stream_seek( d, 0. );
}
@@ -599,7 +625,7 @@ hb_stream_t * hb_stream_open( char *path, hb_title_t *title )
return d;
}
fclose( d->file_handle );
- d->file_handle = NULL;
+ d->file_handle = NULL;
if ( ffmpeg_open( d, title ) )
{
return d;
@@ -618,6 +644,89 @@ hb_stream_t * hb_stream_open( char *path, hb_title_t *title )
return NULL;
}
+hb_stream_t * hb_bd_stream_open( hb_title_t *title )
+{
+ int ii;
+
+ hb_stream_t *d = calloc( sizeof( hb_stream_t ), 1 );
+ if ( d == NULL )
+ {
+ hb_log( "hb_bd_stream_open: can't allocate space for stream state" );
+ return NULL;
+ }
+
+ for (ii = 0; ii < kMaxNumberDecodeStreams; ii++)
+ {
+ d->ts_streamcont[ii] = -1;
+ d->ts_pids[ii] = -1;
+ }
+
+ d->file_handle = NULL;
+ d->title = title;
+ d->path = NULL;
+ d->ts_packet = NULL;
+
+ d->ts_number_pids = 0;
+ d->ts_pids[0] = title->video_id;
+ d->ts_stream_type[0] = title->video_stream_type;
+ d->ts_stream_kind[0] = V;
+ d->ts_number_pids++;
+
+ hb_audio_t * audio;
+ for ( ii = 0; ( audio = hb_list_item( title->list_audio, ii ) ); ++ii )
+ {
+ d->ts_pids[d->ts_number_pids] = audio->id;
+ d->ts_stream_type[d->ts_number_pids] = audio->config.in.stream_type;
+ d->ts_stream_kind[d->ts_number_pids] = A;
+
+ if ( d->ts_stream_type[d->ts_number_pids] == 0x83 &&
+ title->reg_desc == STR4_TO_UINT32("HDMV") )
+ {
+ // This is an interleaved TrueHD/AC-3 stream and the esid of
+ // the AC-3 is 0x76
+ d->ts_multiplexed[d->ts_number_pids] = 0x76;
+ d->ts_stream_type[d->ts_number_pids] = 0x81;
+ }
+ if ( d->ts_stream_type[d->ts_number_pids] == 0x86 &&
+ title->reg_desc == STR4_TO_UINT32("HDMV") )
+ {
+ // This is an interleaved DTS-HD/DTS stream and the esid of
+ // the DTS is 0x71
+ d->ts_multiplexed[d->ts_number_pids] = 0x71;
+ d->ts_stream_type[d->ts_number_pids] = 0x82;
+ }
+
+ d->ts_number_pids++;
+ }
+
+ d->ts_flags = TS_HAS_RAP;
+ // When scanning, title->job == NULL. We don't need to wait for
+ // a PCR when scanning. In fact, it trips us up on the first
+ // preview of every title since we would have to read quite a
+ // lot of data before finding the PCR.
+ if (title->pcr_pid != 0xFFFF && title->job)
+ {
+ if ( index_of_pid( d, title->pcr_pid ) < 0 )
+ {
+ // BD PCR PID is specified to always be 0x1001
+ d->ts_pids[d->ts_number_pids] = 0x1001;
+ d->ts_stream_kind[d->ts_number_pids] = P;
+ d->ts_number_pids++;
+ }
+ }
+
+ d->packetsize = 192;
+ d->hb_stream_type = transport;
+
+ for ( ii = 0; ii < d->ts_number_pids; ii++ )
+ {
+ d->ts_buf[ii] = hb_buffer_init(d->packetsize);
+ d->ts_buf[ii]->size = 0;
+ }
+
+ return d;
+}
+
/***********************************************************************
* hb_stream_close
***********************************************************************
@@ -670,28 +779,14 @@ void hb_stream_close( hb_stream_t ** _d )
* by setting its PID to an invalid value so no packet will match it. (We can't
* move any of the entries since the index of the entry is used as the id
* of the media stream for HB. */
-static void hb_stream_delete_audio_entry(hb_stream_t *stream, int indx)
+static void hb_stream_delete_entry(hb_stream_t *stream, int indx)
{
- if ( stream->ts_audio_pids[indx] > 0 )
+ if ( stream->ts_pids[indx] > 0 )
{
- stream->ts_audio_pids[indx] = -stream->ts_audio_pids[indx];
+ stream->ts_pids[indx] = -stream->ts_pids[indx];
}
}
-static int index_of_pid(int pid, hb_stream_t *stream)
-{
- int i;
-
- if ( pid == stream->ts_video_pids[0] )
- return 0;
-
- for ( i = 0; i < stream->ts_number_audio_pids; ++i )
- if ( pid == stream->ts_audio_pids[i] )
- return i + 1;
-
- return -1;
-}
-
/***********************************************************************
* hb_ps_stream_title_scan
***********************************************************************
@@ -699,7 +794,7 @@ static int index_of_pid(int pid, hb_stream_t *stream)
**********************************************************************/
hb_title_t * hb_stream_title_scan(hb_stream_t *stream)
{
- if ( stream->hb_stream_type == ffmpeg )
+ if ( stream->hb_stream_type == ffmpeg )
return ffmpeg_title_scan( stream );
// 'Barebones Title'
@@ -707,13 +802,13 @@ hb_title_t * hb_stream_title_scan(hb_stream_t *stream)
aTitle->type = HB_STREAM_TYPE;
aTitle->index = 1;
- // 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';
+ // 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
@@ -736,33 +831,48 @@ hb_title_t * hb_stream_title_scan(hb_stream_t *stream)
// the elementary stream is an audio type.
// - For program streams read the first 4MB and take every unique
// audio stream we find.
- if (stream->hb_stream_type == transport)
- {
+ if (stream->hb_stream_type == transport)
+ {
int i;
- for (i=0; i < stream->ts_number_audio_pids; i++)
+ for (i=0; i < stream->ts_number_pids; i++)
{
hb_audio_t *audio = hb_ts_stream_set_audio_id_and_codec(stream, i);
- if (audio->config.in.codec)
- hb_list_add( aTitle->list_audio, audio );
- else
+ if ( audio )
{
- free(audio);
- hb_stream_delete_audio_entry(stream, i);
+ hb_list_add( aTitle->list_audio, audio );
}
}
// make sure we're grabbing the PCR PID
- if ( index_of_pid( stream->pmt_info.PCR_PID, stream ) < 0 )
+ if ( index_of_pid( stream, stream->pmt_info.PCR_PID ) < 0 )
{
- stream->ts_audio_pids[stream->ts_number_audio_pids++] =
- stream->pmt_info.PCR_PID;
+ stream->ts_pids[stream->ts_number_pids] = stream->pmt_info.PCR_PID;
+ stream->ts_stream_kind[stream->ts_number_pids] = P;
+ stream->ts_number_pids++;
+ }
+
+ for (i = 0; i < stream->ts_number_pids; i++)
+ {
+ kind_t kind = stream->ts_stream_kind[i];
+
+ if ( kind == N || kind == U )
+ {
+ hb_stream_delete_entry(stream, i);
+ }
}
// set the video id, codec & muxer
- aTitle->video_id = 0;
- aTitle->video_codec = st2codec[stream->ts_stream_type[0]].codec;
- aTitle->video_codec_param = st2codec[stream->ts_stream_type[0]].codec_param;
+ int idx = index_of_video( stream );
+ if ( idx < 0 )
+ {
+ hb_title_close( &aTitle );
+ return NULL;
+ }
+
+ aTitle->video_id = stream->ts_pids[idx];
+ aTitle->video_codec = st2codec[stream->ts_stream_type[idx]].codec;
+ aTitle->video_codec_param = st2codec[stream->ts_stream_type[idx]].codec_param;
aTitle->demuxer = HB_MPEG2_TS_DEMUXER;
if ( ( stream->ts_flags & TS_HAS_PCR ) == 0 )
@@ -775,13 +885,13 @@ hb_title_t * hb_stream_title_scan(hb_stream_t *stream)
hb_log( "transport stream doesn't seem to have video IDR frames" );
aTitle->flags |= HBTF_NO_IDR;
}
- }
+ }
else
{
hb_ps_stream_find_audio_ids(stream, aTitle);
}
- return aTitle;
+ return aTitle;
}
/*
@@ -1079,7 +1189,8 @@ static struct pts_pos hb_sample_pts(hb_stream_t *stream, uint64_t fpos)
const uint8_t *buf;
fseeko( stream->file_handle, fpos, SEEK_SET );
align_to_next_packet( stream );
- buf = hb_ts_stream_getPEStype( stream, stream->ts_video_pids[0] );
+ int pid = stream->ts_pids[index_of_video(stream)];
+ buf = hb_ts_stream_getPEStype( stream, pid );
if ( buf == NULL )
{
hb_log("hb_sample_pts: couldn't find video packet near %"PRIu64, fpos);
@@ -1157,7 +1268,7 @@ static double compute_stream_rate( struct pts_pos *pp, int n )
{
*rp = ((double)( pp[j].pts - pp[i].pts )) /
((double)( pp[j].pos - pp[i].pos ));
- ++rp;
+ ++rp;
}
}
}
@@ -1199,7 +1310,7 @@ static void hb_stream_duration(hb_stream_t *stream, hb_title_t *inTitle)
**********************************************************************/
int hb_stream_read( hb_stream_t * src_stream, hb_buffer_t * b )
{
- if ( src_stream->hb_stream_type == ffmpeg )
+ if ( src_stream->hb_stream_type == ffmpeg )
{
return ffmpeg_read( src_stream, b );
}
@@ -1365,7 +1476,7 @@ int hb_stream_chapter( hb_stream_t * src_stream )
**********************************************************************/
int hb_stream_seek( hb_stream_t * stream, float f )
{
- if ( stream->hb_stream_type == ffmpeg )
+ if ( stream->hb_stream_type == ffmpeg )
{
return ffmpeg_seek( stream, f );
}
@@ -1414,7 +1525,7 @@ int hb_stream_seek( hb_stream_t * stream, float f )
int hb_stream_seek_ts( hb_stream_t * stream, int64_t ts )
{
- if ( stream->hb_stream_type == ffmpeg )
+ if ( stream->hb_stream_type == ffmpeg )
{
return ffmpeg_seek_ts( stream, ts );
}
@@ -1494,37 +1605,43 @@ static void set_audio_description( hb_audio_t *audio, iso639_lang_t *lang )
}
static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
- int aud_pid_index)
+ int idx)
{
off_t cur_pos = ftello(stream->file_handle);
- hb_audio_t *audio = calloc( sizeof( hb_audio_t ), 1 );
+ hb_audio_t *audio = NULL;
const uint8_t *buf;
+ kind_t kind;
+ uint8_t stype = 0;
+
+ kind = stream->ts_stream_kind[idx];
+
+ if ( kind != A && kind != U && kind != N )
+ {
+ // Not audio
+ return NULL;
+ }
+ stype = stream->ts_stream_type[idx];
fseeko(stream->file_handle, 0, SEEK_SET);
align_to_next_packet(stream);
- buf = hb_ts_stream_getPEStype(stream, stream->ts_audio_pids[aud_pid_index]);
- /* check that we found a PES header */
- uint8_t stype = 0;
- kind_t kind;
+ buf = hb_ts_stream_getPEStype(stream, stream->ts_pids[idx]);
+ /* check that we found a PES header */
if (buf && buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01)
{
- stype = stream->ts_stream_type[1 + aud_pid_index];
- kind = st2codec[stype].kind;
-
// 0xbd ("private stream 1") is the normal container for non-ISO
// media - AC3/DCA/PCM/etc.
if ( buf[3] == 0xbd )
{
- if ( st2codec[stype].kind == U )
+ if ( kind == U )
{
// XXX assume unknown stream types are AC-3 (if they're not
// audio we'll find that out during the scan but if they're
// some other type of audio we'll end up ignoring them).
stype = 0x81;
- stream->ts_stream_type[1 + aud_pid_index] = 0x81;
- kind = st2codec[stype].kind;
+ stream->ts_stream_type[idx] = 0x81;
+ kind = A;
}
if ( stype == 0x80 &&
stream->pmt_info.reg_desc == STR4_TO_UINT32("HDMV") )
@@ -1545,23 +1662,23 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
// distinguish them. So we have to check if that's happening and
// if so tell the runtime what esid we want.
if ( st2codec[stype].kind == A && stype == 0x83 &&
- stream->ts_format_id[1 + aud_pid_index] == TS_FORMAT_ID_AC3 )
+ stream->ts_format_id[idx] == TS_FORMAT_ID_AC3 )
{
// This is an interleaved TrueHD/AC-3 stream and the esid of
// the AC-3 is 0x76
- stream->ts_multiplexed[1 + aud_pid_index] = 0x76;
+ stream->ts_multiplexed[idx] = 0x76;
stype = 0x81;
- stream->ts_stream_type[1 + aud_pid_index] = 0x81;
- kind = st2codec[stype].kind;
+ stream->ts_stream_type[idx] = 0x81;
+ kind = A;
}
if ( st2codec[stype].kind == A && stype == 0x86 )
{
// This is an interleaved DTS-HD/DTS stream and the esid of
// the DTS is 0x71
- stream->ts_multiplexed[1 + aud_pid_index] = 0x71;
+ stream->ts_multiplexed[idx] = 0x71;
stype = 0x82;
- stream->ts_stream_type[1 + aud_pid_index] = 0x82;
- kind = st2codec[stype].kind;
+ stream->ts_stream_type[idx] = 0x82;
+ kind = A;
}
}
else if ((buf[3] & 0xe0) == 0xc0)
@@ -1572,45 +1689,51 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream,
{
// XXX assume unknown stream types are MPEG audio
stype = 0x03;
- stream->ts_stream_type[1 + aud_pid_index] = 0x03;
- kind = st2codec[stype].kind;
+ stream->ts_stream_type[idx] = 0x03;
+ kind = A;
}
}
else
{
stype = 0;
- kind = st2codec[stype].kind;
+ kind = N;
}
}
+
// if we found an audio stream type & HB has a codec that can decode it
// finish configuring the audio so we'll add it to the title's list.
if ( kind == A && st2codec[stype].codec )
{
- audio->id = 1 + aud_pid_index;
+ audio = calloc( sizeof( hb_audio_t ), 1 );
+
+ stream->ts_stream_kind[idx] = A;
+ audio->id = stream->ts_pids[idx];
audio->config.in.codec = st2codec[stype].codec;
audio->config.in.codec_param = st2codec[stype].codec_param;
set_audio_description( audio,
- lang_for_code( stream->a52_info[aud_pid_index].lang_code ) );
+ lang_for_code( stream->a52_info[idx].lang_code ) );
hb_log("transport stream pid 0x%x (type 0x%x) may be %s audio (id 0x%x)",
- stream->ts_audio_pids[aud_pid_index],
+ stream->ts_pids[idx],
stype, st2codec[stype].name, audio->id);
+
}
else
{
if ( buf )
{
hb_log("transport stream pid 0x%x (type 0x%x, substream 0x%x) "
- "isn't audio", stream->ts_audio_pids[aud_pid_index],
- stream->ts_stream_type[1 + aud_pid_index], buf[3]);
+ "isn't audio", stream->ts_pids[idx],
+ stream->ts_stream_type[idx], buf[3]);
}
else
{
hb_log("transport stream pid 0x%x (type 0x%x) isn't audio",
- stream->ts_audio_pids[aud_pid_index],
- stream->ts_stream_type[1 + aud_pid_index]);
+ stream->ts_pids[idx],
+ stream->ts_stream_type[idx]);
}
- }
+ }
fseeko(stream->file_handle, cur_pos, SEEK_SET);
+
return audio;
}
@@ -1703,29 +1826,24 @@ static void hb_ps_stream_find_audio_ids(hb_stream_t *stream, hb_title_t *title)
static void hb_ts_stream_init(hb_stream_t *stream)
{
- int i;
+ int i;
- for (i=0; i < kMaxNumberDecodeStreams; i++)
- {
- stream->ts_streamcont[i] = -1;
- }
- stream->ts_video_pids[0] = -1;
- for ( i = 0; i < stream->ts_number_audio_pids; i++ )
+ for (i=0; i < kMaxNumberDecodeStreams; i++)
{
- stream-> ts_audio_pids[i] = -1;
+ stream->ts_streamcont[i] = -1;
+ stream-> ts_pids[i] = -1;
}
-
stream->ts_packet = malloc( stream->packetsize );
- // Find the audio and video pids in the stream
- hb_ts_stream_find_pids(stream);
+ // Find the audio and video pids in the stream
+ hb_ts_stream_find_pids(stream);
- for (i = 0; i < stream->ts_number_video_pids + stream->ts_number_audio_pids; i++)
- {
+ for (i = 0; i < stream->ts_number_pids; i++)
+ {
// demuxing buffer for TS to PS conversion
- stream->ts_buf[i] = hb_buffer_init(stream->packetsize);
- stream->ts_buf[i]->size = 0;
- }
+ stream->ts_buf[i] = hb_buffer_init(stream->packetsize);
+ stream->ts_buf[i]->size = 0;
+ }
}
#define MAX_HOLE 208*80
@@ -1782,53 +1900,53 @@ typedef struct {
} bitbuf_t;
static const 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};
+ 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(bitbuf_t *bb, uint8_t* buf, int bufsize, int clear)
{
- bb->pos = 0;
- bb->buf = buf;
- bb->val = (bb->buf[0] << 24) | (bb->buf[1] << 16) |
+ bb->pos = 0;
+ bb->buf = buf;
+ bb->val = (bb->buf[0] << 24) | (bb->buf[1] << 16) |
(bb->buf[2] << 8) | bb->buf[3];
- if (clear)
- memset(bb->buf, 0, bufsize);
+ if (clear)
+ memset(bb->buf, 0, bufsize);
}
static inline int buf_size(bitbuf_t *bb)
{
- return bb->pos >> 3;
+ return bb->pos >> 3;
}
static inline unsigned int get_bits(bitbuf_t *bb, int bits)
{
- unsigned int val;
- int left = 32 - (bb->pos & 31);
+ unsigned int val;
+ int left = 32 - (bb->pos & 31);
- if (bits < left)
- {
- val = (bb->val >> (left - bits)) & bitmask[bits];
- bb->pos += bits;
- }
- else
- {
- val = (bb->val & bitmask[left]) << (bits - left);
- bb->pos += left;
- bits -= left;
+ if (bits < left)
+ {
+ val = (bb->val >> (left - bits)) & bitmask[bits];
+ bb->pos += bits;
+ }
+ else
+ {
+ val = (bb->val & bitmask[left]) << (bits - left);
+ bb->pos += left;
+ bits -= left;
- int pos = bb->pos >> 3;
- bb->val = (bb->buf[pos] << 24) | (bb->buf[pos + 1] << 16) | (bb->buf[pos + 2] << 8) | bb->buf[pos + 3];
+ int pos = bb->pos >> 3;
+ bb->val = (bb->buf[pos] << 24) | (bb->buf[pos + 1] << 16) | (bb->buf[pos + 2] << 8) | bb->buf[pos + 3];
- if (bits > 0)
- {
- val |= (bb->val >> (32 - bits)) & bitmask[bits];
- bb->pos += bits;
- }
- }
+ if (bits > 0)
+ {
+ val |= (bb->val >> (32 - bits)) & bitmask[bits];
+ bb->pos += bits;
+ }
+ }
- return val;
+ return val;
}
// extract what useful information we can from the elementary stream
@@ -1846,7 +1964,7 @@ static void decode_element_descriptors(hb_stream_t* stream, int esindx,
switch (dp[0])
{
case 5: // Registration descriptor
- stream->ts_format_id[esindx+1] = (dp[2] << 24) | (dp[3] << 16) |
+ stream->ts_format_id[esindx] = (dp[2] << 24) | (dp[3] << 16) |
(dp[4] << 8) | dp[5];
break;
@@ -1855,7 +1973,7 @@ static void decode_element_descriptors(hb_stream_t* stream, int esindx,
break;
case 0x6a: // DVB AC-3 descriptor
- stream->ts_stream_type[esindx+1] = 0x81;
+ stream->ts_stream_type[esindx] = 0x81;
break;
default:
@@ -1940,38 +2058,27 @@ int decode_program_map(hb_stream_t* stream)
ES_info_buf[i] = get_bits(&bb, 8);
}
-
- if ( index_of_pid( elementary_PID, stream ) < 0 )
+ if ( index_of_pid( stream, elementary_PID ) < 0 )
{
- // don't have this pid yet
- if (stream->ts_number_video_pids == 0 &&
- st2codec[stream_type].kind == V )
+ // Defined audio stream types are 0x81 for AC-3/A52 audio
+ // and 0x03 for mpeg audio. But content producers seem to
+ // use other values (0x04 and 0x06 have both been observed)
+ // so at this point we say everything that isn't a video
+ // pid is audio then at the end of hb_stream_title_scan
+ // we'll figure out which are really audio by looking at
+ // the PES headers.
+ i = stream->ts_number_pids;
+ if (i < kMaxNumberDecodeStreams)
{
- stream->ts_video_pids[0] = elementary_PID;
- stream->ts_stream_type[0] = stream_type;
- stream->ts_number_video_pids = 1;
- }
- else
- {
- // Defined audio stream types are 0x81 for AC-3/A52 audio
- // and 0x03 for mpeg audio. But content producers seem to
- // use other values (0x04 and 0x06 have both been observed)
- // so at this point we say everything that isn't a video
- // pid is audio then at the end of hb_stream_title_scan
- // we'll figure out which are really audio by looking at
- // the PES headers.
- i = stream->ts_number_audio_pids;
- if (i < kMaxNumberAudioPIDS)
+ stream->ts_pids[i] = elementary_PID;
+ stream->ts_stream_type[i] = stream_type;
+ if (ES_info_length > 0)
{
- stream->ts_audio_pids[i] = elementary_PID;
- stream->ts_stream_type[1 + i] = stream_type;
- if (ES_info_length > 0)
- {
- decode_element_descriptors(stream, i, ES_info_buf,
- ES_info_length);
- }
- ++stream->ts_number_audio_pids;
+ decode_element_descriptors(stream, i, ES_info_buf,
+ ES_info_length);
}
+ stream->ts_stream_kind[i] = ts_stream_kind(stream, i);
+ ++stream->ts_number_pids;
}
}
@@ -2007,26 +2114,26 @@ static int build_program_map(const uint8_t *buf, hb_stream_t *stream)
// Get pointer length - only valid in packets with a start flag
int pointer_len = 0;
- if (start)
- {
- pointer_len = buf[4 + adapt_len] + 1;
- stream->pmt_info.tablepos = 0;
- }
- // Get Continuity Counter
- int continuity_counter = buf[3] & 0x0f;
- if (!start && (stream->pmt_info.current_continuity_counter + 1 != continuity_counter))
- {
- hb_log("build_program_map - Continuity Counter %d out of sequence - expected %d", continuity_counter, stream->pmt_info.current_continuity_counter+1);
- return 0;
- }
- stream->pmt_info.current_continuity_counter = continuity_counter;
- stream->pmt_info.reading |= start;
+ if (start)
+ {
+ pointer_len = buf[4 + adapt_len] + 1;
+ stream->pmt_info.tablepos = 0;
+ }
+ // Get Continuity Counter
+ int continuity_counter = buf[3] & 0x0f;
+ if (!start && (stream->pmt_info.current_continuity_counter + 1 != continuity_counter))
+ {
+ hb_log("build_program_map - Continuity Counter %d out of sequence - expected %d", continuity_counter, stream->pmt_info.current_continuity_counter+1);
+ return 0;
+ }
+ stream->pmt_info.current_continuity_counter = continuity_counter;
+ stream->pmt_info.reading |= start;
// Add the payload for this packet to the current buffer
- int amount_to_copy = 184 - adapt_len - pointer_len;
+ int amount_to_copy = 184 - adapt_len - pointer_len;
if (stream->pmt_info.reading && (amount_to_copy > 0))
{
- stream->pmt_info.tablebuf = realloc(stream->pmt_info.tablebuf, stream->pmt_info.tablepos + amount_to_copy);
+ stream->pmt_info.tablebuf = realloc(stream->pmt_info.tablebuf, stream->pmt_info.tablepos + amount_to_copy);
memcpy(stream->pmt_info.tablebuf + stream->pmt_info.tablepos, buf + 4 + adapt_len + pointer_len, amount_to_copy);
stream->pmt_info.tablepos += amount_to_copy;
@@ -2109,9 +2216,9 @@ static int decode_PAT(const uint8_t *buf, hb_stream_t *stream)
bitbuf_t bb;
set_buf(&bb, tablebuf + pos, tablepos - pos, 0);
- unsigned char section_id = get_bits(&bb, 8);
+ unsigned char section_id = get_bits(&bb, 8);
get_bits(&bb, 4);
- unsigned int section_len = get_bits(&bb, 12);
+ unsigned int section_len = get_bits(&bb, 12);
get_bits(&bb, 16); // transport_id
get_bits(&bb, 2);
get_bits(&bb, 5); // version_num
@@ -2127,11 +2234,11 @@ static int decode_PAT(const uint8_t *buf, hb_stream_t *stream)
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;
- stream->ts_number_pat_entries = 0;
+ stream->ts_number_pat_entries = 0;
while ((curr_pos < section_len) && (stream->ts_number_pat_entries < kMaxNumberPMTStreams))
{
unsigned int pkt_program_num = get_bits(&bb, 16);
- stream->pat_info[stream->ts_number_pat_entries].program_number = pkt_program_num;
+ stream->pat_info[stream->ts_number_pat_entries].program_number = pkt_program_num;
get_bits(&bb, 3); // Reserved
if (pkt_program_num == 0)
@@ -2144,7 +2251,7 @@ static int decode_PAT(const uint8_t *buf, hb_stream_t *stream)
stream->pat_info[stream->ts_number_pat_entries].program_map_PID = pkt_program_map_PID;
}
curr_pos += 4;
- stream->ts_number_pat_entries++;
+ stream->ts_number_pat_entries++;
}
}
break;
@@ -2177,72 +2284,78 @@ static void hb_ts_stream_find_pids(hb_stream_t *stream)
fseeko(stream->file_handle, fsize >> 1, SEEK_SET);
align_to_next_packet(stream);
- // 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
+ // 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
- for (;;)
- {
+ for (;;)
+ {
const uint8_t *buf = next_packet( stream );
if ( buf == NULL )
{
- hb_log("hb_ts_stream_find_pids - end of file");
- break;
- }
+ hb_log("hb_ts_stream_find_pids - end of file");
+ break;
+ }
- // Get pid
- int pid = (((buf[1] & 0x1F) << 8) | buf[2]) & 0x1FFF;
+ // Get pid
+ int pid = (((buf[1] & 0x1F) << 8) | buf[2]) & 0x1FFF;
if ((pid == 0x0000) && (stream->ts_number_pat_entries == 0))
- {
- decode_PAT(buf, stream);
- continue;
- }
-
- int pat_index = 0;
- for (pat_index = 0; pat_index < stream->ts_number_pat_entries; pat_index++)
- {
- // There are some streams where the PAT table has multiple entries as if their are
- // multiple programs in the same transport stream, and yet there's actually only one
- // program really in the stream. This seems to be true for transport streams that
- // originate in the HDHomeRun but have been output by EyeTV's export utility. What I think
- // is happening is that the HDHomeRun is sending the entire transport stream as broadcast,
- // but the EyeTV is only recording a single (selected) program number and not rewriting the
- // PAT info on export to match what's actually on the stream.
- // Until we have a way of handling multiple programs per transport stream elegantly we'll match
- // on the first pat entry for which we find a matching program map PID. The ideal solution would
- // be to build a title choice popup from the PAT program number details and then select from
- // their - but right now the API's not capable of that.
+ {
+ decode_PAT(buf, stream);
+ continue;
+ }
+
+ int pat_index = 0;
+ for (pat_index = 0; pat_index < stream->ts_number_pat_entries; pat_index++)
+ {
+ // There are some streams where the PAT table has multiple entries as if their are
+ // multiple programs in the same transport stream, and yet there's actually only one
+ // program really in the stream. This seems to be true for transport streams that
+ // originate in the HDHomeRun but have been output by EyeTV's export utility. What I think
+ // is happening is that the HDHomeRun is sending the entire transport stream as broadcast,
+ // but the EyeTV is only recording a single (selected) program number and not rewriting the
+ // PAT info on export to match what's actually on the stream.
+ // Until we have a way of handling multiple programs per transport stream elegantly we'll match
+ // on the first pat entry for which we find a matching program map PID. The ideal solution would
+ // be to build a title choice popup from the PAT program number details and then select from
+ // their - but right now the API's not capable of that.
if (stream->pat_info[pat_index].program_number != 0 &&
pid == stream->pat_info[pat_index].program_map_PID)
- {
- if (build_program_map(buf, stream) > 0)
- break;
- }
- }
- // Keep going until we have a complete set of PIDs
- if (stream->ts_number_video_pids > 0)
- break;
- }
-
- hb_log("hb_ts_stream_find_pids - found the following PIDS");
- hb_log(" Video PIDS : ");
+ {
+ if (build_program_map(buf, stream) > 0)
+ break;
+ }
+ }
+ // Keep going until we have a complete set of PIDs
+ if ( index_of_video( stream ) >= 0 )
+ break;
+ }
+
+ hb_log("hb_ts_stream_find_pids - found the following PIDS");
+ hb_log(" Video PIDS : ");
int i;
- for (i=0; i < stream->ts_number_video_pids; i++)
- {
- hb_log( " 0x%x type %s (0x%x)",
- stream->ts_video_pids[i],
- stream_type_name(stream->ts_stream_type[i]),
- stream->ts_stream_type[i]);
- }
- hb_log(" Audio PIDS : ");
- for (i = 0; i < stream->ts_number_audio_pids; i++)
- {
- hb_log( " 0x%x type %s (0x%x)",
- stream->ts_audio_pids[i],
- stream_type_name(stream->ts_stream_type[i+1]),
- stream->ts_stream_type[i+1] );
- }
+ for (i=0; i < stream->ts_number_pids; i++)
+ {
+ if ( stream->ts_stream_kind[i] == V )
+ {
+ hb_log( " 0x%x type %s (0x%x)",
+ stream->ts_pids[i],
+ stream_type_name(stream->ts_stream_type[i]),
+ stream->ts_stream_type[i]);
+ }
+ }
+ hb_log(" Audio PIDS : ");
+ for (i = 0; i < stream->ts_number_pids; i++)
+ {
+ if ( stream->ts_stream_kind[i] != V )
+ {
+ hb_log( " 0x%x type %s (0x%x)",
+ stream->ts_pids[i],
+ stream_type_name(stream->ts_stream_type[i]),
+ stream->ts_stream_type[i] );
+ }
+ }
}
@@ -2275,7 +2388,21 @@ static void generate_output_data(hb_stream_t *stream, int curstream)
hb_buffer_t *buf = stream->fwrite_buf;
uint8_t *tdat = stream->ts_buf[curstream]->data;
- buf->id = curstream;
+ buf->id = stream->ts_pids[curstream];
+ switch (stream->ts_stream_kind[curstream])
+ {
+ case A:
+ buf->type = AUDIO_BUF;
+ break;
+
+ case V:
+ buf->type = VIDEO_BUF;
+ break;
+
+ default:
+ buf->type = OTHER_BUF;
+ break;
+ }
// check if this packet was referenced to an older pcr and if that
// pcr was significantly different than the one we're using now.
@@ -2349,7 +2476,7 @@ static void hb_ts_stream_append_pkt(hb_stream_t *stream, int idx, const uint8_t
***********************************************************************
*
**********************************************************************/
-static int hb_ts_stream_decode( hb_stream_t *stream, hb_buffer_t *obuf )
+int hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt, hb_buffer_t *obuf )
{
/*
* stash the output buffer pointer in our stream so we don't have to
@@ -2358,272 +2485,286 @@ static int hb_ts_stream_decode( hb_stream_t *stream, hb_buffer_t *obuf )
obuf->size = 0;
stream->fwrite_buf = obuf;
- // spin until we get a packet of data from some stream or hit eof
- while ( 1 )
- {
- int curstream;
+ int video_index = index_of_video(stream);
- const uint8_t *buf = next_packet(stream);
- if ( buf == NULL )
- {
- // end of file - we didn't finish filling our ps write buffer
- // so just discard the remainder (the partial buffer is useless)
- hb_log("hb_ts_stream_decode - eof");
- return 0;
- }
+ int curstream;
- /* This next section validates the packet */
+ /* This next section validates the packet */
- // Get pid and use it to find stream state.
- int pid = ((buf[1] & 0x1F) << 8) | buf[2];
- if ( ( curstream = index_of_pid( pid, stream ) ) < 0 )
- continue;
+ // Get pid and use it to find stream state.
+ int pid = ((pkt[1] & 0x1F) << 8) | pkt[2];
+ if ( ( curstream = index_of_pid( stream, pid ) ) < 0 )
+ {
+ return 0;
+ }
- // Get error
- int errorbit = (buf[1] & 0x80) != 0;
- if (errorbit)
- {
- ts_err( stream, curstream, "packet error bit set");
- continue;
- }
-
- // Get adaption header info
- int adaption = (buf[3] & 0x30) >> 4;
- int adapt_len = 0;
- if (adaption == 0)
- {
- ts_err( stream, curstream, "adaptation code 0");
- continue;
- }
- else if (adaption == 0x2)
- adapt_len = 184;
- else if (adaption == 0x3)
- {
- adapt_len = buf[4] + 1;
- if (adapt_len > 184)
- {
- ts_err( stream, curstream, "invalid adapt len %d", adapt_len);
- continue;
- }
- }
+ // Get error
+ int errorbit = (pkt[1] & 0x80) != 0;
+ if (errorbit)
+ {
+ ts_err( stream, curstream, "packet error bit set");
+ return 0;
+ }
- if ( adapt_len > 0 )
+ // Get adaption header info
+ int adaption = (pkt[3] & 0x30) >> 4;
+ int adapt_len = 0;
+ if (adaption == 0)
+ {
+ ts_err( stream, curstream, "adaptation code 0");
+ return 0;
+ }
+ else if (adaption == 0x2)
+ adapt_len = 184;
+ else if (adaption == 0x3)
+ {
+ adapt_len = pkt[4] + 1;
+ if (adapt_len > 184)
{
- if ( buf[5] & 0x40 )
- {
- // found a random access point
- }
- // if there's an adaptation header & PCR_flag is set
- // get the PCR (Program Clock Reference)
- if ( adapt_len > 7 && ( buf[5] & 0x10 ) != 0 )
- {
- stream->ts_pcr = ( (uint64_t)buf[6] << (33 - 8) ) |
- ( (uint64_t)buf[7] << (33 - 16) ) |
- ( (uint64_t)buf[8] << (33 - 24) ) |
- ( (uint64_t)buf[9] << (33 - 32) ) |
- ( buf[10] >> 7 );
- ++stream->ts_pcr_in;
- stream->ts_found_pcr = 1;
- }
+ ts_err( stream, curstream, "invalid adapt len %d", adapt_len);
+ return 0;
}
+ }
- // If we don't have a PCR yet but the stream has PCRs just loop
- // so we don't process anything until we have a clock reference.
- // Unfortunately the HD Home Run appears to null out the PCR so if
- // we didn't detect a PCR during scan keep going and we'll use
- // the video stream DTS for the PCR.
-
- if ( !stream->ts_found_pcr && ( stream->ts_flags & TS_HAS_PCR ) )
+ if ( adapt_len > 0 )
+ {
+ if ( pkt[5] & 0x40 )
{
- continue;
+ // found a random access point
+ }
+ // if there's an adaptation header & PCR_flag is set
+ // get the PCR (Program Clock Reference)
+ if ( adapt_len > 7 && ( pkt[5] & 0x10 ) != 0 )
+ {
+ stream->ts_pcr = ( (uint64_t)pkt[6] << (33 - 8) ) |
+ ( (uint64_t)pkt[7] << (33 - 16) ) |
+ ( (uint64_t)pkt[8] << (33 - 24) ) |
+ ( (uint64_t)pkt[9] << (33 - 32) ) |
+ ( pkt[10] >> 7 );
+ ++stream->ts_pcr_in;
+ stream->ts_found_pcr = 1;
}
+ }
- // Get continuity
- // Continuity only increments for adaption values of 0x3 or 0x01
- // and is not checked for start packets.
+ // If we don't have a PCR yet but the stream has PCRs just loop
+ // so we don't process anything until we have a clock reference.
+ // Unfortunately the HD Home Run appears to null out the PCR so if
+ // we didn't detect a PCR during scan keep going and we'll use
+ // the video stream DTS for the PCR.
- int start = (buf[1] & 0x40) != 0;
+ if ( !stream->ts_found_pcr && ( stream->ts_flags & TS_HAS_PCR ) )
+ {
+ return 0;
+ }
- if ( (adaption & 0x01) != 0 )
- {
- int continuity = (buf[3] & 0xF);
- if ( continuity == stream->ts_streamcont[curstream] )
- {
- // Spliced transport streams can have duplicate
- // continuity counts at the splice boundary.
- // Test to see if the packet is really a duplicate
- // by comparing packet summaries to see if they
- // match.
- uint8_t summary[8];
-
- summary[0] = adaption;
- summary[1] = adapt_len;
- if (adapt_len + 4 + 6 + 9 <= 188)
- {
- memcpy(&summary[2], buf+4+adapt_len+9, 6);
- }
- else
- {
- memset(&summary[2], 0, 6);
- }
- if ( memcmp( summary, stream->ts_pkt_summary[curstream], 8 ) == 0 )
- {
- // we got a duplicate packet (usually used to introduce
- // a PCR when one is needed). The only thing that can
- // change in the dup is the PCR which we grabbed above
- // so ignore the rest.
- continue;
- }
- }
- if ( !start && (stream->ts_streamcont[curstream] != -1) &&
- !stream->ts_skipbad[curstream] &&
- (continuity != ( (stream->ts_streamcont[curstream] + 1) & 0xf ) ) )
- {
- ts_err( stream, curstream, "continuity error: got %d expected %d",
- (int)continuity,
- (stream->ts_streamcont[curstream] + 1) & 0xf );
- stream->ts_streamcont[curstream] = continuity;
- continue;
- }
- stream->ts_streamcont[curstream] = continuity;
+ // Get continuity
+ // Continuity only increments for adaption values of 0x3 or 0x01
+ // and is not checked for start packets.
+
+ int start = (pkt[1] & 0x40) != 0;
+
+ if ( (adaption & 0x01) != 0 )
+ {
+ int continuity = (pkt[3] & 0xF);
+ if ( continuity == stream->ts_streamcont[curstream] )
+ {
+ // Spliced transport streams can have duplicate
+ // continuity counts at the splice boundary.
+ // Test to see if the packet is really a duplicate
+ // by comparing packet summaries to see if they
+ // match.
+ uint8_t summary[8];
- // Save a summary of this packet for later duplicate
- // testing. The summary includes some header information
- // and payload bytes. Should be enough to detect
- // non-duplicates.
- stream->ts_pkt_summary[curstream][0] = adaption;
- stream->ts_pkt_summary[curstream][1] = adapt_len;
+ summary[0] = adaption;
+ summary[1] = adapt_len;
if (adapt_len + 4 + 6 + 9 <= 188)
{
- memcpy(&stream->ts_pkt_summary[curstream][2],
- buf+4+adapt_len+9, 6);
+ memcpy(&summary[2], pkt+4+adapt_len+9, 6);
}
else
{
- memset(&stream->ts_pkt_summary[curstream][2], 0, 6);
+ memset(&summary[2], 0, 6);
+ }
+ if ( memcmp( summary, stream->ts_pkt_summary[curstream], 8 ) == 0 )
+ {
+ // we got a duplicate packet (usually used to introduce
+ // a PCR when one is needed). The only thing that can
+ // change in the dup is the PCR which we grabbed above
+ // so ignore the rest.
+ return 0;
}
}
+ if ( !start && (stream->ts_streamcont[curstream] != -1) &&
+ !stream->ts_skipbad[curstream] &&
+ (continuity != ( (stream->ts_streamcont[curstream] + 1) & 0xf ) ) )
+ {
+ ts_err( stream, curstream, "continuity error: got %d expected %d",
+ (int)continuity,
+ (stream->ts_streamcont[curstream] + 1) & 0xf );
+ stream->ts_streamcont[curstream] = continuity;
+ return 0;
+ }
+ stream->ts_streamcont[curstream] = continuity;
- /* If we get here the packet is valid - process its data */
-
- if ( start )
+ // Save a summary of this packet for later duplicate
+ // testing. The summary includes some header information
+ // and payload bytes. Should be enough to detect
+ // non-duplicates.
+ stream->ts_pkt_summary[curstream][0] = adaption;
+ stream->ts_pkt_summary[curstream][1] = adapt_len;
+ if (adapt_len + 4 + 6 + 9 <= 188)
{
- // Found a random access point (now we can start a frame/audio packet..)
+ memcpy(&stream->ts_pkt_summary[curstream][2],
+ pkt+4+adapt_len+9, 6);
+ }
+ else
+ {
+ memset(&stream->ts_pkt_summary[curstream][2], 0, 6);
+ }
+ }
- if ( stream->need_keyframe )
- {
- // we're looking for the first video frame because we're
- // doing random access during 'scan'
- if ( curstream != 0 || !isIframe( stream, buf, adapt_len ) )
- {
- // not the video stream or didn't find an I frame
- // but we'll only wait 255 video frames for an I frame.
- if ( curstream != 0 || ++stream->need_keyframe )
- {
- continue;
- }
- }
- stream->need_keyframe = 0;
- }
+ /* If we get here the packet is valid - process its data */
- // 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;
- }
+ if ( start )
+ {
+ // Found a random access point (now we can start a frame/audio packet..)
- if ( curstream == 0 )
+ if ( stream->need_keyframe )
+ {
+ // we're looking for the first video frame because we're
+ // doing random access during 'scan'
+ if ( curstream != video_index || !isIframe( stream, pkt, adapt_len ) )
{
- ++stream->frames;
-
- // if we don't have a pcr yet use the dts from this frame
- if ( !stream->ts_found_pcr )
+ // not the video stream or didn't find an I frame
+ // but we'll only wait 255 video frames for an I frame.
+ if ( curstream != video_index || ++stream->need_keyframe )
{
- // PES must begin with an mpeg start code & contain
- // a DTS or PTS.
- const uint8_t *pes = buf + adapt_len + 4;
- if ( pes[0] != 0x00 || pes[1] != 0x00 || pes[2] != 0x01 ||
- ( pes[7] >> 6 ) == 0 )
- {
- continue;
- }
- // if we have a dts use it otherwise use the pts
- stream->ts_pcr = pes_timestamp( pes + ( pes[7] & 0x40? 14 : 9 ) );
- ++stream->ts_pcr_in;
+ return 0;
}
}
+ stream->need_keyframe = 0;
+ }
+
+ // 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;
+ }
- // if this is a multiplexed stream make sure this is the
- // substream we want.
- if ( stream->ts_multiplexed[curstream] )
+ if ( curstream == video_index )
+ {
+ ++stream->frames;
+
+ // if we don't have a pcr yet use the dts from this frame
+ if ( !stream->ts_found_pcr )
{
// PES must begin with an mpeg start code & contain
// a DTS or PTS.
- const uint8_t *pes = buf + adapt_len + 4;
+ const uint8_t *pes = pkt + adapt_len + 4;
if ( pes[0] != 0x00 || pes[1] != 0x00 || pes[2] != 0x01 ||
- pes[3] != 0xfd )
- {
- stream->ts_skipbad[curstream] = 1;
- continue;
- }
- // the last byte of the header is the extension id. see if
- // it's the one we want.
- if ( pes[pes[8]+8] != stream->ts_multiplexed[curstream] )
+ ( pes[7] >> 6 ) == 0 )
{
- stream->ts_skipbad[curstream] = 1;
- continue;
+ return 0;
}
+ // if we have a dts use it otherwise use the pts
+ stream->ts_pcr = pes_timestamp( pes + ( pes[7] & 0x40? 14 : 9 ) );
+ ++stream->ts_pcr_in;
}
+ }
- // If we have some data already on this stream, turn it into
- // a program stream packet. Then add the payload for this
- // packet to the current pid's buffer.
- if ( stream->ts_pos[curstream] )
+ // if this is a multiplexed stream make sure this is the
+ // substream we want.
+ if ( stream->ts_multiplexed[curstream] )
+ {
+ // PES must begin with an mpeg start code & contain
+ // a DTS or PTS.
+ const uint8_t *pes = pkt + adapt_len + 4;
+ if ( pes[0] != 0x00 || pes[1] != 0x00 || pes[2] != 0x01 ||
+ pes[3] != 0xfd )
{
- // we have to ship the old packet before updating the pcr
- // since the packet we've been accumulating is referenced
- // to the old pcr.
- generate_output_data(stream, curstream);
-
- // remember the pcr that was in effect when we started
- // this packet.
- stream->ts_buf[curstream]->cur = stream->ts_pcr_in;
- hb_ts_stream_append_pkt(stream, curstream, buf + 4 + adapt_len,
- 184 - adapt_len);
- return 1;
+ stream->ts_skipbad[curstream] = 1;
+ return 0;
+ }
+ // the last byte of the header is the extension id. see if
+ // it's the one we want.
+ if ( pes[pes[8]+8] != stream->ts_multiplexed[curstream] )
+ {
+ stream->ts_skipbad[curstream] = 1;
+ return 0;
}
- // remember the pcr that was in effect when we started this packet.
- stream->ts_buf[curstream]->cur = stream->ts_pcr_in;
}
- // Add the payload for this packet to the current buffer
- if (!stream->ts_skipbad[curstream] && (184 - adapt_len) > 0)
- {
- hb_ts_stream_append_pkt(stream, curstream, buf + 4 + adapt_len,
+ // If we have some data already on this stream, turn it into
+ // a program stream packet. Then add the payload for this
+ // packet to the current pid's buffer.
+ if ( stream->ts_pos[curstream] )
+ {
+ // we have to ship the old packet before updating the pcr
+ // since the packet we've been accumulating is referenced
+ // to the old pcr.
+ generate_output_data(stream, curstream);
+
+ // remember the pcr that was in effect when we started
+ // this packet.
+ stream->ts_buf[curstream]->cur = stream->ts_pcr_in;
+ hb_ts_stream_append_pkt(stream, curstream, pkt + 4 + adapt_len,
184 - adapt_len);
- // see if we've hit the end of this PES packet
- const uint8_t *pes = stream->ts_buf[curstream]->data;
- int len = ( pes[4] << 8 ) + pes[5] + 6;
- if ( len > 6 && stream->ts_pos[curstream] == len &&
- pes[0] == 0x00 && pes[1] == 0x00 && pes[2] == 0x01 )
- {
- generate_output_data(stream, curstream);
- return 1;
- }
- }
- }
+ return 1;
+ }
+ // remember the pcr that was in effect when we started this packet.
+ stream->ts_buf[curstream]->cur = stream->ts_pcr_in;
+ }
+
+ // Add the payload for this packet to the current buffer
+ if (!stream->ts_skipbad[curstream] && (184 - adapt_len) > 0)
+ {
+ hb_ts_stream_append_pkt(stream, curstream, pkt + 4 + adapt_len,
+ 184 - adapt_len);
+ // see if we've hit the end of this PES packet
+ const uint8_t *pes = stream->ts_buf[curstream]->data;
+ int len = ( pes[4] << 8 ) + pes[5] + 6;
+ if ( len > 6 && stream->ts_pos[curstream] == len &&
+ pes[0] == 0x00 && pes[1] == 0x00 && pes[2] == 0x01 )
+ {
+ generate_output_data(stream, curstream);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int hb_ts_stream_decode( hb_stream_t *stream, hb_buffer_t *obuf )
+{
+ // spin until we get a packet of data from some stream or hit eof
+ while ( 1 )
+ {
+ const uint8_t *buf = next_packet(stream);
+ if ( buf == NULL )
+ {
+ // end of file - we didn't finish filling our ps write buffer
+ // so just discard the remainder (the partial buffer is useless)
+ hb_log("hb_ts_stream_decode - eof");
+ return 0;
+ }
+
+ if (hb_ts_decode_pkt( stream, buf, obuf ))
+ {
+ return 1;
+ }
+ }
+ return 0;
}
static void hb_ts_stream_reset(hb_stream_t *stream)
{
- int i;
+ int i;
- for (i=0; i < kMaxNumberDecodeStreams; i++)
- {
- stream->ts_pos[i] = 0;
- stream->ts_skipbad[i] = 1;
- stream->ts_streamcont[i] = -1;
- }
+ for (i=0; i < kMaxNumberDecodeStreams; i++)
+ {
+ stream->ts_pos[i] = 0;
+ stream->ts_skipbad[i] = 1;
+ stream->ts_streamcont[i] = -1;
+ }
stream->need_keyframe = 0;
@@ -3041,13 +3182,13 @@ static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream )
title->type = HB_STREAM_TYPE;
title->index = 1;
- // Copy part of the stream path to the title name
- char *sep = strrchr(stream->path, '/');
- if (sep)
- strcpy(title->name, sep+1);
- char *dot_term = strrchr(title->name, '.');
- if (dot_term)
- *dot_term = '\0';
+ // Copy part of the stream path to the title name
+ char *sep = strrchr(stream->path, '/');
+ if (sep)
+ strcpy(title->name, sep+1);
+ char *dot_term = strrchr(title->name, '.');
+ if (dot_term)
+ *dot_term = '\0';
uint64_t dur = ic->duration * 90000 / AV_TIME_BASE;
title->duration = dur;