[PCLF] Add support for dumping video with multiple receivers
Bug: b/197896468 Change-Id: I7896246eedb2e9efe847df4dddfc8ef05f7d152b Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/230424 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Commit-Queue: Artem Titov <titovartem@webrtc.org> Cr-Commit-Position: refs/heads/main@{#34866}
This commit is contained in:
parent
6eb30e40af
commit
8177f58dde
@ -233,9 +233,31 @@ class PeerConnectionE2EQualityTestFixture {
|
||||
// written to the dump file. The value must be greater than 0.
|
||||
int input_dump_sampling_modulo = 1;
|
||||
// If specified this file will be used as output on the receiver side for
|
||||
// this stream. If multiple streams will be produced by input stream,
|
||||
// output files will be appended with indexes. The produced files contains
|
||||
// what was rendered for this video stream on receiver side.
|
||||
// this stream.
|
||||
//
|
||||
// If multiple output streams will be produced by this stream (e.g. when the
|
||||
// stream represented by this `VideoConfig` is received by more than one
|
||||
// peer), output files will be appended with receiver names. If the second
|
||||
// and other receivers will be added in the middle of the call after the
|
||||
// first frame for this stream has been already written to the output file,
|
||||
// then only dumps for newly added peers will be appended with receiver
|
||||
// name, the dump for the first receiver will have name equal to the
|
||||
// specified one. For example:
|
||||
// * If we have peers A and B and A has `VideoConfig` V_a with
|
||||
// V_a.output_dump_file_name = "/foo/a_output.yuv", then the stream
|
||||
// related to V_a will be written into "/foo/a_output.yuv".
|
||||
// * If we have peers A, B and C and A has `VideoConfig` V_a with
|
||||
// V_a.output_dump_file_name = "/foo/a_output.yuv", then the stream
|
||||
// related to V_a will be written for peer B into "/foo/a_output.yuv.B"
|
||||
// and for peer C into "/foo/a_output.yuv.C"
|
||||
// * If we have peers A and B and A has `VideoConfig` V_a with
|
||||
// V_a.output_dump_file_name = "/foo/a_output.yuv", then if after B
|
||||
// received the first frame related to V_a peer C joined the call, then
|
||||
// the stream related to V_a will be written for peer B into
|
||||
// "/foo/a_output.yuv" and for peer C into "/foo/a_output.yuv.C"
|
||||
//
|
||||
// The produced files contains what was rendered for this video stream on
|
||||
// receiver side.
|
||||
absl::optional<std::string> output_dump_file_name;
|
||||
// Used only if `output_dump_file_name` is set. Specifies the module for the
|
||||
// video frames to be dumped. Modulo equals X means every Xth frame will be
|
||||
|
||||
@ -187,6 +187,7 @@ if (!build_with_chromium) {
|
||||
"../../../api/video:video_rtp_headers",
|
||||
"../../../api/video_codecs:video_codecs_api",
|
||||
"../../../rtc_base:criticalsection",
|
||||
"../../../rtc_base:stringutils",
|
||||
"../../../rtc_base/synchronization:mutex",
|
||||
"../../../test:video_test_common",
|
||||
"../../../test:video_test_support",
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/array_view.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "test/pc/e2e/analyzer/video/quality_analyzing_video_decoder.h"
|
||||
#include "test/pc/e2e/analyzer/video/quality_analyzing_video_encoder.h"
|
||||
#include "test/pc/e2e/analyzer/video/simulcast_dummy_buffer_helper.h"
|
||||
@ -134,7 +135,7 @@ VideoQualityAnalyzerInjectionHelper::CreateFramePreprocessor(
|
||||
config.width, config.height)));
|
||||
}
|
||||
{
|
||||
MutexLock lock(&lock_);
|
||||
MutexLock lock(&mutex_);
|
||||
known_video_configs_.insert({*config.stream_label, config});
|
||||
}
|
||||
return std::make_unique<AnalyzingFramePreprocessor>(
|
||||
@ -154,6 +155,16 @@ void VideoQualityAnalyzerInjectionHelper::Start(
|
||||
int max_threads_count) {
|
||||
analyzer_->Start(std::move(test_case_name), peer_names, max_threads_count);
|
||||
extractor_->Start(peer_names.size());
|
||||
MutexLock lock(&mutex_);
|
||||
peers_count_ = peer_names.size();
|
||||
}
|
||||
|
||||
void VideoQualityAnalyzerInjectionHelper::RegisterParticipantInCall(
|
||||
absl::string_view peer_name) {
|
||||
analyzer_->RegisterParticipantInCall(peer_name);
|
||||
extractor_->AddParticipantInCall();
|
||||
MutexLock lock(&mutex_);
|
||||
peers_count_++;
|
||||
}
|
||||
|
||||
void VideoQualityAnalyzerInjectionHelper::OnStatsReports(
|
||||
@ -203,7 +214,7 @@ void VideoQualityAnalyzerInjectionHelper::OnFrame(absl::string_view peer_name,
|
||||
|
||||
std::string stream_label = analyzer_->GetStreamLabel(frame.id());
|
||||
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>* sinks =
|
||||
PopulateSinks(stream_label);
|
||||
PopulateSinks(ReceiverStream(peer_name, stream_label));
|
||||
if (sinks == nullptr) {
|
||||
return;
|
||||
}
|
||||
@ -214,20 +225,27 @@ void VideoQualityAnalyzerInjectionHelper::OnFrame(absl::string_view peer_name,
|
||||
|
||||
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>*
|
||||
VideoQualityAnalyzerInjectionHelper::PopulateSinks(
|
||||
const std::string& stream_label) {
|
||||
MutexLock lock(&lock_);
|
||||
auto sinks_it = sinks_.find(stream_label);
|
||||
const ReceiverStream& receiver_stream) {
|
||||
MutexLock lock(&mutex_);
|
||||
auto sinks_it = sinks_.find(receiver_stream);
|
||||
if (sinks_it != sinks_.end()) {
|
||||
return &sinks_it->second;
|
||||
}
|
||||
auto it = known_video_configs_.find(stream_label);
|
||||
auto it = known_video_configs_.find(receiver_stream.stream_label);
|
||||
RTC_DCHECK(it != known_video_configs_.end())
|
||||
<< "No video config for stream " << stream_label;
|
||||
<< "No video config for stream " << receiver_stream.stream_label;
|
||||
const VideoConfig& config = it->second;
|
||||
|
||||
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
|
||||
absl::optional<std::string> output_dump_file_name =
|
||||
config.output_dump_file_name;
|
||||
if (output_dump_file_name.has_value() && peers_count_ > 2) {
|
||||
rtc::StringBuilder builder(*output_dump_file_name);
|
||||
builder << "." << receiver_stream.peer_name;
|
||||
output_dump_file_name = builder.str();
|
||||
}
|
||||
test::VideoFrameWriter* writer =
|
||||
MaybeCreateVideoWriter(config.output_dump_file_name, config);
|
||||
MaybeCreateVideoWriter(output_dump_file_name, config);
|
||||
if (writer) {
|
||||
sinks.push_back(std::make_unique<VideoWriter>(
|
||||
writer, config.output_dump_sampling_modulo));
|
||||
@ -237,8 +255,8 @@ VideoQualityAnalyzerInjectionHelper::PopulateSinks(
|
||||
test::VideoRenderer::Create((*config.stream_label + "-render").c_str(),
|
||||
config.width, config.height)));
|
||||
}
|
||||
sinks_.insert({stream_label, std::move(sinks)});
|
||||
return &(sinks_.find(stream_label)->second);
|
||||
sinks_.insert({receiver_stream, std::move(sinks)});
|
||||
return &(sinks_.find(receiver_stream)->second);
|
||||
}
|
||||
|
||||
} // namespace webrtc_pc_e2e
|
||||
|
||||
@ -45,13 +45,6 @@ class VideoQualityAnalyzerInjectionHelper : public StatsObserverInterface {
|
||||
EncodedImageDataExtractor* extractor);
|
||||
~VideoQualityAnalyzerInjectionHelper() override;
|
||||
|
||||
// Registers new call participant to the underlying video quality analyzer.
|
||||
// The method should be called before the participant is actually added.
|
||||
void RegisterParticipantInCall(absl::string_view peer_name) {
|
||||
analyzer_->RegisterParticipantInCall(peer_name);
|
||||
extractor_->AddParticipantInCall();
|
||||
}
|
||||
|
||||
// Wraps video encoder factory to give video quality analyzer access to frames
|
||||
// before encoding and encoded images after.
|
||||
std::unique_ptr<VideoEncoderFactory> WrapVideoEncoderFactory(
|
||||
@ -83,6 +76,9 @@ class VideoQualityAnalyzerInjectionHelper : public StatsObserverInterface {
|
||||
void Start(std::string test_case_name,
|
||||
rtc::ArrayView<const std::string> peer_names,
|
||||
int max_threads_count = 1);
|
||||
// Registers new call participant to the underlying video quality analyzer.
|
||||
// The method should be called before the participant is actually added.
|
||||
void RegisterParticipantInCall(absl::string_view peer_name);
|
||||
|
||||
// Forwards `stats_reports` for Peer Connection `pc_label` to
|
||||
// `analyzer_`.
|
||||
@ -111,6 +107,23 @@ class VideoQualityAnalyzerInjectionHelper : public StatsObserverInterface {
|
||||
VideoQualityAnalyzerInjectionHelper* const helper_;
|
||||
};
|
||||
|
||||
struct ReceiverStream {
|
||||
ReceiverStream(absl::string_view peer_name, absl::string_view stream_label)
|
||||
: peer_name(peer_name), stream_label(stream_label) {}
|
||||
|
||||
std::string peer_name;
|
||||
std::string stream_label;
|
||||
|
||||
// Define operators required to use ReceiverStream as std::map key.
|
||||
bool operator==(const ReceiverStream& o) const {
|
||||
return peer_name == o.peer_name && stream_label == o.stream_label;
|
||||
}
|
||||
bool operator<(const ReceiverStream& o) const {
|
||||
return (peer_name == o.peer_name) ? stream_label < o.stream_label
|
||||
: peer_name < o.peer_name;
|
||||
}
|
||||
};
|
||||
|
||||
test::VideoFrameWriter* MaybeCreateVideoWriter(
|
||||
absl::optional<std::string> file_name,
|
||||
const PeerConnectionE2EQualityTestFixture::VideoConfig& config);
|
||||
@ -118,7 +131,7 @@ class VideoQualityAnalyzerInjectionHelper : public StatsObserverInterface {
|
||||
// passing real frame to the sinks
|
||||
void OnFrame(absl::string_view peer_name, const VideoFrame& frame);
|
||||
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>*
|
||||
PopulateSinks(const std::string& stream_label);
|
||||
PopulateSinks(const ReceiverStream& receiver_stream);
|
||||
|
||||
std::unique_ptr<VideoQualityAnalyzerInterface> analyzer_;
|
||||
EncodedImageDataInjector* injector_;
|
||||
@ -126,11 +139,14 @@ class VideoQualityAnalyzerInjectionHelper : public StatsObserverInterface {
|
||||
|
||||
std::vector<std::unique_ptr<test::VideoFrameWriter>> video_writers_;
|
||||
|
||||
Mutex lock_;
|
||||
std::map<std::string, VideoConfig> known_video_configs_ RTC_GUARDED_BY(lock_);
|
||||
std::map<std::string,
|
||||
Mutex mutex_;
|
||||
int peers_count_ RTC_GUARDED_BY(mutex_);
|
||||
// Map from stream label to the video config.
|
||||
std::map<std::string, VideoConfig> known_video_configs_
|
||||
RTC_GUARDED_BY(mutex_);
|
||||
std::map<ReceiverStream,
|
||||
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>>
|
||||
sinks_ RTC_GUARDED_BY(lock_);
|
||||
sinks_ RTC_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace webrtc_pc_e2e
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user