diff --git a/api/test/peerconnection_quality_test_fixture.cc b/api/test/peerconnection_quality_test_fixture.cc index 78fe0dbefc..5d15ba327e 100644 --- a/api/test/peerconnection_quality_test_fixture.cc +++ b/api/test/peerconnection_quality_test_fixture.cc @@ -125,10 +125,19 @@ std::string PeerConnectionE2EQualityTestFixture::VideoSubscription::ToString() PeerConnectionE2EQualityTestFixture::VideoDumpOptions::VideoDumpOptions( absl::string_view output_directory, - int sampling_modulo) - : output_directory_(output_directory), sampling_modulo_(sampling_modulo) { + int sampling_modulo, + bool export_frame_ids) + : output_directory_(output_directory), + sampling_modulo_(sampling_modulo), + export_frame_ids_(export_frame_ids) { RTC_CHECK_GT(sampling_modulo, 0); } +PeerConnectionE2EQualityTestFixture::VideoDumpOptions::VideoDumpOptions( + absl::string_view output_directory, + bool export_frame_ids) + : VideoDumpOptions(output_directory, + kDefaultSamplingModulo, + export_frame_ids) {} std::string PeerConnectionE2EQualityTestFixture::VideoDumpOptions::GetInputDumpFileName( @@ -138,6 +147,15 @@ PeerConnectionE2EQualityTestFixture::VideoDumpOptions::GetInputDumpFileName( return test::JoinFilename(output_directory_, file_name.Release()); } +absl::optional PeerConnectionE2EQualityTestFixture:: + VideoDumpOptions::GetInputFrameIdsDumpFileName( + absl::string_view stream_label) const { + if (!export_frame_ids_) { + return absl::nullopt; + } + return GetInputDumpFileName(stream_label) + ".frame_ids.txt"; +} + std::string PeerConnectionE2EQualityTestFixture::VideoDumpOptions::GetOutputDumpFileName( absl::string_view stream_label, @@ -147,6 +165,16 @@ PeerConnectionE2EQualityTestFixture::VideoDumpOptions::GetOutputDumpFileName( return test::JoinFilename(output_directory_, file_name.Release()); } +absl::optional PeerConnectionE2EQualityTestFixture:: + VideoDumpOptions::GetOutputFrameIdsDumpFileName( + absl::string_view stream_label, + absl::string_view receiver) const { + if (!export_frame_ids_) { + return absl::nullopt; + } + return GetOutputDumpFileName(stream_label, receiver) + ".frame_ids.txt"; +} + PeerConnectionE2EQualityTestFixture::VideoConfig::VideoConfig( const VideoResolution& resolution) : width(resolution.width()), diff --git a/api/test/peerconnection_quality_test_fixture.h b/api/test/peerconnection_quality_test_fixture.h index fb34affbf4..ca1c2882a1 100644 --- a/api/test/peerconnection_quality_test_fixture.h +++ b/api/test/peerconnection_quality_test_fixture.h @@ -219,6 +219,8 @@ class PeerConnectionE2EQualityTestFixture { class VideoDumpOptions { public: + static constexpr int kDefaultSamplingModulo = 1; + // output_directory - the output directory where stream will be dumped. The // output files' names will be constructed as // _. for output dumps and @@ -227,8 +229,16 @@ class PeerConnectionE2EQualityTestFixture { // sampling_modulo - the module for the video frames to be dumped. Modulo // equals X means every Xth frame will be written to the dump file. The // value must be greater than 0. (Default: 1) + // export_frame_ids - specifies if frame ids should be exported together + // with content of the stream. If true, an output file with the same name as + // video dump and suffix ".frame_ids.txt" will be created. It will contain + // the frame ids in the same order as original frames in the output + // file with stream content. File will contain one frame id per line. + // (Default: false) explicit VideoDumpOptions(absl::string_view output_directory, - int sampling_modulo = 1); + int sampling_modulo = kDefaultSamplingModulo, + bool export_frame_ids = false); + VideoDumpOptions(absl::string_view output_directory, bool export_frame_ids); VideoDumpOptions(const VideoDumpOptions&) = default; VideoDumpOptions& operator=(const VideoDumpOptions&) = default; @@ -237,14 +247,25 @@ class PeerConnectionE2EQualityTestFixture { std::string output_directory() const { return output_directory_; } int sampling_modulo() const { return sampling_modulo_; } + bool export_frame_ids() const { return export_frame_ids_; } std::string GetInputDumpFileName(absl::string_view stream_label) const; + // Returns file name for input frame ids dump if `export_frame_ids()` is + // true, absl::nullopt otherwise. + absl::optional GetInputFrameIdsDumpFileName( + absl::string_view stream_label) const; std::string GetOutputDumpFileName(absl::string_view stream_label, absl::string_view receiver) const; + // Returns file name for output frame ids dump if `export_frame_ids()` is + // true, absl::nullopt otherwise. + absl::optional GetOutputFrameIdsDumpFileName( + absl::string_view stream_label, + absl::string_view receiver) const; private: std::string output_directory_; int sampling_modulo_ = 1; + bool export_frame_ids_ = false; }; // Contains properties of single video stream. diff --git a/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.cc b/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.cc index c423611e1c..661208e888 100644 --- a/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.cc +++ b/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.cc @@ -10,6 +10,8 @@ #include "test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.h" +#include + #include #include @@ -87,6 +89,46 @@ class AnalyzingFramePreprocessor } // namespace +VideoQualityAnalyzerInjectionHelper::VideoFrameIdsWriter::VideoFrameIdsWriter( + absl::string_view file_name) + : file_name_(file_name) { + output_file_ = fopen(file_name_.c_str(), "wb"); + RTC_CHECK(output_file_ != nullptr) + << "Failed to open file to dump frame ids for writing: " << file_name_; +} + +VideoQualityAnalyzerInjectionHelper::VideoFrameIdsWriter:: + ~VideoFrameIdsWriter() { + fclose(output_file_); +} + +void VideoQualityAnalyzerInjectionHelper::VideoFrameIdsWriter::WriteFrameId( + uint16_t frame_id) { + int chars_written = fprintf(output_file_, "%d\n", frame_id); + RTC_CHECK_GE(chars_written, 2) + << "Failed to write frame id to the output file: " << file_name_; +} + +VideoQualityAnalyzerInjectionHelper::VideoWriter2::VideoWriter2( + test::VideoFrameWriter* video_writer, + VideoFrameIdsWriter* frame_ids_writer, + int sampling_modulo) + : video_writer_(video_writer), + frame_ids_writer_(frame_ids_writer), + sampling_modulo_(sampling_modulo) {} + +void VideoQualityAnalyzerInjectionHelper::VideoWriter2::OnFrame( + const VideoFrame& frame) { + if (frames_counter_++ % sampling_modulo_ != 0) { + return; + } + bool result = video_writer_->WriteFrame(frame); + RTC_CHECK(result) << "Failed to write frame"; + if (frame_ids_writer_) { + frame_ids_writer_->WriteFrameId(frame.id()); + } +} + VideoQualityAnalyzerInjectionHelper::VideoQualityAnalyzerInjectionHelper( Clock* clock, std::unique_ptr analyzer, @@ -132,10 +174,19 @@ VideoQualityAnalyzerInjectionHelper::CreateFramePreprocessor( if (config.input_dump_options.has_value()) { // Using new API for video dumping. writer = MaybeCreateVideoWriter( - config.input_dump_options->GetInputDumpFileName(peer_name), config); + config.input_dump_options->GetInputDumpFileName(*config.stream_label), + config); RTC_CHECK(writer); - sinks.push_back(std::make_unique( - writer, config.input_dump_options->sampling_modulo())); + VideoFrameIdsWriter* frame_ids_writer = nullptr; + if (config.input_dump_options->export_frame_ids()) { + frame_ids_writers_.push_back(std::make_unique( + *config.input_dump_options->GetInputFrameIdsDumpFileName( + *config.stream_label))); + frame_ids_writer = frame_ids_writers_.back().get(); + } + sinks.push_back(std::make_unique( + writer, frame_ids_writer, + config.input_dump_options->sampling_modulo())); } else { // Using old API. To be removed. writer = MaybeCreateVideoWriter(config.input_dump_file_name, config); @@ -202,6 +253,7 @@ void VideoQualityAnalyzerInjectionHelper::Stop() { video_writer->Close(); } video_writers_.clear(); + frame_ids_writers_.clear(); } test::VideoFrameWriter* @@ -275,8 +327,16 @@ VideoQualityAnalyzerInjectionHelper::PopulateSinks( receiver_stream.stream_label, receiver_stream.peer_name), config); RTC_CHECK(writer); - sinks.push_back(std::make_unique( - writer, config.output_dump_options->sampling_modulo())); + VideoFrameIdsWriter* frame_ids_writer = nullptr; + if (config.output_dump_options->export_frame_ids()) { + frame_ids_writers_.push_back(std::make_unique( + *config.output_dump_options->GetOutputFrameIdsDumpFileName( + receiver_stream.stream_label, receiver_stream.peer_name))); + frame_ids_writer = frame_ids_writers_.back().get(); + } + sinks.push_back(std::make_unique( + writer, frame_ids_writer, + config.output_dump_options->sampling_modulo())); } else { // Using old API. To be removed. absl::optional output_dump_file_name = diff --git a/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.h b/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.h index 03fdd61870..76b8b446bc 100644 --- a/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.h +++ b/test/pc/e2e/analyzer/video/video_quality_analyzer_injection_helper.h @@ -11,6 +11,8 @@ #ifndef TEST_PC_E2E_ANALYZER_VIDEO_VIDEO_QUALITY_ANALYZER_INJECTION_HELPER_H_ #define TEST_PC_E2E_ANALYZER_VIDEO_VIDEO_QUALITY_ANALYZER_INJECTION_HELPER_H_ +#include + #include #include #include @@ -131,6 +133,35 @@ class VideoQualityAnalyzerInjectionHelper : public StatsObserverInterface { } }; + class VideoFrameIdsWriter { + public: + explicit VideoFrameIdsWriter(absl::string_view file_name); + ~VideoFrameIdsWriter(); + + void WriteFrameId(uint16_t frame_id); + + private: + const std::string file_name_; + FILE* output_file_; + }; + + class VideoWriter2 final : public rtc::VideoSinkInterface { + public: + VideoWriter2(test::VideoFrameWriter* video_writer, + VideoFrameIdsWriter* frame_ids_writer, + int sampling_modulo); + ~VideoWriter2() override = default; + + void OnFrame(const VideoFrame& frame) override; + + private: + test::VideoFrameWriter* const video_writer_; + VideoFrameIdsWriter* const frame_ids_writer_; + const int sampling_modulo_; + + int64_t frames_counter_ = 0; + }; + test::VideoFrameWriter* MaybeCreateVideoWriter( absl::optional file_name, const PeerConnectionE2EQualityTestFixture::VideoConfig& config); @@ -146,6 +177,7 @@ class VideoQualityAnalyzerInjectionHelper : public StatsObserverInterface { EncodedImageDataExtractor* extractor_; std::vector> video_writers_; + std::vector> frame_ids_writers_; Mutex mutex_; int peers_count_ RTC_GUARDED_BY(mutex_);