static void add_subtitle(int track, hb_list_t *list_subtitle, BLURAY_STREAM_INFO *bdsub, uint32_t codec) { hb_subtitle_t * subtitle; iso639_lang_t * lang; subtitle = calloc( sizeof( hb_subtitle_t ), 1 ); subtitle->track = track; subtitle->id = bdsub->pid; switch ( bdsub->coding_type ) { case BLURAY_STREAM_TYPE_SUB_PG: subtitle->source = PGSSUB; subtitle->format = PICTURESUB; subtitle->config.dest = RENDERSUB; break; default: // Unrecognized, don't add to list free( subtitle ); return; } lang = lang_for_code2( (char*)bdsub->lang ); snprintf(subtitle->lang, sizeof( subtitle->lang ), "%s [%s]", strlen(lang->native_name) ? lang->native_name : lang->eng_name, hb_subsource_name(subtitle->source)); snprintf(subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "%s", lang->iso639_2); subtitle->reg_desc = STR4_TO_UINT32("HDMV"); subtitle->stream_type = bdsub->coding_type; subtitle->codec = codec; hb_log( "bd: subtitle id=0x%x, lang=%s, 3cc=%s", subtitle->id, subtitle->lang, subtitle->iso639_2 ); hb_list_add( list_subtitle, subtitle ); return; }
static void add_audio(int track, hb_list_t *list_audio, BLURAY_STREAM_INFO *bdaudio, int substream_type, uint32_t codec, uint32_t codec_param) { const char * codec_name; hb_audio_t * audio; iso639_lang_t * lang; audio = calloc( sizeof( hb_audio_t ), 1 ); audio->id = (substream_type << 16) | bdaudio->pid; audio->config.in.reg_desc = STR4_TO_UINT32("HDMV"); audio->config.in.stream_type = bdaudio->coding_type; audio->config.in.substream_type = substream_type; audio->config.in.codec = codec; audio->config.in.codec_param = codec_param; switch( audio->config.in.codec ) { case HB_ACODEC_AC3: codec_name = "AC3"; break; case HB_ACODEC_DCA: codec_name = "DTS"; break; default: { if( audio->config.in.codec & HB_ACODEC_FF_MASK ) { switch( bdaudio->coding_type ) { case BLURAY_STREAM_TYPE_AUDIO_AC3PLUS: codec_name = "E-AC3"; break; case BLURAY_STREAM_TYPE_AUDIO_DTSHD: codec_name = "DTS-HD HRA"; break; case BLURAY_STREAM_TYPE_AUDIO_DTSHD_MASTER: codec_name = "DTS-HD MA"; break; case BLURAY_STREAM_TYPE_AUDIO_LPCM: codec_name = "BD LPCM"; break; case BLURAY_STREAM_TYPE_AUDIO_MPEG1: codec_name = "MPEG1"; break; case BLURAY_STREAM_TYPE_AUDIO_MPEG2: codec_name = "MPEG2"; break; case BLURAY_STREAM_TYPE_AUDIO_TRUHD: codec_name = "TrueHD"; break; default: codec_name = "Unknown FFmpeg"; break; } } else { codec_name = "Unknown"; } } break; } lang = lang_for_code2( (char*)bdaudio->lang ); audio->config.lang.attributes = HB_AUDIO_ATTR_NONE; 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=0x%x, lang=%s (%s), 3cc=%s", audio->id, audio->config.lang.simple, codec_name, audio->config.lang.iso639_2); audio->config.in.track = track; hb_list_add( list_audio, audio ); return; }
/*********************************************************************** * 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, jj; BLURAY_TITLE_INFO * ti = NULL; hb_log( "bd: scanning title %d", tt ); title = hb_title_init( d->path, tt ); title->demuxer = HB_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( IS_DIR_SEP(p_cur[0]) && p_cur[1] ) { p_last = &p_cur[1]; } } snprintf( title->name, sizeof( title->name ), "%s", p_last ); char *dot_term = strrchr(title->name, '.'); if (dot_term) *dot_term = '\0'; title->vts = 0; title->ttn = 0; if (tt <= d->title_count) { ti = d->title_info[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; } hb_log( "bd: playlist %05d.MPLS", ti->playlist ); title->playlist = ti->playlist; 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; /* 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 (%"PRIu64" ms)", title->hours, title->minutes, title->seconds, title->duration / 90 ); /* ignore short titles because they're often stills */ if( ti->duration < min_duration ) { hb_log( "bd: ignoring title (too short)" ); goto fail; } if (global_verbosity_level >= 2) { show_clip_list(ti); } 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=0x%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 ? "H.264" : "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" ); switch( bdvideo->coding_type ) { case BLURAY_STREAM_TYPE_VIDEO_MPEG1: case BLURAY_STREAM_TYPE_VIDEO_MPEG2: title->video_codec = WORK_DECAVCODECV; title->video_codec_param = AV_CODEC_ID_MPEG2VIDEO; break; case BLURAY_STREAM_TYPE_VIDEO_VC1: title->video_codec = WORK_DECAVCODECV; title->video_codec_param = AV_CODEC_ID_VC1; break; case BLURAY_STREAM_TYPE_VIDEO_H264: title->video_codec = WORK_DECAVCODECV; title->video_codec_param = AV_CODEC_ID_H264; break; default: hb_log( "scan: unknown video codec (0x%x)", bdvideo->coding_type ); goto fail; } switch ( bdvideo->aspect ) { case BLURAY_ASPECT_RATIO_4_3: title->container_dar.num = 4; title->container_dar.den = 3; break; case BLURAY_ASPECT_RATIO_16_9: title->container_dar.num = 16; title->container_dar.den = 9; break; default: hb_log( "bd: unknown aspect" ); goto fail; } hb_log("bd: aspect = %d:%d", title->container_dar.num, title->container_dar.den); /* Detect audio */ // Max primary BD audios is 32 int matches; int most_audio = 0; int audio_clip_index = 0; if (ti->clip_count > 2) { // All BD clips are not all required to have the same audio. // But clips that have seamless transition are required // to have the same audio as the previous clip. // So find the clip that has the most other clips with the // matching audio. for ( ii = 0; ii < ti->clip_count; ii++ ) { matches = 0; for ( jj = 0; jj < ti->clip_count; jj++ ) { if ( bd_audio_equal( &ti->clips[ii], &ti->clips[jj] ) ) { matches++; } } if ( matches > most_audio ) { most_audio = matches; audio_clip_index = ii; } } } else if (ti->clip_count == 2) { // If there are only 2 clips, pick audios from the longer clip if (ti->clips[0].pkt_count < ti->clips[1].pkt_count) audio_clip_index = 1; } // Add all the audios found in the above clip. for (ii = 0; ii < ti->clips[audio_clip_index].audio_stream_count; ii++) { BLURAY_STREAM_INFO * bdaudio; bdaudio = &ti->clips[audio_clip_index].audio_streams[ii]; switch (bdaudio->coding_type) { case BLURAY_STREAM_TYPE_AUDIO_TRUHD: // Add 2 audio tracks. One for TrueHD and one for AC-3 add_audio(ii, title->list_audio, bdaudio, HB_SUBSTREAM_BD_AC3, HB_ACODEC_AC3, AV_CODEC_ID_AC3); add_audio(ii, title->list_audio, bdaudio, HB_SUBSTREAM_BD_TRUEHD, HB_ACODEC_FFTRUEHD, AV_CODEC_ID_TRUEHD); break; case BLURAY_STREAM_TYPE_AUDIO_DTS: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_DCA, AV_CODEC_ID_DTS); break; case BLURAY_STREAM_TYPE_AUDIO_MPEG2: case BLURAY_STREAM_TYPE_AUDIO_MPEG1: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_FFMPEG, AV_CODEC_ID_MP2); break; case BLURAY_STREAM_TYPE_AUDIO_AC3PLUS: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_FFEAC3, AV_CODEC_ID_EAC3); break; case BLURAY_STREAM_TYPE_AUDIO_LPCM: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_FFMPEG, AV_CODEC_ID_PCM_BLURAY); break; case BLURAY_STREAM_TYPE_AUDIO_AC3: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_AC3, AV_CODEC_ID_AC3); break; case BLURAY_STREAM_TYPE_AUDIO_DTSHD_MASTER: case BLURAY_STREAM_TYPE_AUDIO_DTSHD: // Add 2 audio tracks. One for DTS-HD and one for DTS add_audio(ii, title->list_audio, bdaudio, HB_SUBSTREAM_BD_DTS, HB_ACODEC_DCA, AV_CODEC_ID_DTS); // DTS-HD is special. The substreams must be concatinated // DTS-core followed by DTS-hd-extensions. Setting // a substream id of 0 says use all substreams. add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_DCA_HD, AV_CODEC_ID_DTS); break; default: hb_log("scan: unknown audio pid 0x%x codec 0x%x", bdaudio->pid, bdaudio->coding_type); break; } } // Add all the subtitles found in the above clip. for ( ii = 0; ii < ti->clips[audio_clip_index].pg_stream_count; ii++ ) { BLURAY_STREAM_INFO * bdpgs; bdpgs = &ti->clips[audio_clip_index].pg_streams[ii]; switch( bdpgs->coding_type ) { case BLURAY_STREAM_TYPE_SUB_PG: add_subtitle(ii, title->list_subtitle, bdpgs, WORK_DECPGSSUB); break; default: hb_log( "scan: unknown subtitle pid 0x%x codec 0x%x", bdpgs->pid, bdpgs->coding_type ); break; } } /* Chapters */ for ( ii = 0, jj = 0; ii < ti->chapter_count; ii++ ) { char chapter_title[80]; // Sanity check start time of this chapter. // If it is beyond the end of the title, drop it. if (ti->chapters[ii].start > ti->duration) { hb_log("bd: chapter %d invalid start %"PRIu64", dropping", ii+1, ti->chapters[ii].start); continue; } chapter = calloc( sizeof( hb_chapter_t ), 1 ); chapter->index = ++jj; sprintf( chapter_title, "Chapter %d", chapter->index ); hb_chapter_set_title( chapter, chapter_title ); chapter->duration = ti->chapters[ii].duration; chapter->block_start = ti->chapters[ii].offset; // Sanity check chapter duration and start times // Have seen some invalid durations in the wild if (ii < ti->chapter_count - 1) { // Validate start time if (ti->chapters[ii+1].start < ti->chapters[ii].start) { hb_log("bd: chapter %d invalid start %"PRIu64"", ii+1, ti->chapters[ii+1].start); ti->chapters[ii+1].start = ti->chapters[ii].start + chapter->duration; } if (ti->chapters[ii+1].start - ti->chapters[ii].start != chapter->duration) { hb_log("bd: chapter %d invalid duration %"PRIu64"", ii+1, chapter->duration); chapter->duration = ti->chapters[ii+1].start - ti->chapters[ii].start; } } else { if (ti->duration - ti->chapters[ii].start != chapter->duration) { hb_log("bd: chapter %d invalid duration %"PRIu64"", ii+1, chapter->duration); chapter->duration = ti->duration - ti->chapters[ii].start; } } int 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", %"PRIu64" 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_title_close( &title ); cleanup: return title; }
/*********************************************************************** * 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, jj; BLURAY_TITLE_INFO * ti = NULL; hb_log( "bd: scanning title %d", tt ); title = hb_title_init( d->path, tt ); title->demuxer = HB_MPEG_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 = d->title_info[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; } hb_log( "bd: playlist %05d.MPLS", ti->playlist ); title->playlist = ti->playlist; 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; /* 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 ); /* ignore short titles because they're often stills */ if( ti->duration < min_duration ) { hb_log( "bd: ignoring title (too short)" ); goto fail; } 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=0x%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 ? "H.264" : "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 (0x%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 */ // All BD clips are not all required to have the same audio. // But clips that have seamless transition are required // to have the same audio as the previous clip. // So find the clip that has the most other clips with the // matching audio. // Max primary BD audios is 32 int matches; int most_audio = 0; int audio_clip_index = 0; for ( ii = 0; ii < ti->clip_count; ii++ ) { matches = 0; for ( jj = 0; jj < ti->clip_count; jj++ ) { if ( bd_audio_equal( &ti->clips[ii], &ti->clips[jj] ) ) { matches++; } } if ( matches > most_audio ) { most_audio = matches; 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++ ) { BLURAY_STREAM_INFO * bdaudio; bdaudio = &ti->clips[audio_clip_index].audio_streams[ii]; switch( bdaudio->coding_type ) { case BLURAY_STREAM_TYPE_AUDIO_TRUHD: // Add 2 audio tracks. One for TrueHD and one for AC-3 add_audio(ii, title->list_audio, bdaudio, HB_SUBSTREAM_BD_AC3, HB_ACODEC_AC3, 0); add_audio(ii, title->list_audio, bdaudio, HB_SUBSTREAM_BD_TRUEHD, HB_ACODEC_FFMPEG, CODEC_ID_TRUEHD); break; case BLURAY_STREAM_TYPE_AUDIO_DTS: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_DCA, 0); break; case BLURAY_STREAM_TYPE_AUDIO_MPEG2: case BLURAY_STREAM_TYPE_AUDIO_MPEG1: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_FFMPEG, CODEC_ID_MP2); break; case BLURAY_STREAM_TYPE_AUDIO_AC3PLUS: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_FFMPEG, CODEC_ID_EAC3); break; case BLURAY_STREAM_TYPE_AUDIO_LPCM: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_FFMPEG, CODEC_ID_PCM_BLURAY); break; case BLURAY_STREAM_TYPE_AUDIO_AC3: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_AC3, 0); break; case BLURAY_STREAM_TYPE_AUDIO_DTSHD_MASTER: case BLURAY_STREAM_TYPE_AUDIO_DTSHD: // Add 2 audio tracks. One for DTS-HD and one for DTS add_audio(ii, title->list_audio, bdaudio, HB_SUBSTREAM_BD_DTS, HB_ACODEC_DCA, 0); // DTS-HD is special. The substreams must be concatinated // DTS-core followed by DTS-hd-extensions. Setting // a substream id of 0 says use all substreams. add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_DCA_HD, CODEC_ID_DTS); break; default: hb_log( "scan: unknown audio pid 0x%x codec 0x%x", bdaudio->pid, bdaudio->coding_type ); break; } } // Add all the subtitles found in the above clip. for ( ii = 0; ii < ti->clips[audio_clip_index].pg_stream_count; ii++ ) { BLURAY_STREAM_INFO * bdpgs; bdpgs = &ti->clips[audio_clip_index].pg_streams[ii]; switch( bdpgs->coding_type ) { case BLURAY_STREAM_TYPE_SUB_PG: add_subtitle(ii, title->list_subtitle, bdpgs, WORK_DECPGSSUB); break; default: hb_log( "scan: unknown subtitle pid 0x%x codec 0x%x", bdpgs->pid, bdpgs->coding_type ); break; } } /* Chapters */ for ( ii = 0; ii < ti->chapter_count; ii++ ) { chapter = calloc( sizeof( hb_chapter_t ), 1 ); chapter->index = ii + 1; sprintf( chapter->title, "Chapter %d", chapter->index ); chapter->duration = ti->chapters[ii].duration; chapter->block_start = ti->chapters[ii].offset; int 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_title_close( &title ); cleanup: return title; }