• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2019 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 "rtc_tools/rtp_generator/rtp_generator.h"
12 
13 #include <algorithm>
14 #include <memory>
15 #include <utility>
16 
17 #include "api/task_queue/default_task_queue_factory.h"
18 #include "api/test/create_frame_generator.h"
19 #include "api/video_codecs/builtin_video_decoder_factory.h"
20 #include "api/video_codecs/builtin_video_encoder_factory.h"
21 #include "api/video_codecs/video_encoder.h"
22 #include "api/video_codecs/video_encoder_config.h"
23 #include "media/base/media_constants.h"
24 #include "rtc_base/strings/json.h"
25 #include "rtc_base/system/file_wrapper.h"
26 #include "rtc_base/thread.h"
27 #include "test/testsupport/file_utils.h"
28 
29 namespace webrtc {
30 namespace {
31 
32 // Payload types.
33 constexpr int kPayloadTypeVp8 = 125;
34 constexpr int kPayloadTypeVp9 = 124;
35 constexpr int kPayloadTypeH264 = 123;
36 constexpr int kFakeVideoSendPayloadType = 122;
37 
38 // Defaults
39 constexpr int kDefaultSsrc = 1337;
40 constexpr int kMaxConfigBufferSize = 8192;
41 
42 // Utility function to validate a correct codec type has been passed in.
IsValidCodecType(const std::string & codec_name)43 bool IsValidCodecType(const std::string& codec_name) {
44   return cricket::kVp8CodecName == codec_name ||
45          cricket::kVp9CodecName == codec_name ||
46          cricket::kH264CodecName == codec_name;
47 }
48 
49 // Utility function to return some base payload type for a codec_name.
GetDefaultTypeForPayloadName(const std::string & codec_name)50 int GetDefaultTypeForPayloadName(const std::string& codec_name) {
51   if (cricket::kVp8CodecName == codec_name) {
52     return kPayloadTypeVp8;
53   }
54   if (cricket::kVp9CodecName == codec_name) {
55     return kPayloadTypeVp9;
56   }
57   if (cricket::kH264CodecName == codec_name) {
58     return kPayloadTypeH264;
59   }
60   return kFakeVideoSendPayloadType;
61 }
62 
63 // Creates a single VideoSendStream configuration.
64 absl::optional<RtpGeneratorOptions::VideoSendStreamConfig>
ParseVideoSendStreamConfig(const Json::Value & json)65 ParseVideoSendStreamConfig(const Json::Value& json) {
66   RtpGeneratorOptions::VideoSendStreamConfig config;
67 
68   // Parse video source settings.
69   if (!rtc::GetIntFromJsonObject(json, "duration_ms", &config.duration_ms)) {
70     RTC_LOG(LS_WARNING) << "duration_ms not specified using default: "
71                         << config.duration_ms;
72   }
73   if (!rtc::GetIntFromJsonObject(json, "video_width", &config.video_width)) {
74     RTC_LOG(LS_WARNING) << "video_width not specified using default: "
75                         << config.video_width;
76   }
77   if (!rtc::GetIntFromJsonObject(json, "video_height", &config.video_height)) {
78     RTC_LOG(LS_WARNING) << "video_height not specified using default: "
79                         << config.video_height;
80   }
81   if (!rtc::GetIntFromJsonObject(json, "video_fps", &config.video_fps)) {
82     RTC_LOG(LS_WARNING) << "video_fps not specified using default: "
83                         << config.video_fps;
84   }
85   if (!rtc::GetIntFromJsonObject(json, "num_squares", &config.num_squares)) {
86     RTC_LOG(LS_WARNING) << "num_squares not specified using default: "
87                         << config.num_squares;
88   }
89 
90   // Parse RTP settings for this configuration.
91   config.rtp.ssrcs.push_back(kDefaultSsrc);
92   Json::Value rtp_json;
93   if (!rtc::GetValueFromJsonObject(json, "rtp", &rtp_json)) {
94     RTC_LOG(LS_ERROR) << "video_streams must have an rtp section";
95     return absl::nullopt;
96   }
97   if (!rtc::GetStringFromJsonObject(rtp_json, "payload_name",
98                                     &config.rtp.payload_name)) {
99     RTC_LOG(LS_ERROR) << "rtp.payload_name must be specified";
100     return absl::nullopt;
101   }
102   if (!IsValidCodecType(config.rtp.payload_name)) {
103     RTC_LOG(LS_ERROR) << "rtp.payload_name must be VP8,VP9 or H264";
104     return absl::nullopt;
105   }
106 
107   config.rtp.payload_type =
108       GetDefaultTypeForPayloadName(config.rtp.payload_name);
109   if (!rtc::GetIntFromJsonObject(rtp_json, "payload_type",
110                                  &config.rtp.payload_type)) {
111     RTC_LOG(LS_WARNING)
112         << "rtp.payload_type not specified using default for codec type"
113         << config.rtp.payload_type;
114   }
115 
116   return config;
117 }
118 
119 }  // namespace
120 
ParseRtpGeneratorOptionsFromFile(const std::string & options_file)121 absl::optional<RtpGeneratorOptions> ParseRtpGeneratorOptionsFromFile(
122     const std::string& options_file) {
123   if (!test::FileExists(options_file)) {
124     RTC_LOG(LS_ERROR) << " configuration file does not exist";
125     return absl::nullopt;
126   }
127 
128   // Read the configuration file from disk.
129   FileWrapper config_file = FileWrapper::OpenReadOnly(options_file);
130   std::vector<char> raw_json_buffer(kMaxConfigBufferSize, 0);
131   size_t bytes_read =
132       config_file.Read(raw_json_buffer.data(), raw_json_buffer.size() - 1);
133   if (bytes_read == 0) {
134     RTC_LOG(LS_ERROR) << "Unable to read the configuration file.";
135     return absl::nullopt;
136   }
137 
138   // Parse the file as JSON
139   Json::Reader json_reader;
140   Json::Value json;
141   if (!json_reader.parse(raw_json_buffer.data(), json)) {
142     RTC_LOG(LS_ERROR) << "Unable to parse the corpus config json file";
143     return absl::nullopt;
144   }
145 
146   RtpGeneratorOptions gen_options;
147   for (const auto& video_stream_json : json["video_streams"]) {
148     absl::optional<RtpGeneratorOptions::VideoSendStreamConfig>
149         video_stream_config = ParseVideoSendStreamConfig(video_stream_json);
150     if (!video_stream_config.has_value()) {
151       RTC_LOG(LS_ERROR) << "Unable to parse the corpus config json file";
152       return absl::nullopt;
153     }
154     gen_options.video_streams.push_back(*video_stream_config);
155   }
156   return gen_options;
157 }
158 
RtpGenerator(const RtpGeneratorOptions & options)159 RtpGenerator::RtpGenerator(const RtpGeneratorOptions& options)
160     : options_(options),
161       video_encoder_factory_(CreateBuiltinVideoEncoderFactory()),
162       video_decoder_factory_(CreateBuiltinVideoDecoderFactory()),
163       video_bitrate_allocator_factory_(
164           CreateBuiltinVideoBitrateAllocatorFactory()),
165       event_log_(std::make_unique<RtcEventLogNull>()),
166       call_(Call::Create(CallConfig(event_log_.get()))),
167       task_queue_(CreateDefaultTaskQueueFactory()) {
168   constexpr int kMinBitrateBps = 30000;    // 30 Kbps
169   constexpr int kMaxBitrateBps = 2500000;  // 2.5 Mbps
170 
171   int stream_count = 0;
172   for (const auto& send_config : options.video_streams) {
173     webrtc::VideoSendStream::Config video_config(this);
174     video_config.encoder_settings.encoder_factory =
175         video_encoder_factory_.get();
176     video_config.encoder_settings.bitrate_allocator_factory =
177         video_bitrate_allocator_factory_.get();
178     video_config.rtp = send_config.rtp;
179     // Update some required to be unique values.
180     stream_count++;
181     video_config.rtp.mid = "mid-" + std::to_string(stream_count);
182 
183     // Configure the video encoder configuration.
184     VideoEncoderConfig encoder_config;
185     encoder_config.content_type =
186         VideoEncoderConfig::ContentType::kRealtimeVideo;
187     encoder_config.codec_type =
188         PayloadStringToCodecType(video_config.rtp.payload_name);
189     if (video_config.rtp.payload_name == cricket::kVp8CodecName) {
190       VideoCodecVP8 settings = VideoEncoder::GetDefaultVp8Settings();
191       encoder_config.encoder_specific_settings = new rtc::RefCountedObject<
192           VideoEncoderConfig::Vp8EncoderSpecificSettings>(settings);
193     } else if (video_config.rtp.payload_name == cricket::kVp9CodecName) {
194       VideoCodecVP9 settings = VideoEncoder::GetDefaultVp9Settings();
195       encoder_config.encoder_specific_settings = new rtc::RefCountedObject<
196           VideoEncoderConfig::Vp9EncoderSpecificSettings>(settings);
197     } else if (video_config.rtp.payload_name == cricket::kH264CodecName) {
198       VideoCodecH264 settings = VideoEncoder::GetDefaultH264Settings();
199       encoder_config.encoder_specific_settings = new rtc::RefCountedObject<
200           VideoEncoderConfig::H264EncoderSpecificSettings>(settings);
201     }
202     encoder_config.video_format.name = video_config.rtp.payload_name;
203     encoder_config.min_transmit_bitrate_bps = 0;
204     encoder_config.max_bitrate_bps = kMaxBitrateBps;
205     encoder_config.content_type =
206         VideoEncoderConfig::ContentType::kRealtimeVideo;
207 
208     // Configure the simulcast layers.
209     encoder_config.number_of_streams = video_config.rtp.ssrcs.size();
210     encoder_config.bitrate_priority = 1.0;
211     encoder_config.simulcast_layers.resize(encoder_config.number_of_streams);
212     for (size_t i = 0; i < encoder_config.number_of_streams; ++i) {
213       encoder_config.simulcast_layers[i].active = true;
214       encoder_config.simulcast_layers[i].min_bitrate_bps = kMinBitrateBps;
215       encoder_config.simulcast_layers[i].max_bitrate_bps = kMaxBitrateBps;
216       encoder_config.simulcast_layers[i].max_framerate = send_config.video_fps;
217     }
218 
219     encoder_config.video_stream_factory =
220         new rtc::RefCountedObject<cricket::EncoderStreamFactory>(
221             video_config.rtp.payload_name, /*max qp*/ 56, /*screencast*/ false,
222             /*screenshare enabled*/ false);
223 
224     // Setup the fake video stream for this.
225     std::unique_ptr<test::FrameGeneratorCapturer> frame_generator =
226         std::make_unique<test::FrameGeneratorCapturer>(
227             Clock::GetRealTimeClock(),
228             test::CreateSquareFrameGenerator(send_config.video_width,
229                                              send_config.video_height,
230                                              absl::nullopt, absl::nullopt),
231             send_config.video_fps, *task_queue_);
232     frame_generator->Init();
233 
234     VideoSendStream* video_send_stream = call_->CreateVideoSendStream(
235         std::move(video_config), std::move(encoder_config));
236     video_send_stream->SetSource(
237         frame_generator.get(),
238         webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
239     // Store these objects so we can destropy them at the end.
240     frame_generators_.push_back(std::move(frame_generator));
241     video_send_streams_.push_back(video_send_stream);
242   }
243 }
244 
~RtpGenerator()245 RtpGenerator::~RtpGenerator() {
246   for (VideoSendStream* send_stream : video_send_streams_) {
247     call_->DestroyVideoSendStream(send_stream);
248   }
249 }
250 
GenerateRtpDump(const std::string & rtp_dump_path)251 void RtpGenerator::GenerateRtpDump(const std::string& rtp_dump_path) {
252   rtp_dump_writer_.reset(test::RtpFileWriter::Create(
253       test::RtpFileWriter::kRtpDump, rtp_dump_path));
254 
255   call_->SignalChannelNetworkState(webrtc::MediaType::VIDEO,
256                                    webrtc::kNetworkUp);
257   for (VideoSendStream* send_stream : video_send_streams_) {
258     send_stream->Start();
259   }
260 
261   // Spinlock until all the durations end.
262   WaitUntilAllVideoStreamsFinish();
263 
264   call_->SignalChannelNetworkState(webrtc::MediaType::VIDEO,
265                                    webrtc::kNetworkDown);
266 }
267 
SendRtp(const uint8_t * packet,size_t length,const webrtc::PacketOptions & options)268 bool RtpGenerator::SendRtp(const uint8_t* packet,
269                            size_t length,
270                            const webrtc::PacketOptions& options) {
271   test::RtpPacket rtp_packet = DataToRtpPacket(packet, length);
272   rtp_dump_writer_->WritePacket(&rtp_packet);
273   return true;
274 }
275 
SendRtcp(const uint8_t * packet,size_t length)276 bool RtpGenerator::SendRtcp(const uint8_t* packet, size_t length) {
277   test::RtpPacket rtcp_packet = DataToRtpPacket(packet, length);
278   rtp_dump_writer_->WritePacket(&rtcp_packet);
279   return true;
280 }
281 
GetMaxDuration() const282 int RtpGenerator::GetMaxDuration() const {
283   int max_end_ms = 0;
284   for (const auto& video_stream : options_.video_streams) {
285     max_end_ms = std::max(video_stream.duration_ms, max_end_ms);
286   }
287   return max_end_ms;
288 }
289 
WaitUntilAllVideoStreamsFinish()290 void RtpGenerator::WaitUntilAllVideoStreamsFinish() {
291   // Find the maximum duration required by the streams.
292   start_ms_ = Clock::GetRealTimeClock()->TimeInMilliseconds();
293   int64_t max_end_ms = start_ms_ + GetMaxDuration();
294 
295   int64_t current_time = 0;
296   do {
297     int64_t min_wait_time = 0;
298     current_time = Clock::GetRealTimeClock()->TimeInMilliseconds();
299     // Stop any streams that are no longer active.
300     for (size_t i = 0; i < options_.video_streams.size(); ++i) {
301       const int64_t end_ms = start_ms_ + options_.video_streams[i].duration_ms;
302       if (current_time > end_ms) {
303         video_send_streams_[i]->Stop();
304       } else {
305         min_wait_time = std::min(min_wait_time, end_ms - current_time);
306       }
307     }
308     rtc::Thread::Current()->SleepMs(min_wait_time);
309   } while (current_time < max_end_ms);
310 }
311 
DataToRtpPacket(const uint8_t * packet,size_t packet_len)312 test::RtpPacket RtpGenerator::DataToRtpPacket(const uint8_t* packet,
313                                               size_t packet_len) {
314   webrtc::test::RtpPacket rtp_packet;
315   memcpy(rtp_packet.data, packet, packet_len);
316   rtp_packet.length = packet_len;
317   rtp_packet.original_length = packet_len;
318   rtp_packet.time_ms =
319       webrtc::Clock::GetRealTimeClock()->TimeInMilliseconds() - start_ms_;
320   return rtp_packet;
321 }
322 
323 }  // namespace webrtc
324