media_configuration.cc (10817B)
1 /* 2 * Copyright 2022 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 "api/test/pclf/media_configuration.h" 12 13 #include <cstddef> 14 #include <cstdint> 15 #include <functional> 16 #include <map> 17 #include <memory> 18 #include <optional> 19 #include <string> 20 #include <utility> 21 #include <vector> 22 23 #include "absl/strings/string_view.h" 24 #include "api/array_view.h" 25 #include "api/test/video/video_frame_writer.h" 26 #include "api/units/time_delta.h" 27 #include "rtc_base/checks.h" 28 #include "rtc_base/strings/string_builder.h" 29 #include "test/pc/e2e/analyzer/video/video_dumping.h" 30 #include "test/testsupport/file_utils.h" 31 #include "test/testsupport/video_frame_writer.h" 32 33 namespace webrtc { 34 namespace webrtc_pc_e2e { 35 namespace { 36 37 absl::string_view SpecToString(VideoResolution::Spec spec) { 38 switch (spec) { 39 case VideoResolution::Spec::kNone: 40 return "None"; 41 case VideoResolution::Spec::kMaxFromSender: 42 return "MaxFromSender"; 43 } 44 } 45 46 void AppendResolution(const VideoResolution& resolution, 47 StringBuilder& builder) { 48 builder << "_" << resolution.width() << "x" << resolution.height() << "_" 49 << resolution.fps(); 50 } 51 52 } // namespace 53 54 ScreenShareConfig::ScreenShareConfig(TimeDelta slide_change_interval) 55 : slide_change_interval(slide_change_interval) { 56 RTC_CHECK_GT(slide_change_interval.ms(), 0); 57 } 58 VideoSimulcastConfig::VideoSimulcastConfig(int simulcast_streams_count) 59 : simulcast_streams_count(simulcast_streams_count) { 60 RTC_CHECK_GT(simulcast_streams_count, 1); 61 } 62 EmulatedSFUConfig::EmulatedSFUConfig(int target_layer_index) 63 : target_layer_index(target_layer_index) { 64 RTC_CHECK_GE(target_layer_index, 0); 65 } 66 67 EmulatedSFUConfig::EmulatedSFUConfig(std::optional<int> target_layer_index, 68 std::optional<int> target_temporal_index) 69 : target_layer_index(target_layer_index), 70 target_temporal_index(target_temporal_index) { 71 RTC_CHECK_GE(target_temporal_index.value_or(0), 0); 72 if (target_temporal_index) 73 RTC_CHECK_GE(*target_temporal_index, 0); 74 } 75 76 VideoResolution::VideoResolution(size_t width, size_t height, int32_t fps) 77 : width_(width), height_(height), fps_(fps), spec_(Spec::kNone) {} 78 VideoResolution::VideoResolution(Spec spec) 79 : width_(0), height_(0), fps_(0), spec_(spec) {} 80 81 bool VideoResolution::operator==(const VideoResolution& other) const { 82 if (spec_ != Spec::kNone && spec_ == other.spec_) { 83 // If there is some particular spec set, then it doesn't matter what 84 // values we have in other fields. 85 return true; 86 } 87 return width_ == other.width_ && height_ == other.height_ && 88 fps_ == other.fps_ && spec_ == other.spec_; 89 } 90 bool VideoResolution::operator!=(const VideoResolution& other) const { 91 return !(*this == other); 92 } 93 94 bool VideoResolution::IsRegular() const { 95 return spec_ == Spec::kNone; 96 } 97 std::string VideoResolution::ToString() const { 98 StringBuilder out; 99 out << "{ width=" << width_ << ", height=" << height_ << ", fps=" << fps_ 100 << ", spec=" << SpecToString(spec_) << " }"; 101 return out.Release(); 102 } 103 104 VideoDumpOptions::VideoDumpOptions( 105 absl::string_view output_directory, 106 int sampling_modulo, 107 bool export_frame_ids, 108 std::function<std::unique_ptr<test::VideoFrameWriter>( 109 absl::string_view file_name_prefix, 110 const VideoResolution& resolution)> video_frame_writer_factory) 111 : output_directory_(output_directory), 112 sampling_modulo_(sampling_modulo), 113 export_frame_ids_(export_frame_ids), 114 video_frame_writer_factory_(video_frame_writer_factory) { 115 RTC_CHECK_GT(sampling_modulo, 0); 116 } 117 118 VideoDumpOptions::VideoDumpOptions(absl::string_view output_directory, 119 bool export_frame_ids) 120 : VideoDumpOptions(output_directory, 121 kDefaultSamplingModulo, 122 export_frame_ids) {} 123 124 std::unique_ptr<test::VideoFrameWriter> 125 VideoDumpOptions::CreateInputDumpVideoFrameWriter( 126 absl::string_view stream_label, 127 const VideoResolution& resolution) const { 128 std::unique_ptr<test::VideoFrameWriter> writer = video_frame_writer_factory_( 129 GetInputDumpFileName(stream_label, resolution), resolution); 130 std::optional<std::string> frame_ids_file = 131 GetInputFrameIdsDumpFileName(stream_label, resolution); 132 if (frame_ids_file.has_value()) { 133 writer = CreateVideoFrameWithIdsWriter(std::move(writer), *frame_ids_file); 134 } 135 return writer; 136 } 137 138 std::unique_ptr<test::VideoFrameWriter> 139 VideoDumpOptions::CreateOutputDumpVideoFrameWriter( 140 absl::string_view stream_label, 141 absl::string_view receiver, 142 const VideoResolution& resolution) const { 143 std::unique_ptr<test::VideoFrameWriter> writer = video_frame_writer_factory_( 144 GetOutputDumpFileName(stream_label, receiver, resolution), resolution); 145 std::optional<std::string> frame_ids_file = 146 GetOutputFrameIdsDumpFileName(stream_label, receiver, resolution); 147 if (frame_ids_file.has_value()) { 148 writer = CreateVideoFrameWithIdsWriter(std::move(writer), *frame_ids_file); 149 } 150 return writer; 151 } 152 153 std::unique_ptr<test::VideoFrameWriter> 154 VideoDumpOptions::Y4mVideoFrameWriterFactory( 155 absl::string_view file_name_prefix, 156 const VideoResolution& resolution) { 157 return std::make_unique<test::Y4mVideoFrameWriterImpl>( 158 std::string(file_name_prefix) + ".y4m", resolution.width(), 159 resolution.height(), resolution.fps()); 160 } 161 162 std::string VideoDumpOptions::GetInputDumpFileName( 163 absl::string_view stream_label, 164 const VideoResolution& resolution) const { 165 StringBuilder file_name; 166 file_name << stream_label; 167 AppendResolution(resolution, file_name); 168 return test::JoinFilename(output_directory_, file_name.Release()); 169 } 170 171 std::optional<std::string> VideoDumpOptions::GetInputFrameIdsDumpFileName( 172 absl::string_view stream_label, 173 const VideoResolution& resolution) const { 174 if (!export_frame_ids_) { 175 return std::nullopt; 176 } 177 return GetInputDumpFileName(stream_label, resolution) + ".frame_ids.txt"; 178 } 179 180 std::string VideoDumpOptions::GetOutputDumpFileName( 181 absl::string_view stream_label, 182 absl::string_view receiver, 183 const VideoResolution& resolution) const { 184 StringBuilder file_name; 185 file_name << stream_label << "_" << receiver; 186 AppendResolution(resolution, file_name); 187 return test::JoinFilename(output_directory_, file_name.Release()); 188 } 189 190 std::optional<std::string> VideoDumpOptions::GetOutputFrameIdsDumpFileName( 191 absl::string_view stream_label, 192 absl::string_view receiver, 193 const VideoResolution& resolution) const { 194 if (!export_frame_ids_) { 195 return std::nullopt; 196 } 197 return GetOutputDumpFileName(stream_label, receiver, resolution) + 198 ".frame_ids.txt"; 199 } 200 201 std::string VideoDumpOptions::ToString() const { 202 StringBuilder out; 203 out << "{ output_directory_=" << output_directory_ 204 << ", sampling_modulo_=" << sampling_modulo_ 205 << ", export_frame_ids_=" << export_frame_ids_ << " }"; 206 return out.Release(); 207 } 208 209 VideoConfig::VideoConfig(const VideoResolution& resolution) 210 : width(resolution.width()), 211 height(resolution.height()), 212 fps(resolution.fps()) { 213 RTC_CHECK(resolution.IsRegular()); 214 } 215 VideoConfig::VideoConfig(size_t width, size_t height, int32_t fps) 216 : width(width), height(height), fps(fps) {} 217 VideoConfig::VideoConfig(absl::string_view stream_label, 218 size_t width, 219 size_t height, 220 int32_t fps) 221 : width(width), height(height), fps(fps), stream_label(stream_label) {} 222 223 VideoCodecConfig::VideoCodecConfig(absl::string_view name) 224 : name(name), required_params() {} 225 226 VideoCodecConfig::VideoCodecConfig( 227 absl::string_view name, 228 std::map<std::string, std::string> required_params) 229 : name(name), required_params(std::move(required_params)) {} 230 231 std::optional<VideoResolution> VideoSubscription::GetMaxResolution( 232 ArrayView<const VideoConfig> video_configs) { 233 std::vector<VideoResolution> resolutions; 234 for (const auto& video_config : video_configs) { 235 resolutions.push_back(video_config.GetResolution()); 236 } 237 return GetMaxResolution(resolutions); 238 } 239 240 std::optional<VideoResolution> VideoSubscription::GetMaxResolution( 241 ArrayView<const VideoResolution> resolutions) { 242 if (resolutions.empty()) { 243 return std::nullopt; 244 } 245 246 VideoResolution max_resolution; 247 for (const VideoResolution& resolution : resolutions) { 248 if (max_resolution.width() < resolution.width()) { 249 max_resolution.set_width(resolution.width()); 250 } 251 if (max_resolution.height() < resolution.height()) { 252 max_resolution.set_height(resolution.height()); 253 } 254 if (max_resolution.fps() < resolution.fps()) { 255 max_resolution.set_fps(resolution.fps()); 256 } 257 } 258 return max_resolution; 259 } 260 261 bool VideoSubscription::operator==(const VideoSubscription& other) const { 262 return default_resolution_ == other.default_resolution_ && 263 peers_resolution_ == other.peers_resolution_; 264 } 265 bool VideoSubscription::operator!=(const VideoSubscription& other) const { 266 return !(*this == other); 267 } 268 269 VideoSubscription& VideoSubscription::SubscribeToPeer( 270 absl::string_view peer_name, 271 VideoResolution resolution) { 272 peers_resolution_[std::string(peer_name)] = resolution; 273 return *this; 274 } 275 276 VideoSubscription& VideoSubscription::SubscribeToAllPeers( 277 VideoResolution resolution) { 278 default_resolution_ = resolution; 279 return *this; 280 } 281 282 std::optional<VideoResolution> VideoSubscription::GetResolutionForPeer( 283 absl::string_view peer_name) const { 284 auto it = peers_resolution_.find(std::string(peer_name)); 285 if (it == peers_resolution_.end()) { 286 return default_resolution_; 287 } 288 return it->second; 289 } 290 291 std::vector<std::string> VideoSubscription::GetSubscribedPeers() const { 292 std::vector<std::string> subscribed_streams; 293 subscribed_streams.reserve(peers_resolution_.size()); 294 for (const auto& entry : peers_resolution_) { 295 subscribed_streams.push_back(entry.first); 296 } 297 return subscribed_streams; 298 } 299 300 std::string VideoSubscription::ToString() const { 301 StringBuilder out; 302 out << "{ default_resolution_=["; 303 if (default_resolution_.has_value()) { 304 out << default_resolution_->ToString(); 305 } else { 306 out << "undefined"; 307 } 308 out << "], {"; 309 for (const auto& [peer_name, resolution] : peers_resolution_) { 310 out << "[" << peer_name << ": " << resolution.ToString() << "], "; 311 } 312 out << "} }"; 313 return out.Release(); 314 } 315 } // namespace webrtc_pc_e2e 316 } // namespace webrtc