Skip to content

Commit

Permalink
Improve heuristic for variable frame rate detection (#1242)
Browse files Browse the repository at this point in the history
Description: Previous heuristic would fail for videos with
a fractional fps, such as 29.97(30000/1001). It would classify
such videos as vfr even if they might be cfr.
This change will compare the frame rate reported by the container
to an estimate based off on the first video frame.
While this check is not exhaustive, but in my testing it was accurate.

Why we need this PR?
Refactoring to improve what
Improves vfr detection heuristic.
What happened in this PR?
Explain solution of the problem, new feature added.
Compare the frame rate reported by the container
to an estimate based off on the first video frame
What was changed, added, removed?
Replaced old check with new check for vfr.
Was this PR tested? How?
Tested with constant frame rate video with 29.97 fps
Were docs and examples updated, if necessary?
No

Signed-off-by: Abhishek Sansanwal <asansanwal@nvidia.com>
  • Loading branch information
a-sansanwal authored and awolant committed Sep 13, 2019
1 parent edfca46 commit 801c888
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 2 deletions.
2 changes: 1 addition & 1 deletion DALI_EXTRA_VERSION
@@ -1 +1 @@
c5b3b50608fbe293a5cca49b72763e3ec8f80801
1b224243c057d413cf3e1c75694d4d1acf73d0dc
24 changes: 23 additions & 1 deletion dali/pipeline/operators/reader/loader/video_loader.cc
Expand Up @@ -21,6 +21,7 @@
#include <string>
#include <utility>
#include <fstream>
#include <limits>

namespace dali {

Expand Down Expand Up @@ -136,6 +137,18 @@ static constexpr auto frames_used_warning_ratio = 3.0f;
static constexpr auto frames_used_warning_minimum = 1000;
static constexpr auto frames_used_warning_interval = 10000;

// Source: http://en.cppreference.com/w/cpp/types/numeric_limits/epsilon
template<class T>
typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type
almost_equal(T x, T y, int ulp) {
if (x == y) return true;
// the machine epsilon has to be scaled to the magnitude of the values used
// and multiplied by the desired precision in ULPs (units in the last place)
return std::abs(x-y) <= std::numeric_limits<T>::epsilon() * std::abs(x+y) * ulp
// unless the result is subnormal
|| std::abs(x-y) < std::numeric_limits<T>::min();
}

OpenFile& VideoLoader::get_or_open_file(const std::string &filename) {
auto& file = open_files_[filename];

Expand Down Expand Up @@ -217,9 +230,18 @@ OpenFile& VideoLoader::get_or_open_file(const std::string &filename) {
stream->avg_frame_rate.num};

// This check is based on heuristic FFMPEG API
AVPacket pkt = AVPacket{};
int ret;
while ((ret = av_read_frame(file.fmt_ctx_.get(), &pkt)) >= 0) {
if (pkt.stream_index == file.vid_stream_idx_) break;
}

DALI_ENFORCE(ret >=0, "Unable to read frame from file :" + filename);

DALI_ENFORCE(
file.frame_base_.num == 1,
almost_equal(av_q2d(file.frame_base_), pkt.duration * av_q2d(file.stream_base_), 2),
"Variable frame rate videos are unsupported. Check failed for file: " + filename);

file.frame_count_ = av_rescale_q(stream->duration,
stream->time_base,
file.frame_base_);
Expand Down
16 changes: 16 additions & 0 deletions dali/pipeline/operators/reader/video_reader_op_test.cc
Expand Up @@ -41,6 +41,22 @@ TEST_F(VideoReaderTest, VariableFrameRate) {
EXPECT_THROW(pipe.Build(this->Outputs()), std::runtime_error);
}

TEST_F(VideoReaderTest, FractionalConstantFrameRate) {
Pipeline pipe(1, 1, 0);
const int sequence_length = 60;

pipe.AddOperator(
OpSpec("VideoReader")
.AddArg("device", "gpu")
.AddArg("sequence_length", sequence_length)
.AddArg(
"filenames",
std::vector<std::string>{testing::dali_extra_path() + "/db/video/cfr_ntsc_29_97_test.mp4"})
.AddOutput("frames", "gpu"));

pipe.Build(this->Outputs());
}

TEST_F(VideoReaderTest, ConstantFrameRate) {
Pipeline pipe(1, 1, 0);
const int sequence_length = 60;
Expand Down

0 comments on commit 801c888

Please sign in to comment.