blob: 1a7bfadbca1f08c9651319674e5d425affef33ad [file] [log] [blame]
pbos@webrtc.org4b5625e2014-08-06 16:26:561/*
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 18:33:2913#include <fstream>
pbos@webrtc.org4b5625e2014-08-06 16:26:5614#include <map>
kwiberg27f982b2016-03-01 19:52:3315#include <memory>
pbos@webrtc.org4b5625e2014-08-06 16:26:5616
Mirko Bonadei2ab97f62019-07-18 11:44:1217#include "absl/flags/flag.h"
18#include "absl/flags/parse.h"
Danil Chapovalov83bbe912019-08-07 10:24:5319#include "api/rtc_event_log/rtc_event_log.h"
Sergey Silkin626f7ff2019-09-12 08:51:1920#include "api/task_queue/default_task_queue_factory.h"
Danil Chapovalov99b71df2018-10-26 13:57:4821#include "api/test/video/function_video_decoder_factory.h"
Ilya Nikolaevskiy33aaa352020-01-21 12:38:1922#include "api/transport/field_trial_based_config.h"
Philipp Hanckefae4fb12021-01-26 17:05:5623#include "api/video/video_codec_type.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3124#include "api/video_codecs/video_decoder.h"
25#include "call/call.h"
26#include "common_video/libyuv/include/webrtc_libyuv.h"
Steve Anton10542f22019-01-11 17:11:0027#include "media/engine/internal_decoder_factory.h"
Danil Chapovalov76a35d92021-06-08 10:30:4628#include "modules/rtp_rtcp/source/rtp_packet.h"
Philipp Hanckefae4fb12021-01-26 17:05:5629#include "modules/video_coding/utility/ivf_file_writer.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3130#include "rtc_base/checks.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3131#include "rtc_base/string_to_number.h"
Sam Zackrissonb45bdb52018-10-02 14:25:5932#include "rtc_base/strings/json.h"
Steve Anton10542f22019-01-11 17:11:0033#include "rtc_base/time_utils.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3134#include "system_wrappers/include/clock.h"
35#include "system_wrappers/include/sleep.h"
Benjamin Wright8efafdf2019-01-11 18:48:4236#include "test/call_config_utils.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3137#include "test/call_test.h"
38#include "test/encoder_settings.h"
39#include "test/fake_decoder.h"
40#include "test/gtest.h"
41#include "test/null_transport.h"
42#include "test/rtp_file_reader.h"
43#include "test/run_loop.h"
44#include "test/run_test.h"
Sebastian Janssonf1f363f2018-08-13 12:24:5845#include "test/test_video_capturer.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3146#include "test/testsupport/frame_writer.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3147#include "test/video_renderer.h"
pbos@webrtc.org4b5625e2014-08-06 16:26:5648
Mirko Bonadei2ab97f62019-07-18 11:44:1249// Flag for payload type.
50ABSL_FLAG(int,
51 media_payload_type,
52 webrtc::test::CallTest::kPayloadTypeVP8,
53 "Media payload type");
54
55// Flag for RED payload type.
56ABSL_FLAG(int,
57 red_payload_type,
58 webrtc::test::CallTest::kRedPayloadType,
59 "RED payload type");
60
61// Flag for ULPFEC payload type.
62ABSL_FLAG(int,
63 ulpfec_payload_type,
64 webrtc::test::CallTest::kUlpfecPayloadType,
65 "ULPFEC payload type");
66
Philipp Hancke1c951ec2022-05-25 08:44:2267// Flag for FLEXFEC payload type.
68ABSL_FLAG(int,
69 flexfec_payload_type,
70 webrtc::test::CallTest::kFlexfecPayloadType,
71 "FLEXFEC payload type");
72
Mirko Bonadei2ab97f62019-07-18 11:44:1273ABSL_FLAG(int,
74 media_payload_type_rtx,
75 webrtc::test::CallTest::kSendRtxPayloadType,
76 "Media over RTX payload type");
77
78ABSL_FLAG(int,
79 red_payload_type_rtx,
80 webrtc::test::CallTest::kRtxRedPayloadType,
81 "RED over RTX payload type");
82
Philipp Hanckec060ce42021-03-15 12:15:5983// Flag for SSRC and RTX SSRC.
84ABSL_FLAG(uint32_t,
85 ssrc,
86 webrtc::test::CallTest::kVideoSendSsrcs[0],
87 "Incoming SSRC");
88ABSL_FLAG(uint32_t,
89 ssrc_rtx,
90 webrtc::test::CallTest::kSendRtxSsrcs[0],
91 "Incoming RTX SSRC");
Mirko Bonadei2ab97f62019-07-18 11:44:1292
Philipp Hancke1c951ec2022-05-25 08:44:2293ABSL_FLAG(uint32_t,
94 ssrc_flexfec,
95 webrtc::test::CallTest::kFlexfecSendSsrc,
96 "Incoming FLEXFEC SSRC");
97
Mirko Bonadei2ab97f62019-07-18 11:44:1298// Flag for abs-send-time id.
99ABSL_FLAG(int, abs_send_time_id, -1, "RTP extension ID for abs-send-time");
100
101// Flag for transmission-offset id.
102ABSL_FLAG(int,
103 transmission_offset_id,
104 -1,
105 "RTP extension ID for transmission-offset");
106
107// Flag for rtpdump input file.
108ABSL_FLAG(std::string, input_file, "", "input file");
109
110ABSL_FLAG(std::string, config_file, "", "config file");
111
112// Flag for raw output files.
113ABSL_FLAG(std::string,
114 out_base,
115 "",
116 "Basename (excluding .jpg) for raw output");
117
118ABSL_FLAG(std::string,
119 decoder_bitstream_filename,
120 "",
121 "Decoder bitstream output file");
122
Philipp Hanckefae4fb12021-01-26 17:05:56123ABSL_FLAG(std::string, decoder_ivf_filename, "", "Decoder ivf output file");
124
Mirko Bonadei2ab97f62019-07-18 11:44:12125// Flag for video codec.
126ABSL_FLAG(std::string, codec, "VP8", "Video codec");
127
Philipp Hanckef3a687a2021-03-15 11:04:39128// Flags for rtp start and stop timestamp.
129ABSL_FLAG(uint32_t,
130 start_timestamp,
131 0,
132 "RTP start timestamp, packets with smaller timestamp will be ignored "
133 "(no wraparound)");
134ABSL_FLAG(uint32_t,
135 stop_timestamp,
136 4294967295,
137 "RTP stop timestamp, packets with larger timestamp will be ignored "
138 "(no wraparound)");
139
Jonathan Lennox03df29c2021-07-13 16:41:31140// Flags for render window width and height
141ABSL_FLAG(uint32_t, render_width, 640, "Width of render window");
142ABSL_FLAG(uint32_t, render_height, 480, "Height of render window");
143
oprypin6e09d872017-08-31 10:21:39144namespace {
145
146static bool ValidatePayloadType(int32_t payload_type) {
147 return payload_type > 0 && payload_type <= 127;
148}
149
oprypin6e09d872017-08-31 10:21:39150static bool ValidateOptionalPayloadType(int32_t payload_type) {
151 return payload_type == -1 || ValidatePayloadType(payload_type);
152}
153
154static bool ValidateRtpHeaderExtensionId(int32_t extension_id) {
155 return extension_id >= -1 && extension_id < 15;
156}
157
158bool ValidateInputFilenameNotEmpty(const std::string& string) {
159 return !string.empty();
160}
161
Mirko Bonadei2ab97f62019-07-18 11:44:12162static int MediaPayloadType() {
163 return absl::GetFlag(FLAGS_media_payload_type);
164}
165
166static int RedPayloadType() {
167 return absl::GetFlag(FLAGS_red_payload_type);
168}
169
170static int UlpfecPayloadType() {
171 return absl::GetFlag(FLAGS_ulpfec_payload_type);
172}
173
Philipp Hancke1c951ec2022-05-25 08:44:22174static int FlexfecPayloadType() {
175 return absl::GetFlag(FLAGS_flexfec_payload_type);
176}
177
Mirko Bonadei2ab97f62019-07-18 11:44:12178static int MediaPayloadTypeRtx() {
179 return absl::GetFlag(FLAGS_media_payload_type_rtx);
180}
181
182static int RedPayloadTypeRtx() {
183 return absl::GetFlag(FLAGS_red_payload_type_rtx);
184}
185
186static uint32_t Ssrc() {
Philipp Hanckec060ce42021-03-15 12:15:59187 return absl::GetFlag(FLAGS_ssrc);
Mirko Bonadei2ab97f62019-07-18 11:44:12188}
189
190static uint32_t SsrcRtx() {
Philipp Hanckec060ce42021-03-15 12:15:59191 return absl::GetFlag(FLAGS_ssrc_rtx);
Mirko Bonadei2ab97f62019-07-18 11:44:12192}
193
Philipp Hancke1c951ec2022-05-25 08:44:22194static uint32_t SsrcFlexfec() {
195 return absl::GetFlag(FLAGS_ssrc_flexfec);
196}
197
Mirko Bonadei2ab97f62019-07-18 11:44:12198static int AbsSendTimeId() {
199 return absl::GetFlag(FLAGS_abs_send_time_id);
200}
201
202static int TransmissionOffsetId() {
203 return absl::GetFlag(FLAGS_transmission_offset_id);
204}
205
206static std::string InputFile() {
207 return absl::GetFlag(FLAGS_input_file);
208}
209
210static std::string ConfigFile() {
211 return absl::GetFlag(FLAGS_config_file);
212}
213
214static std::string OutBase() {
215 return absl::GetFlag(FLAGS_out_base);
216}
217
218static std::string DecoderBitstreamFilename() {
219 return absl::GetFlag(FLAGS_decoder_bitstream_filename);
220}
221
Philipp Hanckefae4fb12021-01-26 17:05:56222static std::string IVFFilename() {
223 return absl::GetFlag(FLAGS_decoder_ivf_filename);
224}
225
Mirko Bonadei2ab97f62019-07-18 11:44:12226static std::string Codec() {
227 return absl::GetFlag(FLAGS_codec);
228}
229
Jonathan Lennox03df29c2021-07-13 16:41:31230static uint32_t RenderWidth() {
231 return absl::GetFlag(FLAGS_render_width);
232}
233
234static uint32_t RenderHeight() {
235 return absl::GetFlag(FLAGS_render_height);
236}
237
oprypin6e09d872017-08-31 10:21:39238} // namespace
239
pbos@webrtc.org4b5625e2014-08-06 16:26:56240namespace webrtc {
pbos@webrtc.org4b5625e2014-08-06 16:26:56241
242static const uint32_t kReceiverLocalSsrc = 0x123456;
243
nisse7ade7b32016-03-23 11:48:10244class FileRenderPassthrough : public rtc::VideoSinkInterface<VideoFrame> {
pbos@webrtc.org4b5625e2014-08-06 16:26:56245 public:
nisse7ade7b32016-03-23 11:48:10246 FileRenderPassthrough(const std::string& basename,
247 rtc::VideoSinkInterface<VideoFrame>* renderer)
philipel99b63452017-08-25 14:24:21248 : basename_(basename), renderer_(renderer), file_(nullptr), count_(0) {}
pbos@webrtc.org4b5625e2014-08-06 16:26:56249
Mirko Bonadeife055c12019-01-29 21:53:28250 ~FileRenderPassthrough() override {
Peter Boström74f6e9e2016-04-04 15:56:10251 if (file_)
pbos@webrtc.org4b5625e2014-08-06 16:26:56252 fclose(file_);
253 }
254
255 private:
nisseeb83a1a2016-03-21 08:27:56256 void OnFrame(const VideoFrame& video_frame) override {
Peter Boström74f6e9e2016-04-04 15:56:10257 if (renderer_)
nisseeb83a1a2016-03-21 08:27:56258 renderer_->OnFrame(video_frame);
philipel99b63452017-08-25 14:24:21259
pbosbb36fdf2015-07-09 14:48:14260 if (basename_.empty())
pbos@webrtc.org4b5625e2014-08-06 16:26:56261 return;
philipel99b63452017-08-25 14:24:21262
263 std::stringstream filename;
264 filename << basename_ << count_++ << "_" << video_frame.timestamp()
265 << ".jpg";
266
philipel99b63452017-08-25 14:24:21267 test::JpegFrameWriter frame_writer(filename.str());
268 RTC_CHECK(frame_writer.WriteFrame(video_frame, 100));
pbos@webrtc.org4b5625e2014-08-06 16:26:56269 }
270
271 const std::string basename_;
nisse7ade7b32016-03-23 11:48:10272 rtc::VideoSinkInterface<VideoFrame>* const renderer_;
pbos@webrtc.org4b5625e2014-08-06 16:26:56273 FILE* file_;
274 size_t count_;
pbos@webrtc.org4b5625e2014-08-06 16:26:56275};
276
Niels Möllerf88a22c2018-06-19 15:05:03277class DecoderBitstreamFileWriter : public test::FakeDecoder {
stefan@webrtc.org48ac2262015-03-02 16:18:56278 public:
279 explicit DecoderBitstreamFileWriter(const char* filename)
280 : file_(fopen(filename, "wb")) {
Peter Boström74f6e9e2016-04-04 15:56:10281 RTC_DCHECK(file_);
stefan@webrtc.org48ac2262015-03-02 16:18:56282 }
Mirko Bonadeife055c12019-01-29 21:53:28283 ~DecoderBitstreamFileWriter() override { fclose(file_); }
stefan@webrtc.org48ac2262015-03-02 16:18:56284
Niels Möllerf88a22c2018-06-19 15:05:03285 int32_t Decode(const EncodedImage& encoded_frame,
Benjamin Wright8efafdf2019-01-11 18:48:42286 bool /* missing_frames */,
Benjamin Wright8efafdf2019-01-11 18:48:42287 int64_t /* render_time_ms */) override {
Niels Möller77536a22019-01-15 07:50:01288 if (fwrite(encoded_frame.data(), 1, encoded_frame.size(), file_) <
289 encoded_frame.size()) {
Niels Möllerf88a22c2018-06-19 15:05:03290 RTC_LOG_ERR(LS_ERROR) << "fwrite of encoded frame failed.";
291 return WEBRTC_VIDEO_CODEC_ERROR;
292 }
293 return WEBRTC_VIDEO_CODEC_OK;
stefan@webrtc.org48ac2262015-03-02 16:18:56294 }
295
296 private:
297 FILE* file_;
298};
299
Philipp Hanckefae4fb12021-01-26 17:05:56300class DecoderIvfFileWriter : public test::FakeDecoder {
301 public:
302 explicit DecoderIvfFileWriter(const char* filename, const std::string& codec)
303 : file_writer_(
304 IvfFileWriter::Wrap(FileWrapper::OpenWriteOnly(filename), 0)) {
305 RTC_DCHECK(file_writer_.get());
306 if (codec == "VP8") {
307 video_codec_type_ = VideoCodecType::kVideoCodecVP8;
308 } else if (codec == "VP9") {
309 video_codec_type_ = VideoCodecType::kVideoCodecVP9;
310 } else if (codec == "H264") {
311 video_codec_type_ = VideoCodecType::kVideoCodecH264;
Philipp Hanckede7fcff2022-05-12 16:13:16312 } else if (codec == "AV1") {
313 video_codec_type_ = VideoCodecType::kVideoCodecAV1;
Philipp Hanckefae4fb12021-01-26 17:05:56314 } else {
315 RTC_LOG(LS_ERROR) << "Unsupported video codec " << codec;
Artem Titovd3251962021-11-15 15:57:07316 RTC_DCHECK_NOTREACHED();
Philipp Hanckefae4fb12021-01-26 17:05:56317 }
318 }
319 ~DecoderIvfFileWriter() override { file_writer_->Close(); }
320
321 int32_t Decode(const EncodedImage& encoded_frame,
322 bool /* missing_frames */,
323 int64_t render_time_ms) override {
324 if (!file_writer_->WriteFrame(encoded_frame, video_codec_type_)) {
325 return WEBRTC_VIDEO_CODEC_ERROR;
326 }
327 return WEBRTC_VIDEO_CODEC_OK;
328 }
329
330 private:
331 std::unique_ptr<IvfFileWriter> file_writer_;
332 VideoCodecType video_codec_type_;
333};
334
Benjamin Wright90ab76d2018-08-23 18:33:29335// The RtpReplayer is responsible for parsing the configuration provided by the
Niels Möllerbe74b802022-03-18 13:10:15336// user, setting up the windows, receive streams and decoders and then replaying
Benjamin Wright90ab76d2018-08-23 18:33:29337// the provided RTP dump.
338class RtpReplayer final {
339 public:
340 // Replay a rtp dump with an optional json configuration.
341 static void Replay(const std::string& replay_config_path,
342 const std::string& rtp_dump_path) {
Ali Tofigh4b681942022-08-23 10:57:16343 webrtc::RtcEventLogNull event_log;
344 Call::Config call_config(&event_log);
345 call_config.trials = new FieldTrialBasedConfig();
346
Sergey Silkin626f7ff2019-09-12 08:51:19347 std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
Ali Tofigh4b681942022-08-23 10:57:16348 webrtc::CreateDefaultTaskQueueFactory(call_config.trials);
Ilya Nikolaevskiy6a646902020-12-11 10:07:01349 auto worker_thread = task_queue_factory->CreateTaskQueue(
350 "worker_thread", TaskQueueFactory::Priority::NORMAL);
351 rtc::Event sync_event(/*manual_reset=*/false,
352 /*initially_signalled=*/false);
Sergey Silkin626f7ff2019-09-12 08:51:19353 call_config.task_queue_factory = task_queue_factory.get();
Ilya Nikolaevskiy6a646902020-12-11 10:07:01354
355 // Creation of the streams must happen inside a task queue because it is
356 // resued as a worker thread.
Ali Tofigh4b681942022-08-23 10:57:16357 std::unique_ptr<Call> call;
358 std::unique_ptr<StreamState> stream_state;
Danil Chapovalov5286dcf2022-07-18 15:04:56359 worker_thread->PostTask([&]() {
Ilya Nikolaevskiy6a646902020-12-11 10:07:01360 call.reset(Call::Create(call_config));
361
362 // Attempt to load the configuration
363 if (replay_config_path.empty()) {
364 stream_state = ConfigureFromFlags(rtp_dump_path, call.get());
365 } else {
366 stream_state = ConfigureFromFile(replay_config_path, call.get());
367 }
368
369 if (stream_state == nullptr) {
370 return;
371 }
372 // Start replaying the provided stream now that it has been configured.
373 // VideoReceiveStreams must be started on the same thread as they were
374 // created on.
375 for (const auto& receive_stream : stream_state->receive_streams) {
376 receive_stream->Start();
377 }
378 sync_event.Set();
Danil Chapovalov5286dcf2022-07-18 15:04:56379 });
Ilya Nikolaevskiy6a646902020-12-11 10:07:01380
Benjamin Wright90ab76d2018-08-23 18:33:29381 // Attempt to create an RtpReader from the input file.
382 std::unique_ptr<test::RtpFileReader> rtp_reader =
383 CreateRtpReader(rtp_dump_path);
Ilya Nikolaevskiy6a646902020-12-11 10:07:01384
385 // Wait for streams creation.
Markus Handell2cfc1af2022-08-19 08:16:48386 sync_event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
Ilya Nikolaevskiy6a646902020-12-11 10:07:01387
388 if (stream_state == nullptr || rtp_reader == nullptr) {
Benjamin Wright90ab76d2018-08-23 18:33:29389 return;
390 }
Ilya Nikolaevskiy6a646902020-12-11 10:07:01391
392 ReplayPackets(call.get(), rtp_reader.get(), worker_thread.get());
393
394 // Destruction of streams and the call must happen on the same thread as
395 // their creation.
Danil Chapovalov5286dcf2022-07-18 15:04:56396 worker_thread->PostTask([&]() {
Ilya Nikolaevskiy6a646902020-12-11 10:07:01397 for (const auto& receive_stream : stream_state->receive_streams) {
398 call->DestroyVideoReceiveStream(receive_stream);
399 }
Philipp Hancke1c951ec2022-05-25 08:44:22400 for (const auto& flexfec_stream : stream_state->flexfec_streams) {
401 call->DestroyFlexfecReceiveStream(flexfec_stream);
402 }
Ilya Nikolaevskiy6a646902020-12-11 10:07:01403 call.reset();
404 sync_event.Set();
Danil Chapovalov5286dcf2022-07-18 15:04:56405 });
Markus Handell2cfc1af2022-08-19 08:16:48406 sync_event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
pbos@webrtc.org4b5625e2014-08-06 16:26:56407 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56408
Benjamin Wright90ab76d2018-08-23 18:33:29409 private:
Niels Möllerbe74b802022-03-18 13:10:15410 // Holds all the shared memory structures required for a receive stream. This
Benjamin Wright90ab76d2018-08-23 18:33:29411 // structure is used to prevent members being deallocated before the replay
412 // has been finished.
413 struct StreamState {
414 test::NullTransport transport;
415 std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
Tommif6f45432022-05-20 13:21:20416 std::vector<VideoReceiveStreamInterface*> receive_streams;
Philipp Hancke1c951ec2022-05-25 08:44:22417 std::vector<FlexfecReceiveStream*> flexfec_streams;
Niels Möllercbcbc222018-09-28 07:07:24418 std::unique_ptr<VideoDecoderFactory> decoder_factory;
Benjamin Wright90ab76d2018-08-23 18:33:29419 };
420
421 // Loads multiple configurations from the provided configuration file.
422 static std::unique_ptr<StreamState> ConfigureFromFile(
423 const std::string& config_path,
424 Call* call) {
Mirko Bonadei317a1f02019-09-17 15:06:18425 auto stream_state = std::make_unique<StreamState>();
Benjamin Wright90ab76d2018-08-23 18:33:29426 // Parse the configuration file.
427 std::ifstream config_file(config_path);
428 std::stringstream raw_json_buffer;
429 raw_json_buffer << config_file.rdbuf();
430 std::string raw_json = raw_json_buffer.str();
Mirko Bonadeie99f6872021-06-24 13:24:59431 Json::CharReaderBuilder builder;
Benjamin Wright90ab76d2018-08-23 18:33:29432 Json::Value json_configs;
Mirko Bonadeie99f6872021-06-24 13:24:59433 std::string error_message;
434 std::unique_ptr<Json::CharReader> json_reader(builder.newCharReader());
435 if (!json_reader->parse(raw_json.data(), raw_json.data() + raw_json.size(),
436 &json_configs, &error_message)) {
Benjamin Wright90ab76d2018-08-23 18:33:29437 fprintf(stderr, "Error parsing JSON config\n");
Mirko Bonadeie99f6872021-06-24 13:24:59438 fprintf(stderr, "%s\n", error_message.c_str());
Benjamin Wright90ab76d2018-08-23 18:33:29439 return nullptr;
440 }
441
Mirko Bonadei317a1f02019-09-17 15:06:18442 stream_state->decoder_factory = std::make_unique<InternalDecoderFactory>();
Benjamin Wright90ab76d2018-08-23 18:33:29443 size_t config_count = 0;
444 for (const auto& json : json_configs) {
445 // Create the configuration and parse the JSON into the config.
Benjamin Wright8efafdf2019-01-11 18:48:42446 auto receive_config =
447 ParseVideoReceiveStreamJsonConfig(&(stream_state->transport), json);
Benjamin Wright90ab76d2018-08-23 18:33:29448 // Instantiate the underlying decoder.
449 for (auto& decoder : receive_config.decoders) {
Niels Möllercbcbc222018-09-28 07:07:24450 decoder = test::CreateMatchingDecoder(decoder.payload_type,
451 decoder.video_format.name);
Benjamin Wright90ab76d2018-08-23 18:33:29452 }
453 // Create a window for this config.
454 std::stringstream window_title;
455 window_title << "Playback Video (" << config_count++ << ")";
Jonathan Lennox03df29c2021-07-13 16:41:31456 stream_state->sinks.emplace_back(test::VideoRenderer::Create(
457 window_title.str().c_str(), RenderWidth(), RenderHeight()));
Benjamin Wright90ab76d2018-08-23 18:33:29458 // Create a receive stream for this config.
459 receive_config.renderer = stream_state->sinks.back().get();
Philip Eliasson2b068ce2020-08-03 15:55:10460 receive_config.decoder_factory = stream_state->decoder_factory.get();
Benjamin Wright90ab76d2018-08-23 18:33:29461 stream_state->receive_streams.emplace_back(
462 call->CreateVideoReceiveStream(std::move(receive_config)));
463 }
464 return stream_state;
stefan@webrtc.org48ac2262015-03-02 16:18:56465 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56466
Benjamin Wright90ab76d2018-08-23 18:33:29467 // Loads the base configuration from flags passed in on the commandline.
468 static std::unique_ptr<StreamState> ConfigureFromFlags(
469 const std::string& rtp_dump_path,
470 Call* call) {
Mirko Bonadei317a1f02019-09-17 15:06:18471 auto stream_state = std::make_unique<StreamState>();
Benjamin Wright90ab76d2018-08-23 18:33:29472 // Create the video renderers. We must add both to the stream state to keep
473 // them from deallocating.
474 std::stringstream window_title;
475 window_title << "Playback Video (" << rtp_dump_path << ")";
476 std::unique_ptr<test::VideoRenderer> playback_video(
Jonathan Lennox03df29c2021-07-13 16:41:31477 test::VideoRenderer::Create(window_title.str().c_str(), RenderWidth(),
478 RenderHeight()));
Mirko Bonadei317a1f02019-09-17 15:06:18479 auto file_passthrough = std::make_unique<FileRenderPassthrough>(
Mirko Bonadei2ab97f62019-07-18 11:44:12480 OutBase(), playback_video.get());
Benjamin Wright90ab76d2018-08-23 18:33:29481 stream_state->sinks.push_back(std::move(playback_video));
482 stream_state->sinks.push_back(std::move(file_passthrough));
483 // Setup the configuration from the flags.
Tommif6f45432022-05-20 13:21:20484 VideoReceiveStreamInterface::Config receive_config(
485 &(stream_state->transport));
Mirko Bonadei2ab97f62019-07-18 11:44:12486 receive_config.rtp.remote_ssrc = Ssrc();
Benjamin Wright90ab76d2018-08-23 18:33:29487 receive_config.rtp.local_ssrc = kReceiverLocalSsrc;
Mirko Bonadei2ab97f62019-07-18 11:44:12488 receive_config.rtp.rtx_ssrc = SsrcRtx();
489 receive_config.rtp.rtx_associated_payload_types[MediaPayloadTypeRtx()] =
490 MediaPayloadType();
491 receive_config.rtp.rtx_associated_payload_types[RedPayloadTypeRtx()] =
492 RedPayloadType();
493 receive_config.rtp.ulpfec_payload_type = UlpfecPayloadType();
494 receive_config.rtp.red_payload_type = RedPayloadType();
Benjamin Wright90ab76d2018-08-23 18:33:29495 receive_config.rtp.nack.rtp_history_ms = 1000;
Philipp Hancke1c951ec2022-05-25 08:44:22496
497 if (FlexfecPayloadType() != -1) {
498 receive_config.rtp.protected_by_flexfec = true;
499 webrtc::FlexfecReceiveStream::Config flexfec_config(
500 &(stream_state->transport));
501 flexfec_config.payload_type = FlexfecPayloadType();
502 flexfec_config.protected_media_ssrcs.push_back(Ssrc());
503 flexfec_config.rtp.remote_ssrc = SsrcFlexfec();
504 FlexfecReceiveStream* flexfec_stream =
505 call->CreateFlexfecReceiveStream(flexfec_config);
506 receive_config.rtp.packet_sink_ = flexfec_stream;
507 stream_state->flexfec_streams.push_back(flexfec_stream);
508 }
509
Mirko Bonadei2ab97f62019-07-18 11:44:12510 if (TransmissionOffsetId() != -1) {
Benjamin Wright90ab76d2018-08-23 18:33:29511 receive_config.rtp.extensions.push_back(RtpExtension(
Mirko Bonadei2ab97f62019-07-18 11:44:12512 RtpExtension::kTimestampOffsetUri, TransmissionOffsetId()));
Benjamin Wright90ab76d2018-08-23 18:33:29513 }
Mirko Bonadei2ab97f62019-07-18 11:44:12514 if (AbsSendTimeId() != -1) {
Benjamin Wright90ab76d2018-08-23 18:33:29515 receive_config.rtp.extensions.push_back(
Mirko Bonadei2ab97f62019-07-18 11:44:12516 RtpExtension(RtpExtension::kAbsSendTimeUri, AbsSendTimeId()));
Benjamin Wright90ab76d2018-08-23 18:33:29517 }
518 receive_config.renderer = stream_state->sinks.back().get();
pbos@webrtc.org4b5625e2014-08-06 16:26:56519
Benjamin Wright90ab76d2018-08-23 18:33:29520 // Setup the receiving stream
Tommif6f45432022-05-20 13:21:20521 VideoReceiveStreamInterface::Decoder decoder;
Mirko Bonadei2ab97f62019-07-18 11:44:12522 decoder = test::CreateMatchingDecoder(MediaPayloadType(), Codec());
Philipp Hanckefae4fb12021-01-26 17:05:56523 if (!DecoderBitstreamFilename().empty()) {
Benjamin Wright90ab76d2018-08-23 18:33:29524 // Replace decoder with file writer if we're writing the bitstream to a
525 // file instead.
Niels Möllercbcbc222018-09-28 07:07:24526 stream_state->decoder_factory =
Mirko Bonadei317a1f02019-09-17 15:06:18527 std::make_unique<test::FunctionVideoDecoderFactory>([]() {
528 return std::make_unique<DecoderBitstreamFileWriter>(
Mirko Bonadei2ab97f62019-07-18 11:44:12529 DecoderBitstreamFilename().c_str());
Niels Möllercbcbc222018-09-28 07:07:24530 });
Philipp Hanckefae4fb12021-01-26 17:05:56531 } else if (!IVFFilename().empty()) {
532 // Replace decoder with file writer if we're writing the ivf to a
533 // file instead.
534 stream_state->decoder_factory =
535 std::make_unique<test::FunctionVideoDecoderFactory>([]() {
536 return std::make_unique<DecoderIvfFileWriter>(IVFFilename().c_str(),
537 Codec());
538 });
539 } else {
540 stream_state->decoder_factory =
541 std::make_unique<InternalDecoderFactory>();
Benjamin Wright90ab76d2018-08-23 18:33:29542 }
Philip Eliasson2b068ce2020-08-03 15:55:10543 receive_config.decoder_factory = stream_state->decoder_factory.get();
Benjamin Wright90ab76d2018-08-23 18:33:29544 receive_config.decoders.push_back(decoder);
545
546 stream_state->receive_streams.emplace_back(
547 call->CreateVideoReceiveStream(std::move(receive_config)));
548 return stream_state;
549 }
550
551 static std::unique_ptr<test::RtpFileReader> CreateRtpReader(
552 const std::string& rtp_dump_path) {
553 std::unique_ptr<test::RtpFileReader> rtp_reader(test::RtpFileReader::Create(
554 test::RtpFileReader::kRtpDump, rtp_dump_path));
Peter Boström74f6e9e2016-04-04 15:56:10555 if (!rtp_reader) {
Benjamin Wright90ab76d2018-08-23 18:33:29556 rtp_reader.reset(test::RtpFileReader::Create(test::RtpFileReader::kPcap,
557 rtp_dump_path));
Peter Boström74f6e9e2016-04-04 15:56:10558 if (!rtp_reader) {
Benjamin Wright90ab76d2018-08-23 18:33:29559 fprintf(
560 stderr,
561 "Couldn't open input file as either a rtpdump or .pcap. Note "
562 "that .pcapng is not supported.\nTrying to interpret the file as "
563 "length/packet interleaved.\n");
564 rtp_reader.reset(test::RtpFileReader::Create(
565 test::RtpFileReader::kLengthPacketInterleaved, rtp_dump_path));
566 if (!rtp_reader) {
567 fprintf(stderr,
568 "Unable to open input file with any supported format\n");
569 return nullptr;
570 }
stefan@webrtc.org48ac2262015-03-02 16:18:56571 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56572 }
Benjamin Wright90ab76d2018-08-23 18:33:29573 return rtp_reader;
pbos@webrtc.org4b5625e2014-08-06 16:26:56574 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56575
Ilya Nikolaevskiy6a646902020-12-11 10:07:01576 static void ReplayPackets(Call* call,
577 test::RtpFileReader* rtp_reader,
578 TaskQueueBase* worker_thread) {
Benjamin Wright90ab76d2018-08-23 18:33:29579 int64_t replay_start_ms = -1;
580 int num_packets = 0;
581 std::map<uint32_t, int> unknown_packets;
Ilya Nikolaevskiy6a646902020-12-11 10:07:01582 rtc::Event event(/*manual_reset=*/false, /*initially_signalled=*/false);
Philipp Hanckef3a687a2021-03-15 11:04:39583 uint32_t start_timestamp = absl::GetFlag(FLAGS_start_timestamp);
584 uint32_t stop_timestamp = absl::GetFlag(FLAGS_stop_timestamp);
Benjamin Wright90ab76d2018-08-23 18:33:29585 while (true) {
586 int64_t now_ms = rtc::TimeMillis();
587 if (replay_start_ms == -1) {
588 replay_start_ms = now_ms;
589 }
philipel02f03962018-01-11 16:28:35590
Benjamin Wright90ab76d2018-08-23 18:33:29591 test::RtpPacket packet;
592 if (!rtp_reader->NextPacket(&packet)) {
pbos@webrtc.org4b5625e2014-08-06 16:26:56593 break;
594 }
Danil Chapovalov76a35d92021-06-08 10:30:46595 rtc::CopyOnWriteBuffer packet_buffer(packet.data, packet.length);
596 RtpPacket header;
597 header.Parse(packet_buffer);
598 if (header.Timestamp() < start_timestamp ||
599 header.Timestamp() > stop_timestamp) {
Philipp Hanckef3a687a2021-03-15 11:04:39600 continue;
601 }
Benjamin Wright90ab76d2018-08-23 18:33:29602
603 int64_t deliver_in_ms = replay_start_ms + packet.time_ms - now_ms;
604 if (deliver_in_ms > 0) {
605 SleepMs(deliver_in_ms);
606 }
607
608 ++num_packets;
Ilya Nikolaevskiy6a646902020-12-11 10:07:01609 PacketReceiver::DeliveryStatus result = PacketReceiver::DELIVERY_OK;
Danil Chapovalov5286dcf2022-07-18 15:04:56610 worker_thread->PostTask([&]() {
Danil Chapovalov76a35d92021-06-08 10:30:46611 result = call->Receiver()->DeliverPacket(webrtc::MediaType::VIDEO,
612 std::move(packet_buffer),
613 /* packet_time_us */ -1);
Ilya Nikolaevskiy6a646902020-12-11 10:07:01614 event.Set();
Danil Chapovalov5286dcf2022-07-18 15:04:56615 });
Markus Handell2cfc1af2022-08-19 08:16:48616 event.Wait(/*give_up_after=*/TimeDelta::Seconds(10));
Ilya Nikolaevskiy6a646902020-12-11 10:07:01617 switch (result) {
Benjamin Wright90ab76d2018-08-23 18:33:29618 case PacketReceiver::DELIVERY_OK:
619 break;
620 case PacketReceiver::DELIVERY_UNKNOWN_SSRC: {
Danil Chapovalov76a35d92021-06-08 10:30:46621 if (unknown_packets[header.Ssrc()] == 0)
622 fprintf(stderr, "Unknown SSRC: %u!\n", header.Ssrc());
623 ++unknown_packets[header.Ssrc()];
Benjamin Wright90ab76d2018-08-23 18:33:29624 break;
625 }
626 case PacketReceiver::DELIVERY_PACKET_ERROR: {
627 fprintf(stderr,
628 "Packet error, corrupt packets or incorrect setup?\n");
Benjamin Wright90ab76d2018-08-23 18:33:29629 fprintf(stderr, "Packet len=%zu pt=%u seq=%u ts=%u ssrc=0x%8x\n",
Danil Chapovalov76a35d92021-06-08 10:30:46630 packet.length, header.PayloadType(), header.SequenceNumber(),
631 header.Timestamp(), header.Ssrc());
Benjamin Wright90ab76d2018-08-23 18:33:29632 break;
633 }
philipp.hancke7b589602017-01-26 12:54:04634 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56635 }
Benjamin Wright90ab76d2018-08-23 18:33:29636 fprintf(stderr, "num_packets: %d\n", num_packets);
637
638 for (std::map<uint32_t, int>::const_iterator it = unknown_packets.begin();
639 it != unknown_packets.end(); ++it) {
640 fprintf(stderr, "Packets for unknown ssrc '%u': %d\n", it->first,
641 it->second);
642 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56643 }
Benjamin Wright90ab76d2018-08-23 18:33:29644}; // class RtpReplayer
pbos@webrtc.org4b5625e2014-08-06 16:26:56645
Benjamin Wright90ab76d2018-08-23 18:33:29646void RtpReplay() {
Mirko Bonadei2ab97f62019-07-18 11:44:12647 RtpReplayer::Replay(ConfigFile(), InputFile());
pbos@webrtc.org4b5625e2014-08-06 16:26:56648}
Benjamin Wright90ab76d2018-08-23 18:33:29649
pbos@webrtc.org4b5625e2014-08-06 16:26:56650} // namespace webrtc
651
652int main(int argc, char* argv[]) {
653 ::testing::InitGoogleTest(&argc, argv);
Mirko Bonadei2ab97f62019-07-18 11:44:12654 absl::ParseCommandLine(argc, argv);
oprypin6e09d872017-08-31 10:21:39655
Mirko Bonadei2ab97f62019-07-18 11:44:12656 RTC_CHECK(ValidatePayloadType(absl::GetFlag(FLAGS_media_payload_type)));
657 RTC_CHECK(ValidatePayloadType(absl::GetFlag(FLAGS_media_payload_type_rtx)));
658 RTC_CHECK(ValidateOptionalPayloadType(absl::GetFlag(FLAGS_red_payload_type)));
philipel752968e2017-12-05 11:40:28659 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 11:44:12660 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_red_payload_type_rtx)));
philipel752968e2017-12-05 11:40:28661 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 11:44:12662 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_ulpfec_payload_type)));
Yves Gerey665174f2018-06-19 13:03:05663 RTC_CHECK(
Philipp Hancke1c951ec2022-05-25 08:44:22664 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_flexfec_payload_type)));
665 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 11:44:12666 ValidateRtpHeaderExtensionId(absl::GetFlag(FLAGS_abs_send_time_id)));
667 RTC_CHECK(ValidateRtpHeaderExtensionId(
668 absl::GetFlag(FLAGS_transmission_offset_id)));
669 RTC_CHECK(ValidateInputFilenameNotEmpty(absl::GetFlag(FLAGS_input_file)));
pbos@webrtc.org4b5625e2014-08-06 16:26:56670
Philipp Hancke07b7dec2020-07-28 10:34:48671 rtc::ThreadManager::Instance()->WrapCurrentThread();
pbos@webrtc.org4b5625e2014-08-06 16:26:56672 webrtc::test::RunTest(webrtc::RtpReplay);
673 return 0;
674}