/*********************************************************************** * 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; }
/*********************************************************************** * 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; }
int main(int argc, char *argv[]) { double fps, k; int i, width, height, len; CvCapture *in; CvVideoWriter *out; IplImage *bufimg; if (argc != 3) { ERROR("Usage: interpolation source destination"); return EXIT_FAILURE; } in = cvCaptureFromFile(argv[1]); if (!in) { ERROR("Failed to open for reading '%s'", argv[1]); return EXIT_FAILURE; } fps = cvGetCaptureProperty(in, CV_CAP_PROP_FPS); if (fps > TARGET) { ERROR("Video already has a framerate of %.2f (target was %.2f)", fps, TARGET); return EXIT_FAILURE; } width = cvGetCaptureProperty(in, CV_CAP_PROP_FRAME_WIDTH); height = cvGetCaptureProperty(in, CV_CAP_PROP_FRAME_HEIGHT); len = cvGetCaptureProperty(in, CV_CAP_PROP_FRAME_COUNT); out = cvCreateVideoWriter(TMPFILE, CODEC, TARGET, cvSize(width, height), 1); if (!out) { ERROR("Failed to open for writing '%s'", TMPFILE); return EXIT_FAILURE; } k = TARGET / fps; bufimg = cvQueryFrame(in); for (i = 1; i < len; ++i) { IplImage *img; img = cvCloneImage(bufimg); bufimg = cvQueryFrame(in); progressbar(100 * i / len); if (interpolate(k, out, img, bufimg)) return EXIT_FAILURE; cvReleaseImage(&img); } cvWriteFrame(out, bufimg); progressbar(0); cvReleaseVideoWriter(&out); cvReleaseCapture(&in); if (add_audio(argv[1], argv[2])) return EXIT_FAILURE; return EXIT_SUCCESS; }