blob: ff90a151a3d7846aa946efd7a246b7bb09d89eab [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"
Danil Chapovalov83bbe912019-08-07 12:24:53 +020020#include "api/rtc_event_log/rtc_event_log.h"
Sergey Silkin626f7ff2019-09-12 10:51:19 +020021#include "api/task_queue/default_task_queue_factory.h"
Danil Chapovalov99b71df2018-10-26 15:57:48 +020022#include "api/test/video/function_video_decoder_factory.h"
Ilya Nikolaevskiy33aaa352020-01-21 13:38:19 +010023#include "api/transport/field_trial_based_config.h"
Philipp Hanckefae4fb12021-01-26 18:05:56 +010024#include "api/video/video_codec_type.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025#include "api/video_codecs/video_decoder.h"
26#include "call/call.h"
27#include "common_video/libyuv/include/webrtc_libyuv.h"
Steve Anton10542f22019-01-11 09:11:00 -080028#include "media/engine/internal_decoder_factory.h"
Danil Chapovalov76a35d92021-06-08 12:30:46 +020029#include "modules/rtp_rtcp/source/rtp_packet.h"
philipel6cbb4682022-09-30 17:23:04 +020030#include "modules/rtp_rtcp/source/rtp_util.h"
Philipp Hanckefae4fb12021-01-26 18:05:56 +010031#include "modules/video_coding/utility/ivf_file_writer.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020032#include "rtc_base/checks.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020033#include "rtc_base/string_to_number.h"
Sam Zackrissonb45bdb52018-10-02 16:25:59 +020034#include "rtc_base/strings/json.h"
Steve Anton10542f22019-01-11 09:11:00 -080035#include "rtc_base/time_utils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020036#include "system_wrappers/include/clock.h"
37#include "system_wrappers/include/sleep.h"
Benjamin Wright8efafdf2019-01-11 10:48:42 -080038#include "test/call_config_utils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020039#include "test/call_test.h"
40#include "test/encoder_settings.h"
41#include "test/fake_decoder.h"
42#include "test/gtest.h"
43#include "test/null_transport.h"
44#include "test/rtp_file_reader.h"
45#include "test/run_loop.h"
46#include "test/run_test.h"
Sebastian Janssonf1f363f2018-08-13 14:24:58 +020047#include "test/test_video_capturer.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020048#include "test/testsupport/frame_writer.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020049#include "test/video_renderer.h"
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +000050
Mirko Bonadei2ab97f62019-07-18 13:44:12 +020051// Flag for payload type.
52ABSL_FLAG(int,
53 media_payload_type,
54 webrtc::test::CallTest::kPayloadTypeVP8,
55 "Media payload type");
56
57// Flag for RED payload type.
58ABSL_FLAG(int,
59 red_payload_type,
60 webrtc::test::CallTest::kRedPayloadType,
61 "RED payload type");
62
63// Flag for ULPFEC payload type.
64ABSL_FLAG(int,
65 ulpfec_payload_type,
66 webrtc::test::CallTest::kUlpfecPayloadType,
67 "ULPFEC payload type");
68
Philipp Hancke1c951ec2022-05-25 10:44:22 +020069// Flag for FLEXFEC payload type.
70ABSL_FLAG(int,
71 flexfec_payload_type,
72 webrtc::test::CallTest::kFlexfecPayloadType,
73 "FLEXFEC payload type");
74
Mirko Bonadei2ab97f62019-07-18 13:44:12 +020075ABSL_FLAG(int,
76 media_payload_type_rtx,
77 webrtc::test::CallTest::kSendRtxPayloadType,
78 "Media over RTX payload type");
79
80ABSL_FLAG(int,
81 red_payload_type_rtx,
82 webrtc::test::CallTest::kRtxRedPayloadType,
83 "RED over RTX payload type");
84
Philipp Hanckec060ce42021-03-15 13:15:59 +010085// Flag for SSRC and RTX SSRC.
86ABSL_FLAG(uint32_t,
87 ssrc,
88 webrtc::test::CallTest::kVideoSendSsrcs[0],
89 "Incoming SSRC");
90ABSL_FLAG(uint32_t,
91 ssrc_rtx,
92 webrtc::test::CallTest::kSendRtxSsrcs[0],
93 "Incoming RTX SSRC");
Mirko Bonadei2ab97f62019-07-18 13:44:12 +020094
Philipp Hancke1c951ec2022-05-25 10:44:22 +020095ABSL_FLAG(uint32_t,
96 ssrc_flexfec,
97 webrtc::test::CallTest::kFlexfecSendSsrc,
98 "Incoming FLEXFEC SSRC");
99
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200100// Flag for abs-send-time id.
101ABSL_FLAG(int, abs_send_time_id, -1, "RTP extension ID for abs-send-time");
102
103// Flag for transmission-offset id.
104ABSL_FLAG(int,
105 transmission_offset_id,
106 -1,
107 "RTP extension ID for transmission-offset");
108
109// Flag for rtpdump input file.
110ABSL_FLAG(std::string, input_file, "", "input file");
111
112ABSL_FLAG(std::string, config_file, "", "config file");
113
114// Flag for raw output files.
115ABSL_FLAG(std::string,
116 out_base,
117 "",
118 "Basename (excluding .jpg) for raw output");
119
120ABSL_FLAG(std::string,
121 decoder_bitstream_filename,
122 "",
123 "Decoder bitstream output file");
124
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100125ABSL_FLAG(std::string, decoder_ivf_filename, "", "Decoder ivf output file");
126
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200127// Flag for video codec.
128ABSL_FLAG(std::string, codec, "VP8", "Video codec");
129
Philipp Hanckef3a687a2021-03-15 12:04:39 +0100130// Flags for rtp start and stop timestamp.
131ABSL_FLAG(uint32_t,
132 start_timestamp,
133 0,
134 "RTP start timestamp, packets with smaller timestamp will be ignored "
135 "(no wraparound)");
136ABSL_FLAG(uint32_t,
137 stop_timestamp,
138 4294967295,
139 "RTP stop timestamp, packets with larger timestamp will be ignored "
140 "(no wraparound)");
141
Jonathan Lennox03df29c2021-07-13 12:41:31 -0400142// Flags for render window width and height
143ABSL_FLAG(uint32_t, render_width, 640, "Width of render window");
144ABSL_FLAG(uint32_t, render_height, 480, "Height of render window");
145
philipel525891a2022-10-03 14:54:09 +0200146ABSL_FLAG(
147 std::string,
148 force_fieldtrials,
149 "",
150 "Field trials control experimental feature code which can be forced. "
151 "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enabled/"
152 " will assign the group Enable to field trial WebRTC-FooFeature. Multiple "
153 "trials are separated by \"/\"");
154
oprypin6e09d872017-08-31 03:21:39 -0700155namespace {
philipel05f48222022-10-03 09:19:55 +0200156bool ValidatePayloadType(int32_t payload_type) {
oprypin6e09d872017-08-31 03:21:39 -0700157 return payload_type > 0 && payload_type <= 127;
158}
159
philipel05f48222022-10-03 09:19:55 +0200160bool ValidateOptionalPayloadType(int32_t payload_type) {
oprypin6e09d872017-08-31 03:21:39 -0700161 return payload_type == -1 || ValidatePayloadType(payload_type);
162}
163
philipel05f48222022-10-03 09:19:55 +0200164bool ValidateRtpHeaderExtensionId(int32_t extension_id) {
oprypin6e09d872017-08-31 03:21:39 -0700165 return extension_id >= -1 && extension_id < 15;
166}
167
168bool ValidateInputFilenameNotEmpty(const std::string& string) {
169 return !string.empty();
170}
oprypin6e09d872017-08-31 03:21:39 -0700171} // namespace
172
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000173namespace webrtc {
philipel05f48222022-10-03 09:19:55 +0200174namespace {
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000175
philipel05f48222022-10-03 09:19:55 +0200176const uint32_t kReceiverLocalSsrc = 0x123456;
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000177
nisse7ade7b32016-03-23 04:48:10 -0700178class FileRenderPassthrough : public rtc::VideoSinkInterface<VideoFrame> {
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000179 public:
nisse7ade7b32016-03-23 04:48:10 -0700180 FileRenderPassthrough(const std::string& basename,
181 rtc::VideoSinkInterface<VideoFrame>* renderer)
philipel99b63452017-08-25 07:24:21 -0700182 : basename_(basename), renderer_(renderer), file_(nullptr), count_(0) {}
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000183
Mirko Bonadeife055c12019-01-29 22:53:28 +0100184 ~FileRenderPassthrough() override {
Peter Boström74f6e9e2016-04-04 17:56:10 +0200185 if (file_)
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000186 fclose(file_);
187 }
188
189 private:
nisseeb83a1a2016-03-21 01:27:56 -0700190 void OnFrame(const VideoFrame& video_frame) override {
Peter Boström74f6e9e2016-04-04 17:56:10 +0200191 if (renderer_)
nisseeb83a1a2016-03-21 01:27:56 -0700192 renderer_->OnFrame(video_frame);
philipel99b63452017-08-25 07:24:21 -0700193
pbosbb36fdf2015-07-09 07:48:14 -0700194 if (basename_.empty())
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000195 return;
philipel99b63452017-08-25 07:24:21 -0700196
197 std::stringstream filename;
198 filename << basename_ << count_++ << "_" << video_frame.timestamp()
199 << ".jpg";
200
philipel99b63452017-08-25 07:24:21 -0700201 test::JpegFrameWriter frame_writer(filename.str());
202 RTC_CHECK(frame_writer.WriteFrame(video_frame, 100));
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000203 }
204
205 const std::string basename_;
nisse7ade7b32016-03-23 04:48:10 -0700206 rtc::VideoSinkInterface<VideoFrame>* const renderer_;
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000207 FILE* file_;
208 size_t count_;
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000209};
210
Niels Möllerf88a22c2018-06-19 17:05:03 +0200211class DecoderBitstreamFileWriter : public test::FakeDecoder {
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000212 public:
213 explicit DecoderBitstreamFileWriter(const char* filename)
214 : file_(fopen(filename, "wb")) {
Peter Boström74f6e9e2016-04-04 17:56:10 +0200215 RTC_DCHECK(file_);
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000216 }
Mirko Bonadeife055c12019-01-29 22:53:28 +0100217 ~DecoderBitstreamFileWriter() override { fclose(file_); }
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000218
Niels Möllerf88a22c2018-06-19 17:05:03 +0200219 int32_t Decode(const EncodedImage& encoded_frame,
Benjamin Wright8efafdf2019-01-11 10:48:42 -0800220 bool /* missing_frames */,
Benjamin Wright8efafdf2019-01-11 10:48:42 -0800221 int64_t /* render_time_ms */) override {
Niels Möller77536a22019-01-15 08:50:01 +0100222 if (fwrite(encoded_frame.data(), 1, encoded_frame.size(), file_) <
223 encoded_frame.size()) {
Niels Möllerf88a22c2018-06-19 17:05:03 +0200224 RTC_LOG_ERR(LS_ERROR) << "fwrite of encoded frame failed.";
225 return WEBRTC_VIDEO_CODEC_ERROR;
226 }
227 return WEBRTC_VIDEO_CODEC_OK;
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000228 }
229
230 private:
231 FILE* file_;
232};
233
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100234class DecoderIvfFileWriter : public test::FakeDecoder {
235 public:
236 explicit DecoderIvfFileWriter(const char* filename, const std::string& codec)
237 : file_writer_(
238 IvfFileWriter::Wrap(FileWrapper::OpenWriteOnly(filename), 0)) {
239 RTC_DCHECK(file_writer_.get());
240 if (codec == "VP8") {
241 video_codec_type_ = VideoCodecType::kVideoCodecVP8;
242 } else if (codec == "VP9") {
243 video_codec_type_ = VideoCodecType::kVideoCodecVP9;
244 } else if (codec == "H264") {
245 video_codec_type_ = VideoCodecType::kVideoCodecH264;
Philipp Hanckede7fcff2022-05-12 18:13:16 +0200246 } else if (codec == "AV1") {
247 video_codec_type_ = VideoCodecType::kVideoCodecAV1;
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100248 } else {
249 RTC_LOG(LS_ERROR) << "Unsupported video codec " << codec;
Artem Titovd3251962021-11-15 16:57:07 +0100250 RTC_DCHECK_NOTREACHED();
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100251 }
252 }
253 ~DecoderIvfFileWriter() override { file_writer_->Close(); }
254
255 int32_t Decode(const EncodedImage& encoded_frame,
256 bool /* missing_frames */,
257 int64_t render_time_ms) override {
258 if (!file_writer_->WriteFrame(encoded_frame, video_codec_type_)) {
259 return WEBRTC_VIDEO_CODEC_ERROR;
260 }
261 return WEBRTC_VIDEO_CODEC_OK;
262 }
263
264 private:
265 std::unique_ptr<IvfFileWriter> file_writer_;
266 VideoCodecType video_codec_type_;
267};
268
philipel5f551372022-10-03 11:56:58 +0200269// Holds all the shared memory structures required for a receive stream. This
270// structure is used to prevent members being deallocated before the replay
271// has been finished.
272struct StreamState {
273 test::NullTransport transport;
274 std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
275 std::vector<VideoReceiveStreamInterface*> receive_streams;
276 std::vector<FlexfecReceiveStream*> flexfec_streams;
277 std::unique_ptr<VideoDecoderFactory> decoder_factory;
278};
279
280// Loads multiple configurations from the provided configuration file.
281std::unique_ptr<StreamState> ConfigureFromFile(const std::string& config_path,
282 Call* call) {
283 auto stream_state = std::make_unique<StreamState>();
284 // Parse the configuration file.
285 std::ifstream config_file(config_path);
286 std::stringstream raw_json_buffer;
287 raw_json_buffer << config_file.rdbuf();
288 std::string raw_json = raw_json_buffer.str();
289 Json::CharReaderBuilder builder;
290 Json::Value json_configs;
291 std::string error_message;
292 std::unique_ptr<Json::CharReader> json_reader(builder.newCharReader());
293 if (!json_reader->parse(raw_json.data(), raw_json.data() + raw_json.size(),
294 &json_configs, &error_message)) {
295 fprintf(stderr, "Error parsing JSON config\n");
296 fprintf(stderr, "%s\n", error_message.c_str());
297 return nullptr;
298 }
299
300 stream_state->decoder_factory = std::make_unique<InternalDecoderFactory>();
301 size_t config_count = 0;
302 for (const auto& json : json_configs) {
303 // Create the configuration and parse the JSON into the config.
304 auto receive_config =
305 ParseVideoReceiveStreamJsonConfig(&(stream_state->transport), json);
306 // Instantiate the underlying decoder.
307 for (auto& decoder : receive_config.decoders) {
308 decoder = test::CreateMatchingDecoder(decoder.payload_type,
309 decoder.video_format.name);
310 }
311 // Create a window for this config.
312 std::stringstream window_title;
313 window_title << "Playback Video (" << config_count++ << ")";
314 stream_state->sinks.emplace_back(test::VideoRenderer::Create(
315 window_title.str().c_str(), absl::GetFlag(FLAGS_render_width),
316 absl::GetFlag(FLAGS_render_height)));
317 // Create a receive stream for this config.
318 receive_config.renderer = stream_state->sinks.back().get();
319 receive_config.decoder_factory = stream_state->decoder_factory.get();
320 stream_state->receive_streams.emplace_back(
321 call->CreateVideoReceiveStream(std::move(receive_config)));
322 }
323 return stream_state;
324}
325
326// Loads the base configuration from flags passed in on the commandline.
327std::unique_ptr<StreamState> ConfigureFromFlags(
328 const std::string& rtp_dump_path,
329 Call* call) {
330 auto stream_state = std::make_unique<StreamState>();
331 // Create the video renderers. We must add both to the stream state to keep
332 // them from deallocating.
333 std::stringstream window_title;
334 window_title << "Playback Video (" << rtp_dump_path << ")";
335 std::unique_ptr<test::VideoRenderer> playback_video(
336 test::VideoRenderer::Create(window_title.str().c_str(),
337 absl::GetFlag(FLAGS_render_width),
338 absl::GetFlag(FLAGS_render_height)));
339 auto file_passthrough = std::make_unique<FileRenderPassthrough>(
340 absl::GetFlag(FLAGS_out_base), playback_video.get());
341 stream_state->sinks.push_back(std::move(playback_video));
342 stream_state->sinks.push_back(std::move(file_passthrough));
343 // Setup the configuration from the flags.
344 VideoReceiveStreamInterface::Config receive_config(
345 &(stream_state->transport));
346 receive_config.rtp.remote_ssrc = absl::GetFlag(FLAGS_ssrc);
347 receive_config.rtp.local_ssrc = kReceiverLocalSsrc;
348 receive_config.rtp.rtx_ssrc = absl::GetFlag(FLAGS_ssrc_rtx);
349 receive_config.rtp.rtx_associated_payload_types[absl::GetFlag(
350 FLAGS_media_payload_type_rtx)] = absl::GetFlag(FLAGS_media_payload_type);
351 receive_config.rtp
352 .rtx_associated_payload_types[absl::GetFlag(FLAGS_red_payload_type_rtx)] =
353 absl::GetFlag(FLAGS_red_payload_type);
354 receive_config.rtp.ulpfec_payload_type =
355 absl::GetFlag(FLAGS_ulpfec_payload_type);
356 receive_config.rtp.red_payload_type = absl::GetFlag(FLAGS_red_payload_type);
357 receive_config.rtp.nack.rtp_history_ms = 1000;
358
359 if (absl::GetFlag(FLAGS_flexfec_payload_type) != -1) {
360 receive_config.rtp.protected_by_flexfec = true;
philipelfaaa57b2022-10-03 14:53:20 +0200361 FlexfecReceiveStream::Config flexfec_config(&(stream_state->transport));
philipel5f551372022-10-03 11:56:58 +0200362 flexfec_config.payload_type = absl::GetFlag(FLAGS_flexfec_payload_type);
363 flexfec_config.protected_media_ssrcs.push_back(absl::GetFlag(FLAGS_ssrc));
364 flexfec_config.rtp.remote_ssrc = absl::GetFlag(FLAGS_ssrc_flexfec);
365 FlexfecReceiveStream* flexfec_stream =
366 call->CreateFlexfecReceiveStream(flexfec_config);
367 receive_config.rtp.packet_sink_ = flexfec_stream;
368 stream_state->flexfec_streams.push_back(flexfec_stream);
369 }
370
371 if (absl::GetFlag(FLAGS_transmission_offset_id) != -1) {
372 receive_config.rtp.extensions.push_back(
373 RtpExtension(RtpExtension::kTimestampOffsetUri,
374 absl::GetFlag(FLAGS_transmission_offset_id)));
375 }
376 if (absl::GetFlag(FLAGS_abs_send_time_id) != -1) {
377 receive_config.rtp.extensions.push_back(RtpExtension(
378 RtpExtension::kAbsSendTimeUri, absl::GetFlag(FLAGS_abs_send_time_id)));
379 }
380 receive_config.renderer = stream_state->sinks.back().get();
381
382 // Setup the receiving stream
383 VideoReceiveStreamInterface::Decoder decoder;
384 decoder = test::CreateMatchingDecoder(absl::GetFlag(FLAGS_media_payload_type),
385 absl::GetFlag(FLAGS_codec));
386 if (!absl::GetFlag(FLAGS_decoder_bitstream_filename).empty()) {
387 // Replace decoder with file writer if we're writing the bitstream to a
388 // file instead.
389 stream_state->decoder_factory =
390 std::make_unique<test::FunctionVideoDecoderFactory>([]() {
391 return std::make_unique<DecoderBitstreamFileWriter>(
392 absl::GetFlag(FLAGS_decoder_bitstream_filename).c_str());
393 });
394 } else if (!absl::GetFlag(FLAGS_decoder_ivf_filename).empty()) {
395 // Replace decoder with file writer if we're writing the ivf to a
396 // file instead.
397 stream_state->decoder_factory =
398 std::make_unique<test::FunctionVideoDecoderFactory>([]() {
399 return std::make_unique<DecoderIvfFileWriter>(
400 absl::GetFlag(FLAGS_decoder_ivf_filename).c_str(),
401 absl::GetFlag(FLAGS_codec));
402 });
403 } else {
404 stream_state->decoder_factory = std::make_unique<InternalDecoderFactory>();
405 }
406 receive_config.decoder_factory = stream_state->decoder_factory.get();
407 receive_config.decoders.push_back(decoder);
408
409 stream_state->receive_streams.emplace_back(
410 call->CreateVideoReceiveStream(std::move(receive_config)));
411 return stream_state;
412}
413
414std::unique_ptr<test::RtpFileReader> CreateRtpReader(
415 const std::string& rtp_dump_path) {
416 std::unique_ptr<test::RtpFileReader> rtp_reader(test::RtpFileReader::Create(
417 test::RtpFileReader::kRtpDump, rtp_dump_path));
418 if (!rtp_reader) {
419 rtp_reader.reset(
420 test::RtpFileReader::Create(test::RtpFileReader::kPcap, rtp_dump_path));
421 if (!rtp_reader) {
422 fprintf(stderr,
423 "Couldn't open input file as either a rtpdump or .pcap. Note "
424 "that .pcapng is not supported.\nTrying to interpret the file as "
425 "length/packet interleaved.\n");
426 rtp_reader.reset(test::RtpFileReader::Create(
427 test::RtpFileReader::kLengthPacketInterleaved, rtp_dump_path));
428 if (!rtp_reader) {
429 fprintf(stderr,
430 "Unable to open input file with any supported format\n");
431 return nullptr;
432 }
433 }
434 }
435 return rtp_reader;
436}
437
438// The RtpReplayer is responsible for parsing the configuration provided by
439// the user, setting up the windows, receive streams and decoders and then
440// replaying the provided RTP dump.
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700441class RtpReplayer final {
442 public:
philipel5f551372022-10-03 11:56:58 +0200443 RtpReplayer(absl::string_view replay_config_path,
444 absl::string_view rtp_dump_path,
445 std::unique_ptr<FieldTrialsView> field_trials)
446 : replay_config_path_(replay_config_path),
447 rtp_dump_path_(rtp_dump_path),
448 field_trials_(std::move(field_trials)),
philipelfaaa57b2022-10-03 14:53:20 +0200449 task_queue_factory_(CreateDefaultTaskQueueFactory(field_trials_.get())),
philipel5f551372022-10-03 11:56:58 +0200450 worker_thread_(std::make_unique<rtc::TaskQueue>(
451 task_queue_factory_->CreateTaskQueue(
452 "worker_thread",
philipel2671e242022-10-03 13:59:07 +0200453 TaskQueueFactory::Priority::NORMAL))),
454 rtp_reader_(CreateRtpReader(rtp_dump_path_)) {
455 rtc::Event event;
456 worker_thread_->PostTask([&]() {
457 Call::Config call_config(&event_log_);
458 call_config.trials = field_trials_.get();
459 call_config.task_queue_factory = task_queue_factory_.get();
460 call_.reset(Call::Create(call_config));
461
462 // Creation of the streams must happen inside a task queue because it is
463 // resued as a worker thread.
464 if (replay_config_path_.empty()) {
465 stream_state_ = ConfigureFromFlags(rtp_dump_path_, call_.get());
466 } else {
467 stream_state_ = ConfigureFromFile(replay_config_path_, call_.get());
468 }
469 event.Set();
470 });
471 event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
472
473 RTC_CHECK(stream_state_);
474 RTC_CHECK(rtp_reader_);
475 }
476
477 ~RtpReplayer() {
478 // Destruction of streams and the call must happen on the same thread as
479 // their creation.
480 rtc::Event event;
481 worker_thread_->PostTask([&]() {
482 for (const auto& receive_stream : stream_state_->receive_streams) {
483 call_->DestroyVideoReceiveStream(receive_stream);
484 }
485 for (const auto& flexfec_stream : stream_state_->flexfec_streams) {
486 call_->DestroyFlexfecReceiveStream(flexfec_stream);
487 }
488 call_.reset();
489 event.Set();
490 });
491 event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
492 }
philipel5f551372022-10-03 11:56:58 +0200493
494 void Run() {
philipel2671e242022-10-03 13:59:07 +0200495 rtc::Event event;
philipel5f551372022-10-03 11:56:58 +0200496 worker_thread_->PostTask([&]() {
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100497 // Start replaying the provided stream now that it has been configured.
498 // VideoReceiveStreams must be started on the same thread as they were
499 // created on.
philipel2671e242022-10-03 13:59:07 +0200500 for (const auto& receive_stream : stream_state_->receive_streams) {
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100501 receive_stream->Start();
502 }
philipel2671e242022-10-03 13:59:07 +0200503 event.Set();
Danil Chapovalov5286dcf2022-07-18 17:04:56 +0200504 });
philipel2671e242022-10-03 13:59:07 +0200505 event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100506
philipel2671e242022-10-03 13:59:07 +0200507 ReplayPackets();
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000508 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000509
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700510 private:
philipel2671e242022-10-03 13:59:07 +0200511 void ReplayPackets() {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700512 int64_t replay_start_ms = -1;
513 int num_packets = 0;
514 std::map<uint32_t, int> unknown_packets;
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100515 rtc::Event event(/*manual_reset=*/false, /*initially_signalled=*/false);
Philipp Hanckef3a687a2021-03-15 12:04:39 +0100516 uint32_t start_timestamp = absl::GetFlag(FLAGS_start_timestamp);
517 uint32_t stop_timestamp = absl::GetFlag(FLAGS_stop_timestamp);
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700518 while (true) {
519 int64_t now_ms = rtc::TimeMillis();
520 if (replay_start_ms == -1) {
521 replay_start_ms = now_ms;
522 }
philipel02f03962018-01-11 17:28:35 +0100523
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700524 test::RtpPacket packet;
philipel2671e242022-10-03 13:59:07 +0200525 if (!rtp_reader_->NextPacket(&packet)) {
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000526 break;
527 }
Danil Chapovalov76a35d92021-06-08 12:30:46 +0200528 rtc::CopyOnWriteBuffer packet_buffer(packet.data, packet.length);
529 RtpPacket header;
530 header.Parse(packet_buffer);
531 if (header.Timestamp() < start_timestamp ||
532 header.Timestamp() > stop_timestamp) {
Philipp Hanckef3a687a2021-03-15 12:04:39 +0100533 continue;
534 }
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700535
536 int64_t deliver_in_ms = replay_start_ms + packet.time_ms - now_ms;
537 if (deliver_in_ms > 0) {
538 SleepMs(deliver_in_ms);
539 }
540
541 ++num_packets;
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100542 PacketReceiver::DeliveryStatus result = PacketReceiver::DELIVERY_OK;
philipel5f551372022-10-03 11:56:58 +0200543 worker_thread_->PostTask([&]() {
philipel6cbb4682022-09-30 17:23:04 +0200544 MediaType media_type =
545 IsRtcpPacket(packet_buffer) ? MediaType::ANY : MediaType::VIDEO;
philipel2671e242022-10-03 13:59:07 +0200546 result = call_->Receiver()->DeliverPacket(media_type,
547 std::move(packet_buffer),
548 /* packet_time_us */ -1);
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100549 event.Set();
Danil Chapovalov5286dcf2022-07-18 17:04:56 +0200550 });
Markus Handell2cfc1af2022-08-19 08:16:48 +0000551 event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100552 switch (result) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700553 case PacketReceiver::DELIVERY_OK:
554 break;
555 case PacketReceiver::DELIVERY_UNKNOWN_SSRC: {
Danil Chapovalov76a35d92021-06-08 12:30:46 +0200556 if (unknown_packets[header.Ssrc()] == 0)
557 fprintf(stderr, "Unknown SSRC: %u!\n", header.Ssrc());
558 ++unknown_packets[header.Ssrc()];
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700559 break;
560 }
561 case PacketReceiver::DELIVERY_PACKET_ERROR: {
562 fprintf(stderr,
563 "Packet error, corrupt packets or incorrect setup?\n");
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700564 fprintf(stderr, "Packet len=%zu pt=%u seq=%u ts=%u ssrc=0x%8x\n",
Danil Chapovalov76a35d92021-06-08 12:30:46 +0200565 packet.length, header.PayloadType(), header.SequenceNumber(),
566 header.Timestamp(), header.Ssrc());
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700567 break;
568 }
philipp.hancke7b589602017-01-26 04:54:04 -0800569 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000570 }
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700571 fprintf(stderr, "num_packets: %d\n", num_packets);
572
573 for (std::map<uint32_t, int>::const_iterator it = unknown_packets.begin();
574 it != unknown_packets.end(); ++it) {
575 fprintf(stderr, "Packets for unknown ssrc '%u': %d\n", it->first,
576 it->second);
577 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000578 }
philipel5f551372022-10-03 11:56:58 +0200579
580 const std::string replay_config_path_;
581 const std::string rtp_dump_path_;
philipelfaaa57b2022-10-03 14:53:20 +0200582 RtcEventLogNull event_log_;
philipel5f551372022-10-03 11:56:58 +0200583 std::unique_ptr<FieldTrialsView> field_trials_;
584 std::unique_ptr<TaskQueueFactory> task_queue_factory_;
585 std::unique_ptr<rtc::TaskQueue> worker_thread_;
philipel2671e242022-10-03 13:59:07 +0200586 std::unique_ptr<Call> call_;
587 std::unique_ptr<test::RtpFileReader> rtp_reader_;
588 std::unique_ptr<StreamState> stream_state_;
589};
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000590
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700591void RtpReplay() {
philipel525891a2022-10-03 14:54:09 +0200592 RtpReplayer replayer(
593 absl::GetFlag(FLAGS_config_file), absl::GetFlag(FLAGS_input_file),
594 std::make_unique<FieldTrials>(absl::GetFlag(FLAGS_force_fieldtrials)));
philipel5f551372022-10-03 11:56:58 +0200595 replayer.Run();
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000596}
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700597
philipel05f48222022-10-03 09:19:55 +0200598} // namespace
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000599} // namespace webrtc
600
601int main(int argc, char* argv[]) {
602 ::testing::InitGoogleTest(&argc, argv);
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200603 absl::ParseCommandLine(argc, argv);
oprypin6e09d872017-08-31 03:21:39 -0700604
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200605 RTC_CHECK(ValidatePayloadType(absl::GetFlag(FLAGS_media_payload_type)));
606 RTC_CHECK(ValidatePayloadType(absl::GetFlag(FLAGS_media_payload_type_rtx)));
607 RTC_CHECK(ValidateOptionalPayloadType(absl::GetFlag(FLAGS_red_payload_type)));
philipel752968e2017-12-05 12:40:28 +0100608 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200609 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_red_payload_type_rtx)));
philipel752968e2017-12-05 12:40:28 +0100610 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200611 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_ulpfec_payload_type)));
Yves Gerey665174f2018-06-19 15:03:05 +0200612 RTC_CHECK(
Philipp Hancke1c951ec2022-05-25 10:44:22 +0200613 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_flexfec_payload_type)));
614 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200615 ValidateRtpHeaderExtensionId(absl::GetFlag(FLAGS_abs_send_time_id)));
616 RTC_CHECK(ValidateRtpHeaderExtensionId(
617 absl::GetFlag(FLAGS_transmission_offset_id)));
618 RTC_CHECK(ValidateInputFilenameNotEmpty(absl::GetFlag(FLAGS_input_file)));
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000619
Philipp Hancke07b7dec2020-07-28 12:34:48 +0200620 rtc::ThreadManager::Instance()->WrapCurrentThread();
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000621 webrtc::test::RunTest(webrtc::RtpReplay);
622 return 0;
623}