blob: 66ab032a12b8c174d145a673eedbe703268041f2 [file] [log] [blame]
Benjamin Wright87bbb912019-02-01 18:00:051/*
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>
Mirko Bonadei317a1f02019-09-17 15:06:1814#include <memory>
Benjamin Wright87bbb912019-02-01 18:00:0515#include <utility>
16
Danil Chapovalov702820d2023-11-28 17:25:2817#include "api/environment/environment_factory.h"
Artem Titov33f9d2b2019-12-05 14:59:0018#include "api/test/create_frame_generator.h"
Ying Wangbc959b12023-04-03 02:54:3119#include "api/video_codecs/video_decoder_factory_template.h"
20#include "api/video_codecs/video_decoder_factory_template_dav1d_adapter.h"
21#include "api/video_codecs/video_decoder_factory_template_libvpx_vp8_adapter.h"
22#include "api/video_codecs/video_decoder_factory_template_libvpx_vp9_adapter.h"
Benjamin Wright87bbb912019-02-01 18:00:0523#include "api/video_codecs/video_encoder.h"
Ying Wangbc959b12023-04-03 02:54:3124#include "api/video_codecs/video_encoder_factory.h"
25#include "api/video_codecs/video_encoder_factory_template.h"
26#include "api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h"
27#include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h"
28#include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h"
Benjamin Wright87bbb912019-02-01 18:00:0529#include "media/base/media_constants.h"
30#include "rtc_base/strings/json.h"
31#include "rtc_base/system/file_wrapper.h"
32#include "rtc_base/thread.h"
33#include "test/testsupport/file_utils.h"
Jonas Oreland1262eb52022-09-27 14:53:0434#include "video/config/encoder_stream_factory.h"
Jonas Oreland6c2dae22022-09-29 08:28:2435#include "video/config/video_encoder_config.h"
Benjamin Wright87bbb912019-02-01 18:00:0536
37namespace webrtc {
38namespace {
39
40// Payload types.
41constexpr int kPayloadTypeVp8 = 125;
42constexpr int kPayloadTypeVp9 = 124;
43constexpr int kPayloadTypeH264 = 123;
44constexpr int kFakeVideoSendPayloadType = 122;
45
46// Defaults
47constexpr int kDefaultSsrc = 1337;
48constexpr int kMaxConfigBufferSize = 8192;
49
50// Utility function to validate a correct codec type has been passed in.
51bool IsValidCodecType(const std::string& codec_name) {
52 return cricket::kVp8CodecName == codec_name ||
53 cricket::kVp9CodecName == codec_name ||
54 cricket::kH264CodecName == codec_name;
55}
56
57// Utility function to return some base payload type for a codec_name.
58int GetDefaultTypeForPayloadName(const std::string& codec_name) {
59 if (cricket::kVp8CodecName == codec_name) {
60 return kPayloadTypeVp8;
61 }
62 if (cricket::kVp9CodecName == codec_name) {
63 return kPayloadTypeVp9;
64 }
65 if (cricket::kH264CodecName == codec_name) {
66 return kPayloadTypeH264;
67 }
68 return kFakeVideoSendPayloadType;
69}
70
71// Creates a single VideoSendStream configuration.
Florent Castelli8037fc62024-08-29 13:00:4072std::optional<RtpGeneratorOptions::VideoSendStreamConfig>
Benjamin Wright87bbb912019-02-01 18:00:0573ParseVideoSendStreamConfig(const Json::Value& json) {
74 RtpGeneratorOptions::VideoSendStreamConfig config;
75
76 // Parse video source settings.
77 if (!rtc::GetIntFromJsonObject(json, "duration_ms", &config.duration_ms)) {
78 RTC_LOG(LS_WARNING) << "duration_ms not specified using default: "
79 << config.duration_ms;
80 }
81 if (!rtc::GetIntFromJsonObject(json, "video_width", &config.video_width)) {
82 RTC_LOG(LS_WARNING) << "video_width not specified using default: "
83 << config.video_width;
84 }
85 if (!rtc::GetIntFromJsonObject(json, "video_height", &config.video_height)) {
86 RTC_LOG(LS_WARNING) << "video_height not specified using default: "
87 << config.video_height;
88 }
89 if (!rtc::GetIntFromJsonObject(json, "video_fps", &config.video_fps)) {
90 RTC_LOG(LS_WARNING) << "video_fps not specified using default: "
91 << config.video_fps;
92 }
93 if (!rtc::GetIntFromJsonObject(json, "num_squares", &config.num_squares)) {
94 RTC_LOG(LS_WARNING) << "num_squares not specified using default: "
95 << config.num_squares;
96 }
97
98 // Parse RTP settings for this configuration.
99 config.rtp.ssrcs.push_back(kDefaultSsrc);
100 Json::Value rtp_json;
101 if (!rtc::GetValueFromJsonObject(json, "rtp", &rtp_json)) {
102 RTC_LOG(LS_ERROR) << "video_streams must have an rtp section";
Florent Castelli8037fc62024-08-29 13:00:40103 return std::nullopt;
Benjamin Wright87bbb912019-02-01 18:00:05104 }
105 if (!rtc::GetStringFromJsonObject(rtp_json, "payload_name",
106 &config.rtp.payload_name)) {
107 RTC_LOG(LS_ERROR) << "rtp.payload_name must be specified";
Florent Castelli8037fc62024-08-29 13:00:40108 return std::nullopt;
Benjamin Wright87bbb912019-02-01 18:00:05109 }
110 if (!IsValidCodecType(config.rtp.payload_name)) {
111 RTC_LOG(LS_ERROR) << "rtp.payload_name must be VP8,VP9 or H264";
Florent Castelli8037fc62024-08-29 13:00:40112 return std::nullopt;
Benjamin Wright87bbb912019-02-01 18:00:05113 }
114
115 config.rtp.payload_type =
116 GetDefaultTypeForPayloadName(config.rtp.payload_name);
117 if (!rtc::GetIntFromJsonObject(rtp_json, "payload_type",
118 &config.rtp.payload_type)) {
119 RTC_LOG(LS_WARNING)
120 << "rtp.payload_type not specified using default for codec type"
121 << config.rtp.payload_type;
122 }
123
124 return config;
125}
126
127} // namespace
128
Florent Castelli8037fc62024-08-29 13:00:40129std::optional<RtpGeneratorOptions> ParseRtpGeneratorOptionsFromFile(
Benjamin Wright87bbb912019-02-01 18:00:05130 const std::string& options_file) {
131 if (!test::FileExists(options_file)) {
132 RTC_LOG(LS_ERROR) << " configuration file does not exist";
Florent Castelli8037fc62024-08-29 13:00:40133 return std::nullopt;
Benjamin Wright87bbb912019-02-01 18:00:05134 }
135
136 // Read the configuration file from disk.
137 FileWrapper config_file = FileWrapper::OpenReadOnly(options_file);
138 std::vector<char> raw_json_buffer(kMaxConfigBufferSize, 0);
139 size_t bytes_read =
140 config_file.Read(raw_json_buffer.data(), raw_json_buffer.size() - 1);
141 if (bytes_read == 0) {
142 RTC_LOG(LS_ERROR) << "Unable to read the configuration file.";
Florent Castelli8037fc62024-08-29 13:00:40143 return std::nullopt;
Benjamin Wright87bbb912019-02-01 18:00:05144 }
145
146 // Parse the file as JSON
Mirko Bonadeie99f6872021-06-24 13:24:59147 Json::CharReaderBuilder builder;
Benjamin Wright87bbb912019-02-01 18:00:05148 Json::Value json;
Mirko Bonadeie99f6872021-06-24 13:24:59149 std::string error_message;
150 std::unique_ptr<Json::CharReader> json_reader(builder.newCharReader());
151 if (!json_reader->parse(raw_json_buffer.data(),
152 raw_json_buffer.data() + raw_json_buffer.size(),
153 &json, &error_message)) {
154 RTC_LOG(LS_ERROR) << "Unable to parse the corpus config json file. Error:"
155 << error_message;
Florent Castelli8037fc62024-08-29 13:00:40156 return std::nullopt;
Benjamin Wright87bbb912019-02-01 18:00:05157 }
158
159 RtpGeneratorOptions gen_options;
160 for (const auto& video_stream_json : json["video_streams"]) {
Florent Castelli8037fc62024-08-29 13:00:40161 std::optional<RtpGeneratorOptions::VideoSendStreamConfig>
Benjamin Wright87bbb912019-02-01 18:00:05162 video_stream_config = ParseVideoSendStreamConfig(video_stream_json);
163 if (!video_stream_config.has_value()) {
164 RTC_LOG(LS_ERROR) << "Unable to parse the corpus config json file";
Florent Castelli8037fc62024-08-29 13:00:40165 return std::nullopt;
Benjamin Wright87bbb912019-02-01 18:00:05166 }
167 gen_options.video_streams.push_back(*video_stream_config);
168 }
169 return gen_options;
170}
171
172RtpGenerator::RtpGenerator(const RtpGeneratorOptions& options)
173 : options_(options),
Danil Chapovalov702820d2023-11-28 17:25:28174 env_(CreateEnvironment()),
Ying Wangbc959b12023-04-03 02:54:31175 video_encoder_factory_(
176 std::make_unique<webrtc::VideoEncoderFactoryTemplate<
177 webrtc::LibvpxVp8EncoderTemplateAdapter,
178 webrtc::LibvpxVp9EncoderTemplateAdapter,
179 webrtc::LibaomAv1EncoderTemplateAdapter>>()),
180 video_decoder_factory_(
181 std::make_unique<webrtc::VideoDecoderFactoryTemplate<
182 webrtc::LibvpxVp8DecoderTemplateAdapter,
183 webrtc::LibvpxVp9DecoderTemplateAdapter,
184 webrtc::Dav1dDecoderTemplateAdapter>>()),
Benjamin Wright87bbb912019-02-01 18:00:05185 video_bitrate_allocator_factory_(
186 CreateBuiltinVideoBitrateAllocatorFactory()),
Danil Chapovalov702820d2023-11-28 17:25:28187 call_(Call::Create(CallConfig(env_))) {
Benjamin Wright87bbb912019-02-01 18:00:05188 constexpr int kMinBitrateBps = 30000; // 30 Kbps
189 constexpr int kMaxBitrateBps = 2500000; // 2.5 Mbps
190
191 int stream_count = 0;
Jonas Oreland80c87d72022-09-29 13:01:09192 webrtc::VideoEncoder::EncoderInfo encoder_info;
Benjamin Wright87bbb912019-02-01 18:00:05193 for (const auto& send_config : options.video_streams) {
194 webrtc::VideoSendStream::Config video_config(this);
195 video_config.encoder_settings.encoder_factory =
196 video_encoder_factory_.get();
197 video_config.encoder_settings.bitrate_allocator_factory =
198 video_bitrate_allocator_factory_.get();
199 video_config.rtp = send_config.rtp;
200 // Update some required to be unique values.
201 stream_count++;
202 video_config.rtp.mid = "mid-" + std::to_string(stream_count);
Benjamin Wright87bbb912019-02-01 18:00:05203
204 // Configure the video encoder configuration.
205 VideoEncoderConfig encoder_config;
206 encoder_config.content_type =
207 VideoEncoderConfig::ContentType::kRealtimeVideo;
208 encoder_config.codec_type =
209 PayloadStringToCodecType(video_config.rtp.payload_name);
210 if (video_config.rtp.payload_name == cricket::kVp8CodecName) {
211 VideoCodecVP8 settings = VideoEncoder::GetDefaultVp8Settings();
Tommi87f70902021-04-27 12:43:08212 encoder_config.encoder_specific_settings =
213 rtc::make_ref_counted<VideoEncoderConfig::Vp8EncoderSpecificSettings>(
214 settings);
Benjamin Wright87bbb912019-02-01 18:00:05215 } else if (video_config.rtp.payload_name == cricket::kVp9CodecName) {
216 VideoCodecVP9 settings = VideoEncoder::GetDefaultVp9Settings();
Tommi87f70902021-04-27 12:43:08217 encoder_config.encoder_specific_settings =
218 rtc::make_ref_counted<VideoEncoderConfig::Vp9EncoderSpecificSettings>(
219 settings);
Benjamin Wright87bbb912019-02-01 18:00:05220 } else if (video_config.rtp.payload_name == cricket::kH264CodecName) {
Niels Möllercf2c8912022-05-18 08:45:46221 encoder_config.encoder_specific_settings = nullptr;
Benjamin Wright87bbb912019-02-01 18:00:05222 }
223 encoder_config.video_format.name = video_config.rtp.payload_name;
224 encoder_config.min_transmit_bitrate_bps = 0;
225 encoder_config.max_bitrate_bps = kMaxBitrateBps;
226 encoder_config.content_type =
227 VideoEncoderConfig::ContentType::kRealtimeVideo;
228
229 // Configure the simulcast layers.
230 encoder_config.number_of_streams = video_config.rtp.ssrcs.size();
231 encoder_config.bitrate_priority = 1.0;
232 encoder_config.simulcast_layers.resize(encoder_config.number_of_streams);
233 for (size_t i = 0; i < encoder_config.number_of_streams; ++i) {
234 encoder_config.simulcast_layers[i].active = true;
235 encoder_config.simulcast_layers[i].min_bitrate_bps = kMinBitrateBps;
236 encoder_config.simulcast_layers[i].max_bitrate_bps = kMaxBitrateBps;
237 encoder_config.simulcast_layers[i].max_framerate = send_config.video_fps;
238 }
239
Benjamin Wright87bbb912019-02-01 18:00:05240 // Setup the fake video stream for this.
Ilya Nikolaevskiyca160212019-07-10 13:34:45241 std::unique_ptr<test::FrameGeneratorCapturer> frame_generator =
Mirko Bonadei317a1f02019-09-17 15:06:18242 std::make_unique<test::FrameGeneratorCapturer>(
Danil Chapovalov702820d2023-11-28 17:25:28243 &env_.clock(),
Artem Titov33f9d2b2019-12-05 14:59:00244 test::CreateSquareFrameGenerator(send_config.video_width,
245 send_config.video_height,
Florent Castelli8037fc62024-08-29 13:00:40246 std::nullopt, std::nullopt),
Danil Chapovalov702820d2023-11-28 17:25:28247 send_config.video_fps, env_.task_queue_factory());
Benjamin Wright87bbb912019-02-01 18:00:05248 frame_generator->Init();
249
250 VideoSendStream* video_send_stream = call_->CreateVideoSendStream(
251 std::move(video_config), std::move(encoder_config));
252 video_send_stream->SetSource(
253 frame_generator.get(),
254 webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
255 // Store these objects so we can destropy them at the end.
256 frame_generators_.push_back(std::move(frame_generator));
257 video_send_streams_.push_back(video_send_stream);
258 }
259}
260
261RtpGenerator::~RtpGenerator() {
262 for (VideoSendStream* send_stream : video_send_streams_) {
263 call_->DestroyVideoSendStream(send_stream);
264 }
265}
266
267void RtpGenerator::GenerateRtpDump(const std::string& rtp_dump_path) {
268 rtp_dump_writer_.reset(test::RtpFileWriter::Create(
269 test::RtpFileWriter::kRtpDump, rtp_dump_path));
270
271 call_->SignalChannelNetworkState(webrtc::MediaType::VIDEO,
272 webrtc::kNetworkUp);
273 for (VideoSendStream* send_stream : video_send_streams_) {
274 send_stream->Start();
275 }
276
277 // Spinlock until all the durations end.
278 WaitUntilAllVideoStreamsFinish();
279
280 call_->SignalChannelNetworkState(webrtc::MediaType::VIDEO,
281 webrtc::kNetworkDown);
282}
283
Harald Alvestrandd43af912023-08-15 11:41:45284bool RtpGenerator::SendRtp(rtc::ArrayView<const uint8_t> packet,
Benjamin Wright87bbb912019-02-01 18:00:05285 const webrtc::PacketOptions& options) {
Harald Alvestrandd43af912023-08-15 11:41:45286 test::RtpPacket rtp_packet = DataToRtpPacket(packet.data(), packet.size());
Benjamin Wright87bbb912019-02-01 18:00:05287 rtp_dump_writer_->WritePacket(&rtp_packet);
288 return true;
289}
290
Harald Alvestrandd43af912023-08-15 11:41:45291bool RtpGenerator::SendRtcp(rtc::ArrayView<const uint8_t> packet) {
292 test::RtpPacket rtcp_packet = DataToRtpPacket(packet.data(), packet.size());
Benjamin Wright87bbb912019-02-01 18:00:05293 rtp_dump_writer_->WritePacket(&rtcp_packet);
294 return true;
295}
296
297int RtpGenerator::GetMaxDuration() const {
298 int max_end_ms = 0;
299 for (const auto& video_stream : options_.video_streams) {
300 max_end_ms = std::max(video_stream.duration_ms, max_end_ms);
301 }
302 return max_end_ms;
303}
304
305void RtpGenerator::WaitUntilAllVideoStreamsFinish() {
306 // Find the maximum duration required by the streams.
307 start_ms_ = Clock::GetRealTimeClock()->TimeInMilliseconds();
308 int64_t max_end_ms = start_ms_ + GetMaxDuration();
309
310 int64_t current_time = 0;
311 do {
312 int64_t min_wait_time = 0;
313 current_time = Clock::GetRealTimeClock()->TimeInMilliseconds();
314 // Stop any streams that are no longer active.
315 for (size_t i = 0; i < options_.video_streams.size(); ++i) {
316 const int64_t end_ms = start_ms_ + options_.video_streams[i].duration_ms;
317 if (current_time > end_ms) {
318 video_send_streams_[i]->Stop();
319 } else {
320 min_wait_time = std::min(min_wait_time, end_ms - current_time);
321 }
322 }
323 rtc::Thread::Current()->SleepMs(min_wait_time);
324 } while (current_time < max_end_ms);
325}
326
327test::RtpPacket RtpGenerator::DataToRtpPacket(const uint8_t* packet,
328 size_t packet_len) {
329 webrtc::test::RtpPacket rtp_packet;
330 memcpy(rtp_packet.data, packet, packet_len);
331 rtp_packet.length = packet_len;
332 rtp_packet.original_length = packet_len;
333 rtp_packet.time_ms =
334 webrtc::Clock::GetRealTimeClock()->TimeInMilliseconds() - start_ms_;
335 return rtp_packet;
336}
337
338} // namespace webrtc