Start making RtpReplayer into an actual class.
This is to simplify implementation of new feature flags. - Move helper functions to anonymous namespace. - Add members to avoid passing everything by function paramaters. Bug: webrtc:14508 Change-Id: I0a4958645a4eb76515f28d8ce868a66be6748919 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/277720 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Commit-Queue: Philip Eliasson <philipel@webrtc.org> Cr-Commit-Position: refs/heads/main@{#38274}
This commit is contained in:
@ -256,93 +256,19 @@ class DecoderIvfFileWriter : public test::FakeDecoder {
|
|||||||
VideoCodecType video_codec_type_;
|
VideoCodecType video_codec_type_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The RtpReplayer is responsible for parsing the configuration provided by the
|
// Holds all the shared memory structures required for a receive stream. This
|
||||||
// user, setting up the windows, receive streams and decoders and then replaying
|
// structure is used to prevent members being deallocated before the replay
|
||||||
// the provided RTP dump.
|
// has been finished.
|
||||||
class RtpReplayer final {
|
struct StreamState {
|
||||||
public:
|
|
||||||
// Replay a rtp dump with an optional json configuration.
|
|
||||||
static void Replay(const std::string& replay_config_path,
|
|
||||||
const std::string& rtp_dump_path) {
|
|
||||||
webrtc::RtcEventLogNull event_log;
|
|
||||||
Call::Config call_config(&event_log);
|
|
||||||
call_config.trials = new FieldTrialBasedConfig();
|
|
||||||
|
|
||||||
std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
|
|
||||||
webrtc::CreateDefaultTaskQueueFactory(call_config.trials);
|
|
||||||
auto worker_thread = task_queue_factory->CreateTaskQueue(
|
|
||||||
"worker_thread", TaskQueueFactory::Priority::NORMAL);
|
|
||||||
rtc::Event sync_event(/*manual_reset=*/false,
|
|
||||||
/*initially_signalled=*/false);
|
|
||||||
call_config.task_queue_factory = task_queue_factory.get();
|
|
||||||
|
|
||||||
// Creation of the streams must happen inside a task queue because it is
|
|
||||||
// resued as a worker thread.
|
|
||||||
std::unique_ptr<Call> call;
|
|
||||||
std::unique_ptr<StreamState> stream_state;
|
|
||||||
worker_thread->PostTask([&]() {
|
|
||||||
call.reset(Call::Create(call_config));
|
|
||||||
|
|
||||||
// Attempt to load the configuration
|
|
||||||
if (replay_config_path.empty()) {
|
|
||||||
stream_state = ConfigureFromFlags(rtp_dump_path, call.get());
|
|
||||||
} else {
|
|
||||||
stream_state = ConfigureFromFile(replay_config_path, call.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream_state == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Start replaying the provided stream now that it has been configured.
|
|
||||||
// VideoReceiveStreams must be started on the same thread as they were
|
|
||||||
// created on.
|
|
||||||
for (const auto& receive_stream : stream_state->receive_streams) {
|
|
||||||
receive_stream->Start();
|
|
||||||
}
|
|
||||||
sync_event.Set();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Attempt to create an RtpReader from the input file.
|
|
||||||
std::unique_ptr<test::RtpFileReader> rtp_reader =
|
|
||||||
CreateRtpReader(rtp_dump_path);
|
|
||||||
|
|
||||||
// Wait for streams creation.
|
|
||||||
sync_event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
|
|
||||||
|
|
||||||
if (stream_state != nullptr && rtp_reader != nullptr) {
|
|
||||||
ReplayPackets(call.get(), rtp_reader.get(), worker_thread.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destruction of streams and the call must happen on the same thread as
|
|
||||||
// their creation.
|
|
||||||
worker_thread->PostTask([&]() {
|
|
||||||
for (const auto& receive_stream : stream_state->receive_streams) {
|
|
||||||
call->DestroyVideoReceiveStream(receive_stream);
|
|
||||||
}
|
|
||||||
for (const auto& flexfec_stream : stream_state->flexfec_streams) {
|
|
||||||
call->DestroyFlexfecReceiveStream(flexfec_stream);
|
|
||||||
}
|
|
||||||
call.reset();
|
|
||||||
sync_event.Set();
|
|
||||||
});
|
|
||||||
sync_event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Holds all the shared memory structures required for a receive stream. This
|
|
||||||
// structure is used to prevent members being deallocated before the replay
|
|
||||||
// has been finished.
|
|
||||||
struct StreamState {
|
|
||||||
test::NullTransport transport;
|
test::NullTransport transport;
|
||||||
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
|
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
|
||||||
std::vector<VideoReceiveStreamInterface*> receive_streams;
|
std::vector<VideoReceiveStreamInterface*> receive_streams;
|
||||||
std::vector<FlexfecReceiveStream*> flexfec_streams;
|
std::vector<FlexfecReceiveStream*> flexfec_streams;
|
||||||
std::unique_ptr<VideoDecoderFactory> decoder_factory;
|
std::unique_ptr<VideoDecoderFactory> decoder_factory;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Loads multiple configurations from the provided configuration file.
|
// Loads multiple configurations from the provided configuration file.
|
||||||
static std::unique_ptr<StreamState> ConfigureFromFile(
|
std::unique_ptr<StreamState> ConfigureFromFile(const std::string& config_path,
|
||||||
const std::string& config_path,
|
|
||||||
Call* call) {
|
Call* call) {
|
||||||
auto stream_state = std::make_unique<StreamState>();
|
auto stream_state = std::make_unique<StreamState>();
|
||||||
// Parse the configuration file.
|
// Parse the configuration file.
|
||||||
@ -385,10 +311,10 @@ class RtpReplayer final {
|
|||||||
call->CreateVideoReceiveStream(std::move(receive_config)));
|
call->CreateVideoReceiveStream(std::move(receive_config)));
|
||||||
}
|
}
|
||||||
return stream_state;
|
return stream_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads the base configuration from flags passed in on the commandline.
|
// Loads the base configuration from flags passed in on the commandline.
|
||||||
static std::unique_ptr<StreamState> ConfigureFromFlags(
|
std::unique_ptr<StreamState> ConfigureFromFlags(
|
||||||
const std::string& rtp_dump_path,
|
const std::string& rtp_dump_path,
|
||||||
Call* call) {
|
Call* call) {
|
||||||
auto stream_state = std::make_unique<StreamState>();
|
auto stream_state = std::make_unique<StreamState>();
|
||||||
@ -411,10 +337,10 @@ class RtpReplayer final {
|
|||||||
receive_config.rtp.local_ssrc = kReceiverLocalSsrc;
|
receive_config.rtp.local_ssrc = kReceiverLocalSsrc;
|
||||||
receive_config.rtp.rtx_ssrc = absl::GetFlag(FLAGS_ssrc_rtx);
|
receive_config.rtp.rtx_ssrc = absl::GetFlag(FLAGS_ssrc_rtx);
|
||||||
receive_config.rtp.rtx_associated_payload_types[absl::GetFlag(
|
receive_config.rtp.rtx_associated_payload_types[absl::GetFlag(
|
||||||
FLAGS_media_payload_type_rtx)] =
|
FLAGS_media_payload_type_rtx)] = absl::GetFlag(FLAGS_media_payload_type);
|
||||||
absl::GetFlag(FLAGS_media_payload_type);
|
receive_config.rtp
|
||||||
receive_config.rtp.rtx_associated_payload_types[absl::GetFlag(
|
.rtx_associated_payload_types[absl::GetFlag(FLAGS_red_payload_type_rtx)] =
|
||||||
FLAGS_red_payload_type_rtx)] = absl::GetFlag(FLAGS_red_payload_type);
|
absl::GetFlag(FLAGS_red_payload_type);
|
||||||
receive_config.rtp.ulpfec_payload_type =
|
receive_config.rtp.ulpfec_payload_type =
|
||||||
absl::GetFlag(FLAGS_ulpfec_payload_type);
|
absl::GetFlag(FLAGS_ulpfec_payload_type);
|
||||||
receive_config.rtp.red_payload_type = absl::GetFlag(FLAGS_red_payload_type);
|
receive_config.rtp.red_payload_type = absl::GetFlag(FLAGS_red_payload_type);
|
||||||
@ -439,16 +365,15 @@ class RtpReplayer final {
|
|||||||
absl::GetFlag(FLAGS_transmission_offset_id)));
|
absl::GetFlag(FLAGS_transmission_offset_id)));
|
||||||
}
|
}
|
||||||
if (absl::GetFlag(FLAGS_abs_send_time_id) != -1) {
|
if (absl::GetFlag(FLAGS_abs_send_time_id) != -1) {
|
||||||
receive_config.rtp.extensions.push_back(
|
receive_config.rtp.extensions.push_back(RtpExtension(
|
||||||
RtpExtension(RtpExtension::kAbsSendTimeUri,
|
RtpExtension::kAbsSendTimeUri, absl::GetFlag(FLAGS_abs_send_time_id)));
|
||||||
absl::GetFlag(FLAGS_abs_send_time_id)));
|
|
||||||
}
|
}
|
||||||
receive_config.renderer = stream_state->sinks.back().get();
|
receive_config.renderer = stream_state->sinks.back().get();
|
||||||
|
|
||||||
// Setup the receiving stream
|
// Setup the receiving stream
|
||||||
VideoReceiveStreamInterface::Decoder decoder;
|
VideoReceiveStreamInterface::Decoder decoder;
|
||||||
decoder = test::CreateMatchingDecoder(
|
decoder = test::CreateMatchingDecoder(absl::GetFlag(FLAGS_media_payload_type),
|
||||||
absl::GetFlag(FLAGS_media_payload_type), absl::GetFlag(FLAGS_codec));
|
absl::GetFlag(FLAGS_codec));
|
||||||
if (!absl::GetFlag(FLAGS_decoder_bitstream_filename).empty()) {
|
if (!absl::GetFlag(FLAGS_decoder_bitstream_filename).empty()) {
|
||||||
// Replace decoder with file writer if we're writing the bitstream to a
|
// Replace decoder with file writer if we're writing the bitstream to a
|
||||||
// file instead.
|
// file instead.
|
||||||
@ -467,8 +392,7 @@ class RtpReplayer final {
|
|||||||
absl::GetFlag(FLAGS_codec));
|
absl::GetFlag(FLAGS_codec));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
stream_state->decoder_factory =
|
stream_state->decoder_factory = std::make_unique<InternalDecoderFactory>();
|
||||||
std::make_unique<InternalDecoderFactory>();
|
|
||||||
}
|
}
|
||||||
receive_config.decoder_factory = stream_state->decoder_factory.get();
|
receive_config.decoder_factory = stream_state->decoder_factory.get();
|
||||||
receive_config.decoders.push_back(decoder);
|
receive_config.decoders.push_back(decoder);
|
||||||
@ -476,18 +400,17 @@ class RtpReplayer final {
|
|||||||
stream_state->receive_streams.emplace_back(
|
stream_state->receive_streams.emplace_back(
|
||||||
call->CreateVideoReceiveStream(std::move(receive_config)));
|
call->CreateVideoReceiveStream(std::move(receive_config)));
|
||||||
return stream_state;
|
return stream_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::unique_ptr<test::RtpFileReader> CreateRtpReader(
|
std::unique_ptr<test::RtpFileReader> CreateRtpReader(
|
||||||
const std::string& rtp_dump_path) {
|
const std::string& rtp_dump_path) {
|
||||||
std::unique_ptr<test::RtpFileReader> rtp_reader(test::RtpFileReader::Create(
|
std::unique_ptr<test::RtpFileReader> rtp_reader(test::RtpFileReader::Create(
|
||||||
test::RtpFileReader::kRtpDump, rtp_dump_path));
|
test::RtpFileReader::kRtpDump, rtp_dump_path));
|
||||||
if (!rtp_reader) {
|
if (!rtp_reader) {
|
||||||
rtp_reader.reset(test::RtpFileReader::Create(test::RtpFileReader::kPcap,
|
rtp_reader.reset(
|
||||||
rtp_dump_path));
|
test::RtpFileReader::Create(test::RtpFileReader::kPcap, rtp_dump_path));
|
||||||
if (!rtp_reader) {
|
if (!rtp_reader) {
|
||||||
fprintf(
|
fprintf(stderr,
|
||||||
stderr,
|
|
||||||
"Couldn't open input file as either a rtpdump or .pcap. Note "
|
"Couldn't open input file as either a rtpdump or .pcap. Note "
|
||||||
"that .pcapng is not supported.\nTrying to interpret the file as "
|
"that .pcapng is not supported.\nTrying to interpret the file as "
|
||||||
"length/packet interleaved.\n");
|
"length/packet interleaved.\n");
|
||||||
@ -501,11 +424,89 @@ class RtpReplayer final {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rtp_reader;
|
return rtp_reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The RtpReplayer is responsible for parsing the configuration provided by
|
||||||
|
// the user, setting up the windows, receive streams and decoders and then
|
||||||
|
// replaying the provided RTP dump.
|
||||||
|
class RtpReplayer final {
|
||||||
|
public:
|
||||||
|
RtpReplayer(absl::string_view replay_config_path,
|
||||||
|
absl::string_view rtp_dump_path,
|
||||||
|
std::unique_ptr<FieldTrialsView> field_trials)
|
||||||
|
: replay_config_path_(replay_config_path),
|
||||||
|
rtp_dump_path_(rtp_dump_path),
|
||||||
|
field_trials_(std::move(field_trials)),
|
||||||
|
task_queue_factory_(
|
||||||
|
webrtc::CreateDefaultTaskQueueFactory(field_trials_.get())),
|
||||||
|
worker_thread_(std::make_unique<rtc::TaskQueue>(
|
||||||
|
task_queue_factory_->CreateTaskQueue(
|
||||||
|
"worker_thread",
|
||||||
|
TaskQueueFactory::Priority::NORMAL))) {}
|
||||||
|
|
||||||
|
void Run() {
|
||||||
|
webrtc::RtcEventLogNull event_log;
|
||||||
|
Call::Config call_config(&event_log);
|
||||||
|
call_config.trials = field_trials_.get();
|
||||||
|
|
||||||
|
rtc::Event sync_event(/*manual_reset=*/false,
|
||||||
|
/*initially_signalled=*/false);
|
||||||
|
call_config.task_queue_factory = task_queue_factory_.get();
|
||||||
|
|
||||||
|
// Creation of the streams must happen inside a task queue because it is
|
||||||
|
// resued as a worker thread.
|
||||||
|
std::unique_ptr<Call> call;
|
||||||
|
std::unique_ptr<StreamState> stream_state;
|
||||||
|
worker_thread_->PostTask([&]() {
|
||||||
|
call.reset(Call::Create(call_config));
|
||||||
|
|
||||||
|
// Attempt to load the configuration
|
||||||
|
if (replay_config_path_.empty()) {
|
||||||
|
stream_state = ConfigureFromFlags(rtp_dump_path_, call.get());
|
||||||
|
} else {
|
||||||
|
stream_state = ConfigureFromFile(replay_config_path_, call.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReplayPackets(Call* call,
|
if (stream_state == nullptr) {
|
||||||
test::RtpFileReader* rtp_reader,
|
return;
|
||||||
TaskQueueBase* worker_thread) {
|
}
|
||||||
|
// Start replaying the provided stream now that it has been configured.
|
||||||
|
// VideoReceiveStreams must be started on the same thread as they were
|
||||||
|
// created on.
|
||||||
|
for (const auto& receive_stream : stream_state->receive_streams) {
|
||||||
|
receive_stream->Start();
|
||||||
|
}
|
||||||
|
sync_event.Set();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Attempt to create an RtpReader from the input file.
|
||||||
|
std::unique_ptr<test::RtpFileReader> rtp_reader =
|
||||||
|
CreateRtpReader(rtp_dump_path_);
|
||||||
|
|
||||||
|
// Wait for streams creation.
|
||||||
|
sync_event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
|
||||||
|
|
||||||
|
if (stream_state != nullptr && rtp_reader != nullptr) {
|
||||||
|
ReplayPackets(call.get(), rtp_reader.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destruction of streams and the call must happen on the same thread as
|
||||||
|
// their creation.
|
||||||
|
worker_thread_->PostTask([&]() {
|
||||||
|
for (const auto& receive_stream : stream_state->receive_streams) {
|
||||||
|
call->DestroyVideoReceiveStream(receive_stream);
|
||||||
|
}
|
||||||
|
for (const auto& flexfec_stream : stream_state->flexfec_streams) {
|
||||||
|
call->DestroyFlexfecReceiveStream(flexfec_stream);
|
||||||
|
}
|
||||||
|
call.reset();
|
||||||
|
sync_event.Set();
|
||||||
|
});
|
||||||
|
sync_event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ReplayPackets(Call* call, test::RtpFileReader* rtp_reader) {
|
||||||
int64_t replay_start_ms = -1;
|
int64_t replay_start_ms = -1;
|
||||||
int num_packets = 0;
|
int num_packets = 0;
|
||||||
std::map<uint32_t, int> unknown_packets;
|
std::map<uint32_t, int> unknown_packets;
|
||||||
@ -537,7 +538,7 @@ class RtpReplayer final {
|
|||||||
|
|
||||||
++num_packets;
|
++num_packets;
|
||||||
PacketReceiver::DeliveryStatus result = PacketReceiver::DELIVERY_OK;
|
PacketReceiver::DeliveryStatus result = PacketReceiver::DELIVERY_OK;
|
||||||
worker_thread->PostTask([&]() {
|
worker_thread_->PostTask([&]() {
|
||||||
MediaType media_type =
|
MediaType media_type =
|
||||||
IsRtcpPacket(packet_buffer) ? MediaType::ANY : MediaType::VIDEO;
|
IsRtcpPacket(packet_buffer) ? MediaType::ANY : MediaType::VIDEO;
|
||||||
result = call->Receiver()->DeliverPacket(media_type,
|
result = call->Receiver()->DeliverPacket(media_type,
|
||||||
@ -573,11 +574,19 @@ class RtpReplayer final {
|
|||||||
it->second);
|
it->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string replay_config_path_;
|
||||||
|
const std::string rtp_dump_path_;
|
||||||
|
std::unique_ptr<FieldTrialsView> field_trials_;
|
||||||
|
std::unique_ptr<TaskQueueFactory> task_queue_factory_;
|
||||||
|
std::unique_ptr<rtc::TaskQueue> worker_thread_;
|
||||||
}; // class RtpReplayer
|
}; // class RtpReplayer
|
||||||
|
|
||||||
void RtpReplay() {
|
void RtpReplay() {
|
||||||
RtpReplayer::Replay(absl::GetFlag(FLAGS_config_file),
|
RtpReplayer replayer(absl::GetFlag(FLAGS_config_file),
|
||||||
absl::GetFlag(FLAGS_input_file));
|
absl::GetFlag(FLAGS_input_file),
|
||||||
|
std::make_unique<FieldTrialBasedConfig>());
|
||||||
|
replayer.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
Reference in New Issue
Block a user