diff --git a/api/test/create_frame_generator.cc b/api/test/create_frame_generator.cc index 7ed06473a1..5e6fb3228b 100644 --- a/api/test/create_frame_generator.cc +++ b/api/test/create_frame_generator.cc @@ -47,6 +47,23 @@ std::unique_ptr CreateFromYuvFileFrameGenerator( frame_repeat_count); } +std::unique_ptr CreateFromNV12FileFrameGenerator( + std::vector filenames, + size_t width, + size_t height, + int frame_repeat_count) { + RTC_DCHECK(!filenames.empty()); + std::vector files; + for (const std::string& filename : filenames) { + FILE* file = fopen(filename.c_str(), "rb"); + RTC_DCHECK(file != nullptr) << "Failed to open: '" << filename << "'\n"; + files.push_back(file); + } + + return std::make_unique(files, width, height, + frame_repeat_count); +} + std::unique_ptr CreateFromIvfFileFrameGenerator( std::string filename) { return std::make_unique(std::move(filename)); diff --git a/api/test/create_frame_generator.h b/api/test/create_frame_generator.h index cd4fcccd69..70be0c4e8e 100644 --- a/api/test/create_frame_generator.h +++ b/api/test/create_frame_generator.h @@ -41,6 +41,15 @@ std::unique_ptr CreateFromYuvFileFrameGenerator( size_t height, int frame_repeat_count); +// Creates a frame generator that repeatedly plays a set of nv12 files. +// The frame_repeat_count determines how many times each frame is shown, +// with 1 = show each frame once, etc. +std::unique_ptr CreateFromNV12FileFrameGenerator( + std::vector filenames, + size_t width, + size_t height, + int frame_repeat_count = 1); + // Creates a frame generator that repeatedly plays an ivf file. std::unique_ptr CreateFromIvfFileFrameGenerator( std::string filename); diff --git a/common_video/libyuv/include/webrtc_libyuv.h b/common_video/libyuv/include/webrtc_libyuv.h index d7939dcb2f..08a035a8d7 100644 --- a/common_video/libyuv/include/webrtc_libyuv.h +++ b/common_video/libyuv/include/webrtc_libyuv.h @@ -39,6 +39,7 @@ enum class VideoType { kUYVY, kMJPEG, kBGRA, + kNV12, }; // This is the max PSNR value our algorithms can return. diff --git a/common_video/libyuv/webrtc_libyuv.cc b/common_video/libyuv/webrtc_libyuv.cc index 51a766c1be..14e2d22612 100644 --- a/common_video/libyuv/webrtc_libyuv.cc +++ b/common_video/libyuv/webrtc_libyuv.cc @@ -26,7 +26,8 @@ size_t CalcBufferSize(VideoType type, int width, int height) { switch (type) { case VideoType::kI420: case VideoType::kIYUV: - case VideoType::kYV12: { + case VideoType::kYV12: + case VideoType::kNV12: { int half_width = (width + 1) >> 1; int half_height = (height + 1) >> 1; buffer_size = width * height + half_width * half_height * 2; @@ -105,6 +106,8 @@ int ConvertVideoType(VideoType video_type) { return libyuv::FOURCC_ARGB; case VideoType::kBGRA: return libyuv::FOURCC_BGRA; + case VideoType::kNV12: + return libyuv::FOURCC_NV12; } RTC_DCHECK_NOTREACHED(); return libyuv::FOURCC_ANY; diff --git a/test/frame_generator.cc b/test/frame_generator.cc index 92e95f68a0..b6f16a573d 100644 --- a/test/frame_generator.cc +++ b/test/frame_generator.cc @@ -205,6 +205,68 @@ bool YuvFileGenerator::ReadNextFrame() { return frame_index_ != prev_frame_index || file_index_ != prev_file_index; } +NV12FileGenerator::NV12FileGenerator(std::vector files, + size_t width, + size_t height, + int frame_repeat_count) + : file_index_(0), + frame_index_(std::numeric_limits::max()), + files_(files), + width_(width), + height_(height), + frame_size_(CalcBufferSize(VideoType::kNV12, + static_cast(width_), + static_cast(height_))), + frame_buffer_(new uint8_t[frame_size_]), + frame_display_count_(frame_repeat_count), + current_display_count_(0) { + RTC_DCHECK_GT(width, 0); + RTC_DCHECK_GT(height, 0); + RTC_DCHECK_GT(frame_repeat_count, 0); +} + +NV12FileGenerator::~NV12FileGenerator() { + for (FILE* file : files_) + fclose(file); +} + +FrameGeneratorInterface::VideoFrameData NV12FileGenerator::NextFrame() { + // Empty update by default. + VideoFrame::UpdateRect update_rect{0, 0, 0, 0}; + if (current_display_count_ == 0) { + const bool got_new_frame = ReadNextFrame(); + // Full update on a new frame from file. + if (got_new_frame) { + update_rect = VideoFrame::UpdateRect{0, 0, static_cast(width_), + static_cast(height_)}; + } + } + if (++current_display_count_ >= frame_display_count_) + current_display_count_ = 0; + + return VideoFrameData(last_read_buffer_, update_rect); +} + +bool NV12FileGenerator::ReadNextFrame() { + size_t prev_frame_index = frame_index_; + size_t prev_file_index = file_index_; + last_read_buffer_ = test::ReadNV12Buffer( + static_cast(width_), static_cast(height_), files_[file_index_]); + ++frame_index_; + if (!last_read_buffer_) { + // No more frames to read in this file, rewind and move to next file. + rewind(files_[file_index_]); + + frame_index_ = 0; + file_index_ = (file_index_ + 1) % files_.size(); + last_read_buffer_ = + test::ReadNV12Buffer(static_cast(width_), + static_cast(height_), files_[file_index_]); + RTC_CHECK(last_read_buffer_); + } + return frame_index_ != prev_frame_index || file_index_ != prev_file_index; +} + SlideGenerator::SlideGenerator(int width, int height, int frame_repeat_count) : width_(width), height_(height), diff --git a/test/frame_generator.h b/test/frame_generator.h index 3e2a4cb708..9a8f08cea6 100644 --- a/test/frame_generator.h +++ b/test/frame_generator.h @@ -17,6 +17,7 @@ #include "api/scoped_refptr.h" #include "api/test/frame_generator_interface.h" #include "api/video/i420_buffer.h" +#include "api/video/nv12_buffer.h" #include "api/video/video_frame.h" #include "api/video/video_frame_buffer.h" #include "api/video/video_source_interface.h" @@ -76,8 +77,7 @@ class YuvFileGenerator : public FrameGeneratorInterface { VideoFrameData NextFrame() override; void ChangeResolution(size_t width, size_t height) override { - RTC_LOG(LS_WARNING) - << "ScrollingImageFrameGenerator::ChangeResolution not implemented"; + RTC_LOG(LS_WARNING) << "YuvFileGenerator::ChangeResolution not implemented"; } private: @@ -97,6 +97,38 @@ class YuvFileGenerator : public FrameGeneratorInterface { rtc::scoped_refptr last_read_buffer_; }; +class NV12FileGenerator : public FrameGeneratorInterface { + public: + NV12FileGenerator(std::vector files, + size_t width, + size_t height, + int frame_repeat_count); + + ~NV12FileGenerator(); + + VideoFrameData NextFrame() override; + void ChangeResolution(size_t width, size_t height) override { + RTC_LOG(LS_WARNING) + << "NV12FileGenerator::ChangeResolution not implemented"; + } + + private: + // Returns true if the new frame was loaded. + // False only in case of a single file with a single frame in it. + bool ReadNextFrame(); + + size_t file_index_; + size_t frame_index_; + const std::vector files_; + const size_t width_; + const size_t height_; + const size_t frame_size_; + const std::unique_ptr frame_buffer_; + const int frame_display_count_; + int current_display_count_; + rtc::scoped_refptr last_read_buffer_; +}; + // SlideGenerator works similarly to YuvFileGenerator but it fills the frames // with randomly sized and colored squares instead of reading their content // from files. diff --git a/test/frame_generator_unittest.cc b/test/frame_generator_unittest.cc index 467323a0d5..ece37a547f 100644 --- a/test/frame_generator_unittest.cc +++ b/test/frame_generator_unittest.cc @@ -27,28 +27,44 @@ namespace webrtc { namespace test { -static const int kFrameWidth = 4; -static const int kFrameHeight = 4; +constexpr int kFrameWidth = 4; +constexpr int kFrameHeight = 4; +constexpr int y_size = kFrameWidth * kFrameHeight; +constexpr int uv_size = ((kFrameHeight + 1) / 2) * ((kFrameWidth + 1) / 2); class FrameGeneratorTest : public ::testing::Test { public: void SetUp() override { - two_frame_filename_ = + two_frame_yuv_filename_ = test::TempFilename(test::OutputPath(), "2_frame_yuv_file"); - one_frame_filename_ = + one_frame_yuv_filename_ = test::TempFilename(test::OutputPath(), "1_frame_yuv_file"); + two_frame_nv12_filename_ = + test::TempFilename(test::OutputPath(), "2_frame_nv12_file"); + one_frame_nv12_filename_ = + test::TempFilename(test::OutputPath(), "1_frame_nv12_file"); - FILE* file = fopen(two_frame_filename_.c_str(), "wb"); + FILE* file = fopen(two_frame_yuv_filename_.c_str(), "wb"); WriteYuvFile(file, 0, 0, 0); - WriteYuvFile(file, 127, 127, 127); + WriteYuvFile(file, 127, 128, 129); fclose(file); - file = fopen(one_frame_filename_.c_str(), "wb"); + file = fopen(one_frame_yuv_filename_.c_str(), "wb"); WriteYuvFile(file, 255, 255, 255); fclose(file); + file = fopen(two_frame_nv12_filename_.c_str(), "wb"); + WriteNV12File(file, 0, 0, 0); + WriteNV12File(file, 127, 128, 129); + fclose(file); + file = fopen(one_frame_nv12_filename_.c_str(), "wb"); + WriteNV12File(file, 255, 255, 255); + fclose(file); } + void TearDown() override { - remove(one_frame_filename_.c_str()); - remove(two_frame_filename_.c_str()); + remove(one_frame_yuv_filename_.c_str()); + remove(two_frame_yuv_filename_.c_str()); + remove(one_frame_nv12_filename_.c_str()); + remove(two_frame_nv12_filename_.c_str()); } protected: @@ -63,6 +79,19 @@ class FrameGeneratorTest : public ::testing::Test { fwrite(plane_buffer.get(), 1, uv_size, file); } + void WriteNV12File(FILE* file, uint8_t y, uint8_t u, uint8_t v) { + RTC_DCHECK(file); + uint8_t plane_buffer[y_size]; + + memset(&plane_buffer, y, y_size); + fwrite(&plane_buffer, 1, y_size, file); + for (size_t i = 0; i < uv_size; ++i) { + plane_buffer[2 * i] = u; + plane_buffer[2 * i + 1] = v; + } + fwrite(&plane_buffer, 1, 2 * uv_size, file); + } + void CheckFrameAndMutate(const FrameGeneratorInterface::VideoFrameData& frame, uint8_t y, uint8_t u, @@ -102,69 +131,131 @@ class FrameGeneratorTest : public ::testing::Test { return hash; } - std::string two_frame_filename_; - std::string one_frame_filename_; - const int y_size = kFrameWidth * kFrameHeight; - const int uv_size = ((kFrameHeight + 1) / 2) * ((kFrameWidth + 1) / 2); + std::string two_frame_yuv_filename_; + std::string one_frame_yuv_filename_; + std::string two_frame_nv12_filename_; + std::string one_frame_nv12_filename_; }; -TEST_F(FrameGeneratorTest, SingleFrameFile) { +TEST_F(FrameGeneratorTest, SingleFrameYuvFile) { std::unique_ptr generator( CreateFromYuvFileFrameGenerator( - std::vector(1, one_frame_filename_), kFrameWidth, + std::vector(1, one_frame_yuv_filename_), kFrameWidth, kFrameHeight, 1)); CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255); CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255); } -TEST_F(FrameGeneratorTest, TwoFrameFile) { +TEST_F(FrameGeneratorTest, TwoFrameYuvFile) { std::unique_ptr generator( CreateFromYuvFileFrameGenerator( - std::vector(1, two_frame_filename_), kFrameWidth, + std::vector(1, two_frame_yuv_filename_), kFrameWidth, kFrameHeight, 1)); CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); - CheckFrameAndMutate(generator->NextFrame(), 127, 127, 127); + CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129); CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); } -TEST_F(FrameGeneratorTest, MultipleFrameFiles) { +TEST_F(FrameGeneratorTest, MultipleFrameYuvFiles) { std::vector files; - files.push_back(two_frame_filename_); - files.push_back(one_frame_filename_); + files.push_back(two_frame_yuv_filename_); + files.push_back(one_frame_yuv_filename_); std::unique_ptr generator( CreateFromYuvFileFrameGenerator(files, kFrameWidth, kFrameHeight, 1)); CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); - CheckFrameAndMutate(generator->NextFrame(), 127, 127, 127); + CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129); CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255); CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); } -TEST_F(FrameGeneratorTest, TwoFrameFileWithRepeat) { +TEST_F(FrameGeneratorTest, TwoFrameYuvFileWithRepeat) { const int kRepeatCount = 3; std::unique_ptr generator( CreateFromYuvFileFrameGenerator( - std::vector(1, two_frame_filename_), kFrameWidth, + std::vector(1, two_frame_yuv_filename_), kFrameWidth, kFrameHeight, kRepeatCount)); for (int i = 0; i < kRepeatCount; ++i) CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); for (int i = 0; i < kRepeatCount; ++i) - CheckFrameAndMutate(generator->NextFrame(), 127, 127, 127); + CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129); CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); } -TEST_F(FrameGeneratorTest, MultipleFrameFilesWithRepeat) { +TEST_F(FrameGeneratorTest, MultipleFrameYuvFilesWithRepeat) { const int kRepeatCount = 3; std::vector files; - files.push_back(two_frame_filename_); - files.push_back(one_frame_filename_); + files.push_back(two_frame_yuv_filename_); + files.push_back(one_frame_yuv_filename_); std::unique_ptr generator( CreateFromYuvFileFrameGenerator(files, kFrameWidth, kFrameHeight, kRepeatCount)); for (int i = 0; i < kRepeatCount; ++i) CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); for (int i = 0; i < kRepeatCount; ++i) - CheckFrameAndMutate(generator->NextFrame(), 127, 127, 127); + CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129); + for (int i = 0; i < kRepeatCount; ++i) + CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255); + CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); +} + +TEST_F(FrameGeneratorTest, SingleFrameNV12File) { + std::unique_ptr generator( + CreateFromNV12FileFrameGenerator( + std::vector(1, one_frame_nv12_filename_), kFrameWidth, + kFrameHeight, 1)); + CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255); + CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255); +} + +TEST_F(FrameGeneratorTest, TwoFrameNV12File) { + std::unique_ptr generator( + CreateFromNV12FileFrameGenerator( + std::vector(1, two_frame_nv12_filename_), kFrameWidth, + kFrameHeight, 1)); + CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); + CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129); + CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); +} + +TEST_F(FrameGeneratorTest, MultipleFrameNV12Files) { + std::vector files; + files.push_back(two_frame_nv12_filename_); + files.push_back(one_frame_nv12_filename_); + + std::unique_ptr generator( + CreateFromNV12FileFrameGenerator(files, kFrameWidth, kFrameHeight, 1)); + CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); + CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129); + CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255); + CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); +} + +TEST_F(FrameGeneratorTest, TwoFrameNV12FileWithRepeat) { + const int kRepeatCount = 3; + std::unique_ptr generator( + CreateFromNV12FileFrameGenerator( + std::vector(1, two_frame_nv12_filename_), kFrameWidth, + kFrameHeight, kRepeatCount)); + for (int i = 0; i < kRepeatCount; ++i) + CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); + for (int i = 0; i < kRepeatCount; ++i) + CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129); + CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); +} + +TEST_F(FrameGeneratorTest, MultipleFrameNV12FilesWithRepeat) { + const int kRepeatCount = 3; + std::vector files; + files.push_back(two_frame_nv12_filename_); + files.push_back(one_frame_nv12_filename_); + std::unique_ptr generator( + CreateFromNV12FileFrameGenerator(files, kFrameWidth, kFrameHeight, + kRepeatCount)); + for (int i = 0; i < kRepeatCount; ++i) + CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); + for (int i = 0; i < kRepeatCount; ++i) + CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129); for (int i = 0; i < kRepeatCount; ++i) CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255); CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0); diff --git a/test/frame_utils.cc b/test/frame_utils.cc index 30389fb56d..b280de1ad1 100644 --- a/test/frame_utils.cc +++ b/test/frame_utils.cc @@ -14,6 +14,7 @@ #include #include "api/video/i420_buffer.h" +#include "api/video/nv12_buffer.h" #include "api/video/video_frame.h" namespace webrtc { @@ -87,5 +88,17 @@ rtc::scoped_refptr ReadI420Buffer(int width, int height, FILE* f) { return buffer; } +rtc::scoped_refptr ReadNV12Buffer(int width, int height, FILE* f) { + rtc::scoped_refptr buffer(NV12Buffer::Create(width, height)); + size_t size_y = static_cast(width) * height; + size_t size_uv = static_cast(width + width % 2) * ((height + 1) / 2); + + if (fread(buffer->MutableDataY(), 1, size_y, f) < size_y) + return nullptr; + if (fread(buffer->MutableDataUV(), 1, size_uv, f) < size_uv) + return nullptr; + return buffer; +} + } // namespace test } // namespace webrtc diff --git a/test/frame_utils.h b/test/frame_utils.h index 93515848ed..1f2b381afb 100644 --- a/test/frame_utils.h +++ b/test/frame_utils.h @@ -13,6 +13,7 @@ #include #include "api/scoped_refptr.h" +#include "api/video/nv12_buffer.h" namespace webrtc { class I420Buffer; @@ -42,6 +43,8 @@ bool FrameBufsEqual(const rtc::scoped_refptr& f1, rtc::scoped_refptr ReadI420Buffer(int width, int height, FILE*); +rtc::scoped_refptr ReadNV12Buffer(int width, int height, FILE*); + } // namespace test } // namespace webrtc