From b5f2c7edda5d856481566eee24bb17110e98ce81 Mon Sep 17 00:00:00 2001 From: Mirko Bonadei Date: Mon, 27 Feb 2023 11:13:13 +0100 Subject: [PATCH] Make Y4mFrameGenerator read FPS from file format. Bug: b/269577953 Change-Id: Ied0072e1fdfbfb4d2b11e74a814c0718cad01d66 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/294862 Reviewed-by: Artem Titov Commit-Queue: Mirko Bonadei Cr-Commit-Position: refs/heads/main@{#39401} --- api/test/frame_generator_interface.h | 6 +++++ test/BUILD.gn | 5 +++- test/frame_generator.h | 10 +++++++ test/testsupport/ivf_video_frame_generator.h | 2 ++ .../ivf_video_frame_generator_unittest.cc | 6 +++++ test/testsupport/y4m_frame_generator.cc | 6 ++++- test/testsupport/y4m_frame_generator.h | 4 +++ test/testsupport/y4m_frame_generator_test.cc | 26 +++++++++++++++++++ 8 files changed, 63 insertions(+), 2 deletions(-) diff --git a/api/test/frame_generator_interface.h b/api/test/frame_generator_interface.h index d82ba55bdd..287a01eb91 100644 --- a/api/test/frame_generator_interface.h +++ b/api/test/frame_generator_interface.h @@ -49,6 +49,12 @@ class FrameGeneratorInterface { virtual void ChangeResolution(size_t width, size_t height) = 0; virtual Resolution GetResolution() const = 0; + + // Returns the frames per second this generator is supposed to provide + // according to its data source. Not all frame generators know the frames per + // second of the data source, in such case this method returns absl::nullopt. + // TODO(mbonadei): Make this pure virtual. + virtual absl::optional fps() const { return absl::nullopt; } }; } // namespace test diff --git a/test/BUILD.gn b/test/BUILD.gn index 57e602f31e..abc8d4ee9c 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -92,7 +92,10 @@ rtc_library("y4m_frame_generator") { "../api/video:video_frame", "../rtc_base:checks", ] - absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] } rtc_library("frame_utils") { diff --git a/test/frame_generator.h b/test/frame_generator.h index 6eb6bf77cd..76f195d326 100644 --- a/test/frame_generator.h +++ b/test/frame_generator.h @@ -40,6 +40,8 @@ class SquareGenerator : public FrameGeneratorInterface { VideoFrameData NextFrame() override; Resolution GetResolution() const override; + absl::optional fps() const override { return absl::nullopt; } + private: rtc::scoped_refptr CreateI420Buffer(int width, int height); @@ -82,6 +84,8 @@ class YuvFileGenerator : public FrameGeneratorInterface { } Resolution GetResolution() const override; + absl::optional fps() const override { return absl::nullopt; } + private: // Returns true if the new frame was loaded. // False only in case of a single file with a single frame in it. @@ -115,6 +119,8 @@ class NV12FileGenerator : public FrameGeneratorInterface { } Resolution GetResolution() const override; + absl::optional fps() const override { return absl::nullopt; } + private: // Returns true if the new frame was loaded. // False only in case of a single file with a single frame in it. @@ -145,6 +151,8 @@ class SlideGenerator : public FrameGeneratorInterface { } Resolution GetResolution() const override; + absl::optional fps() const override { return absl::nullopt; } + private: // Generates some randomly sized and colored squares scattered // over the frame. @@ -177,6 +185,8 @@ class ScrollingImageFrameGenerator : public FrameGeneratorInterface { } Resolution GetResolution() const override; + absl::optional fps() const override { return absl::nullopt; } + private: void UpdateSourceFrame(size_t frame_num); void CropSourceToScrolledImage(double scroll_factor); diff --git a/test/testsupport/ivf_video_frame_generator.h b/test/testsupport/ivf_video_frame_generator.h index 9a4c1e2858..6c6fa4951d 100644 --- a/test/testsupport/ivf_video_frame_generator.h +++ b/test/testsupport/ivf_video_frame_generator.h @@ -37,6 +37,8 @@ class IvfVideoFrameGenerator : public FrameGeneratorInterface { void ChangeResolution(size_t width, size_t height) override; Resolution GetResolution() const override; + absl::optional fps() const override { return absl::nullopt; } + private: class DecodedCallback : public DecodedImageCallback { public: diff --git a/test/testsupport/ivf_video_frame_generator_unittest.cc b/test/testsupport/ivf_video_frame_generator_unittest.cc index dd60f3d3fc..ca5e0b50e2 100644 --- a/test/testsupport/ivf_video_frame_generator_unittest.cc +++ b/test/testsupport/ivf_video_frame_generator_unittest.cc @@ -164,6 +164,12 @@ class IvfVideoFrameGeneratorTest : public ::testing::Test { } // namespace +TEST_F(IvfVideoFrameGeneratorTest, DoesNotKnowFps) { + CreateTestVideoFile(VideoCodecType::kVideoCodecVP8, VP8Encoder::Create()); + IvfVideoFrameGenerator generator(file_name_); + EXPECT_EQ(generator.fps(), absl::nullopt); +} + TEST_F(IvfVideoFrameGeneratorTest, Vp8) { CreateTestVideoFile(VideoCodecType::kVideoCodecVP8, VP8Encoder::Create()); IvfVideoFrameGenerator generator(file_name_); diff --git a/test/testsupport/y4m_frame_generator.cc b/test/testsupport/y4m_frame_generator.cc index 7614016f0a..39a5ad97aa 100644 --- a/test/testsupport/y4m_frame_generator.cc +++ b/test/testsupport/y4m_frame_generator.cc @@ -41,7 +41,11 @@ Y4mFrameGenerator::Y4mFrameGenerator(absl::string_view filename, RTC_CHECK(fgets(header, sizeof(header), file) != nullptr) << "File " << filename_ << " is too small"; fclose(file); - RTC_CHECK_EQ(sscanf(header, "YUV4MPEG2 W%zu H%zu", &width_, &height_), 2); + int fps_denominator; + RTC_CHECK_EQ(sscanf(header, "YUV4MPEG2 W%zu H%zu F%i:%i", &width_, &height_, + &fps_, &fps_denominator), + 4); + fps_ /= fps_denominator; RTC_CHECK_GT(width_, 0); RTC_CHECK_GT(height_, 0); diff --git a/test/testsupport/y4m_frame_generator.h b/test/testsupport/y4m_frame_generator.h index b90b083b65..bccd109318 100644 --- a/test/testsupport/y4m_frame_generator.h +++ b/test/testsupport/y4m_frame_generator.h @@ -16,6 +16,7 @@ #include #include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "api/test/frame_generator_interface.h" #include "rtc_base/checks.h" #include "test/testsupport/frame_reader.h" @@ -50,6 +51,8 @@ class Y4mFrameGenerator : public FrameGeneratorInterface { Resolution GetResolution() const override; + absl::optional fps() const override { return fps_; } + private: YuvFrameReaderImpl::RepeatMode ToYuvFrameReaderRepeatMode( RepeatMode repeat_mode) const; @@ -57,6 +60,7 @@ class Y4mFrameGenerator : public FrameGeneratorInterface { std::string filename_; size_t width_; size_t height_; + int fps_; const RepeatMode repeat_mode_; }; diff --git a/test/testsupport/y4m_frame_generator_test.cc b/test/testsupport/y4m_frame_generator_test.cc index 2af05dfec3..4341c3e3eb 100644 --- a/test/testsupport/y4m_frame_generator_test.cc +++ b/test/testsupport/y4m_frame_generator_test.cc @@ -56,6 +56,32 @@ TEST_F(Y4mFrameGeneratorTest, CanReadResolutionFromFile) { EXPECT_EQ(res.height, 2u); } +TEST_F(Y4mFrameGeneratorTest, CanReadFPSFromFile) { + Y4mFrameGenerator generator(input_filepath_, + Y4mFrameGenerator::RepeatMode::kSingle); + EXPECT_EQ(*generator.fps(), 2); +} + +TEST_F(Y4mFrameGeneratorTest, CanReadFPSFromFileWhenRoundingIsNeeded) { + std::string input_filepath = TempFilename(OutputPath(), "2x2_23_FPS.y4m"); + FILE* y4m_file = fopen(input_filepath.c_str(), "wb"); + + // Input Y4M file: 3 YUV frames of 2x2 resolution. + std::string y4m_content = + "YUV4MPEG2 W2 H2 F24000:1001 C420\n" + "FRAME\n" + "123456FRAME\n" + "abcdefFRAME\n" + "987654"; + std::fprintf(y4m_file, "%s", y4m_content.c_str()); + fclose(y4m_file); + + Y4mFrameGenerator generator(input_filepath, + Y4mFrameGenerator::RepeatMode::kSingle); + EXPECT_EQ(generator.fps(), 23); + remove(input_filepath.c_str()); +} + TEST_F(Y4mFrameGeneratorTest, SingleRepeatMode) { Y4mFrameGenerator generator(input_filepath_, Y4mFrameGenerator::RepeatMode::kSingle);