From b63a63ceb2a43ef20e7ba79f173276d465575d14 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Mon, 17 Sep 2018 00:27:30 -0500 Subject: [PATCH] Fix many bugs around FPS and video length calculation (especially for MP3 and streaming WEBM formats). Also protecting samples_per_frame calculation to keep from crashing on undetected FPS. --- src/FFmpegReader.cpp | 33 ++++++++++++++++++++++++--------- src/Frame.cpp | 2 ++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 9711b2e5..243d7385 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -333,6 +333,11 @@ void FFmpegReader::UpdateAudioInfo() info.height = 480; } + // Fix invalid video lengths for certain types of files (MP3 for example) + if (info.has_video && ((info.duration * info.fps.ToDouble()) - info.video_length > 60)) { + info.video_length = info.duration * info.fps.ToDouble(); + } + // Add audio metadata (if any found) AVDictionaryEntry *tag = NULL; while ((tag = av_dict_get(aStream->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { @@ -422,7 +427,7 @@ void FFmpegReader::UpdateVideoInfo() } // Override an invalid framerate - if (info.fps.ToFloat() > 240.0f || (info.fps.num == 0 || info.fps.den == 0)) { + if (info.fps.ToFloat() > 240.0f || (info.fps.num <= 0 || info.fps.den <= 0) || info.video_length <= 0) { // Calculate FPS, duration, video bit rate, and video length manually // by scanning through all the video stream packets CheckFPS(); @@ -1864,7 +1869,6 @@ void FFmpegReader::CheckWorkingFrames(bool end_of_stream, int64_t requested_fram void FFmpegReader::CheckFPS() { check_fps = true; - AV_ALLOCATE_IMAGE(pFrame, AV_GET_CODEC_PIXEL_FORMAT(pStream, pCodecCtx), info.width, info.height); int first_second_counter = 0; int second_second_counter = 0; @@ -1872,6 +1876,7 @@ void FFmpegReader::CheckFPS() int forth_second_counter = 0; int fifth_second_counter = 0; int frames_detected = 0; + int64_t pts = 0; // Loop through the stream while (true) @@ -1891,7 +1896,7 @@ void FFmpegReader::CheckFPS() UpdatePTSOffset(true); // Get PTS of this packet - int64_t pts = GetVideoPTS(); + pts = GetVideoPTS(); // Remove pFrame RemoveAVFrame(pFrame); @@ -1922,7 +1927,7 @@ void FFmpegReader::CheckFPS() // Double check that all counters have greater than zero (or give up) if (second_second_counter != 0 && third_second_counter != 0 && forth_second_counter != 0 && fifth_second_counter != 0) { - // Calculate average FPS + // Calculate average FPS (average of first few seconds) int sum_fps = second_second_counter + third_second_counter + forth_second_counter + fifth_second_counter; int avg_fps = round(sum_fps / 4.0f); @@ -1931,22 +1936,32 @@ void FFmpegReader::CheckFPS() // Update Duration and Length info.video_length = frames_detected; - info.duration = frames_detected / round(sum_fps / 4.0f); + info.duration = frames_detected / (sum_fps / 4.0f); + + // Update video bit rate + info.video_bit_rate = info.file_size / info.duration; + } else if (second_second_counter != 0 && third_second_counter != 0) { + // Calculate average FPS (only on second 2) + int sum_fps = second_second_counter; + + // Update FPS + info.fps = Fraction(sum_fps, 1); + + // Update Duration and Length + info.video_length = frames_detected; + info.duration = frames_detected / float(sum_fps); // Update video bit rate info.video_bit_rate = info.file_size / info.duration; } else { - // Too short to determine framerate, just default FPS // Set a few important default video settings (so audio can be divided into frames) info.fps.num = 30; info.fps.den = 1; - info.video_timebase.num = info.fps.den; - info.video_timebase.den = info.fps.num; // Calculate number of frames info.video_length = frames_detected; - info.duration = frames_detected / info.video_timebase.ToFloat(); + info.duration = frames_detected / info.fps.ToFloat(); } } diff --git a/src/Frame.cpp b/src/Frame.cpp index 16a0d267..a00fc232 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -512,6 +512,8 @@ int Frame::GetSamplesPerFrame(int64_t number, Fraction fps, int sample_rate, int // Subtract the previous frame's total samples with this frame's total samples. Not all sample rates can // be evenly divided into frames, so each frame can have have different # of samples. int samples_per_frame = round(total_samples - previous_samples); + if (samples_per_frame < 0) + samples_per_frame = 0; return samples_per_frame; }