blob: 1faad28ff15532783ff89900b1074480d41a0c4e [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"
Danil Chapovalov83bbe912019-08-07 12:24:53 +020019#include "api/rtc_event_log/rtc_event_log.h"
Sergey Silkin626f7ff2019-09-12 10:51:19 +020020#include "api/task_queue/default_task_queue_factory.h"
Danil Chapovalov99b71df2018-10-26 15:57:48 +020021#include "api/test/video/function_video_decoder_factory.h"
Ilya Nikolaevskiy33aaa352020-01-21 13:38:19 +010022#include "api/transport/field_trial_based_config.h"
Philipp Hanckefae4fb12021-01-26 18:05:56 +010023#include "api/video/video_codec_type.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#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 09:11:00 -080027#include "media/engine/internal_decoder_factory.h"
Philipp Hanckefae4fb12021-01-26 18:05:56 +010028#include "modules/video_coding/utility/ivf_file_writer.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020029#include "rtc_base/checks.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020030#include "rtc_base/string_to_number.h"
Sam Zackrissonb45bdb52018-10-02 16:25:59 +020031#include "rtc_base/strings/json.h"
Steve Anton10542f22019-01-11 09:11:00 -080032#include "rtc_base/time_utils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020033#include "system_wrappers/include/clock.h"
34#include "system_wrappers/include/sleep.h"
Benjamin Wright8efafdf2019-01-11 10:48:42 -080035#include "test/call_config_utils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020036#include "test/call_test.h"
37#include "test/encoder_settings.h"
38#include "test/fake_decoder.h"
39#include "test/gtest.h"
40#include "test/null_transport.h"
41#include "test/rtp_file_reader.h"
Sergey Silkin626f7ff2019-09-12 10:51:19 +020042#include "test/rtp_header_parser.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020043#include "test/run_loop.h"
44#include "test/run_test.h"
Sebastian Janssonf1f363f2018-08-13 14:24:58 +020045#include "test/test_video_capturer.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020046#include "test/testsupport/frame_writer.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020047#include "test/video_renderer.h"
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +000048
Mirko Bonadei2ab97f62019-07-18 13:44:12 +020049// 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
67ABSL_FLAG(int,
68 media_payload_type_rtx,
69 webrtc::test::CallTest::kSendRtxPayloadType,
70 "Media over RTX payload type");
71
72ABSL_FLAG(int,
73 red_payload_type_rtx,
74 webrtc::test::CallTest::kRtxRedPayloadType,
75 "RED over RTX payload type");
76
77// Flag for SSRC.
78const std::string& DefaultSsrc() {
79 static const std::string ssrc =
80 std::to_string(webrtc::test::CallTest::kVideoSendSsrcs[0]);
81 return ssrc;
82}
83ABSL_FLAG(std::string, ssrc, DefaultSsrc().c_str(), "Incoming SSRC");
84
85const std::string& DefaultSsrcRtx() {
86 static const std::string ssrc_rtx =
87 std::to_string(webrtc::test::CallTest::kSendRtxSsrcs[0]);
88 return ssrc_rtx;
89}
90ABSL_FLAG(std::string, ssrc_rtx, DefaultSsrcRtx().c_str(), "Incoming RTX SSRC");
91
92// Flag for abs-send-time id.
93ABSL_FLAG(int, abs_send_time_id, -1, "RTP extension ID for abs-send-time");
94
95// Flag for transmission-offset id.
96ABSL_FLAG(int,
97 transmission_offset_id,
98 -1,
99 "RTP extension ID for transmission-offset");
100
101// Flag for rtpdump input file.
102ABSL_FLAG(std::string, input_file, "", "input file");
103
104ABSL_FLAG(std::string, config_file, "", "config file");
105
106// Flag for raw output files.
107ABSL_FLAG(std::string,
108 out_base,
109 "",
110 "Basename (excluding .jpg) for raw output");
111
112ABSL_FLAG(std::string,
113 decoder_bitstream_filename,
114 "",
115 "Decoder bitstream output file");
116
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100117ABSL_FLAG(std::string, decoder_ivf_filename, "", "Decoder ivf output file");
118
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200119// Flag for video codec.
120ABSL_FLAG(std::string, codec, "VP8", "Video codec");
121
oprypin6e09d872017-08-31 03:21:39 -0700122namespace {
123
124static bool ValidatePayloadType(int32_t payload_type) {
125 return payload_type > 0 && payload_type <= 127;
126}
127
128static bool ValidateSsrc(const char* ssrc_string) {
129 return rtc::StringToNumber<uint32_t>(ssrc_string).has_value();
130}
131
132static bool ValidateOptionalPayloadType(int32_t payload_type) {
133 return payload_type == -1 || ValidatePayloadType(payload_type);
134}
135
136static bool ValidateRtpHeaderExtensionId(int32_t extension_id) {
137 return extension_id >= -1 && extension_id < 15;
138}
139
140bool ValidateInputFilenameNotEmpty(const std::string& string) {
141 return !string.empty();
142}
143
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200144static int MediaPayloadType() {
145 return absl::GetFlag(FLAGS_media_payload_type);
146}
147
148static int RedPayloadType() {
149 return absl::GetFlag(FLAGS_red_payload_type);
150}
151
152static int UlpfecPayloadType() {
153 return absl::GetFlag(FLAGS_ulpfec_payload_type);
154}
155
156static int MediaPayloadTypeRtx() {
157 return absl::GetFlag(FLAGS_media_payload_type_rtx);
158}
159
160static int RedPayloadTypeRtx() {
161 return absl::GetFlag(FLAGS_red_payload_type_rtx);
162}
163
164static uint32_t Ssrc() {
165 return rtc::StringToNumber<uint32_t>(absl::GetFlag(FLAGS_ssrc)).value();
166}
167
168static uint32_t SsrcRtx() {
169 return rtc::StringToNumber<uint32_t>(absl::GetFlag(FLAGS_ssrc_rtx)).value();
170}
171
172static int AbsSendTimeId() {
173 return absl::GetFlag(FLAGS_abs_send_time_id);
174}
175
176static int TransmissionOffsetId() {
177 return absl::GetFlag(FLAGS_transmission_offset_id);
178}
179
180static std::string InputFile() {
181 return absl::GetFlag(FLAGS_input_file);
182}
183
184static std::string ConfigFile() {
185 return absl::GetFlag(FLAGS_config_file);
186}
187
188static std::string OutBase() {
189 return absl::GetFlag(FLAGS_out_base);
190}
191
192static std::string DecoderBitstreamFilename() {
193 return absl::GetFlag(FLAGS_decoder_bitstream_filename);
194}
195
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100196static std::string IVFFilename() {
197 return absl::GetFlag(FLAGS_decoder_ivf_filename);
198}
199
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200200static std::string Codec() {
201 return absl::GetFlag(FLAGS_codec);
202}
203
oprypin6e09d872017-08-31 03:21:39 -0700204} // namespace
205
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000206namespace webrtc {
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000207
208static const uint32_t kReceiverLocalSsrc = 0x123456;
209
nisse7ade7b32016-03-23 04:48:10 -0700210class FileRenderPassthrough : public rtc::VideoSinkInterface<VideoFrame> {
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000211 public:
nisse7ade7b32016-03-23 04:48:10 -0700212 FileRenderPassthrough(const std::string& basename,
213 rtc::VideoSinkInterface<VideoFrame>* renderer)
philipel99b63452017-08-25 07:24:21 -0700214 : basename_(basename), renderer_(renderer), file_(nullptr), count_(0) {}
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000215
Mirko Bonadeife055c12019-01-29 22:53:28 +0100216 ~FileRenderPassthrough() override {
Peter Boström74f6e9e2016-04-04 17:56:10 +0200217 if (file_)
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000218 fclose(file_);
219 }
220
221 private:
nisseeb83a1a2016-03-21 01:27:56 -0700222 void OnFrame(const VideoFrame& video_frame) override {
Peter Boström74f6e9e2016-04-04 17:56:10 +0200223 if (renderer_)
nisseeb83a1a2016-03-21 01:27:56 -0700224 renderer_->OnFrame(video_frame);
philipel99b63452017-08-25 07:24:21 -0700225
pbosbb36fdf2015-07-09 07:48:14 -0700226 if (basename_.empty())
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000227 return;
philipel99b63452017-08-25 07:24:21 -0700228
229 std::stringstream filename;
230 filename << basename_ << count_++ << "_" << video_frame.timestamp()
231 << ".jpg";
232
philipel99b63452017-08-25 07:24:21 -0700233 test::JpegFrameWriter frame_writer(filename.str());
234 RTC_CHECK(frame_writer.WriteFrame(video_frame, 100));
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000235 }
236
237 const std::string basename_;
nisse7ade7b32016-03-23 04:48:10 -0700238 rtc::VideoSinkInterface<VideoFrame>* const renderer_;
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000239 FILE* file_;
240 size_t count_;
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000241};
242
Niels Möllerf88a22c2018-06-19 17:05:03 +0200243class DecoderBitstreamFileWriter : public test::FakeDecoder {
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000244 public:
245 explicit DecoderBitstreamFileWriter(const char* filename)
246 : file_(fopen(filename, "wb")) {
Peter Boström74f6e9e2016-04-04 17:56:10 +0200247 RTC_DCHECK(file_);
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000248 }
Mirko Bonadeife055c12019-01-29 22:53:28 +0100249 ~DecoderBitstreamFileWriter() override { fclose(file_); }
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000250
Niels Möllerf88a22c2018-06-19 17:05:03 +0200251 int32_t Decode(const EncodedImage& encoded_frame,
Benjamin Wright8efafdf2019-01-11 10:48:42 -0800252 bool /* missing_frames */,
Benjamin Wright8efafdf2019-01-11 10:48:42 -0800253 int64_t /* render_time_ms */) override {
Niels Möller77536a22019-01-15 08:50:01 +0100254 if (fwrite(encoded_frame.data(), 1, encoded_frame.size(), file_) <
255 encoded_frame.size()) {
Niels Möllerf88a22c2018-06-19 17:05:03 +0200256 RTC_LOG_ERR(LS_ERROR) << "fwrite of encoded frame failed.";
257 return WEBRTC_VIDEO_CODEC_ERROR;
258 }
259 return WEBRTC_VIDEO_CODEC_OK;
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000260 }
261
262 private:
263 FILE* file_;
264};
265
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100266class DecoderIvfFileWriter : public test::FakeDecoder {
267 public:
268 explicit DecoderIvfFileWriter(const char* filename, const std::string& codec)
269 : file_writer_(
270 IvfFileWriter::Wrap(FileWrapper::OpenWriteOnly(filename), 0)) {
271 RTC_DCHECK(file_writer_.get());
272 if (codec == "VP8") {
273 video_codec_type_ = VideoCodecType::kVideoCodecVP8;
274 } else if (codec == "VP9") {
275 video_codec_type_ = VideoCodecType::kVideoCodecVP9;
276 } else if (codec == "H264") {
277 video_codec_type_ = VideoCodecType::kVideoCodecH264;
278 } else {
279 RTC_LOG(LS_ERROR) << "Unsupported video codec " << codec;
280 RTC_DCHECK(false);
281 }
282 }
283 ~DecoderIvfFileWriter() override { file_writer_->Close(); }
284
285 int32_t Decode(const EncodedImage& encoded_frame,
286 bool /* missing_frames */,
287 int64_t render_time_ms) override {
288 if (!file_writer_->WriteFrame(encoded_frame, video_codec_type_)) {
289 return WEBRTC_VIDEO_CODEC_ERROR;
290 }
291 return WEBRTC_VIDEO_CODEC_OK;
292 }
293
294 private:
295 std::unique_ptr<IvfFileWriter> file_writer_;
296 VideoCodecType video_codec_type_;
297};
298
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700299// The RtpReplayer is responsible for parsing the configuration provided by the
300// user, setting up the windows, recieve streams and decoders and then replaying
301// the provided RTP dump.
302class RtpReplayer final {
303 public:
304 // Replay a rtp dump with an optional json configuration.
305 static void Replay(const std::string& replay_config_path,
306 const std::string& rtp_dump_path) {
Sergey Silkin626f7ff2019-09-12 10:51:19 +0200307 std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
308 webrtc::CreateDefaultTaskQueueFactory();
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100309 auto worker_thread = task_queue_factory->CreateTaskQueue(
310 "worker_thread", TaskQueueFactory::Priority::NORMAL);
311 rtc::Event sync_event(/*manual_reset=*/false,
312 /*initially_signalled=*/false);
Danil Chapovalov83bbe912019-08-07 12:24:53 +0200313 webrtc::RtcEventLogNull event_log;
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700314 Call::Config call_config(&event_log);
Sergey Silkin626f7ff2019-09-12 10:51:19 +0200315 call_config.task_queue_factory = task_queue_factory.get();
Ilya Nikolaevskiy33aaa352020-01-21 13:38:19 +0100316 call_config.trials = new FieldTrialBasedConfig();
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100317 std::unique_ptr<Call> call;
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700318 std::unique_ptr<StreamState> stream_state;
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100319
320 // Creation of the streams must happen inside a task queue because it is
321 // resued as a worker thread.
322 worker_thread->PostTask(ToQueuedTask([&]() {
323 call.reset(Call::Create(call_config));
324
325 // Attempt to load the configuration
326 if (replay_config_path.empty()) {
327 stream_state = ConfigureFromFlags(rtp_dump_path, call.get());
328 } else {
329 stream_state = ConfigureFromFile(replay_config_path, call.get());
330 }
331
332 if (stream_state == nullptr) {
333 return;
334 }
335 // Start replaying the provided stream now that it has been configured.
336 // VideoReceiveStreams must be started on the same thread as they were
337 // created on.
338 for (const auto& receive_stream : stream_state->receive_streams) {
339 receive_stream->Start();
340 }
341 sync_event.Set();
342 }));
343
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700344 // Attempt to create an RtpReader from the input file.
345 std::unique_ptr<test::RtpFileReader> rtp_reader =
346 CreateRtpReader(rtp_dump_path);
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100347
348 // Wait for streams creation.
349 sync_event.Wait(/*give_up_after_ms=*/10000);
350
351 if (stream_state == nullptr || rtp_reader == nullptr) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700352 return;
353 }
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100354
355 ReplayPackets(call.get(), rtp_reader.get(), worker_thread.get());
356
357 // Destruction of streams and the call must happen on the same thread as
358 // their creation.
359 worker_thread->PostTask(ToQueuedTask([&]() {
360 for (const auto& receive_stream : stream_state->receive_streams) {
361 call->DestroyVideoReceiveStream(receive_stream);
362 }
363 call.reset();
364 sync_event.Set();
365 }));
366 sync_event.Wait(/*give_up_after_ms=*/10000);
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000367 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000368
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700369 private:
370 // Holds all the shared memory structures required for a recieve stream. This
371 // structure is used to prevent members being deallocated before the replay
372 // has been finished.
373 struct StreamState {
374 test::NullTransport transport;
375 std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
376 std::vector<VideoReceiveStream*> receive_streams;
Niels Möllercbcbc222018-09-28 09:07:24 +0200377 std::unique_ptr<VideoDecoderFactory> decoder_factory;
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700378 };
379
380 // Loads multiple configurations from the provided configuration file.
381 static std::unique_ptr<StreamState> ConfigureFromFile(
382 const std::string& config_path,
383 Call* call) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200384 auto stream_state = std::make_unique<StreamState>();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700385 // Parse the configuration file.
386 std::ifstream config_file(config_path);
387 std::stringstream raw_json_buffer;
388 raw_json_buffer << config_file.rdbuf();
389 std::string raw_json = raw_json_buffer.str();
390 Json::Reader json_reader;
391 Json::Value json_configs;
392 if (!json_reader.parse(raw_json, json_configs)) {
393 fprintf(stderr, "Error parsing JSON config\n");
394 fprintf(stderr, "%s\n", json_reader.getFormatedErrorMessages().c_str());
395 return nullptr;
396 }
397
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200398 stream_state->decoder_factory = std::make_unique<InternalDecoderFactory>();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700399 size_t config_count = 0;
400 for (const auto& json : json_configs) {
401 // Create the configuration and parse the JSON into the config.
Benjamin Wright8efafdf2019-01-11 10:48:42 -0800402 auto receive_config =
403 ParseVideoReceiveStreamJsonConfig(&(stream_state->transport), json);
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700404 // Instantiate the underlying decoder.
405 for (auto& decoder : receive_config.decoders) {
Niels Möllercbcbc222018-09-28 09:07:24 +0200406 decoder = test::CreateMatchingDecoder(decoder.payload_type,
407 decoder.video_format.name);
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700408 }
409 // Create a window for this config.
410 std::stringstream window_title;
411 window_title << "Playback Video (" << config_count++ << ")";
412 stream_state->sinks.emplace_back(
413 test::VideoRenderer::Create(window_title.str().c_str(), 640, 480));
414 // Create a receive stream for this config.
415 receive_config.renderer = stream_state->sinks.back().get();
Philip Eliasson2b068ce2020-08-03 15:55:10 +0000416 receive_config.decoder_factory = stream_state->decoder_factory.get();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700417 stream_state->receive_streams.emplace_back(
418 call->CreateVideoReceiveStream(std::move(receive_config)));
419 }
420 return stream_state;
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000421 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000422
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700423 // Loads the base configuration from flags passed in on the commandline.
424 static std::unique_ptr<StreamState> ConfigureFromFlags(
425 const std::string& rtp_dump_path,
426 Call* call) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200427 auto stream_state = std::make_unique<StreamState>();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700428 // Create the video renderers. We must add both to the stream state to keep
429 // them from deallocating.
430 std::stringstream window_title;
431 window_title << "Playback Video (" << rtp_dump_path << ")";
432 std::unique_ptr<test::VideoRenderer> playback_video(
433 test::VideoRenderer::Create(window_title.str().c_str(), 640, 480));
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200434 auto file_passthrough = std::make_unique<FileRenderPassthrough>(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200435 OutBase(), playback_video.get());
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700436 stream_state->sinks.push_back(std::move(playback_video));
437 stream_state->sinks.push_back(std::move(file_passthrough));
438 // Setup the configuration from the flags.
439 VideoReceiveStream::Config receive_config(&(stream_state->transport));
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200440 receive_config.rtp.remote_ssrc = Ssrc();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700441 receive_config.rtp.local_ssrc = kReceiverLocalSsrc;
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200442 receive_config.rtp.rtx_ssrc = SsrcRtx();
443 receive_config.rtp.rtx_associated_payload_types[MediaPayloadTypeRtx()] =
444 MediaPayloadType();
445 receive_config.rtp.rtx_associated_payload_types[RedPayloadTypeRtx()] =
446 RedPayloadType();
447 receive_config.rtp.ulpfec_payload_type = UlpfecPayloadType();
448 receive_config.rtp.red_payload_type = RedPayloadType();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700449 receive_config.rtp.nack.rtp_history_ms = 1000;
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200450 if (TransmissionOffsetId() != -1) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700451 receive_config.rtp.extensions.push_back(RtpExtension(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200452 RtpExtension::kTimestampOffsetUri, TransmissionOffsetId()));
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700453 }
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200454 if (AbsSendTimeId() != -1) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700455 receive_config.rtp.extensions.push_back(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200456 RtpExtension(RtpExtension::kAbsSendTimeUri, AbsSendTimeId()));
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700457 }
458 receive_config.renderer = stream_state->sinks.back().get();
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000459
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700460 // Setup the receiving stream
461 VideoReceiveStream::Decoder decoder;
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200462 decoder = test::CreateMatchingDecoder(MediaPayloadType(), Codec());
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100463 if (!DecoderBitstreamFilename().empty()) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700464 // Replace decoder with file writer if we're writing the bitstream to a
465 // file instead.
Niels Möllercbcbc222018-09-28 09:07:24 +0200466 stream_state->decoder_factory =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200467 std::make_unique<test::FunctionVideoDecoderFactory>([]() {
468 return std::make_unique<DecoderBitstreamFileWriter>(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200469 DecoderBitstreamFilename().c_str());
Niels Möllercbcbc222018-09-28 09:07:24 +0200470 });
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100471 } else if (!IVFFilename().empty()) {
472 // Replace decoder with file writer if we're writing the ivf to a
473 // file instead.
474 stream_state->decoder_factory =
475 std::make_unique<test::FunctionVideoDecoderFactory>([]() {
476 return std::make_unique<DecoderIvfFileWriter>(IVFFilename().c_str(),
477 Codec());
478 });
479 } else {
480 stream_state->decoder_factory =
481 std::make_unique<InternalDecoderFactory>();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700482 }
Philip Eliasson2b068ce2020-08-03 15:55:10 +0000483 receive_config.decoder_factory = stream_state->decoder_factory.get();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700484 receive_config.decoders.push_back(decoder);
485
486 stream_state->receive_streams.emplace_back(
487 call->CreateVideoReceiveStream(std::move(receive_config)));
488 return stream_state;
489 }
490
491 static std::unique_ptr<test::RtpFileReader> CreateRtpReader(
492 const std::string& rtp_dump_path) {
493 std::unique_ptr<test::RtpFileReader> rtp_reader(test::RtpFileReader::Create(
494 test::RtpFileReader::kRtpDump, rtp_dump_path));
Peter Boström74f6e9e2016-04-04 17:56:10 +0200495 if (!rtp_reader) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700496 rtp_reader.reset(test::RtpFileReader::Create(test::RtpFileReader::kPcap,
497 rtp_dump_path));
Peter Boström74f6e9e2016-04-04 17:56:10 +0200498 if (!rtp_reader) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700499 fprintf(
500 stderr,
501 "Couldn't open input file as either a rtpdump or .pcap. Note "
502 "that .pcapng is not supported.\nTrying to interpret the file as "
503 "length/packet interleaved.\n");
504 rtp_reader.reset(test::RtpFileReader::Create(
505 test::RtpFileReader::kLengthPacketInterleaved, rtp_dump_path));
506 if (!rtp_reader) {
507 fprintf(stderr,
508 "Unable to open input file with any supported format\n");
509 return nullptr;
510 }
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000511 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000512 }
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700513 return rtp_reader;
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000514 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000515
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100516 static void ReplayPackets(Call* call,
517 test::RtpFileReader* rtp_reader,
518 TaskQueueBase* worker_thread) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700519 int64_t replay_start_ms = -1;
520 int num_packets = 0;
521 std::map<uint32_t, int> unknown_packets;
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100522 rtc::Event event(/*manual_reset=*/false, /*initially_signalled=*/false);
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700523 while (true) {
524 int64_t now_ms = rtc::TimeMillis();
525 if (replay_start_ms == -1) {
526 replay_start_ms = now_ms;
527 }
philipel02f03962018-01-11 17:28:35 +0100528
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700529 test::RtpPacket packet;
530 if (!rtp_reader->NextPacket(&packet)) {
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000531 break;
532 }
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700533
534 int64_t deliver_in_ms = replay_start_ms + packet.time_ms - now_ms;
535 if (deliver_in_ms > 0) {
536 SleepMs(deliver_in_ms);
537 }
538
539 ++num_packets;
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100540 PacketReceiver::DeliveryStatus result = PacketReceiver::DELIVERY_OK;
541 worker_thread->PostTask(ToQueuedTask([&]() {
542 result = call->Receiver()->DeliverPacket(
543 webrtc::MediaType::VIDEO,
544 rtc::CopyOnWriteBuffer(packet.data, packet.length),
545 /* packet_time_us */ -1);
546 event.Set();
547 }));
548 event.Wait(/*give_up_after_ms=*/10000);
549 switch (result) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700550 case PacketReceiver::DELIVERY_OK:
551 break;
552 case PacketReceiver::DELIVERY_UNKNOWN_SSRC: {
553 RTPHeader header;
Sergey Silkin626f7ff2019-09-12 10:51:19 +0200554 std::unique_ptr<RtpHeaderParser> parser(
555 RtpHeaderParser::CreateForTest());
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700556 parser->Parse(packet.data, packet.length, &header);
557 if (unknown_packets[header.ssrc] == 0)
558 fprintf(stderr, "Unknown SSRC: %u!\n", header.ssrc);
559 ++unknown_packets[header.ssrc];
560 break;
561 }
562 case PacketReceiver::DELIVERY_PACKET_ERROR: {
563 fprintf(stderr,
564 "Packet error, corrupt packets or incorrect setup?\n");
565 RTPHeader header;
Sergey Silkin626f7ff2019-09-12 10:51:19 +0200566 std::unique_ptr<RtpHeaderParser> parser(
567 RtpHeaderParser::CreateForTest());
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700568 parser->Parse(packet.data, packet.length, &header);
569 fprintf(stderr, "Packet len=%zu pt=%u seq=%u ts=%u ssrc=0x%8x\n",
570 packet.length, header.payloadType, header.sequenceNumber,
571 header.timestamp, header.ssrc);
572 break;
573 }
philipp.hancke7b589602017-01-26 04:54:04 -0800574 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000575 }
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700576 fprintf(stderr, "num_packets: %d\n", num_packets);
577
578 for (std::map<uint32_t, int>::const_iterator it = unknown_packets.begin();
579 it != unknown_packets.end(); ++it) {
580 fprintf(stderr, "Packets for unknown ssrc '%u': %d\n", it->first,
581 it->second);
582 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000583 }
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700584}; // class RtpReplayer
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000585
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700586void RtpReplay() {
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200587 RtpReplayer::Replay(ConfigFile(), InputFile());
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000588}
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700589
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000590} // namespace webrtc
591
592int main(int argc, char* argv[]) {
593 ::testing::InitGoogleTest(&argc, argv);
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200594 absl::ParseCommandLine(argc, argv);
oprypin6e09d872017-08-31 03:21:39 -0700595
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200596 RTC_CHECK(ValidatePayloadType(absl::GetFlag(FLAGS_media_payload_type)));
597 RTC_CHECK(ValidatePayloadType(absl::GetFlag(FLAGS_media_payload_type_rtx)));
598 RTC_CHECK(ValidateOptionalPayloadType(absl::GetFlag(FLAGS_red_payload_type)));
philipel752968e2017-12-05 12:40:28 +0100599 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200600 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_red_payload_type_rtx)));
philipel752968e2017-12-05 12:40:28 +0100601 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200602 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_ulpfec_payload_type)));
603 RTC_CHECK(ValidateSsrc(absl::GetFlag(FLAGS_ssrc).c_str()));
604 RTC_CHECK(ValidateSsrc(absl::GetFlag(FLAGS_ssrc_rtx).c_str()));
Yves Gerey665174f2018-06-19 15:03:05 +0200605 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200606 ValidateRtpHeaderExtensionId(absl::GetFlag(FLAGS_abs_send_time_id)));
607 RTC_CHECK(ValidateRtpHeaderExtensionId(
608 absl::GetFlag(FLAGS_transmission_offset_id)));
609 RTC_CHECK(ValidateInputFilenameNotEmpty(absl::GetFlag(FLAGS_input_file)));
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000610
Philipp Hancke07b7dec2020-07-28 12:34:48 +0200611 rtc::ThreadManager::Instance()->WrapCurrentThread();
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000612 webrtc::test::RunTest(webrtc::RtpReplay);
613 return 0;
614}