Dump codec output to ivf/y4m

Bug: b/261160916, webrtc:14852
Change-Id: I19de2210aa03b56752db5ce8b6fd94498123d6f5
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/296260
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#39490}
This commit is contained in:
Sergey Silkin 2023-03-06 14:20:06 +01:00 committed by WebRTC LUCI CQ
parent 9259b5f72c
commit 1c1382be0f
3 changed files with 135 additions and 9 deletions

View File

@ -12,6 +12,7 @@
#define API_TEST_VIDEO_CODEC_TESTER_H_
#include <memory>
#include <string>
#include "absl/functional/any_invocable.h"
#include "absl/types/optional.h"
@ -46,10 +47,12 @@ class VideoCodecTester {
struct DecoderSettings {
PacingSettings pacing;
absl::optional<std::string> decoded_y4m_base_path;
};
struct EncoderSettings {
PacingSettings pacing;
absl::optional<std::string> encoded_ivf_base_path;
};
virtual ~VideoCodecTester() = default;

View File

@ -98,7 +98,9 @@ class TestRawVideoSource : public VideoCodecTester::RawVideoSource {
frame_settings_(frame_settings),
num_frames_(num_frames),
frame_num_(0),
timestamp_rtp_(0) {
// Start with non-zero timestamp to force using frame RTP timestamps in
// IvfFrameWriter.
timestamp_rtp_(90000) {
// Ensure settings for the first frame are provided.
RTC_CHECK_GT(frame_settings_.size(), 0u);
RTC_CHECK_EQ(frame_settings_.begin()->first, 0);
@ -439,6 +441,16 @@ void SetTargetRates(const std::map<int, EncodingSettings>& frame_settings,
f.target_framerate = layer_settings.framerate;
}
}
std::string TestOutputPath() {
std::string output_path =
OutputPath() +
::testing::UnitTest::GetInstance()->current_test_info()->name();
std::string output_dir = DirName(output_path);
bool result = CreateDir(output_dir);
RTC_CHECK(result) << "Cannot create " << output_dir;
return output_path;
}
} // namespace
std::unique_ptr<VideoCodecStats> RunEncodeDecodeTest(
@ -446,7 +458,8 @@ std::unique_ptr<VideoCodecStats> RunEncodeDecodeTest(
std::string codec_impl,
const VideoInfo& video_info,
const std::map<int, EncodingSettings>& frame_settings,
int num_frames) {
int num_frames,
bool save_codec_output) {
std::unique_ptr<TestRawVideoSource> video_source =
CreateVideoSource(video_info, frame_settings, num_frames);
@ -468,6 +481,12 @@ std::unique_ptr<VideoCodecStats> RunEncodeDecodeTest(
? PacingMode::kRealTime
: PacingMode::kNoPacing;
if (save_codec_output) {
std::string output_path = TestOutputPath();
encoder_settings.encoded_ivf_base_path = output_path;
decoder_settings.decoded_y4m_base_path = output_path;
}
std::unique_ptr<VideoCodecTester> tester = CreateVideoCodecTester();
return tester->RunEncodeDecodeTest(video_source.get(), encoder.get(),
decoder.get(), encoder_settings,
@ -479,7 +498,8 @@ std::unique_ptr<VideoCodecStats> RunEncodeTest(
std::string codec_impl,
const VideoInfo& video_info,
const std::map<int, EncodingSettings>& frame_settings,
int num_frames) {
int num_frames,
bool save_codec_output) {
std::unique_ptr<TestRawVideoSource> video_source =
CreateVideoSource(video_info, frame_settings, num_frames);
@ -492,6 +512,10 @@ std::unique_ptr<VideoCodecStats> RunEncodeTest(
? PacingMode::kRealTime
: PacingMode::kNoPacing;
if (save_codec_output) {
encoder_settings.encoded_ivf_base_path = TestOutputPath();
}
std::unique_ptr<VideoCodecTester> tester = CreateVideoCodecTester();
return tester->RunEncodeTest(video_source.get(), encoder.get(),
encoder_settings);
@ -535,8 +559,9 @@ TEST_P(SpatialQualityTest, DISABLED_SpatialQuality) {
int duration_s = 10;
int num_frames = duration_s * framerate_fps;
std::unique_ptr<VideoCodecStats> stats = RunEncodeDecodeTest(
codec_type, codec_impl, video_info, frame_settings, num_frames);
std::unique_ptr<VideoCodecStats> stats =
RunEncodeDecodeTest(codec_type, codec_impl, video_info, frame_settings,
num_frames, /*save_codec_output=*/false);
std::vector<VideoCodecStats::Frame> frames = stats->Slice();
SetTargetRates(frame_settings, frames);
@ -613,8 +638,9 @@ TEST_P(BitrateAdaptationTest, DISABLED_BitrateAdaptation) {
.framerate = video_info.framerate,
.bitrate = DataRate::KilobitsPerSec(bitrate_kbps.second)}}}}}};
std::unique_ptr<VideoCodecStats> stats = RunEncodeTest(
codec_type, codec_impl, video_info, frame_settings, num_frames);
std::unique_ptr<VideoCodecStats> stats =
RunEncodeTest(codec_type, codec_impl, video_info, frame_settings,
num_frames, /*save_codec_output=*/false);
std::vector<VideoCodecStats::Frame> frames =
stats->Slice(VideoCodecStats::Filter{.first_frame = first_frame});
@ -686,8 +712,9 @@ TEST_P(FramerateAdaptationTest, DISABLED_FramerateAdaptation) {
.framerate = Frequency::MilliHertz(1000 * framerate_fps.second),
.bitrate = DataRate::KilobitsPerSec(512)}}}}}};
std::unique_ptr<VideoCodecStats> stats = RunEncodeTest(
codec_type, codec_impl, video_info, frame_settings, num_frames);
std::unique_ptr<VideoCodecStats> stats =
RunEncodeTest(codec_type, codec_impl, video_info, frame_settings,
num_frames, /*save_codec_output=*/false);
std::vector<VideoCodecStats::Frame> frames =
stats->Slice(VideoCodecStats::Filter{.first_frame = first_frame});

