blob: 405948d8e09549037ed7a69c33fece00e9eabaf8 [file] [log] [blame]
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +00001/*
2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <stdio.h>
12
Benjamin Wright90ab76d2018-08-23 11:33:29 -070013#include <fstream>
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +000014#include <map>
kwiberg27f982b2016-03-01 11:52:33 -080015#include <memory>
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +000016
Mirko Bonadei2ab97f62019-07-18 13:44:12 +020017#include "absl/flags/flag.h"
18#include "absl/flags/parse.h"
philipel525891a2022-10-03 14:54:09 +020019#include "api/field_trials.h"
Per Kbdf30c82023-01-18 09:49:28 +010020#include "api/media_types.h"
Danil Chapovalov83bbe912019-08-07 12:24:53 +020021#include "api/rtc_event_log/rtc_event_log.h"
Sergey Silkin626f7ff2019-09-12 10:51:19 +020022#include "api/task_queue/default_task_queue_factory.h"
Danil Chapovalov99b71df2018-10-26 15:57:48 +020023#include "api/test/video/function_video_decoder_factory.h"
Ilya Nikolaevskiy33aaa352020-01-21 13:38:19 +010024#include "api/transport/field_trial_based_config.h"
Per Kbdf30c82023-01-18 09:49:28 +010025#include "api/units/timestamp.h"
Philipp Hanckefae4fb12021-01-26 18:05:56 +010026#include "api/video/video_codec_type.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020027#include "api/video_codecs/video_decoder.h"
28#include "call/call.h"
29#include "common_video/libyuv/include/webrtc_libyuv.h"
Steve Anton10542f22019-01-11 09:11:00 -080030#include "media/engine/internal_decoder_factory.h"
Per Kbdf30c82023-01-18 09:49:28 +010031#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
Danil Chapovalov76a35d92021-06-08 12:30:46 +020032#include "modules/rtp_rtcp/source/rtp_packet.h"
Per Kbdf30c82023-01-18 09:49:28 +010033#include "modules/rtp_rtcp/source/rtp_packet_received.h"
philipel6cbb4682022-09-30 17:23:04 +020034#include "modules/rtp_rtcp/source/rtp_util.h"
Philipp Hanckefae4fb12021-01-26 18:05:56 +010035#include "modules/video_coding/utility/ivf_file_writer.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020036#include "rtc_base/checks.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020037#include "rtc_base/string_to_number.h"
Sam Zackrissonb45bdb52018-10-02 16:25:59 +020038#include "rtc_base/strings/json.h"
Steve Anton10542f22019-01-11 09:11:00 -080039#include "rtc_base/time_utils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020040#include "system_wrappers/include/clock.h"
41#include "system_wrappers/include/sleep.h"
Benjamin Wright8efafdf2019-01-11 10:48:42 -080042#include "test/call_config_utils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020043#include "test/call_test.h"
44#include "test/encoder_settings.h"
45#include "test/fake_decoder.h"
46#include "test/gtest.h"
47#include "test/null_transport.h"
48#include "test/rtp_file_reader.h"
49#include "test/run_loop.h"
50#include "test/run_test.h"
Sebastian Janssonf1f363f2018-08-13 14:24:58 +020051#include "test/test_video_capturer.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020052#include "test/testsupport/frame_writer.h"
philipel4426c472022-10-03 17:28:55 +020053#include "test/time_controller/simulated_time_controller.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020054#include "test/video_renderer.h"
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +000055
Mirko Bonadei2ab97f62019-07-18 13:44:12 +020056// Flag for payload type.
57ABSL_FLAG(int,
58 media_payload_type,
59 webrtc::test::CallTest::kPayloadTypeVP8,
60 "Media payload type");
61
62// Flag for RED payload type.
63ABSL_FLAG(int,
64 red_payload_type,
65 webrtc::test::CallTest::kRedPayloadType,
66 "RED payload type");
67
68// Flag for ULPFEC payload type.
69ABSL_FLAG(int,
70 ulpfec_payload_type,
71 webrtc::test::CallTest::kUlpfecPayloadType,
72 "ULPFEC payload type");
73
Philipp Hancke1c951ec2022-05-25 10:44:22 +020074// Flag for FLEXFEC payload type.
75ABSL_FLAG(int,
76 flexfec_payload_type,
77 webrtc::test::CallTest::kFlexfecPayloadType,
78 "FLEXFEC payload type");
79
Mirko Bonadei2ab97f62019-07-18 13:44:12 +020080ABSL_FLAG(int,
81 media_payload_type_rtx,
82 webrtc::test::CallTest::kSendRtxPayloadType,
83 "Media over RTX payload type");
84
85ABSL_FLAG(int,
86 red_payload_type_rtx,
87 webrtc::test::CallTest::kRtxRedPayloadType,
88 "RED over RTX payload type");
89
Philipp Hanckec060ce42021-03-15 13:15:59 +010090// Flag for SSRC and RTX SSRC.
91ABSL_FLAG(uint32_t,
92 ssrc,
93 webrtc::test::CallTest::kVideoSendSsrcs[0],
94 "Incoming SSRC");
95ABSL_FLAG(uint32_t,
96 ssrc_rtx,
97 webrtc::test::CallTest::kSendRtxSsrcs[0],
98 "Incoming RTX SSRC");
Mirko Bonadei2ab97f62019-07-18 13:44:12 +020099
Philipp Hancke1c951ec2022-05-25 10:44:22 +0200100ABSL_FLAG(uint32_t,
101 ssrc_flexfec,
102 webrtc::test::CallTest::kFlexfecSendSsrc,
103 "Incoming FLEXFEC SSRC");
104
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200105// Flag for abs-send-time id.
106ABSL_FLAG(int, abs_send_time_id, -1, "RTP extension ID for abs-send-time");
107
108// Flag for transmission-offset id.
109ABSL_FLAG(int,
110 transmission_offset_id,
111 -1,
112 "RTP extension ID for transmission-offset");
113
114// Flag for rtpdump input file.
115ABSL_FLAG(std::string, input_file, "", "input file");
116
117ABSL_FLAG(std::string, config_file, "", "config file");
118
119// Flag for raw output files.
120ABSL_FLAG(std::string,
121 out_base,
122 "",
123 "Basename (excluding .jpg) for raw output");
124
125ABSL_FLAG(std::string,
126 decoder_bitstream_filename,
127 "",
128 "Decoder bitstream output file");
129
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100130ABSL_FLAG(std::string, decoder_ivf_filename, "", "Decoder ivf output file");
131
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200132// Flag for video codec.
133ABSL_FLAG(std::string, codec, "VP8", "Video codec");
134
Philipp Hanckef3a687a2021-03-15 12:04:39 +0100135// Flags for rtp start and stop timestamp.
136ABSL_FLAG(uint32_t,
137 start_timestamp,
138 0,
139 "RTP start timestamp, packets with smaller timestamp will be ignored "
140 "(no wraparound)");
141ABSL_FLAG(uint32_t,
142 stop_timestamp,
143 4294967295,
144 "RTP stop timestamp, packets with larger timestamp will be ignored "
145 "(no wraparound)");
146
Jonathan Lennox03df29c2021-07-13 12:41:31 -0400147// Flags for render window width and height
148ABSL_FLAG(uint32_t, render_width, 640, "Width of render window");
149ABSL_FLAG(uint32_t, render_height, 480, "Height of render window");
150
philipel525891a2022-10-03 14:54:09 +0200151ABSL_FLAG(
152 std::string,
153 force_fieldtrials,
154 "",
155 "Field trials control experimental feature code which can be forced. "
156 "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enabled/"
157 " will assign the group Enable to field trial WebRTC-FooFeature. Multiple "
158 "trials are separated by \"/\"");
159
philipel4426c472022-10-03 17:28:55 +0200160ABSL_FLAG(bool, simulated_time, false, "Run in simulated time");
161
philipelf16fc052022-10-04 08:09:45 +0200162ABSL_FLAG(bool, disable_preview, false, "Disable decoded video preview.");
163
philipelc11b0fe2022-10-04 08:10:20 +0200164ABSL_FLAG(bool, disable_decoding, false, "Disable video decoding.");
165
philipel720bc4d2022-10-18 08:51:17 +0200166ABSL_FLAG(int,
167 extend_run_time_duration,
168 0,
169 "Extends the run time of the receiving client after the last RTP "
170 "packet has been delivered. Typically useful to let the last few "
171 "frames be decoded and rendered. Duration given in seconds.");
172
oprypin6e09d872017-08-31 03:21:39 -0700173namespace {
philipel05f48222022-10-03 09:19:55 +0200174bool ValidatePayloadType(int32_t payload_type) {
oprypin6e09d872017-08-31 03:21:39 -0700175 return payload_type > 0 && payload_type <= 127;
176}
177
philipel05f48222022-10-03 09:19:55 +0200178bool ValidateOptionalPayloadType(int32_t payload_type) {
oprypin6e09d872017-08-31 03:21:39 -0700179 return payload_type == -1 || ValidatePayloadType(payload_type);
180}
181
philipel05f48222022-10-03 09:19:55 +0200182bool ValidateRtpHeaderExtensionId(int32_t extension_id) {
oprypin6e09d872017-08-31 03:21:39 -0700183 return extension_id >= -1 && extension_id < 15;
184}
185
186bool ValidateInputFilenameNotEmpty(const std::string& string) {
187 return !string.empty();
188}
oprypin6e09d872017-08-31 03:21:39 -0700189} // namespace
190
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000191namespace webrtc {
philipel05f48222022-10-03 09:19:55 +0200192namespace {
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000193
philipel05f48222022-10-03 09:19:55 +0200194const uint32_t kReceiverLocalSsrc = 0x123456;
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000195
philipelf16fc052022-10-04 08:09:45 +0200196class NullRenderer : public rtc::VideoSinkInterface<VideoFrame> {
197 public:
198 void OnFrame(const VideoFrame& frame) override {}
199};
200
nisse7ade7b32016-03-23 04:48:10 -0700201class FileRenderPassthrough : public rtc::VideoSinkInterface<VideoFrame> {
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000202 public:
nisse7ade7b32016-03-23 04:48:10 -0700203 FileRenderPassthrough(const std::string& basename,
204 rtc::VideoSinkInterface<VideoFrame>* renderer)
philipel99b63452017-08-25 07:24:21 -0700205 : basename_(basename), renderer_(renderer), file_(nullptr), count_(0) {}
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000206
Mirko Bonadeife055c12019-01-29 22:53:28 +0100207 ~FileRenderPassthrough() override {
Peter Boström74f6e9e2016-04-04 17:56:10 +0200208 if (file_)
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000209 fclose(file_);
210 }
211
212 private:
nisseeb83a1a2016-03-21 01:27:56 -0700213 void OnFrame(const VideoFrame& video_frame) override {
Peter Boström74f6e9e2016-04-04 17:56:10 +0200214 if (renderer_)
nisseeb83a1a2016-03-21 01:27:56 -0700215 renderer_->OnFrame(video_frame);
philipel99b63452017-08-25 07:24:21 -0700216
pbosbb36fdf2015-07-09 07:48:14 -0700217 if (basename_.empty())
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000218 return;
philipel99b63452017-08-25 07:24:21 -0700219
220 std::stringstream filename;
221 filename << basename_ << count_++ << "_" << video_frame.timestamp()
222 << ".jpg";
223
philipel99b63452017-08-25 07:24:21 -0700224 test::JpegFrameWriter frame_writer(filename.str());
225 RTC_CHECK(frame_writer.WriteFrame(video_frame, 100));
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000226 }
227
228 const std::string basename_;
nisse7ade7b32016-03-23 04:48:10 -0700229 rtc::VideoSinkInterface<VideoFrame>* const renderer_;
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000230 FILE* file_;
231 size_t count_;
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000232};
233
Niels Möllerf88a22c2018-06-19 17:05:03 +0200234class DecoderBitstreamFileWriter : public test::FakeDecoder {
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000235 public:
236 explicit DecoderBitstreamFileWriter(const char* filename)
237 : file_(fopen(filename, "wb")) {
Peter Boström74f6e9e2016-04-04 17:56:10 +0200238 RTC_DCHECK(file_);
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000239 }
Mirko Bonadeife055c12019-01-29 22:53:28 +0100240 ~DecoderBitstreamFileWriter() override { fclose(file_); }
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000241
Niels Möllerf88a22c2018-06-19 17:05:03 +0200242 int32_t Decode(const EncodedImage& encoded_frame,
Benjamin Wright8efafdf2019-01-11 10:48:42 -0800243 bool /* missing_frames */,
Benjamin Wright8efafdf2019-01-11 10:48:42 -0800244 int64_t /* render_time_ms */) override {
Niels Möller77536a22019-01-15 08:50:01 +0100245 if (fwrite(encoded_frame.data(), 1, encoded_frame.size(), file_) <
246 encoded_frame.size()) {
Niels Möllerf88a22c2018-06-19 17:05:03 +0200247 RTC_LOG_ERR(LS_ERROR) << "fwrite of encoded frame failed.";
248 return WEBRTC_VIDEO_CODEC_ERROR;
249 }
250 return WEBRTC_VIDEO_CODEC_OK;
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000251 }
252
253 private:
254 FILE* file_;
255};
256
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100257class DecoderIvfFileWriter : public test::FakeDecoder {
258 public:
259 explicit DecoderIvfFileWriter(const char* filename, const std::string& codec)
260 : file_writer_(
261 IvfFileWriter::Wrap(FileWrapper::OpenWriteOnly(filename), 0)) {
262 RTC_DCHECK(file_writer_.get());
263 if (codec == "VP8") {
264 video_codec_type_ = VideoCodecType::kVideoCodecVP8;
265 } else if (codec == "VP9") {
266 video_codec_type_ = VideoCodecType::kVideoCodecVP9;
267 } else if (codec == "H264") {
268 video_codec_type_ = VideoCodecType::kVideoCodecH264;
Philipp Hanckede7fcff2022-05-12 18:13:16 +0200269 } else if (codec == "AV1") {
270 video_codec_type_ = VideoCodecType::kVideoCodecAV1;
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100271 } else {
272 RTC_LOG(LS_ERROR) << "Unsupported video codec " << codec;
Artem Titovd3251962021-11-15 16:57:07 +0100273 RTC_DCHECK_NOTREACHED();
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100274 }
275 }
276 ~DecoderIvfFileWriter() override { file_writer_->Close(); }
277
278 int32_t Decode(const EncodedImage& encoded_frame,
279 bool /* missing_frames */,
280 int64_t render_time_ms) override {
281 if (!file_writer_->WriteFrame(encoded_frame, video_codec_type_)) {
282 return WEBRTC_VIDEO_CODEC_ERROR;
283 }
284 return WEBRTC_VIDEO_CODEC_OK;
285 }
286
287 private:
288 std::unique_ptr<IvfFileWriter> file_writer_;
289 VideoCodecType video_codec_type_;
290};
291
philipel5f551372022-10-03 11:56:58 +0200292// Holds all the shared memory structures required for a receive stream. This
293// structure is used to prevent members being deallocated before the replay
294// has been finished.
295struct StreamState {
296 test::NullTransport transport;
297 std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
298 std::vector<VideoReceiveStreamInterface*> receive_streams;
299 std::vector<FlexfecReceiveStream*> flexfec_streams;
300 std::unique_ptr<VideoDecoderFactory> decoder_factory;
301};
302
303// Loads multiple configurations from the provided configuration file.
304std::unique_ptr<StreamState> ConfigureFromFile(const std::string& config_path,
305 Call* call) {
306 auto stream_state = std::make_unique<StreamState>();
307 // Parse the configuration file.
308 std::ifstream config_file(config_path);
309 std::stringstream raw_json_buffer;
310 raw_json_buffer << config_file.rdbuf();
311 std::string raw_json = raw_json_buffer.str();
312 Json::CharReaderBuilder builder;
313 Json::Value json_configs;
314 std::string error_message;
315 std::unique_ptr<Json::CharReader> json_reader(builder.newCharReader());
316 if (!json_reader->parse(raw_json.data(), raw_json.data() + raw_json.size(),
317 &json_configs, &error_message)) {
318 fprintf(stderr, "Error parsing JSON config\n");
319 fprintf(stderr, "%s\n", error_message.c_str());
320 return nullptr;
321 }
322
philipelc11b0fe2022-10-04 08:10:20 +0200323 if (absl::GetFlag(FLAGS_disable_decoding)) {
324 stream_state->decoder_factory =
325 std::make_unique<test::FunctionVideoDecoderFactory>(
326 []() { return std::make_unique<test::FakeDecoder>(); });
327 } else {
328 stream_state->decoder_factory = std::make_unique<InternalDecoderFactory>();
329 }
philipel5f551372022-10-03 11:56:58 +0200330 size_t config_count = 0;
331 for (const auto& json : json_configs) {
332 // Create the configuration and parse the JSON into the config.
333 auto receive_config =
334 ParseVideoReceiveStreamJsonConfig(&(stream_state->transport), json);
335 // Instantiate the underlying decoder.
336 for (auto& decoder : receive_config.decoders) {
337 decoder = test::CreateMatchingDecoder(decoder.payload_type,
338 decoder.video_format.name);
339 }
340 // Create a window for this config.
341 std::stringstream window_title;
342 window_title << "Playback Video (" << config_count++ << ")";
philipelf16fc052022-10-04 08:09:45 +0200343 if (absl::GetFlag(FLAGS_disable_preview)) {
344 stream_state->sinks.emplace_back(std::make_unique<NullRenderer>());
345 } else {
346 stream_state->sinks.emplace_back(test::VideoRenderer::Create(
347 window_title.str().c_str(), absl::GetFlag(FLAGS_render_width),
348 absl::GetFlag(FLAGS_render_height)));
349 }
philipel5f551372022-10-03 11:56:58 +0200350 // Create a receive stream for this config.
351 receive_config.renderer = stream_state->sinks.back().get();
352 receive_config.decoder_factory = stream_state->decoder_factory.get();
353 stream_state->receive_streams.emplace_back(
354 call->CreateVideoReceiveStream(std::move(receive_config)));
355 }
356 return stream_state;
357}
358
359// Loads the base configuration from flags passed in on the commandline.
360std::unique_ptr<StreamState> ConfigureFromFlags(
361 const std::string& rtp_dump_path,
362 Call* call) {
363 auto stream_state = std::make_unique<StreamState>();
364 // Create the video renderers. We must add both to the stream state to keep
365 // them from deallocating.
366 std::stringstream window_title;
367 window_title << "Playback Video (" << rtp_dump_path << ")";
philipelf16fc052022-10-04 08:09:45 +0200368 std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>> playback_video;
369 if (absl::GetFlag(FLAGS_disable_preview)) {
370 playback_video = std::make_unique<NullRenderer>();
371 } else {
372 playback_video.reset(test::VideoRenderer::Create(
373 window_title.str().c_str(), absl::GetFlag(FLAGS_render_width),
374 absl::GetFlag(FLAGS_render_height)));
375 }
philipel5f551372022-10-03 11:56:58 +0200376 auto file_passthrough = std::make_unique<FileRenderPassthrough>(
377 absl::GetFlag(FLAGS_out_base), playback_video.get());
378 stream_state->sinks.push_back(std::move(playback_video));
379 stream_state->sinks.push_back(std::move(file_passthrough));
380 // Setup the configuration from the flags.
381 VideoReceiveStreamInterface::Config receive_config(
382 &(stream_state->transport));
383 receive_config.rtp.remote_ssrc = absl::GetFlag(FLAGS_ssrc);
384 receive_config.rtp.local_ssrc = kReceiverLocalSsrc;
385 receive_config.rtp.rtx_ssrc = absl::GetFlag(FLAGS_ssrc_rtx);
386 receive_config.rtp.rtx_associated_payload_types[absl::GetFlag(
387 FLAGS_media_payload_type_rtx)] = absl::GetFlag(FLAGS_media_payload_type);
388 receive_config.rtp
389 .rtx_associated_payload_types[absl::GetFlag(FLAGS_red_payload_type_rtx)] =
390 absl::GetFlag(FLAGS_red_payload_type);
391 receive_config.rtp.ulpfec_payload_type =
392 absl::GetFlag(FLAGS_ulpfec_payload_type);
393 receive_config.rtp.red_payload_type = absl::GetFlag(FLAGS_red_payload_type);
394 receive_config.rtp.nack.rtp_history_ms = 1000;
395
396 if (absl::GetFlag(FLAGS_flexfec_payload_type) != -1) {
397 receive_config.rtp.protected_by_flexfec = true;
philipelfaaa57b2022-10-03 14:53:20 +0200398 FlexfecReceiveStream::Config flexfec_config(&(stream_state->transport));
philipel5f551372022-10-03 11:56:58 +0200399 flexfec_config.payload_type = absl::GetFlag(FLAGS_flexfec_payload_type);
400 flexfec_config.protected_media_ssrcs.push_back(absl::GetFlag(FLAGS_ssrc));
401 flexfec_config.rtp.remote_ssrc = absl::GetFlag(FLAGS_ssrc_flexfec);
402 FlexfecReceiveStream* flexfec_stream =
403 call->CreateFlexfecReceiveStream(flexfec_config);
404 receive_config.rtp.packet_sink_ = flexfec_stream;
405 stream_state->flexfec_streams.push_back(flexfec_stream);
406 }
407
philipel5f551372022-10-03 11:56:58 +0200408 receive_config.renderer = stream_state->sinks.back().get();
409
410 // Setup the receiving stream
411 VideoReceiveStreamInterface::Decoder decoder;
412 decoder = test::CreateMatchingDecoder(absl::GetFlag(FLAGS_media_payload_type),
413 absl::GetFlag(FLAGS_codec));
414 if (!absl::GetFlag(FLAGS_decoder_bitstream_filename).empty()) {
415 // Replace decoder with file writer if we're writing the bitstream to a
416 // file instead.
417 stream_state->decoder_factory =
418 std::make_unique<test::FunctionVideoDecoderFactory>([]() {
419 return std::make_unique<DecoderBitstreamFileWriter>(
420 absl::GetFlag(FLAGS_decoder_bitstream_filename).c_str());
421 });
422 } else if (!absl::GetFlag(FLAGS_decoder_ivf_filename).empty()) {
423 // Replace decoder with file writer if we're writing the ivf to a
424 // file instead.
425 stream_state->decoder_factory =
426 std::make_unique<test::FunctionVideoDecoderFactory>([]() {
427 return std::make_unique<DecoderIvfFileWriter>(
428 absl::GetFlag(FLAGS_decoder_ivf_filename).c_str(),
429 absl::GetFlag(FLAGS_codec));
430 });
philipelc11b0fe2022-10-04 08:10:20 +0200431 } else if (absl::GetFlag(FLAGS_disable_decoding)) {
432 stream_state->decoder_factory =
433 std::make_unique<test::FunctionVideoDecoderFactory>(
434 []() { return std::make_unique<test::FakeDecoder>(); });
philipel5f551372022-10-03 11:56:58 +0200435 } else {
436 stream_state->decoder_factory = std::make_unique<InternalDecoderFactory>();
437 }
438 receive_config.decoder_factory = stream_state->decoder_factory.get();
439 receive_config.decoders.push_back(decoder);
440
441 stream_state->receive_streams.emplace_back(
442 call->CreateVideoReceiveStream(std::move(receive_config)));
443 return stream_state;
444}
445
446std::unique_ptr<test::RtpFileReader> CreateRtpReader(
447 const std::string& rtp_dump_path) {
448 std::unique_ptr<test::RtpFileReader> rtp_reader(test::RtpFileReader::Create(
449 test::RtpFileReader::kRtpDump, rtp_dump_path));
450 if (!rtp_reader) {
451 rtp_reader.reset(
452 test::RtpFileReader::Create(test::RtpFileReader::kPcap, rtp_dump_path));
453 if (!rtp_reader) {
454 fprintf(stderr,
455 "Couldn't open input file as either a rtpdump or .pcap. Note "
456 "that .pcapng is not supported.\nTrying to interpret the file as "
457 "length/packet interleaved.\n");
458 rtp_reader.reset(test::RtpFileReader::Create(
459 test::RtpFileReader::kLengthPacketInterleaved, rtp_dump_path));
460 if (!rtp_reader) {
461 fprintf(stderr,
462 "Unable to open input file with any supported format\n");
463 return nullptr;
464 }
465 }
466 }
467 return rtp_reader;
468}
469
470// The RtpReplayer is responsible for parsing the configuration provided by
471// the user, setting up the windows, receive streams and decoders and then
472// replaying the provided RTP dump.
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700473class RtpReplayer final {
474 public:
philipel5f551372022-10-03 11:56:58 +0200475 RtpReplayer(absl::string_view replay_config_path,
476 absl::string_view rtp_dump_path,
philipel4426c472022-10-03 17:28:55 +0200477 std::unique_ptr<FieldTrialsView> field_trials,
478 bool simulated_time)
philipel5f551372022-10-03 11:56:58 +0200479 : replay_config_path_(replay_config_path),
480 rtp_dump_path_(rtp_dump_path),
481 field_trials_(std::move(field_trials)),
philipel2671e242022-10-03 13:59:07 +0200482 rtp_reader_(CreateRtpReader(rtp_dump_path_)) {
philipel4426c472022-10-03 17:28:55 +0200483 TaskQueueFactory* task_queue_factory;
484 if (simulated_time) {
485 time_sim_ = std::make_unique<GlobalSimulatedTimeController>(
486 Timestamp::Millis(1 << 30));
487 task_queue_factory = time_sim_->GetTaskQueueFactory();
488 } else {
489 task_queue_factory_ = CreateDefaultTaskQueueFactory(field_trials_.get()),
490 task_queue_factory = task_queue_factory_.get();
491 }
492 worker_thread_ =
493 std::make_unique<rtc::TaskQueue>(task_queue_factory->CreateTaskQueue(
494 "worker_thread", TaskQueueFactory::Priority::NORMAL));
philipel2671e242022-10-03 13:59:07 +0200495 rtc::Event event;
496 worker_thread_->PostTask([&]() {
497 Call::Config call_config(&event_log_);
498 call_config.trials = field_trials_.get();
philipel4426c472022-10-03 17:28:55 +0200499 call_config.task_queue_factory = task_queue_factory;
philipel2671e242022-10-03 13:59:07 +0200500 call_.reset(Call::Create(call_config));
501
502 // Creation of the streams must happen inside a task queue because it is
503 // resued as a worker thread.
504 if (replay_config_path_.empty()) {
505 stream_state_ = ConfigureFromFlags(rtp_dump_path_, call_.get());
506 } else {
507 stream_state_ = ConfigureFromFile(replay_config_path_, call_.get());
508 }
509 event.Set();
510 });
511 event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
512
513 RTC_CHECK(stream_state_);
514 RTC_CHECK(rtp_reader_);
515 }
516
517 ~RtpReplayer() {
518 // Destruction of streams and the call must happen on the same thread as
519 // their creation.
520 rtc::Event event;
521 worker_thread_->PostTask([&]() {
522 for (const auto& receive_stream : stream_state_->receive_streams) {
523 call_->DestroyVideoReceiveStream(receive_stream);
524 }
525 for (const auto& flexfec_stream : stream_state_->flexfec_streams) {
526 call_->DestroyFlexfecReceiveStream(flexfec_stream);
527 }
528 call_.reset();
529 event.Set();
530 });
531 event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
532 }
philipel5f551372022-10-03 11:56:58 +0200533
534 void Run() {
philipel2671e242022-10-03 13:59:07 +0200535 rtc::Event event;
philipel5f551372022-10-03 11:56:58 +0200536 worker_thread_->PostTask([&]() {
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100537 // Start replaying the provided stream now that it has been configured.
538 // VideoReceiveStreams must be started on the same thread as they were
539 // created on.
philipel2671e242022-10-03 13:59:07 +0200540 for (const auto& receive_stream : stream_state_->receive_streams) {
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100541 receive_stream->Start();
542 }
philipel2671e242022-10-03 13:59:07 +0200543 event.Set();
Danil Chapovalov5286dcf2022-07-18 17:04:56 +0200544 });
philipel2671e242022-10-03 13:59:07 +0200545 event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100546
philipel2671e242022-10-03 13:59:07 +0200547 ReplayPackets();
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000548 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000549
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700550 private:
philipel2671e242022-10-03 13:59:07 +0200551 void ReplayPackets() {
Per Kbdf30c82023-01-18 09:49:28 +0100552 enum class Result { kOk, kUnknownSsrc, kParsingFailed };
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700553 int64_t replay_start_ms = -1;
554 int num_packets = 0;
555 std::map<uint32_t, int> unknown_packets;
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100556 rtc::Event event(/*manual_reset=*/false, /*initially_signalled=*/false);
Philipp Hanckef3a687a2021-03-15 12:04:39 +0100557 uint32_t start_timestamp = absl::GetFlag(FLAGS_start_timestamp);
558 uint32_t stop_timestamp = absl::GetFlag(FLAGS_stop_timestamp);
Per Kbdf30c82023-01-18 09:49:28 +0100559
560 RtpHeaderExtensionMap extensions;
561 if (absl::GetFlag(FLAGS_transmission_offset_id) != -1) {
562 extensions.RegisterByUri(absl::GetFlag(FLAGS_transmission_offset_id),
563 RtpExtension::kTimestampOffsetUri);
564 }
565 if (absl::GetFlag(FLAGS_abs_send_time_id) != -1) {
566 extensions.RegisterByUri(absl::GetFlag(FLAGS_abs_send_time_id),
567 RtpExtension::kAbsSendTimeUri);
568 }
569
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700570 while (true) {
philipel4426c472022-10-03 17:28:55 +0200571 int64_t now_ms = CurrentTimeMs();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700572 if (replay_start_ms == -1) {
573 replay_start_ms = now_ms;
574 }
philipel02f03962018-01-11 17:28:35 +0100575
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700576 test::RtpPacket packet;
philipel2671e242022-10-03 13:59:07 +0200577 if (!rtp_reader_->NextPacket(&packet)) {
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000578 break;
579 }
philipel0c298132022-12-30 11:45:22 +0100580 rtc::CopyOnWriteBuffer packet_buffer(
581 packet.original_length > 0 ? packet.original_length : packet.length);
582 memcpy(packet_buffer.MutableData(), packet.data, packet.length);
583 if (packet.length < packet.original_length) {
584 // Only the RTP header was recorded in the RTP dump, payload is not
585 // known and and padding length is not known, zero the payload and
586 // clear the padding bit.
587 memset(packet_buffer.MutableData() + packet.length, 0,
588 packet.original_length - packet.length);
589 packet_buffer.MutableData()[0] &= ~0x20;
590 }
Danil Chapovalov76a35d92021-06-08 12:30:46 +0200591 RtpPacket header;
592 header.Parse(packet_buffer);
593 if (header.Timestamp() < start_timestamp ||
594 header.Timestamp() > stop_timestamp) {
Philipp Hanckef3a687a2021-03-15 12:04:39 +0100595 continue;
596 }
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700597
598 int64_t deliver_in_ms = replay_start_ms + packet.time_ms - now_ms;
philipel4426c472022-10-03 17:28:55 +0200599 SleepOrAdvanceTime(deliver_in_ms);
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700600
601 ++num_packets;
Per Kbdf30c82023-01-18 09:49:28 +0100602
603 Result result = Result::kOk;
philipel5f551372022-10-03 11:56:58 +0200604 worker_thread_->PostTask([&]() {
Per Kbdf30c82023-01-18 09:49:28 +0100605 if (IsRtcpPacket(packet_buffer)) {
606 call_->Receiver()->DeliverRtcpPacket(std::move(packet_buffer));
607 }
608 RtpPacketReceived received_packet(&extensions,
609 Timestamp::Millis(CurrentTimeMs()));
610 if (!received_packet.Parse(std::move(packet_buffer))) {
611 result = Result::kParsingFailed;
612 return;
613 }
614 call_->Receiver()->DeliverRtpPacket(
615 MediaType::VIDEO, received_packet,
616 [&result](const RtpPacketReceived& parsed_packet) -> bool {
617 result = Result::kUnknownSsrc;
618 // No point in trying to demux again.
619 return false;
620 });
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100621 event.Set();
Danil Chapovalov5286dcf2022-07-18 17:04:56 +0200622 });
Markus Handell2cfc1af2022-08-19 08:16:48 +0000623 event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
philipel4426c472022-10-03 17:28:55 +0200624
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100625 switch (result) {
Per Kbdf30c82023-01-18 09:49:28 +0100626 case Result::kOk:
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700627 break;
Per Kbdf30c82023-01-18 09:49:28 +0100628 case Result::kUnknownSsrc: {
Danil Chapovalov76a35d92021-06-08 12:30:46 +0200629 if (unknown_packets[header.Ssrc()] == 0)
630 fprintf(stderr, "Unknown SSRC: %u!\n", header.Ssrc());
631 ++unknown_packets[header.Ssrc()];
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700632 break;
633 }
Per Kbdf30c82023-01-18 09:49:28 +0100634 case Result::kParsingFailed: {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700635 fprintf(stderr,
636 "Packet error, corrupt packets or incorrect setup?\n");
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700637 fprintf(stderr, "Packet len=%zu pt=%u seq=%u ts=%u ssrc=0x%8x\n",
Danil Chapovalov76a35d92021-06-08 12:30:46 +0200638 packet.length, header.PayloadType(), header.SequenceNumber(),
639 header.Timestamp(), header.Ssrc());
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700640 break;
641 }
philipp.hancke7b589602017-01-26 04:54:04 -0800642 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000643 }
philipel720bc4d2022-10-18 08:51:17 +0200644 // Note that even when `extend_run_time_duration` is zero
645 // `SleepOrAdvanceTime` should still be called in order to process the last
philipel4426c472022-10-03 17:28:55 +0200646 // delivered packet when running in simulated time.
philipel720bc4d2022-10-18 08:51:17 +0200647 SleepOrAdvanceTime(absl::GetFlag(FLAGS_extend_run_time_duration) * 1000);
philipel4426c472022-10-03 17:28:55 +0200648
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700649 fprintf(stderr, "num_packets: %d\n", num_packets);
650
651 for (std::map<uint32_t, int>::const_iterator it = unknown_packets.begin();
652 it != unknown_packets.end(); ++it) {
653 fprintf(stderr, "Packets for unknown ssrc '%u': %d\n", it->first,
654 it->second);
655 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000656 }
philipel5f551372022-10-03 11:56:58 +0200657
philipel4426c472022-10-03 17:28:55 +0200658 int64_t CurrentTimeMs() {
659 return time_sim_ ? time_sim_->GetClock()->TimeInMilliseconds()
660 : rtc::TimeMillis();
661 }
662
663 void SleepOrAdvanceTime(int64_t duration_ms) {
664 if (time_sim_) {
665 time_sim_->AdvanceTime(TimeDelta::Millis(duration_ms));
666 } else if (duration_ms > 0) {
667 SleepMs(duration_ms);
668 }
669 }
670
philipel5f551372022-10-03 11:56:58 +0200671 const std::string replay_config_path_;
672 const std::string rtp_dump_path_;
philipelfaaa57b2022-10-03 14:53:20 +0200673 RtcEventLogNull event_log_;
philipel5f551372022-10-03 11:56:58 +0200674 std::unique_ptr<FieldTrialsView> field_trials_;
philipel4426c472022-10-03 17:28:55 +0200675 std::unique_ptr<GlobalSimulatedTimeController> time_sim_;
philipel5f551372022-10-03 11:56:58 +0200676 std::unique_ptr<TaskQueueFactory> task_queue_factory_;
677 std::unique_ptr<rtc::TaskQueue> worker_thread_;
philipel2671e242022-10-03 13:59:07 +0200678 std::unique_ptr<Call> call_;
679 std::unique_ptr<test::RtpFileReader> rtp_reader_;
680 std::unique_ptr<StreamState> stream_state_;
681};
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000682
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700683void RtpReplay() {
philipel525891a2022-10-03 14:54:09 +0200684 RtpReplayer replayer(
685 absl::GetFlag(FLAGS_config_file), absl::GetFlag(FLAGS_input_file),
philipel4426c472022-10-03 17:28:55 +0200686 std::make_unique<FieldTrials>(absl::GetFlag(FLAGS_force_fieldtrials)),
687 absl::GetFlag(FLAGS_simulated_time));
philipel5f551372022-10-03 11:56:58 +0200688 replayer.Run();
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000689}
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700690
philipel05f48222022-10-03 09:19:55 +0200691} // namespace
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000692} // namespace webrtc
693
694int main(int argc, char* argv[]) {
695 ::testing::InitGoogleTest(&argc, argv);
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200696 absl::ParseCommandLine(argc, argv);
oprypin6e09d872017-08-31 03:21:39 -0700697
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200698 RTC_CHECK(ValidatePayloadType(absl::GetFlag(FLAGS_media_payload_type)));
699 RTC_CHECK(ValidatePayloadType(absl::GetFlag(FLAGS_media_payload_type_rtx)));
700 RTC_CHECK(ValidateOptionalPayloadType(absl::GetFlag(FLAGS_red_payload_type)));
philipel752968e2017-12-05 12:40:28 +0100701 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200702 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_red_payload_type_rtx)));
philipel752968e2017-12-05 12:40:28 +0100703 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200704 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_ulpfec_payload_type)));
Yves Gerey665174f2018-06-19 15:03:05 +0200705 RTC_CHECK(
Philipp Hancke1c951ec2022-05-25 10:44:22 +0200706 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_flexfec_payload_type)));
707 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200708 ValidateRtpHeaderExtensionId(absl::GetFlag(FLAGS_abs_send_time_id)));
709 RTC_CHECK(ValidateRtpHeaderExtensionId(
710 absl::GetFlag(FLAGS_transmission_offset_id)));
711 RTC_CHECK(ValidateInputFilenameNotEmpty(absl::GetFlag(FLAGS_input_file)));
philipel720bc4d2022-10-18 08:51:17 +0200712 RTC_CHECK_GE(absl::GetFlag(FLAGS_extend_run_time_duration), 0);
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000713
Philipp Hancke07b7dec2020-07-28 12:34:48 +0200714 rtc::ThreadManager::Instance()->WrapCurrentThread();
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000715 webrtc::test::RunTest(webrtc::RtpReplay);
716 return 0;
717}