From 16fedc94d2bc68668cafefc1c4afd0f5b2682bee Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Fri, 13 Mar 2020 15:19:59 +0100 Subject: [PATCH] MP4 reader: use track duration for frame duration for single-frame video tracks Fixes #2747. --- NEWS.md | 8 ++++++++ src/input/r_qtmp4.cpp | 18 +++++++++++++++--- tests/results.txt | 1 + .../test-690mp4_single_video_frame_duration.rb | 5 +++++ 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100755 tests/test-690mp4_single_video_frame_duration.rb diff --git a/NEWS.md b/NEWS.md index 4a84c7a8e..74d9ead45 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,11 @@ +# Version ? + +## Bug fixes + +* mkvmerge: MP4 reader: fixed calculating the duration of video tracks that + only contain a single frame. Fixes #2747. + + # Version 44.0.0 "Domino" 2020-03-08 ## New features and enhancements diff --git a/src/input/r_qtmp4.cpp b/src/input/r_qtmp4.cpp index c37a448db..4c3703f9f 100644 --- a/src/input/r_qtmp4.cpp +++ b/src/input/r_qtmp4.cpp @@ -2104,8 +2104,20 @@ qtmp4_demuxer_c::calculate_frame_rate() { return; } + if (('v' == type) && time_scale && global_duration && (sample_table.size() < 2)) { + frame_rate = mtx::frame_timing::determine_frame_rate(static_cast(global_duration) * 1'000'000'000ull / static_cast(time_scale)); + if (frame_rate) + m_use_frame_rate_for_duration = boost::rational_cast(int64_rational_c{1'000'000'000ll} / frame_rate); + + mxdebug_if(m_debug_frame_rate, + fmt::format("calculate_frame_rate: case 2: video track with time scale {0} & duration {1} result: {2}\n", + time_scale, global_duration, frame_rate ? format_timestamp(*m_use_frame_rate_for_duration) : ""s)); + + return; + } + if (sample_table.size() < 2) { - mxdebug_if(m_debug_frame_rate, fmt::format("calculate_frame_rate: case 2: sample table too small\n")); + mxdebug_if(m_debug_frame_rate, fmt::format("calculate_frame_rate: case 3: sample table too small\n")); return; } @@ -2126,7 +2138,7 @@ qtmp4_demuxer_c::calculate_frame_rate() { m_use_frame_rate_for_duration = boost::rational_cast(int64_rational_c{1'000'000'000ll} / frame_rate); mxdebug_if(m_debug_frame_rate, - fmt::format("calculate_frame_rate: case 3: duration {0} num_frames {1} frame_duration {2} frame_rate {3}/{4} use_frame_rate_for_duration {5}\n", + fmt::format("calculate_frame_rate: case 4: duration {0} num_frames {1} frame_duration {2} frame_rate {3}/{4} use_frame_rate_for_duration {5}\n", duration, num_frames, duration / num_frames, frame_rate.numerator(), frame_rate.denominator(), m_use_frame_rate_for_duration ? *m_use_frame_rate_for_duration : -1)); return; @@ -2146,7 +2158,7 @@ qtmp4_demuxer_c::calculate_frame_rate() { frame_rate.assign(static_cast(1000000000ll), to_nsecs(most_common.first)); mxdebug_if(m_debug_frame_rate, - fmt::format("calculate_frame_rate: case 4: duration {0} num_frames {1} frame_duration {2} most_common.num_occurances {3} most_common.duration {4} frame_rate {5}/{6}\n", + fmt::format("calculate_frame_rate: case 5: duration {0} num_frames {1} frame_duration {2} most_common.num_occurances {3} most_common.duration {4} frame_rate {5}/{6}\n", duration, num_frames, duration / num_frames, most_common.second, to_nsecs(most_common.first), frame_rate.numerator(), frame_rate.denominator())); } diff --git a/tests/results.txt b/tests/results.txt index 11f2dd61a..e0695564c 100644 --- a/tests/results.txt +++ b/tests/results.txt @@ -535,3 +535,4 @@ T_686container_wrong_num_channels:true:passed:20200101-235713:0.009057097 T_687h264_additional_sps_pps_in_middle:3c9e0781b9b5e75be5dc3da6d59b8795:passed:20200105-163253:0.029150634 T_688opus_single_page:6ce93001a667609670ec7103450f622f:passed:20200113-193521:0.011219948 T_689mpeg_ts_mpeg_2_two_frames_only:b187d13ae7af1f6a61e7369c549cf2c4:passed:20200126-124111:0.034034657 +T_690mp4_single_video_frame_duration:ff234c76e7626f78aded30b3c2ad6669:passed:20200313-151900:0.040140889 diff --git a/tests/test-690mp4_single_video_frame_duration.rb b/tests/test-690mp4_single_video_frame_duration.rb new file mode 100755 index 000000000..5310dce5d --- /dev/null +++ b/tests/test-690mp4_single_video_frame_duration.rb @@ -0,0 +1,5 @@ +#!/usr/bin/ruby -w + +# T_690mp4_single_video_frame_duration +describe "mkvmerge / MP4/MOV with a single video frame, calculating the frame duration" +test_merge "data/mp4/single_video_frame.mov", :args => "-A"