View File

@ -12,6 +12,7 @@
#include <map>
#include <memory>
#include <string>
#include <utility>
#include "api/task_queue/default_task_queue_factory.h"
@ -20,11 +21,14 @@
#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 {
@ -150,6 +154,39 @@ class LimitedTaskQueue {
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 TesterDecoder {
public:
TesterDecoder(Decoder* decoder,
@ -160,6 +197,11 @@ class TesterDecoder {
settings_(settings),
pacer_(settings.pacing) {
RTC_CHECK(analyzer_) << "Analyzer must be provided";
if (settings.decoded_y4m_base_path) {
y4m_writer_ =
std::make_unique<TesterY4mWriter>(*settings.decoded_y4m_base_path);
}
}
void Decode(const EncodedImage& frame) {
@ -168,10 +210,15 @@ class TesterDecoder {
task_queue_.PostScheduledTask(
[this, frame] {
analyzer_->StartDecode(frame);
decoder_->Decode(
frame, [this, spatial_idx = frame.SpatialIndex().value_or(0)](
const VideoFrame& decoded_frame) {
analyzer_->FinishDecode(decoded_frame, spatial_idx);
if (y4m_writer_) {
y4m_writer_->Write(decoded_frame, spatial_idx);
}
});
},
pacer_.Schedule(timestamp));
@ -189,6 +236,45 @@ class TesterDecoder {
const DecoderSettings& settings_;
Pacer pacer_;
LimitedTaskQueue task_queue_;
std::unique_ptr<TesterY4mWriter> y4m_writer_;
};
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 TesterEncoder {
@ -203,6 +289,10 @@ class TesterEncoder {
settings_(settings),
pacer_(settings.pacing) {
RTC_CHECK(analyzer_) << "Analyzer must be provided";
if (settings.encoded_ivf_base_path) {
ivf_writer_ =
std::make_unique<TesterIvfWriter>(*settings.encoded_ivf_base_path);
}
}
void Encode(const VideoFrame& frame) {
@ -213,9 +303,14 @@ class TesterEncoder {
analyzer_->StartEncode(frame);
encoder_->Encode(frame, [this](const EncodedImage& encoded_frame) {
analyzer_->FinishEncode(encoded_frame);
if (decoder_ != nullptr) {
decoder_->Decode(encoded_frame);
}
if (ivf_writer_ != nullptr) {
ivf_writer_->Write(encoded_frame);
}
});
},
pacer_.Schedule(timestamp));
@ -232,6 +327,7 @@ class TesterEncoder {
TesterDecoder* const decoder_;
VideoCodecAnalyzer* const analyzer_;
const EncoderSettings& settings_;
std::unique_ptr<TesterIvfWriter> ivf_writer_;
Pacer pacer_;
LimitedTaskQueue task_queue_;
};