blob: 416da728854e0c3819adb6f5739a52ce76f5437c [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"
Danil Chapovalov76a35d92021-06-08 12:30:46 +020028#include "modules/rtp_rtcp/source/rtp_packet.h"
Philipp Hanckefae4fb12021-01-26 18:05:56 +010029#include "modules/video_coding/utility/ivf_file_writer.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020030#include "rtc_base/checks.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020031#include "rtc_base/string_to_number.h"
Sam Zackrissonb45bdb52018-10-02 16:25:59 +020032#include "rtc_base/strings/json.h"
Steve Anton10542f22019-01-11 09:11:00 -080033#include "rtc_base/time_utils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020034#include "system_wrappers/include/clock.h"
35#include "system_wrappers/include/sleep.h"
Benjamin Wright8efafdf2019-01-11 10:48:42 -080036#include "test/call_config_utils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020037#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 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
Philipp Hanckec060ce42021-03-15 13:15:59 +010077// Flag for SSRC and RTX SSRC.
78ABSL_FLAG(uint32_t,
79 ssrc,
80 webrtc::test::CallTest::kVideoSendSsrcs[0],
81 "Incoming SSRC");
82ABSL_FLAG(uint32_t,
83 ssrc_rtx,
84 webrtc::test::CallTest::kSendRtxSsrcs[0],
85 "Incoming RTX SSRC");
Mirko Bonadei2ab97f62019-07-18 13:44:12 +020086
87// Flag for abs-send-time id.
88ABSL_FLAG(int, abs_send_time_id, -1, "RTP extension ID for abs-send-time");
89
90// Flag for transmission-offset id.
91ABSL_FLAG(int,
92 transmission_offset_id,
93 -1,
94 "RTP extension ID for transmission-offset");
95
96// Flag for rtpdump input file.
97ABSL_FLAG(std::string, input_file, "", "input file");
98
99ABSL_FLAG(std::string, config_file, "", "config file");
100
101// Flag for raw output files.
102ABSL_FLAG(std::string,
103 out_base,
104 "",
105 "Basename (excluding .jpg) for raw output");
106
107ABSL_FLAG(std::string,
108 decoder_bitstream_filename,
109 "",
110 "Decoder bitstream output file");
111
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100112ABSL_FLAG(std::string, decoder_ivf_filename, "", "Decoder ivf output file");
113
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200114// Flag for video codec.
115ABSL_FLAG(std::string, codec, "VP8", "Video codec");
116
Philipp Hanckef3a687a2021-03-15 12:04:39 +0100117// Flags for rtp start and stop timestamp.
118ABSL_FLAG(uint32_t,
119 start_timestamp,
120 0,
121 "RTP start timestamp, packets with smaller timestamp will be ignored "
122 "(no wraparound)");
123ABSL_FLAG(uint32_t,
124 stop_timestamp,
125 4294967295,
126 "RTP stop timestamp, packets with larger timestamp will be ignored "
127 "(no wraparound)");
128
Jonathan Lennox03df29c2021-07-13 12:41:31 -0400129// Flags for render window width and height
130ABSL_FLAG(uint32_t, render_width, 640, "Width of render window");
131ABSL_FLAG(uint32_t, render_height, 480, "Height of render window");
132
oprypin6e09d872017-08-31 03:21:39 -0700133namespace {
134
135static bool ValidatePayloadType(int32_t payload_type) {
136 return payload_type > 0 && payload_type <= 127;
137}
138
oprypin6e09d872017-08-31 03:21:39 -0700139static bool ValidateOptionalPayloadType(int32_t payload_type) {
140 return payload_type == -1 || ValidatePayloadType(payload_type);
141}
142
143static bool ValidateRtpHeaderExtensionId(int32_t extension_id) {
144 return extension_id >= -1 && extension_id < 15;
145}
146
147bool ValidateInputFilenameNotEmpty(const std::string& string) {
148 return !string.empty();
149}
150
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200151static int MediaPayloadType() {
152 return absl::GetFlag(FLAGS_media_payload_type);
153}
154
155static int RedPayloadType() {
156 return absl::GetFlag(FLAGS_red_payload_type);
157}
158
159static int UlpfecPayloadType() {
160 return absl::GetFlag(FLAGS_ulpfec_payload_type);
161}
162
163static int MediaPayloadTypeRtx() {
164 return absl::GetFlag(FLAGS_media_payload_type_rtx);
165}
166
167static int RedPayloadTypeRtx() {
168 return absl::GetFlag(FLAGS_red_payload_type_rtx);
169}
170
171static uint32_t Ssrc() {
Philipp Hanckec060ce42021-03-15 13:15:59 +0100172 return absl::GetFlag(FLAGS_ssrc);
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200173}
174
175static uint32_t SsrcRtx() {
Philipp Hanckec060ce42021-03-15 13:15:59 +0100176 return absl::GetFlag(FLAGS_ssrc_rtx);
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200177}
178
179static int AbsSendTimeId() {
180 return absl::GetFlag(FLAGS_abs_send_time_id);
181}
182
183static int TransmissionOffsetId() {
184 return absl::GetFlag(FLAGS_transmission_offset_id);
185}
186
187static std::string InputFile() {
188 return absl::GetFlag(FLAGS_input_file);
189}
190
191static std::string ConfigFile() {
192 return absl::GetFlag(FLAGS_config_file);
193}
194
195static std::string OutBase() {
196 return absl::GetFlag(FLAGS_out_base);
197}
198
199static std::string DecoderBitstreamFilename() {
200 return absl::GetFlag(FLAGS_decoder_bitstream_filename);
201}
202
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100203static std::string IVFFilename() {
204 return absl::GetFlag(FLAGS_decoder_ivf_filename);
205}
206
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200207static std::string Codec() {
208 return absl::GetFlag(FLAGS_codec);
209}
210
Jonathan Lennox03df29c2021-07-13 12:41:31 -0400211static uint32_t RenderWidth() {
212 return absl::GetFlag(FLAGS_render_width);
213}
214
215static uint32_t RenderHeight() {
216 return absl::GetFlag(FLAGS_render_height);
217}
218
oprypin6e09d872017-08-31 03:21:39 -0700219} // namespace
220
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000221namespace webrtc {
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000222
223static const uint32_t kReceiverLocalSsrc = 0x123456;
224
nisse7ade7b32016-03-23 04:48:10 -0700225class FileRenderPassthrough : public rtc::VideoSinkInterface<VideoFrame> {
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000226 public:
nisse7ade7b32016-03-23 04:48:10 -0700227 FileRenderPassthrough(const std::string& basename,
228 rtc::VideoSinkInterface<VideoFrame>* renderer)
philipel99b63452017-08-25 07:24:21 -0700229 : basename_(basename), renderer_(renderer), file_(nullptr), count_(0) {}
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000230
Mirko Bonadeife055c12019-01-29 22:53:28 +0100231 ~FileRenderPassthrough() override {
Peter Boström74f6e9e2016-04-04 17:56:10 +0200232 if (file_)
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000233 fclose(file_);
234 }
235
236 private:
nisseeb83a1a2016-03-21 01:27:56 -0700237 void OnFrame(const VideoFrame& video_frame) override {
Peter Boström74f6e9e2016-04-04 17:56:10 +0200238 if (renderer_)
nisseeb83a1a2016-03-21 01:27:56 -0700239 renderer_->OnFrame(video_frame);
philipel99b63452017-08-25 07:24:21 -0700240
pbosbb36fdf2015-07-09 07:48:14 -0700241 if (basename_.empty())
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000242 return;
philipel99b63452017-08-25 07:24:21 -0700243
244 std::stringstream filename;
245 filename << basename_ << count_++ << "_" << video_frame.timestamp()
246 << ".jpg";
247
philipel99b63452017-08-25 07:24:21 -0700248 test::JpegFrameWriter frame_writer(filename.str());
249 RTC_CHECK(frame_writer.WriteFrame(video_frame, 100));
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000250 }
251
252 const std::string basename_;
nisse7ade7b32016-03-23 04:48:10 -0700253 rtc::VideoSinkInterface<VideoFrame>* const renderer_;
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000254 FILE* file_;
255 size_t count_;
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000256};
257
Niels Möllerf88a22c2018-06-19 17:05:03 +0200258class DecoderBitstreamFileWriter : public test::FakeDecoder {
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000259 public:
260 explicit DecoderBitstreamFileWriter(const char* filename)
261 : file_(fopen(filename, "wb")) {
Peter Boström74f6e9e2016-04-04 17:56:10 +0200262 RTC_DCHECK(file_);
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000263 }
Mirko Bonadeife055c12019-01-29 22:53:28 +0100264 ~DecoderBitstreamFileWriter() override { fclose(file_); }
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000265
Niels Möllerf88a22c2018-06-19 17:05:03 +0200266 int32_t Decode(const EncodedImage& encoded_frame,
Benjamin Wright8efafdf2019-01-11 10:48:42 -0800267 bool /* missing_frames */,
Benjamin Wright8efafdf2019-01-11 10:48:42 -0800268 int64_t /* render_time_ms */) override {
Niels Möller77536a22019-01-15 08:50:01 +0100269 if (fwrite(encoded_frame.data(), 1, encoded_frame.size(), file_) <
270 encoded_frame.size()) {
Niels Möllerf88a22c2018-06-19 17:05:03 +0200271 RTC_LOG_ERR(LS_ERROR) << "fwrite of encoded frame failed.";
272 return WEBRTC_VIDEO_CODEC_ERROR;
273 }
274 return WEBRTC_VIDEO_CODEC_OK;
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000275 }
276
277 private:
278 FILE* file_;
279};
280
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100281class DecoderIvfFileWriter : public test::FakeDecoder {
282 public:
283 explicit DecoderIvfFileWriter(const char* filename, const std::string& codec)
284 : file_writer_(
285 IvfFileWriter::Wrap(FileWrapper::OpenWriteOnly(filename), 0)) {
286 RTC_DCHECK(file_writer_.get());
287 if (codec == "VP8") {
288 video_codec_type_ = VideoCodecType::kVideoCodecVP8;
289 } else if (codec == "VP9") {
290 video_codec_type_ = VideoCodecType::kVideoCodecVP9;
291 } else if (codec == "H264") {
292 video_codec_type_ = VideoCodecType::kVideoCodecH264;
Philipp Hanckede7fcff2022-05-12 18:13:16 +0200293 } else if (codec == "AV1") {
294 video_codec_type_ = VideoCodecType::kVideoCodecAV1;
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100295 } else {
296 RTC_LOG(LS_ERROR) << "Unsupported video codec " << codec;
Artem Titovd3251962021-11-15 16:57:07 +0100297 RTC_DCHECK_NOTREACHED();
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100298 }
299 }
300 ~DecoderIvfFileWriter() override { file_writer_->Close(); }
301
302 int32_t Decode(const EncodedImage& encoded_frame,
303 bool /* missing_frames */,
304 int64_t render_time_ms) override {
305 if (!file_writer_->WriteFrame(encoded_frame, video_codec_type_)) {
306 return WEBRTC_VIDEO_CODEC_ERROR;
307 }
308 return WEBRTC_VIDEO_CODEC_OK;
309 }
310
311 private:
312 std::unique_ptr<IvfFileWriter> file_writer_;
313 VideoCodecType video_codec_type_;
314};
315
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700316// The RtpReplayer is responsible for parsing the configuration provided by the
Niels Möllerbe74b802022-03-18 14:10:15 +0100317// user, setting up the windows, receive streams and decoders and then replaying
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700318// the provided RTP dump.
319class RtpReplayer final {
320 public:
321 // Replay a rtp dump with an optional json configuration.
322 static void Replay(const std::string& replay_config_path,
323 const std::string& rtp_dump_path) {
Sergey Silkin626f7ff2019-09-12 10:51:19 +0200324 std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
325 webrtc::CreateDefaultTaskQueueFactory();
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100326 auto worker_thread = task_queue_factory->CreateTaskQueue(
327 "worker_thread", TaskQueueFactory::Priority::NORMAL);
328 rtc::Event sync_event(/*manual_reset=*/false,
329 /*initially_signalled=*/false);
Danil Chapovalov83bbe912019-08-07 12:24:53 +0200330 webrtc::RtcEventLogNull event_log;
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700331 Call::Config call_config(&event_log);
Sergey Silkin626f7ff2019-09-12 10:51:19 +0200332 call_config.task_queue_factory = task_queue_factory.get();
Ilya Nikolaevskiy33aaa352020-01-21 13:38:19 +0100333 call_config.trials = new FieldTrialBasedConfig();
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100334 std::unique_ptr<Call> call;
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700335 std::unique_ptr<StreamState> stream_state;
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100336
337 // Creation of the streams must happen inside a task queue because it is
338 // resued as a worker thread.
339 worker_thread->PostTask(ToQueuedTask([&]() {
340 call.reset(Call::Create(call_config));
341
342 // Attempt to load the configuration
343 if (replay_config_path.empty()) {
344 stream_state = ConfigureFromFlags(rtp_dump_path, call.get());
345 } else {
346 stream_state = ConfigureFromFile(replay_config_path, call.get());
347 }
348
349 if (stream_state == nullptr) {
350 return;
351 }
352 // Start replaying the provided stream now that it has been configured.
353 // VideoReceiveStreams must be started on the same thread as they were
354 // created on.
355 for (const auto& receive_stream : stream_state->receive_streams) {
356 receive_stream->Start();
357 }
358 sync_event.Set();
359 }));
360
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700361 // Attempt to create an RtpReader from the input file.
362 std::unique_ptr<test::RtpFileReader> rtp_reader =
363 CreateRtpReader(rtp_dump_path);
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100364
365 // Wait for streams creation.
366 sync_event.Wait(/*give_up_after_ms=*/10000);
367
368 if (stream_state == nullptr || rtp_reader == nullptr) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700369 return;
370 }
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100371
372 ReplayPackets(call.get(), rtp_reader.get(), worker_thread.get());
373
374 // Destruction of streams and the call must happen on the same thread as
375 // their creation.
376 worker_thread->PostTask(ToQueuedTask([&]() {
377 for (const auto& receive_stream : stream_state->receive_streams) {
378 call->DestroyVideoReceiveStream(receive_stream);
379 }
380 call.reset();
381 sync_event.Set();
382 }));
383 sync_event.Wait(/*give_up_after_ms=*/10000);
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000384 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000385
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700386 private:
Niels Möllerbe74b802022-03-18 14:10:15 +0100387 // Holds all the shared memory structures required for a receive stream. This
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700388 // structure is used to prevent members being deallocated before the replay
389 // has been finished.
390 struct StreamState {
391 test::NullTransport transport;
392 std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
Tommif6f45432022-05-20 15:21:20 +0200393 std::vector<VideoReceiveStreamInterface*> receive_streams;
Niels Möllercbcbc222018-09-28 09:07:24 +0200394 std::unique_ptr<VideoDecoderFactory> decoder_factory;
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700395 };
396
397 // Loads multiple configurations from the provided configuration file.
398 static std::unique_ptr<StreamState> ConfigureFromFile(
399 const std::string& config_path,
400 Call* call) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200401 auto stream_state = std::make_unique<StreamState>();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700402 // Parse the configuration file.
403 std::ifstream config_file(config_path);
404 std::stringstream raw_json_buffer;
405 raw_json_buffer << config_file.rdbuf();
406 std::string raw_json = raw_json_buffer.str();
Mirko Bonadeie99f6872021-06-24 15:24:59 +0200407 Json::CharReaderBuilder builder;
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700408 Json::Value json_configs;
Mirko Bonadeie99f6872021-06-24 15:24:59 +0200409 std::string error_message;
410 std::unique_ptr<Json::CharReader> json_reader(builder.newCharReader());
411 if (!json_reader->parse(raw_json.data(), raw_json.data() + raw_json.size(),
412 &json_configs, &error_message)) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700413 fprintf(stderr, "Error parsing JSON config\n");
Mirko Bonadeie99f6872021-06-24 15:24:59 +0200414 fprintf(stderr, "%s\n", error_message.c_str());
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700415 return nullptr;
416 }
417
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200418 stream_state->decoder_factory = std::make_unique<InternalDecoderFactory>();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700419 size_t config_count = 0;
420 for (const auto& json : json_configs) {
421 // Create the configuration and parse the JSON into the config.
Benjamin Wright8efafdf2019-01-11 10:48:42 -0800422 auto receive_config =
423 ParseVideoReceiveStreamJsonConfig(&(stream_state->transport), json);
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700424 // Instantiate the underlying decoder.
425 for (auto& decoder : receive_config.decoders) {
Niels Möllercbcbc222018-09-28 09:07:24 +0200426 decoder = test::CreateMatchingDecoder(decoder.payload_type,
427 decoder.video_format.name);
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700428 }
429 // Create a window for this config.
430 std::stringstream window_title;
431 window_title << "Playback Video (" << config_count++ << ")";
Jonathan Lennox03df29c2021-07-13 12:41:31 -0400432 stream_state->sinks.emplace_back(test::VideoRenderer::Create(
433 window_title.str().c_str(), RenderWidth(), RenderHeight()));
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700434 // Create a receive stream for this config.
435 receive_config.renderer = stream_state->sinks.back().get();
Philip Eliasson2b068ce2020-08-03 15:55:10 +0000436 receive_config.decoder_factory = stream_state->decoder_factory.get();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700437 stream_state->receive_streams.emplace_back(
438 call->CreateVideoReceiveStream(std::move(receive_config)));
439 }
440 return stream_state;
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000441 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000442
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700443 // Loads the base configuration from flags passed in on the commandline.
444 static std::unique_ptr<StreamState> ConfigureFromFlags(
445 const std::string& rtp_dump_path,
446 Call* call) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200447 auto stream_state = std::make_unique<StreamState>();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700448 // Create the video renderers. We must add both to the stream state to keep
449 // them from deallocating.
450 std::stringstream window_title;
451 window_title << "Playback Video (" << rtp_dump_path << ")";
452 std::unique_ptr<test::VideoRenderer> playback_video(
Jonathan Lennox03df29c2021-07-13 12:41:31 -0400453 test::VideoRenderer::Create(window_title.str().c_str(), RenderWidth(),
454 RenderHeight()));
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200455 auto file_passthrough = std::make_unique<FileRenderPassthrough>(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200456 OutBase(), playback_video.get());
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700457 stream_state->sinks.push_back(std::move(playback_video));
458 stream_state->sinks.push_back(std::move(file_passthrough));
459 // Setup the configuration from the flags.
Tommif6f45432022-05-20 15:21:20 +0200460 VideoReceiveStreamInterface::Config receive_config(
461 &(stream_state->transport));
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200462 receive_config.rtp.remote_ssrc = Ssrc();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700463 receive_config.rtp.local_ssrc = kReceiverLocalSsrc;
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200464 receive_config.rtp.rtx_ssrc = SsrcRtx();
465 receive_config.rtp.rtx_associated_payload_types[MediaPayloadTypeRtx()] =
466 MediaPayloadType();
467 receive_config.rtp.rtx_associated_payload_types[RedPayloadTypeRtx()] =
468 RedPayloadType();
469 receive_config.rtp.ulpfec_payload_type = UlpfecPayloadType();
470 receive_config.rtp.red_payload_type = RedPayloadType();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700471 receive_config.rtp.nack.rtp_history_ms = 1000;
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200472 if (TransmissionOffsetId() != -1) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700473 receive_config.rtp.extensions.push_back(RtpExtension(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200474 RtpExtension::kTimestampOffsetUri, TransmissionOffsetId()));
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700475 }
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200476 if (AbsSendTimeId() != -1) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700477 receive_config.rtp.extensions.push_back(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200478 RtpExtension(RtpExtension::kAbsSendTimeUri, AbsSendTimeId()));
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700479 }
480 receive_config.renderer = stream_state->sinks.back().get();
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000481
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700482 // Setup the receiving stream
Tommif6f45432022-05-20 15:21:20 +0200483 VideoReceiveStreamInterface::Decoder decoder;
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200484 decoder = test::CreateMatchingDecoder(MediaPayloadType(), Codec());
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100485 if (!DecoderBitstreamFilename().empty()) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700486 // Replace decoder with file writer if we're writing the bitstream to a
487 // file instead.
Niels Möllercbcbc222018-09-28 09:07:24 +0200488 stream_state->decoder_factory =
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200489 std::make_unique<test::FunctionVideoDecoderFactory>([]() {
490 return std::make_unique<DecoderBitstreamFileWriter>(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200491 DecoderBitstreamFilename().c_str());
Niels Möllercbcbc222018-09-28 09:07:24 +0200492 });
Philipp Hanckefae4fb12021-01-26 18:05:56 +0100493 } else if (!IVFFilename().empty()) {
494 // Replace decoder with file writer if we're writing the ivf to a
495 // file instead.
496 stream_state->decoder_factory =
497 std::make_unique<test::FunctionVideoDecoderFactory>([]() {
498 return std::make_unique<DecoderIvfFileWriter>(IVFFilename().c_str(),
499 Codec());
500 });
501 } else {
502 stream_state->decoder_factory =
503 std::make_unique<InternalDecoderFactory>();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700504 }
Philip Eliasson2b068ce2020-08-03 15:55:10 +0000505 receive_config.decoder_factory = stream_state->decoder_factory.get();
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700506 receive_config.decoders.push_back(decoder);
507
508 stream_state->receive_streams.emplace_back(
509 call->CreateVideoReceiveStream(std::move(receive_config)));
510 return stream_state;
511 }
512
513 static std::unique_ptr<test::RtpFileReader> CreateRtpReader(
514 const std::string& rtp_dump_path) {
515 std::unique_ptr<test::RtpFileReader> rtp_reader(test::RtpFileReader::Create(
516 test::RtpFileReader::kRtpDump, rtp_dump_path));
Peter Boström74f6e9e2016-04-04 17:56:10 +0200517 if (!rtp_reader) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700518 rtp_reader.reset(test::RtpFileReader::Create(test::RtpFileReader::kPcap,
519 rtp_dump_path));
Peter Boström74f6e9e2016-04-04 17:56:10 +0200520 if (!rtp_reader) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700521 fprintf(
522 stderr,
523 "Couldn't open input file as either a rtpdump or .pcap. Note "
524 "that .pcapng is not supported.\nTrying to interpret the file as "
525 "length/packet interleaved.\n");
526 rtp_reader.reset(test::RtpFileReader::Create(
527 test::RtpFileReader::kLengthPacketInterleaved, rtp_dump_path));
528 if (!rtp_reader) {
529 fprintf(stderr,
530 "Unable to open input file with any supported format\n");
531 return nullptr;
532 }
stefan@webrtc.org48ac2262015-03-02 16:18:56 +0000533 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000534 }
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700535 return rtp_reader;
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000536 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000537
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100538 static void ReplayPackets(Call* call,
539 test::RtpFileReader* rtp_reader,
540 TaskQueueBase* worker_thread) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700541 int64_t replay_start_ms = -1;
542 int num_packets = 0;
543 std::map<uint32_t, int> unknown_packets;
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100544 rtc::Event event(/*manual_reset=*/false, /*initially_signalled=*/false);
Philipp Hanckef3a687a2021-03-15 12:04:39 +0100545 uint32_t start_timestamp = absl::GetFlag(FLAGS_start_timestamp);
546 uint32_t stop_timestamp = absl::GetFlag(FLAGS_stop_timestamp);
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700547 while (true) {
548 int64_t now_ms = rtc::TimeMillis();
549 if (replay_start_ms == -1) {
550 replay_start_ms = now_ms;
551 }
philipel02f03962018-01-11 17:28:35 +0100552
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700553 test::RtpPacket packet;
554 if (!rtp_reader->NextPacket(&packet)) {
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000555 break;
556 }
Danil Chapovalov76a35d92021-06-08 12:30:46 +0200557 rtc::CopyOnWriteBuffer packet_buffer(packet.data, packet.length);
558 RtpPacket header;
559 header.Parse(packet_buffer);
560 if (header.Timestamp() < start_timestamp ||
561 header.Timestamp() > stop_timestamp) {
Philipp Hanckef3a687a2021-03-15 12:04:39 +0100562 continue;
563 }
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700564
565 int64_t deliver_in_ms = replay_start_ms + packet.time_ms - now_ms;
566 if (deliver_in_ms > 0) {
567 SleepMs(deliver_in_ms);
568 }
569
570 ++num_packets;
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100571 PacketReceiver::DeliveryStatus result = PacketReceiver::DELIVERY_OK;
572 worker_thread->PostTask(ToQueuedTask([&]() {
Danil Chapovalov76a35d92021-06-08 12:30:46 +0200573 result = call->Receiver()->DeliverPacket(webrtc::MediaType::VIDEO,
574 std::move(packet_buffer),
575 /* packet_time_us */ -1);
Ilya Nikolaevskiy6a646902020-12-11 11:07:01 +0100576 event.Set();
577 }));
578 event.Wait(/*give_up_after_ms=*/10000);
579 switch (result) {
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700580 case PacketReceiver::DELIVERY_OK:
581 break;
582 case PacketReceiver::DELIVERY_UNKNOWN_SSRC: {
Danil Chapovalov76a35d92021-06-08 12:30:46 +0200583 if (unknown_packets[header.Ssrc()] == 0)
584 fprintf(stderr, "Unknown SSRC: %u!\n", header.Ssrc());
585 ++unknown_packets[header.Ssrc()];
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700586 break;
587 }
588 case PacketReceiver::DELIVERY_PACKET_ERROR: {
589 fprintf(stderr,
590 "Packet error, corrupt packets or incorrect setup?\n");
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700591 fprintf(stderr, "Packet len=%zu pt=%u seq=%u ts=%u ssrc=0x%8x\n",
Danil Chapovalov76a35d92021-06-08 12:30:46 +0200592 packet.length, header.PayloadType(), header.SequenceNumber(),
593 header.Timestamp(), header.Ssrc());
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700594 break;
595 }
philipp.hancke7b589602017-01-26 04:54:04 -0800596 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000597 }
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700598 fprintf(stderr, "num_packets: %d\n", num_packets);
599
600 for (std::map<uint32_t, int>::const_iterator it = unknown_packets.begin();
601 it != unknown_packets.end(); ++it) {
602 fprintf(stderr, "Packets for unknown ssrc '%u': %d\n", it->first,
603 it->second);
604 }
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000605 }
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700606}; // class RtpReplayer
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000607
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700608void RtpReplay() {
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200609 RtpReplayer::Replay(ConfigFile(), InputFile());
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000610}
Benjamin Wright90ab76d2018-08-23 11:33:29 -0700611
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000612} // namespace webrtc
613
614int main(int argc, char* argv[]) {
615 ::testing::InitGoogleTest(&argc, argv);
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200616 absl::ParseCommandLine(argc, argv);
oprypin6e09d872017-08-31 03:21:39 -0700617
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200618 RTC_CHECK(ValidatePayloadType(absl::GetFlag(FLAGS_media_payload_type)));
619 RTC_CHECK(ValidatePayloadType(absl::GetFlag(FLAGS_media_payload_type_rtx)));
620 RTC_CHECK(ValidateOptionalPayloadType(absl::GetFlag(FLAGS_red_payload_type)));
philipel752968e2017-12-05 12:40:28 +0100621 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200622 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_red_payload_type_rtx)));
philipel752968e2017-12-05 12:40:28 +0100623 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200624 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_ulpfec_payload_type)));
Yves Gerey665174f2018-06-19 15:03:05 +0200625 RTC_CHECK(
Mirko Bonadei2ab97f62019-07-18 13:44:12 +0200626 ValidateRtpHeaderExtensionId(absl::GetFlag(FLAGS_abs_send_time_id)));
627 RTC_CHECK(ValidateRtpHeaderExtensionId(
628 absl::GetFlag(FLAGS_transmission_offset_id)));
629 RTC_CHECK(ValidateInputFilenameNotEmpty(absl::GetFlag(FLAGS_input_file)));
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000630
Philipp Hancke07b7dec2020-07-28 12:34:48 +0200631 rtc::ThreadManager::Instance()->WrapCurrentThread();
pbos@webrtc.org4b5625e2014-08-06 16:26:56 +0000632 webrtc::test::RunTest(webrtc::RtpReplay);
633 return 0;
634}