[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.
|
// written to the dump file. The value must be greater than 0.
|
||||||
int input_dump_sampling_modulo = 1;
|
int input_dump_sampling_modulo = 1;
|
||||||
// If specified this file will be used as output on the receiver side for
|
// 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,
|
// this stream.
|
||||||
// output files will be appended with indexes. The produced files contains
|
//
|
||||||
// what was rendered for this video stream on receiver side.
|
// 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;
|
absl::optional<std::string> output_dump_file_name;
|
||||||
// Used only if `output_dump_file_name` is set. Specifies the module for the
|
// 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
|
// 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:video_rtp_headers",
|
||||||
"../../../api/video_codecs:video_codecs_api",
|
"../../../api/video_codecs:video_codecs_api",
|
||||||
"../../../rtc_base:criticalsection",
|
"../../../rtc_base:criticalsection",
|
||||||
|
"../../../rtc_base:stringutils",
|
||||||
"../../../rtc_base/synchronization:mutex",
|
"../../../rtc_base/synchronization:mutex",
|
||||||
"../../../test:video_test_common",
|
"../../../test:video_test_common",
|
||||||
"../../../test:video_test_support",
|
"../../../test:video_test_support",
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
#include "absl/memory/memory.h"
|
#include "absl/memory/memory.h"
|
||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
#include "api/array_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_decoder.h"
|
||||||
#include "test/pc/e2e/analyzer/video/quality_analyzing_video_encoder.h"
|
#include "test/pc/e2e/analyzer/video/quality_analyzing_video_encoder.h"
|
||||||
#include "test/pc/e2e/analyzer/video/simulcast_dummy_buffer_helper.h"
|
#include "test/pc/e2e/analyzer/video/simulcast_dummy_buffer_helper.h"
|
||||||
@ -134,7 +135,7 @@ VideoQualityAnalyzerInjectionHelper::CreateFramePreprocessor(
|
|||||||
config.width, config.height)));
|
config.width, config.height)));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
MutexLock lock(&lock_);
|
MutexLock lock(&mutex_);
|
||||||
known_video_configs_.insert({*config.stream_label, config});
|
known_video_configs_.insert({*config.stream_label, config});
|
||||||
}
|
}
|
||||||
return std::make_unique<AnalyzingFramePreprocessor>(
|
return std::make_unique<AnalyzingFramePreprocessor>(
|
||||||
@ -154,6 +155,16 @@ void VideoQualityAnalyzerInjectionHelper::Start(
|
|||||||
int max_threads_count) {
|
int max_threads_count) {
|
||||||
analyzer_->Start(std::move(test_case_name), peer_names, max_threads_count);
|
analyzer_->Start(std::move(test_case_name), peer_names, max_threads_count);
|
||||||
extractor_->Start(peer_names.size());
|
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(
|
void VideoQualityAnalyzerInjectionHelper::OnStatsReports(
|
||||||
@ -203,7 +214,7 @@ void VideoQualityAnalyzerInjectionHelper::OnFrame(absl::string_view peer_name,
|
|||||||
|
|
||||||
std::string stream_label = analyzer_->GetStreamLabel(frame.id());
|
std::string stream_label = analyzer_->GetStreamLabel(frame.id());
|
||||||
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>* sinks =
|
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>* sinks =
|
||||||
PopulateSinks(stream_label);
|
PopulateSinks(ReceiverStream(peer_name, stream_label));
|
||||||
if (sinks == nullptr) {
|
if (sinks == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -214,20 +225,27 @@ void VideoQualityAnalyzerInjectionHelper::OnFrame(absl::string_view peer_name,
|
|||||||
|
|
||||||
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>*
|
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>*
|
||||||
VideoQualityAnalyzerInjectionHelper::PopulateSinks(
|
VideoQualityAnalyzerInjectionHelper::PopulateSinks(
|
||||||
const std::string& stream_label) {
|
const ReceiverStream& receiver_stream) {
|
||||||
MutexLock lock(&lock_);
|
MutexLock lock(&mutex_);
|
||||||
auto sinks_it = sinks_.find(stream_label);
|
auto sinks_it = sinks_.find(receiver_stream);
|
||||||
if (sinks_it != sinks_.end()) {
|
if (sinks_it != sinks_.end()) {
|
||||||
return &sinks_it->second;
|
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())
|
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;
|
const VideoConfig& config = it->second;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
|
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 =
|
test::VideoFrameWriter* writer =
|
||||||
MaybeCreateVideoWriter(config.output_dump_file_name, config);
|
MaybeCreateVideoWriter(output_dump_file_name, config);
|
||||||
if (writer) {
|
if (writer) {
|
||||||
sinks.push_back(std::make_unique<VideoWriter>(
|
sinks.push_back(std::make_unique<VideoWriter>(
|
||||||
writer, config.output_dump_sampling_modulo));
|
writer, config.output_dump_sampling_modulo));
|
||||||
@ -237,8 +255,8 @@ VideoQualityAnalyzerInjectionHelper::PopulateSinks(
|
|||||||
test::VideoRenderer::Create((*config.stream_label + "-render").c_str(),
|
test::VideoRenderer::Create((*config.stream_label + "-render").c_str(),
|
||||||
config.width, config.height)));
|
config.width, config.height)));
|
||||||
}
|
}
|
||||||
sinks_.insert({stream_label, std::move(sinks)});
|
sinks_.insert({receiver_stream, std::move(sinks)});
|
||||||
return &(sinks_.find(stream_label)->second);
|
return &(sinks_.find(receiver_stream)->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc_pc_e2e
|
} // namespace webrtc_pc_e2e
|
||||||
|
|||||||
@ -45,13 +45,6 @@ class VideoQualityAnalyzerInjectionHelper : public StatsObserverInterface {
|
|||||||
EncodedImageDataExtractor* extractor);
|
EncodedImageDataExtractor* extractor);
|
||||||
~VideoQualityAnalyzerInjectionHelper() override;
|
~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
|
// Wraps video encoder factory to give video quality analyzer access to frames
|
||||||
// before encoding and encoded images after.
|
// before encoding and encoded images after.
|
||||||
std::unique_ptr<VideoEncoderFactory> WrapVideoEncoderFactory(
|
std::unique_ptr<VideoEncoderFactory> WrapVideoEncoderFactory(
|
||||||
@ -83,6 +76,9 @@ class VideoQualityAnalyzerInjectionHelper : public StatsObserverInterface {
|
|||||||
void Start(std::string test_case_name,
|
void Start(std::string test_case_name,
|
||||||
rtc::ArrayView<const std::string> peer_names,
|
rtc::ArrayView<const std::string> peer_names,
|
||||||
int max_threads_count = 1);
|
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
|
// Forwards `stats_reports` for Peer Connection `pc_label` to
|
||||||
// `analyzer_`.
|
// `analyzer_`.
|
||||||
@ -111,6 +107,23 @@ class VideoQualityAnalyzerInjectionHelper : public StatsObserverInterface {
|
|||||||
VideoQualityAnalyzerInjectionHelper* const helper_;
|
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(
|
test::VideoFrameWriter* MaybeCreateVideoWriter(
|
||||||
absl::optional<std::string> file_name,
|
absl::optional<std::string> file_name,
|
||||||
const PeerConnectionE2EQualityTestFixture::VideoConfig& config);
|
const PeerConnectionE2EQualityTestFixture::VideoConfig& config);
|
||||||
@ -118,7 +131,7 @@ class VideoQualityAnalyzerInjectionHelper : public StatsObserverInterface {
|
|||||||
// passing real frame to the sinks
|
// passing real frame to the sinks
|
||||||
void OnFrame(absl::string_view peer_name, const VideoFrame& frame);
|
void OnFrame(absl::string_view peer_name, const VideoFrame& frame);
|
||||||
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>*
|
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>*
|
||||||
PopulateSinks(const std::string& stream_label);
|
PopulateSinks(const ReceiverStream& receiver_stream);
|
||||||
|
|
||||||
std::unique_ptr<VideoQualityAnalyzerInterface> analyzer_;
|
std::unique_ptr<VideoQualityAnalyzerInterface> analyzer_;
|
||||||
EncodedImageDataInjector* injector_;
|
EncodedImageDataInjector* injector_;
|
||||||
@ -126,11 +139,14 @@ class VideoQualityAnalyzerInjectionHelper : public StatsObserverInterface {
|
|||||||
|
|
||||||
std::vector<std::unique_ptr<test::VideoFrameWriter>> video_writers_;
|
std::vector<std::unique_ptr<test::VideoFrameWriter>> video_writers_;
|
||||||
|
|
||||||
Mutex lock_;
|
Mutex mutex_;
|
||||||
std::map<std::string, VideoConfig> known_video_configs_ RTC_GUARDED_BY(lock_);
|
int peers_count_ RTC_GUARDED_BY(mutex_);
|
||||||
std::map<std::string,
|
// 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>>>>
|
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>>>
|
||||||
sinks_ RTC_GUARDED_BY(lock_);
|
sinks_ RTC_GUARDED_BY(mutex_);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc_pc_e2e
|
} // namespace webrtc_pc_e2e
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user