From d2a98c751db3e6c0fe0a095e06675968795859f0 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Sun, 3 Nov 2024 00:21:48 +0100 Subject: [PATCH 1/2] Stop shifting timecodes to start at 0ms This reverts commit 1dedfb18cd62f006c39f76ce298f4a3157607271. Such files exist in the wild (one example being a large set of old CR rips, see https://redvice.org/2018/crunchyroll-83ms-delay/) and players like mpv also respect this video delay (at least they do now, they might not have when the linked commit was made). Fixes TypesettingTools/Aegisub#21. Fixes TypesettingTools/Aegisub#169. --- libaegisub/common/vfr.cpp | 16 ++++------------ tests/tests/vfr.cpp | 18 ------------------ 2 files changed, 4 insertions(+), 30 deletions(-) diff --git a/libaegisub/common/vfr.cpp b/libaegisub/common/vfr.cpp index 559bcaebb8..a3456c3c26 100644 --- a/libaegisub/common/vfr.cpp +++ b/libaegisub/common/vfr.cpp @@ -45,13 +45,6 @@ void validate_timecodes(std::vector const& timecodes) { throw InvalidFramerate("Timecodes are all identical"); } -/// @brief Shift timecodes so that frame 0 starts at time 0 -/// @param timecodes List of timecodes to normalize -void normalize_timecodes(std::vector &timecodes) { - if (int front = timecodes.front()) - boost::for_each(timecodes, [=](int &tc) { tc -= front; }); -} - // A "start,end,fps" line in a v1 timecode file struct TimecodeRange { int start; @@ -153,9 +146,8 @@ Framerate::Framerate(int64_t numerator, int64_t denominator, bool drop) void Framerate::SetFromTimecodes() { validate_timecodes(timecodes); - normalize_timecodes(timecodes); denominator = default_denominator; - numerator = (timecodes.size() - 1) * denominator * 1000 / timecodes.back(); + numerator = (timecodes.size() - 1) * denominator * 1000 / (timecodes.back() - timecodes.front()); last = (timecodes.size() - 1) * denominator * 1000; } @@ -221,8 +213,8 @@ int Framerate::FrameAtTime(int ms, Time type) const { if (type == END) return FrameAtTime(ms - 1); - if (ms < 0) - return int((ms * numerator / denominator - 999) / 1000); + if (ms < timecodes.front()) + return int(((ms - timecodes.front()) * numerator / denominator - 999) / 1000); if (ms > timecodes.back()) return ((ms + 1) * numerator - last - numerator / 2 + (1000 * denominator - 1)) / (1000 * denominator) + timecodes.size() - 2; @@ -245,7 +237,7 @@ int Framerate::TimeAtFrame(int frame, Time type) const { } if (frame < 0) - return (int)(frame * denominator * 1000 / numerator); + return (int)(frame * denominator * 1000 / numerator) + timecodes.front(); if (frame >= (signed)timecodes.size()) { int64_t frames_past_end = frame - (int)timecodes.size() + 1; diff --git a/tests/tests/vfr.cpp b/tests/tests/vfr.cpp index 52c5739813..76a73e76ef 100644 --- a/tests/tests/vfr.cpp +++ b/tests/tests/vfr.cpp @@ -394,24 +394,6 @@ TEST(lagi_vfr, load_v1_save_v2_ovr) { EXPECT_TRUE(validate_save("data/vfr/in/v2_100_frames_30_with_override.txt", "data/vfr/out/v2_100_frames_30_with_override.txt")); } -TEST(lagi_vfr, nonzero_start_time) { - Framerate fps; - - ASSERT_NO_THROW(fps = Framerate({ 10, 20, 30, 40, 50 })); - EXPECT_EQ(0, fps.TimeAtFrame(0, EXACT)); - EXPECT_EQ(10, fps.TimeAtFrame(1, EXACT)); - EXPECT_EQ(20, fps.TimeAtFrame(2, EXACT)); - EXPECT_EQ(30, fps.TimeAtFrame(3, EXACT)); - EXPECT_EQ(40, fps.TimeAtFrame(4, EXACT)); - - ASSERT_NO_THROW(fps = Framerate({ -10, 20, 30, 40, 50 })); - EXPECT_EQ(0, fps.TimeAtFrame(0, EXACT)); - EXPECT_EQ(30, fps.TimeAtFrame(1, EXACT)); - EXPECT_EQ(40, fps.TimeAtFrame(2, EXACT)); - EXPECT_EQ(50, fps.TimeAtFrame(3, EXACT)); - EXPECT_EQ(60, fps.TimeAtFrame(4, EXACT)); -} - TEST(lagi_vfr, rational_timebase) { Framerate fps; From 41eada39455608011c9f2f43f579870688278afa Mon Sep 17 00:00:00 2001 From: moi15moi Date: Wed, 31 Dec 2025 22:59:21 -0500 Subject: [PATCH 2/2] Fix timestamps I believe mpv calculate the time of each frame like this (I have no proof) ``` time_of_frame_x = (time_of_frame_x - time_of_frame_0) + (AVStream::start_time - AVFormatContext::start_time) ``` In ffms2 case, I think `time_of_frame_0` is always equals to `AVStream::start_time` (this might not be the case!!!) and they are represented by `FFMS_VideoProperties::FirstTime`. Fix https://github.com/arch1t3cht/Aegisub/issues/172 --- src/video_provider_ffmpegsource.cpp | 6 +++++- subprojects/ffms2.wrap | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/video_provider_ffmpegsource.cpp b/src/video_provider_ffmpegsource.cpp index 1295fa4b2c..f13be56137 100644 --- a/src/video_provider_ffmpegsource.cpp +++ b/src/video_provider_ffmpegsource.cpp @@ -194,6 +194,8 @@ void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename, std::st Index = nullptr; } + int64_t ContainerFirstTime = FFMS_GetContainerFirstTimeI(Indexer); + // moment of truth if (!Index) { auto TrackMask = TrackSelection::None; @@ -281,6 +283,8 @@ void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename, std::st throw VideoOpenError("failed to get track time base"); // build list of keyframes and timecodes + double Offset = ContainerFirstTime / 1000000.0 * 1000.0; + std::vector TimecodesVector; for (int CurFrameNum = 0; CurFrameNum < VideoInfo->NumFrames; CurFrameNum++) { const FFMS_FrameInfo *CurFrameData = FFMS_GetFrameInfo(FrameData, CurFrameNum); @@ -292,7 +296,7 @@ void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename, std::st KeyFramesList.push_back(CurFrameNum); // calculate timestamp and add to timecodes vector - int Timestamp = (int)((CurFrameData->PTS * TimeBase->Num) / TimeBase->Den); + int Timestamp = (int)((CurFrameData->PTS * TimeBase->Num) / TimeBase->Den - Offset); TimecodesVector.push_back(Timestamp); } if (TimecodesVector.size() < 2) diff --git a/subprojects/ffms2.wrap b/subprojects/ffms2.wrap index b5e945a06c..75810c2bac 100644 --- a/subprojects/ffms2.wrap +++ b/subprojects/ffms2.wrap @@ -1,6 +1,6 @@ [wrap-git] -url = https://github.com/FFMS/ffms2.git -revision = head +url = https://github.com/moi15moi/ffms2.git +revision = Add-FFMS_GetContainerFirstTimeI patch_directory = ffms2 [provide]