1 /*
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
13 #include <fstream>
14 #include <map>
15 #include <memory>
16
17 #include "absl/flags/flag.h"
18 #include "absl/flags/parse.h"
19 #include "api/rtc_event_log/rtc_event_log.h"
20 #include "api/task_queue/default_task_queue_factory.h"
21 #include "api/test/video/function_video_decoder_factory.h"
22 #include "api/transport/field_trial_based_config.h"
23 #include "api/video_codecs/video_decoder.h"
24 #include "call/call.h"
25 #include "common_video/libyuv/include/webrtc_libyuv.h"
26 #include "media/engine/internal_decoder_factory.h"
27 #include "rtc_base/checks.h"
28 #include "rtc_base/string_to_number.h"
29 #include "rtc_base/strings/json.h"
30 #include "rtc_base/time_utils.h"
31 #include "system_wrappers/include/clock.h"
32 #include "system_wrappers/include/sleep.h"
33 #include "test/call_config_utils.h"
34 #include "test/call_test.h"
35 #include "test/encoder_settings.h"
36 #include "test/fake_decoder.h"
37 #include "test/gtest.h"
38 #include "test/null_transport.h"
39 #include "test/rtp_file_reader.h"
40 #include "test/rtp_header_parser.h"
41 #include "test/run_loop.h"
42 #include "test/run_test.h"
43 #include "test/test_video_capturer.h"
44 #include "test/testsupport/frame_writer.h"
45 #include "test/video_renderer.h"
46
47 // Flag for payload type.
48 ABSL_FLAG(int,
49 media_payload_type,
50 webrtc::test::CallTest::kPayloadTypeVP8,
51 "Media payload type");
52
53 // Flag for RED payload type.
54 ABSL_FLAG(int,
55 red_payload_type,
56 webrtc::test::CallTest::kRedPayloadType,
57 "RED payload type");
58
59 // Flag for ULPFEC payload type.
60 ABSL_FLAG(int,
61 ulpfec_payload_type,
62 webrtc::test::CallTest::kUlpfecPayloadType,
63 "ULPFEC payload type");
64
65 ABSL_FLAG(int,
66 media_payload_type_rtx,
67 webrtc::test::CallTest::kSendRtxPayloadType,
68 "Media over RTX payload type");
69
70 ABSL_FLAG(int,
71 red_payload_type_rtx,
72 webrtc::test::CallTest::kRtxRedPayloadType,
73 "RED over RTX payload type");
74
75 // Flag for SSRC.
DefaultSsrc()76 const std::string& DefaultSsrc() {
77 static const std::string ssrc =
78 std::to_string(webrtc::test::CallTest::kVideoSendSsrcs[0]);
79 return ssrc;
80 }
81 ABSL_FLAG(std::string, ssrc, DefaultSsrc().c_str(), "Incoming SSRC");
82
DefaultSsrcRtx()83 const std::string& DefaultSsrcRtx() {
84 static const std::string ssrc_rtx =
85 std::to_string(webrtc::test::CallTest::kSendRtxSsrcs[0]);
86 return ssrc_rtx;
87 }
88 ABSL_FLAG(std::string, ssrc_rtx, DefaultSsrcRtx().c_str(), "Incoming RTX SSRC");
89
90 // Flag for abs-send-time id.
91 ABSL_FLAG(int, abs_send_time_id, -1, "RTP extension ID for abs-send-time");
92
93 // Flag for transmission-offset id.
94 ABSL_FLAG(int,
95 transmission_offset_id,
96 -1,
97 "RTP extension ID for transmission-offset");
98
99 // Flag for rtpdump input file.
100 ABSL_FLAG(std::string, input_file, "", "input file");
101
102 ABSL_FLAG(std::string, config_file, "", "config file");
103
104 // Flag for raw output files.
105 ABSL_FLAG(std::string,
106 out_base,
107 "",
108 "Basename (excluding .jpg) for raw output");
109
110 ABSL_FLAG(std::string,
111 decoder_bitstream_filename,
112 "",
113 "Decoder bitstream output file");
114
115 // Flag for video codec.
116 ABSL_FLAG(std::string, codec, "VP8", "Video codec");
117
118 namespace {
119
ValidatePayloadType(int32_t payload_type)120 static bool ValidatePayloadType(int32_t payload_type) {
121 return payload_type > 0 && payload_type <= 127;
122 }
123
ValidateSsrc(const char * ssrc_string)124 static bool ValidateSsrc(const char* ssrc_string) {
125 return rtc::StringToNumber<uint32_t>(ssrc_string).has_value();
126 }
127
ValidateOptionalPayloadType(int32_t payload_type)128 static bool ValidateOptionalPayloadType(int32_t payload_type) {
129 return payload_type == -1 || ValidatePayloadType(payload_type);
130 }
131
ValidateRtpHeaderExtensionId(int32_t extension_id)132 static bool ValidateRtpHeaderExtensionId(int32_t extension_id) {
133 return extension_id >= -1 && extension_id < 15;
134 }
135
ValidateInputFilenameNotEmpty(const std::string & string)136 bool ValidateInputFilenameNotEmpty(const std::string& string) {
137 return !string.empty();
138 }
139
MediaPayloadType()140 static int MediaPayloadType() {
141 return absl::GetFlag(FLAGS_media_payload_type);
142 }
143
RedPayloadType()144 static int RedPayloadType() {
145 return absl::GetFlag(FLAGS_red_payload_type);
146 }
147
UlpfecPayloadType()148 static int UlpfecPayloadType() {
149 return absl::GetFlag(FLAGS_ulpfec_payload_type);
150 }
151
MediaPayloadTypeRtx()152 static int MediaPayloadTypeRtx() {
153 return absl::GetFlag(FLAGS_media_payload_type_rtx);
154 }
155
RedPayloadTypeRtx()156 static int RedPayloadTypeRtx() {
157 return absl::GetFlag(FLAGS_red_payload_type_rtx);
158 }
159
Ssrc()160 static uint32_t Ssrc() {
161 return rtc::StringToNumber<uint32_t>(absl::GetFlag(FLAGS_ssrc)).value();
162 }
163
SsrcRtx()164 static uint32_t SsrcRtx() {
165 return rtc::StringToNumber<uint32_t>(absl::GetFlag(FLAGS_ssrc_rtx)).value();
166 }
167
AbsSendTimeId()168 static int AbsSendTimeId() {
169 return absl::GetFlag(FLAGS_abs_send_time_id);
170 }
171
TransmissionOffsetId()172 static int TransmissionOffsetId() {
173 return absl::GetFlag(FLAGS_transmission_offset_id);
174 }
175
InputFile()176 static std::string InputFile() {
177 return absl::GetFlag(FLAGS_input_file);
178 }
179
ConfigFile()180 static std::string ConfigFile() {
181 return absl::GetFlag(FLAGS_config_file);
182 }
183
OutBase()184 static std::string OutBase() {
185 return absl::GetFlag(FLAGS_out_base);
186 }
187
DecoderBitstreamFilename()188 static std::string DecoderBitstreamFilename() {
189 return absl::GetFlag(FLAGS_decoder_bitstream_filename);
190 }
191
Codec()192 static std::string Codec() {
193 return absl::GetFlag(FLAGS_codec);
194 }
195
196 } // namespace
197
198 namespace webrtc {
199
200 static const uint32_t kReceiverLocalSsrc = 0x123456;
201
202 class FileRenderPassthrough : public rtc::VideoSinkInterface<VideoFrame> {
203 public:
FileRenderPassthrough(const std::string & basename,rtc::VideoSinkInterface<VideoFrame> * renderer)204 FileRenderPassthrough(const std::string& basename,
205 rtc::VideoSinkInterface<VideoFrame>* renderer)
206 : basename_(basename), renderer_(renderer), file_(nullptr), count_(0) {}
207
~FileRenderPassthrough()208 ~FileRenderPassthrough() override {
209 if (file_)
210 fclose(file_);
211 }
212
213 private:
OnFrame(const VideoFrame & video_frame)214 void OnFrame(const VideoFrame& video_frame) override {
215 if (renderer_)
216 renderer_->OnFrame(video_frame);
217
218 if (basename_.empty())
219 return;
220
221 std::stringstream filename;
222 filename << basename_ << count_++ << "_" << video_frame.timestamp()
223 << ".jpg";
224
225 test::JpegFrameWriter frame_writer(filename.str());
226 RTC_CHECK(frame_writer.WriteFrame(video_frame, 100));
227 }
228
229 const std::string basename_;
230 rtc::VideoSinkInterface<VideoFrame>* const renderer_;
231 FILE* file_;
232 size_t count_;
233 };
234
235 class DecoderBitstreamFileWriter : public test::FakeDecoder {
236 public:
DecoderBitstreamFileWriter(const char * filename)237 explicit DecoderBitstreamFileWriter(const char* filename)
238 : file_(fopen(filename, "wb")) {
239 RTC_DCHECK(file_);
240 }
~DecoderBitstreamFileWriter()241 ~DecoderBitstreamFileWriter() override { fclose(file_); }
242
Decode(const EncodedImage & encoded_frame,bool,int64_t)243 int32_t Decode(const EncodedImage& encoded_frame,
244 bool /* missing_frames */,
245 int64_t /* render_time_ms */) override {
246 if (fwrite(encoded_frame.data(), 1, encoded_frame.size(), file_) <
247 encoded_frame.size()) {
248 RTC_LOG_ERR(LS_ERROR) << "fwrite of encoded frame failed.";
249 return WEBRTC_VIDEO_CODEC_ERROR;
250 }
251 return WEBRTC_VIDEO_CODEC_OK;
252 }
253
254 private:
255 FILE* file_;
256 };
257
258 // The RtpReplayer is responsible for parsing the configuration provided by the
259 // user, setting up the windows, recieve streams and decoders and then replaying
260 // the provided RTP dump.
261 class RtpReplayer final {
262 public:
263 // Replay a rtp dump with an optional json configuration.
Replay(const std::string & replay_config_path,const std::string & rtp_dump_path)264 static void Replay(const std::string& replay_config_path,
265 const std::string& rtp_dump_path) {
266 std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory =
267 webrtc::CreateDefaultTaskQueueFactory();
268 webrtc::RtcEventLogNull event_log;
269 Call::Config call_config(&event_log);
270 call_config.task_queue_factory = task_queue_factory.get();
271 call_config.trials = new FieldTrialBasedConfig();
272 std::unique_ptr<Call> call(Call::Create(call_config));
273 std::unique_ptr<StreamState> stream_state;
274 // Attempt to load the configuration
275 if (replay_config_path.empty()) {
276 stream_state = ConfigureFromFlags(rtp_dump_path, call.get());
277 } else {
278 stream_state = ConfigureFromFile(replay_config_path, call.get());
279 }
280 if (stream_state == nullptr) {
281 return;
282 }
283 // Attempt to create an RtpReader from the input file.
284 std::unique_ptr<test::RtpFileReader> rtp_reader =
285 CreateRtpReader(rtp_dump_path);
286 if (rtp_reader == nullptr) {
287 return;
288 }
289 // Start replaying the provided stream now that it has been configured.
290 for (const auto& receive_stream : stream_state->receive_streams) {
291 receive_stream->Start();
292 }
293 ReplayPackets(call.get(), rtp_reader.get());
294 for (const auto& receive_stream : stream_state->receive_streams) {
295 call->DestroyVideoReceiveStream(receive_stream);
296 }
297 }
298
299 private:
300 // Holds all the shared memory structures required for a recieve stream. This
301 // structure is used to prevent members being deallocated before the replay
302 // has been finished.
303 struct StreamState {
304 test::NullTransport transport;
305 std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
306 std::vector<VideoReceiveStream*> receive_streams;
307 std::unique_ptr<VideoDecoderFactory> decoder_factory;
308 };
309
310 // Loads multiple configurations from the provided configuration file.
ConfigureFromFile(const std::string & config_path,Call * call)311 static std::unique_ptr<StreamState> ConfigureFromFile(
312 const std::string& config_path,
313 Call* call) {
314 auto stream_state = std::make_unique<StreamState>();
315 // Parse the configuration file.
316 std::ifstream config_file(config_path);
317 std::stringstream raw_json_buffer;
318 raw_json_buffer << config_file.rdbuf();
319 std::string raw_json = raw_json_buffer.str();
320 Json::Reader json_reader;
321 Json::Value json_configs;
322 if (!json_reader.parse(raw_json, json_configs)) {
323 fprintf(stderr, "Error parsing JSON config\n");
324 fprintf(stderr, "%s\n", json_reader.getFormatedErrorMessages().c_str());
325 return nullptr;
326 }
327
328 stream_state->decoder_factory = std::make_unique<InternalDecoderFactory>();
329 size_t config_count = 0;
330 for (const auto& json : json_configs) {
331 // Create the configuration and parse the JSON into the config.
332 auto receive_config =
333 ParseVideoReceiveStreamJsonConfig(&(stream_state->transport), json);
334 // Instantiate the underlying decoder.
335 for (auto& decoder : receive_config.decoders) {
336 decoder = test::CreateMatchingDecoder(decoder.payload_type,
337 decoder.video_format.name);
338 decoder.decoder_factory = stream_state->decoder_factory.get();
339 }
340 // Create a window for this config.
341 std::stringstream window_title;
342 window_title << "Playback Video (" << config_count++ << ")";
343 stream_state->sinks.emplace_back(
344 test::VideoRenderer::Create(window_title.str().c_str(), 640, 480));
345 // Create a receive stream for this config.
346 receive_config.renderer = stream_state->sinks.back().get();
347 stream_state->receive_streams.emplace_back(
348 call->CreateVideoReceiveStream(std::move(receive_config)));
349 }
350 return stream_state;
351 }
352
353 // Loads the base configuration from flags passed in on the commandline.
ConfigureFromFlags(const std::string & rtp_dump_path,Call * call)354 static std::unique_ptr<StreamState> ConfigureFromFlags(
355 const std::string& rtp_dump_path,
356 Call* call) {
357 auto stream_state = std::make_unique<StreamState>();
358 // Create the video renderers. We must add both to the stream state to keep
359 // them from deallocating.
360 std::stringstream window_title;
361 window_title << "Playback Video (" << rtp_dump_path << ")";
362 std::unique_ptr<test::VideoRenderer> playback_video(
363 test::VideoRenderer::Create(window_title.str().c_str(), 640, 480));
364 auto file_passthrough = std::make_unique<FileRenderPassthrough>(
365 OutBase(), playback_video.get());
366 stream_state->sinks.push_back(std::move(playback_video));
367 stream_state->sinks.push_back(std::move(file_passthrough));
368 // Setup the configuration from the flags.
369 VideoReceiveStream::Config receive_config(&(stream_state->transport));
370 receive_config.rtp.remote_ssrc = Ssrc();
371 receive_config.rtp.local_ssrc = kReceiverLocalSsrc;
372 receive_config.rtp.rtx_ssrc = SsrcRtx();
373 receive_config.rtp.rtx_associated_payload_types[MediaPayloadTypeRtx()] =
374 MediaPayloadType();
375 receive_config.rtp.rtx_associated_payload_types[RedPayloadTypeRtx()] =
376 RedPayloadType();
377 receive_config.rtp.ulpfec_payload_type = UlpfecPayloadType();
378 receive_config.rtp.red_payload_type = RedPayloadType();
379 receive_config.rtp.nack.rtp_history_ms = 1000;
380 if (TransmissionOffsetId() != -1) {
381 receive_config.rtp.extensions.push_back(RtpExtension(
382 RtpExtension::kTimestampOffsetUri, TransmissionOffsetId()));
383 }
384 if (AbsSendTimeId() != -1) {
385 receive_config.rtp.extensions.push_back(
386 RtpExtension(RtpExtension::kAbsSendTimeUri, AbsSendTimeId()));
387 }
388 receive_config.renderer = stream_state->sinks.back().get();
389
390 // Setup the receiving stream
391 VideoReceiveStream::Decoder decoder;
392 decoder = test::CreateMatchingDecoder(MediaPayloadType(), Codec());
393 if (DecoderBitstreamFilename().empty()) {
394 stream_state->decoder_factory =
395 std::make_unique<InternalDecoderFactory>();
396 } else {
397 // Replace decoder with file writer if we're writing the bitstream to a
398 // file instead.
399 stream_state->decoder_factory =
400 std::make_unique<test::FunctionVideoDecoderFactory>([]() {
401 return std::make_unique<DecoderBitstreamFileWriter>(
402 DecoderBitstreamFilename().c_str());
403 });
404 }
405 decoder.decoder_factory = stream_state->decoder_factory.get();
406 receive_config.decoders.push_back(decoder);
407
408 stream_state->receive_streams.emplace_back(
409 call->CreateVideoReceiveStream(std::move(receive_config)));
410 return stream_state;
411 }
412
CreateRtpReader(const std::string & rtp_dump_path)413 static std::unique_ptr<test::RtpFileReader> CreateRtpReader(
414 const std::string& rtp_dump_path) {
415 std::unique_ptr<test::RtpFileReader> rtp_reader(test::RtpFileReader::Create(
416 test::RtpFileReader::kRtpDump, rtp_dump_path));
417 if (!rtp_reader) {
418 rtp_reader.reset(test::RtpFileReader::Create(test::RtpFileReader::kPcap,
419 rtp_dump_path));
420 if (!rtp_reader) {
421 fprintf(
422 stderr,
423 "Couldn't open input file as either a rtpdump or .pcap. Note "
424 "that .pcapng is not supported.\nTrying to interpret the file as "
425 "length/packet interleaved.\n");
426 rtp_reader.reset(test::RtpFileReader::Create(
427 test::RtpFileReader::kLengthPacketInterleaved, rtp_dump_path));
428 if (!rtp_reader) {
429 fprintf(stderr,
430 "Unable to open input file with any supported format\n");
431 return nullptr;
432 }
433 }
434 }
435 return rtp_reader;
436 }
437
ReplayPackets(Call * call,test::RtpFileReader * rtp_reader)438 static void ReplayPackets(Call* call, test::RtpFileReader* rtp_reader) {
439 int64_t replay_start_ms = -1;
440 int num_packets = 0;
441 std::map<uint32_t, int> unknown_packets;
442 while (true) {
443 int64_t now_ms = rtc::TimeMillis();
444 if (replay_start_ms == -1) {
445 replay_start_ms = now_ms;
446 }
447
448 test::RtpPacket packet;
449 if (!rtp_reader->NextPacket(&packet)) {
450 break;
451 }
452
453 int64_t deliver_in_ms = replay_start_ms + packet.time_ms - now_ms;
454 if (deliver_in_ms > 0) {
455 SleepMs(deliver_in_ms);
456 }
457
458 ++num_packets;
459 switch (call->Receiver()->DeliverPacket(
460 webrtc::MediaType::VIDEO,
461 rtc::CopyOnWriteBuffer(packet.data, packet.length),
462 /* packet_time_us */ -1)) {
463 case PacketReceiver::DELIVERY_OK:
464 break;
465 case PacketReceiver::DELIVERY_UNKNOWN_SSRC: {
466 RTPHeader header;
467 std::unique_ptr<RtpHeaderParser> parser(
468 RtpHeaderParser::CreateForTest());
469 parser->Parse(packet.data, packet.length, &header);
470 if (unknown_packets[header.ssrc] == 0)
471 fprintf(stderr, "Unknown SSRC: %u!\n", header.ssrc);
472 ++unknown_packets[header.ssrc];
473 break;
474 }
475 case PacketReceiver::DELIVERY_PACKET_ERROR: {
476 fprintf(stderr,
477 "Packet error, corrupt packets or incorrect setup?\n");
478 RTPHeader header;
479 std::unique_ptr<RtpHeaderParser> parser(
480 RtpHeaderParser::CreateForTest());
481 parser->Parse(packet.data, packet.length, &header);
482 fprintf(stderr, "Packet len=%zu pt=%u seq=%u ts=%u ssrc=0x%8x\n",
483 packet.length, header.payloadType, header.sequenceNumber,
484 header.timestamp, header.ssrc);
485 break;
486 }
487 }
488 }
489 fprintf(stderr, "num_packets: %d\n", num_packets);
490
491 for (std::map<uint32_t, int>::const_iterator it = unknown_packets.begin();
492 it != unknown_packets.end(); ++it) {
493 fprintf(stderr, "Packets for unknown ssrc '%u': %d\n", it->first,
494 it->second);
495 }
496 }
497 }; // class RtpReplayer
498
RtpReplay()499 void RtpReplay() {
500 RtpReplayer::Replay(ConfigFile(), InputFile());
501 }
502
503 } // namespace webrtc
504
main(int argc,char * argv[])505 int main(int argc, char* argv[]) {
506 ::testing::InitGoogleTest(&argc, argv);
507 absl::ParseCommandLine(argc, argv);
508
509 RTC_CHECK(ValidatePayloadType(absl::GetFlag(FLAGS_media_payload_type)));
510 RTC_CHECK(ValidatePayloadType(absl::GetFlag(FLAGS_media_payload_type_rtx)));
511 RTC_CHECK(ValidateOptionalPayloadType(absl::GetFlag(FLAGS_red_payload_type)));
512 RTC_CHECK(
513 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_red_payload_type_rtx)));
514 RTC_CHECK(
515 ValidateOptionalPayloadType(absl::GetFlag(FLAGS_ulpfec_payload_type)));
516 RTC_CHECK(ValidateSsrc(absl::GetFlag(FLAGS_ssrc).c_str()));
517 RTC_CHECK(ValidateSsrc(absl::GetFlag(FLAGS_ssrc_rtx).c_str()));
518 RTC_CHECK(
519 ValidateRtpHeaderExtensionId(absl::GetFlag(FLAGS_abs_send_time_id)));
520 RTC_CHECK(ValidateRtpHeaderExtensionId(
521 absl::GetFlag(FLAGS_transmission_offset_id)));
522 RTC_CHECK(ValidateInputFilenameNotEmpty(absl::GetFlag(FLAGS_input_file)));
523
524 webrtc::test::RunTest(webrtc::RtpReplay);
525 return 0;
526 }
527