diff options
Diffstat (limited to 'modules/video_coding/codecs/test/video_codec_tester_impl.cc')
-rw-r--r-- | modules/video_coding/codecs/test/video_codec_tester_impl.cc | 437 |
1 files changed, 0 insertions, 437 deletions
diff --git a/modules/video_coding/codecs/test/video_codec_tester_impl.cc b/modules/video_coding/codecs/test/video_codec_tester_impl.cc deleted file mode 100644 index f15b1b35f3..0000000000 --- a/modules/video_coding/codecs/test/video_codec_tester_impl.cc +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "modules/video_coding/codecs/test/video_codec_tester_impl.h" - -#include <map> -#include <memory> -#include <string> -#include <utility> - -#include "api/task_queue/default_task_queue_factory.h" -#include "api/units/frequency.h" -#include "api/units/time_delta.h" -#include "api/units/timestamp.h" -#include "api/video/encoded_image.h" -#include "api/video/i420_buffer.h" -#include "api/video/video_codec_type.h" -#include "api/video/video_frame.h" -#include "modules/video_coding/codecs/test/video_codec_analyzer.h" -#include "modules/video_coding/utility/ivf_file_writer.h" -#include "rtc_base/event.h" -#include "rtc_base/time_utils.h" -#include "system_wrappers/include/sleep.h" -#include "test/testsupport/video_frame_writer.h" - -namespace webrtc { -namespace test { - -namespace { -using RawVideoSource = VideoCodecTester::RawVideoSource; -using CodedVideoSource = VideoCodecTester::CodedVideoSource; -using Decoder = VideoCodecTester::Decoder; -using Encoder = VideoCodecTester::Encoder; -using EncoderSettings = VideoCodecTester::EncoderSettings; -using DecoderSettings = VideoCodecTester::DecoderSettings; -using PacingSettings = VideoCodecTester::PacingSettings; -using PacingMode = PacingSettings::PacingMode; - -constexpr Frequency k90kHz = Frequency::Hertz(90000); - -// A thread-safe wrapper for video source to be shared with the quality analyzer -// that reads reference frames from a separate thread. -class SyncRawVideoSource : public VideoCodecAnalyzer::ReferenceVideoSource { - public: - explicit SyncRawVideoSource(RawVideoSource* video_source) - : video_source_(video_source) {} - - absl::optional<VideoFrame> PullFrame() { - MutexLock lock(&mutex_); - return video_source_->PullFrame(); - } - - VideoFrame GetFrame(uint32_t timestamp_rtp, Resolution resolution) override { - MutexLock lock(&mutex_); - return video_source_->GetFrame(timestamp_rtp, resolution); - } - - protected: - RawVideoSource* const video_source_ RTC_GUARDED_BY(mutex_); - Mutex mutex_; -}; - -// Pacer calculates delay necessary to keep frame encode or decode call spaced -// from the previous calls by the pacing time. `Delay` is expected to be called -// as close as possible to posting frame encode or decode task. This class is -// not thread safe. -class Pacer { - public: - explicit Pacer(PacingSettings settings) - : settings_(settings), delay_(TimeDelta::Zero()) {} - Timestamp Schedule(Timestamp timestamp) { - Timestamp now = Timestamp::Micros(rtc::TimeMicros()); - if (settings_.mode == PacingMode::kNoPacing) { - return now; - } - - Timestamp scheduled = now; - if (prev_scheduled_) { - scheduled = *prev_scheduled_ + PacingTime(timestamp); - if (scheduled < now) { - scheduled = now; - } - } - - prev_timestamp_ = timestamp; - prev_scheduled_ = scheduled; - return scheduled; - } - - private: - TimeDelta PacingTime(Timestamp timestamp) { - if (settings_.mode == PacingMode::kRealTime) { - return timestamp - *prev_timestamp_; - } - RTC_CHECK_EQ(PacingMode::kConstantRate, settings_.mode); - return 1 / settings_.constant_rate; - } - - PacingSettings settings_; - absl::optional<Timestamp> prev_timestamp_; - absl::optional<Timestamp> prev_scheduled_; - TimeDelta delay_; -}; - -// Task queue that keeps the number of queued tasks below a certain limit. If -// the limit is reached, posting of a next task is blocked until execution of a -// previously posted task starts. This class is not thread-safe. -class LimitedTaskQueue { - public: - // The codec tester reads frames from video source in the main thread. - // Encoding and decoding are done in separate threads. If encoding or - // decoding is slow, the reading may go far ahead and may buffer too many - // frames in memory. To prevent this we limit the encoding/decoding queue - // size. When the queue is full, the main thread and, hence, reading frames - // from video source is blocked until a previously posted encoding/decoding - // task starts. - static constexpr int kMaxTaskQueueSize = 3; - - LimitedTaskQueue() : queue_size_(0) {} - - void PostScheduledTask(absl::AnyInvocable<void() &&> task, Timestamp start) { - ++queue_size_; - task_queue_.PostTask([this, task = std::move(task), start]() mutable { - int wait_ms = static_cast<int>(start.ms() - rtc::TimeMillis()); - if (wait_ms > 0) { - SleepMs(wait_ms); - } - - std::move(task)(); - --queue_size_; - task_executed_.Set(); - }); - - task_executed_.Reset(); - if (queue_size_ > kMaxTaskQueueSize) { - task_executed_.Wait(rtc::Event::kForever); - } - RTC_CHECK(queue_size_ <= kMaxTaskQueueSize); - } - - void WaitForPreviouslyPostedTasks() { - task_queue_.SendTask([] {}); - } - - TaskQueueForTest task_queue_; - std::atomic_int queue_size_; - rtc::Event task_executed_; -}; - -class TesterY4mWriter { - public: - explicit TesterY4mWriter(absl::string_view base_path) - : base_path_(base_path) {} - - ~TesterY4mWriter() { - task_queue_.SendTask([] {}); - } - - void Write(const VideoFrame& frame, int spatial_idx) { - task_queue_.PostTask([this, frame, spatial_idx] { - if (y4m_writers_.find(spatial_idx) == y4m_writers_.end()) { - std::string file_path = - base_path_ + "_s" + std::to_string(spatial_idx) + ".y4m"; - - Y4mVideoFrameWriterImpl* y4m_writer = new Y4mVideoFrameWriterImpl( - file_path, frame.width(), frame.height(), /*fps=*/30); - RTC_CHECK(y4m_writer); - - y4m_writers_[spatial_idx] = - std::unique_ptr<VideoFrameWriter>(y4m_writer); - } - - y4m_writers_.at(spatial_idx)->WriteFrame(frame); - }); - } - - protected: - std::string base_path_; - std::map<int, std::unique_ptr<VideoFrameWriter>> y4m_writers_; - TaskQueueForTest task_queue_; -}; - -class TesterIvfWriter { - public: - explicit TesterIvfWriter(absl::string_view base_path) - : base_path_(base_path) {} - - ~TesterIvfWriter() { - task_queue_.SendTask([] {}); - } - - void Write(const EncodedImage& encoded_frame) { - task_queue_.PostTask([this, encoded_frame] { - int spatial_idx = encoded_frame.SpatialIndex().value_or(0); - if (ivf_file_writers_.find(spatial_idx) == ivf_file_writers_.end()) { - std::string ivf_path = - base_path_ + "_s" + std::to_string(spatial_idx) + ".ivf"; - - FileWrapper ivf_file = FileWrapper::OpenWriteOnly(ivf_path); - RTC_CHECK(ivf_file.is_open()); - - std::unique_ptr<IvfFileWriter> ivf_writer = - IvfFileWriter::Wrap(std::move(ivf_file), /*byte_limit=*/0); - RTC_CHECK(ivf_writer); - - ivf_file_writers_[spatial_idx] = std::move(ivf_writer); - } - - // To play: ffplay -vcodec vp8|vp9|av1|hevc|h264 filename - ivf_file_writers_.at(spatial_idx) - ->WriteFrame(encoded_frame, VideoCodecType::kVideoCodecGeneric); - }); - } - - protected: - std::string base_path_; - std::map<int, std::unique_ptr<IvfFileWriter>> ivf_file_writers_; - TaskQueueForTest task_queue_; -}; - -class TesterDecoder { - public: - TesterDecoder(Decoder* decoder, - VideoCodecAnalyzer* analyzer, - const DecoderSettings& settings) - : decoder_(decoder), - analyzer_(analyzer), - settings_(settings), - pacer_(settings.pacing) { - RTC_CHECK(analyzer_) << "Analyzer must be provided"; - - if (settings.decoder_input_base_path) { - input_writer_ = - std::make_unique<TesterIvfWriter>(*settings.decoder_input_base_path); - } - - if (settings.decoder_output_base_path) { - output_writer_ = - std::make_unique<TesterY4mWriter>(*settings.decoder_output_base_path); - } - } - - void Initialize() { - task_queue_.PostScheduledTask([this] { decoder_->Initialize(); }, - Timestamp::Zero()); - task_queue_.WaitForPreviouslyPostedTasks(); - } - - void Decode(const EncodedImage& input_frame) { - Timestamp timestamp = - Timestamp::Micros((input_frame.RtpTimestamp() / k90kHz).us()); - - task_queue_.PostScheduledTask( - [this, input_frame] { - analyzer_->StartDecode(input_frame); - - decoder_->Decode( - input_frame, - [this, spatial_idx = input_frame.SpatialIndex().value_or(0)]( - const VideoFrame& output_frame) { - analyzer_->FinishDecode(output_frame, spatial_idx); - - if (output_writer_) { - output_writer_->Write(output_frame, spatial_idx); - } - }); - - if (input_writer_) { - input_writer_->Write(input_frame); - } - }, - pacer_.Schedule(timestamp)); - } - - void Flush() { - task_queue_.PostScheduledTask([this] { decoder_->Flush(); }, - Timestamp::Zero()); - task_queue_.WaitForPreviouslyPostedTasks(); - } - - protected: - Decoder* const decoder_; - VideoCodecAnalyzer* const analyzer_; - const DecoderSettings& settings_; - Pacer pacer_; - LimitedTaskQueue task_queue_; - std::unique_ptr<TesterIvfWriter> input_writer_; - std::unique_ptr<TesterY4mWriter> output_writer_; -}; - -class TesterEncoder { - public: - TesterEncoder(Encoder* encoder, - TesterDecoder* decoder, - VideoCodecAnalyzer* analyzer, - const EncoderSettings& settings) - : encoder_(encoder), - decoder_(decoder), - analyzer_(analyzer), - settings_(settings), - pacer_(settings.pacing) { - RTC_CHECK(analyzer_) << "Analyzer must be provided"; - if (settings.encoder_input_base_path) { - input_writer_ = - std::make_unique<TesterY4mWriter>(*settings.encoder_input_base_path); - } - - if (settings.encoder_output_base_path) { - output_writer_ = - std::make_unique<TesterIvfWriter>(*settings.encoder_output_base_path); - } - } - - void Initialize() { - task_queue_.PostScheduledTask([this] { encoder_->Initialize(); }, - Timestamp::Zero()); - task_queue_.WaitForPreviouslyPostedTasks(); - } - - void Encode(const VideoFrame& input_frame) { - Timestamp timestamp = - Timestamp::Micros((input_frame.timestamp() / k90kHz).us()); - - task_queue_.PostScheduledTask( - [this, input_frame] { - analyzer_->StartEncode(input_frame); - encoder_->Encode(input_frame, - [this](const EncodedImage& encoded_frame) { - analyzer_->FinishEncode(encoded_frame); - - if (decoder_ != nullptr) { - decoder_->Decode(encoded_frame); - } - - if (output_writer_ != nullptr) { - output_writer_->Write(encoded_frame); - } - }); - - if (input_writer_) { - input_writer_->Write(input_frame, /*spatial_idx=*/0); - } - }, - pacer_.Schedule(timestamp)); - } - - void Flush() { - task_queue_.PostScheduledTask([this] { encoder_->Flush(); }, - Timestamp::Zero()); - task_queue_.WaitForPreviouslyPostedTasks(); - } - - protected: - Encoder* const encoder_; - TesterDecoder* const decoder_; - VideoCodecAnalyzer* const analyzer_; - const EncoderSettings& settings_; - std::unique_ptr<TesterY4mWriter> input_writer_; - std::unique_ptr<TesterIvfWriter> output_writer_; - Pacer pacer_; - LimitedTaskQueue task_queue_; -}; - -} // namespace - -std::unique_ptr<VideoCodecStats> VideoCodecTesterImpl::RunDecodeTest( - CodedVideoSource* video_source, - Decoder* decoder, - const DecoderSettings& decoder_settings) { - VideoCodecAnalyzer perf_analyzer; - TesterDecoder tester_decoder(decoder, &perf_analyzer, decoder_settings); - - tester_decoder.Initialize(); - - while (auto frame = video_source->PullFrame()) { - tester_decoder.Decode(*frame); - } - - tester_decoder.Flush(); - - return perf_analyzer.GetStats(); -} - -std::unique_ptr<VideoCodecStats> VideoCodecTesterImpl::RunEncodeTest( - RawVideoSource* video_source, - Encoder* encoder, - const EncoderSettings& encoder_settings) { - SyncRawVideoSource sync_source(video_source); - VideoCodecAnalyzer perf_analyzer; - TesterEncoder tester_encoder(encoder, /*decoder=*/nullptr, &perf_analyzer, - encoder_settings); - - tester_encoder.Initialize(); - - while (auto frame = sync_source.PullFrame()) { - tester_encoder.Encode(*frame); - } - - tester_encoder.Flush(); - - return perf_analyzer.GetStats(); -} - -std::unique_ptr<VideoCodecStats> VideoCodecTesterImpl::RunEncodeDecodeTest( - RawVideoSource* video_source, - Encoder* encoder, - Decoder* decoder, - const EncoderSettings& encoder_settings, - const DecoderSettings& decoder_settings) { - SyncRawVideoSource sync_source(video_source); - VideoCodecAnalyzer perf_analyzer(&sync_source); - TesterDecoder tester_decoder(decoder, &perf_analyzer, decoder_settings); - TesterEncoder tester_encoder(encoder, &tester_decoder, &perf_analyzer, - encoder_settings); - - tester_encoder.Initialize(); - tester_decoder.Initialize(); - - while (auto frame = sync_source.PullFrame()) { - tester_encoder.Encode(*frame); - } - - tester_encoder.Flush(); - tester_decoder.Flush(); - - return perf_analyzer.GetStats(); -} - -} // namespace test -} // namespace webrtc |