summaryrefslogtreecommitdiffstats
path: root/libhb/bd.c
diff options
context:
space:
mode:
Diffstat (limited to 'libhb/bd.c')
-rw-r--r--libhb/bd.c651
1 files changed, 651 insertions, 0 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 );
+ }
+}
+