1/* 2 * libjingle 3 * Copyright 2012 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "talk/app/webrtc/videosource.h" 29 30#include <vector> 31#include <cstdlib> 32 33#include "talk/app/webrtc/mediaconstraintsinterface.h" 34#include "talk/session/media/channelmanager.h" 35#include "webrtc/base/arraysize.h" 36 37using cricket::CaptureState; 38using webrtc::MediaConstraintsInterface; 39using webrtc::MediaSourceInterface; 40 41namespace { 42 43const double kRoundingTruncation = 0.0005; 44 45enum { 46 MSG_VIDEOCAPTURESTATECONNECT, 47 MSG_VIDEOCAPTURESTATEDISCONNECT, 48 MSG_VIDEOCAPTURESTATECHANGE, 49}; 50 51// Default resolution. If no constraint is specified, this is the resolution we 52// will use. 53static const cricket::VideoFormatPod kDefaultFormat = 54 {640, 480, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}; 55 56// List of formats used if the camera doesn't support capability enumeration. 57static const cricket::VideoFormatPod kVideoFormats[] = { 58 {1920, 1080, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, 59 {1280, 720, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, 60 {960, 720, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, 61 {640, 360, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, 62 {640, 480, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, 63 {320, 240, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, 64 {320, 180, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY} 65}; 66 67MediaSourceInterface::SourceState 68GetReadyState(cricket::CaptureState state) { 69 switch (state) { 70 case cricket::CS_STARTING: 71 return MediaSourceInterface::kInitializing; 72 case cricket::CS_RUNNING: 73 return MediaSourceInterface::kLive; 74 case cricket::CS_FAILED: 75 case cricket::CS_NO_DEVICE: 76 case cricket::CS_STOPPED: 77 return MediaSourceInterface::kEnded; 78 case cricket::CS_PAUSED: 79 return MediaSourceInterface::kMuted; 80 default: 81 ASSERT(false && "GetReadyState unknown state"); 82 } 83 return MediaSourceInterface::kEnded; 84} 85 86void SetUpperLimit(int new_limit, int* original_limit) { 87 if (*original_limit < 0 || new_limit < *original_limit) 88 *original_limit = new_limit; 89} 90 91// Updates |format_upper_limit| from |constraint|. 92// If constraint.maxFoo is smaller than format_upper_limit.foo, 93// set format_upper_limit.foo to constraint.maxFoo. 94void SetUpperLimitFromConstraint( 95 const MediaConstraintsInterface::Constraint& constraint, 96 cricket::VideoFormat* format_upper_limit) { 97 if (constraint.key == MediaConstraintsInterface::kMaxWidth) { 98 int value = rtc::FromString<int>(constraint.value); 99 SetUpperLimit(value, &(format_upper_limit->width)); 100 } else if (constraint.key == MediaConstraintsInterface::kMaxHeight) { 101 int value = rtc::FromString<int>(constraint.value); 102 SetUpperLimit(value, &(format_upper_limit->height)); 103 } 104} 105 106// Fills |format_out| with the max width and height allowed by |constraints|. 107void FromConstraintsForScreencast( 108 const MediaConstraintsInterface::Constraints& constraints, 109 cricket::VideoFormat* format_out) { 110 typedef MediaConstraintsInterface::Constraints::const_iterator 111 ConstraintsIterator; 112 113 cricket::VideoFormat upper_limit(-1, -1, 0, 0); 114 for (ConstraintsIterator constraints_it = constraints.begin(); 115 constraints_it != constraints.end(); ++constraints_it) 116 SetUpperLimitFromConstraint(*constraints_it, &upper_limit); 117 118 if (upper_limit.width >= 0) 119 format_out->width = upper_limit.width; 120 if (upper_limit.height >= 0) 121 format_out->height = upper_limit.height; 122} 123 124// Returns true if |constraint| is fulfilled. |format_out| can differ from 125// |format_in| if the format is changed by the constraint. Ie - the frame rate 126// can be changed by setting maxFrameRate. 127bool NewFormatWithConstraints( 128 const MediaConstraintsInterface::Constraint& constraint, 129 const cricket::VideoFormat& format_in, 130 bool mandatory, 131 cricket::VideoFormat* format_out) { 132 ASSERT(format_out != NULL); 133 *format_out = format_in; 134 135 if (constraint.key == MediaConstraintsInterface::kMinWidth) { 136 int value = rtc::FromString<int>(constraint.value); 137 return (value <= format_in.width); 138 } else if (constraint.key == MediaConstraintsInterface::kMaxWidth) { 139 int value = rtc::FromString<int>(constraint.value); 140 return (value >= format_in.width); 141 } else if (constraint.key == MediaConstraintsInterface::kMinHeight) { 142 int value = rtc::FromString<int>(constraint.value); 143 return (value <= format_in.height); 144 } else if (constraint.key == MediaConstraintsInterface::kMaxHeight) { 145 int value = rtc::FromString<int>(constraint.value); 146 return (value >= format_in.height); 147 } else if (constraint.key == MediaConstraintsInterface::kMinFrameRate) { 148 int value = rtc::FromString<int>(constraint.value); 149 return (value <= cricket::VideoFormat::IntervalToFps(format_in.interval)); 150 } else if (constraint.key == MediaConstraintsInterface::kMaxFrameRate) { 151 int value = rtc::FromString<int>(constraint.value); 152 if (value == 0) { 153 if (mandatory) { 154 // TODO(ronghuawu): Convert the constraint value to float when sub-1fps 155 // is supported by the capturer. 156 return false; 157 } else { 158 value = 1; 159 } 160 } 161 if (value <= cricket::VideoFormat::IntervalToFps(format_in.interval)) 162 format_out->interval = cricket::VideoFormat::FpsToInterval(value); 163 return true; 164 } else if (constraint.key == MediaConstraintsInterface::kMinAspectRatio) { 165 double value = rtc::FromString<double>(constraint.value); 166 // The aspect ratio in |constraint.value| has been converted to a string and 167 // back to a double, so it may have a rounding error. 168 // E.g if the value 1/3 is converted to a string, the string will not have 169 // infinite length. 170 // We add a margin of 0.0005 which is high enough to detect the same aspect 171 // ratio but small enough to avoid matching wrong aspect ratios. 172 double ratio = static_cast<double>(format_in.width) / format_in.height; 173 return (value <= ratio + kRoundingTruncation); 174 } else if (constraint.key == MediaConstraintsInterface::kMaxAspectRatio) { 175 double value = rtc::FromString<double>(constraint.value); 176 double ratio = static_cast<double>(format_in.width) / format_in.height; 177 // Subtract 0.0005 to avoid rounding problems. Same as above. 178 const double kRoundingTruncation = 0.0005; 179 return (value >= ratio - kRoundingTruncation); 180 } else if (constraint.key == MediaConstraintsInterface::kNoiseReduction) { 181 // These are actually options, not constraints, so they can be satisfied 182 // regardless of the format. 183 return true; 184 } 185 LOG(LS_WARNING) << "Found unknown MediaStream constraint. Name:" 186 << constraint.key << " Value:" << constraint.value; 187 return false; 188} 189 190// Removes cricket::VideoFormats from |formats| that don't meet |constraint|. 191void FilterFormatsByConstraint( 192 const MediaConstraintsInterface::Constraint& constraint, 193 bool mandatory, 194 std::vector<cricket::VideoFormat>* formats) { 195 std::vector<cricket::VideoFormat>::iterator format_it = 196 formats->begin(); 197 while (format_it != formats->end()) { 198 // Modify the format_it to fulfill the constraint if possible. 199 // Delete it otherwise. 200 if (!NewFormatWithConstraints(constraint, (*format_it), 201 mandatory, &(*format_it))) { 202 format_it = formats->erase(format_it); 203 } else { 204 ++format_it; 205 } 206 } 207} 208 209// Returns a vector of cricket::VideoFormat that best match |constraints|. 210std::vector<cricket::VideoFormat> FilterFormats( 211 const MediaConstraintsInterface::Constraints& mandatory, 212 const MediaConstraintsInterface::Constraints& optional, 213 const std::vector<cricket::VideoFormat>& supported_formats) { 214 typedef MediaConstraintsInterface::Constraints::const_iterator 215 ConstraintsIterator; 216 std::vector<cricket::VideoFormat> candidates = supported_formats; 217 218 for (ConstraintsIterator constraints_it = mandatory.begin(); 219 constraints_it != mandatory.end(); ++constraints_it) 220 FilterFormatsByConstraint(*constraints_it, true, &candidates); 221 222 if (candidates.size() == 0) 223 return candidates; 224 225 // Ok - all mandatory checked and we still have a candidate. 226 // Let's try filtering using the optional constraints. 227 for (ConstraintsIterator constraints_it = optional.begin(); 228 constraints_it != optional.end(); ++constraints_it) { 229 std::vector<cricket::VideoFormat> current_candidates = candidates; 230 FilterFormatsByConstraint(*constraints_it, false, ¤t_candidates); 231 if (current_candidates.size() > 0) { 232 candidates = current_candidates; 233 } 234 } 235 236 // We have done as good as we can to filter the supported resolutions. 237 return candidates; 238} 239 240// Find the format that best matches the default video size. 241// Constraints are optional and since the performance of a video call 242// might be bad due to bitrate limitations, CPU, and camera performance, 243// it is better to select a resolution that is as close as possible to our 244// default and still meets the contraints. 245const cricket::VideoFormat& GetBestCaptureFormat( 246 const std::vector<cricket::VideoFormat>& formats) { 247 ASSERT(formats.size() > 0); 248 249 int default_area = kDefaultFormat.width * kDefaultFormat.height; 250 251 std::vector<cricket::VideoFormat>::const_iterator it = formats.begin(); 252 std::vector<cricket::VideoFormat>::const_iterator best_it = formats.begin(); 253 int best_diff_area = std::abs(default_area - it->width * it->height); 254 int64_t best_diff_interval = kDefaultFormat.interval; 255 for (; it != formats.end(); ++it) { 256 int diff_area = std::abs(default_area - it->width * it->height); 257 int64_t diff_interval = std::abs(kDefaultFormat.interval - it->interval); 258 if (diff_area < best_diff_area || 259 (diff_area == best_diff_area && diff_interval < best_diff_interval)) { 260 best_diff_area = diff_area; 261 best_diff_interval = diff_interval; 262 best_it = it; 263 } 264 } 265 return *best_it; 266} 267 268// Set |option| to the highest-priority value of |key| in the constraints. 269// Return false if the key is mandatory, and the value is invalid. 270bool ExtractOption(const MediaConstraintsInterface* all_constraints, 271 const std::string& key, 272 rtc::Optional<bool>* option) { 273 size_t mandatory = 0; 274 bool value; 275 if (FindConstraint(all_constraints, key, &value, &mandatory)) { 276 *option = rtc::Optional<bool>(value); 277 return true; 278 } 279 280 return mandatory == 0; 281} 282 283// Search |all_constraints| for known video options. Apply all options that are 284// found with valid values, and return false if any mandatory video option was 285// found with an invalid value. 286bool ExtractVideoOptions(const MediaConstraintsInterface* all_constraints, 287 cricket::VideoOptions* options) { 288 bool all_valid = true; 289 290 all_valid &= ExtractOption(all_constraints, 291 MediaConstraintsInterface::kNoiseReduction, 292 &(options->video_noise_reduction)); 293 294 return all_valid; 295} 296 297class FrameInputWrapper : public cricket::VideoRenderer { 298 public: 299 explicit FrameInputWrapper(cricket::VideoCapturer* capturer) 300 : capturer_(capturer) { 301 ASSERT(capturer_ != NULL); 302 } 303 304 virtual ~FrameInputWrapper() {} 305 306 // VideoRenderer implementation. 307 bool RenderFrame(const cricket::VideoFrame* frame) override { 308 if (!capturer_->IsRunning()) { 309 return true; 310 } 311 312 // This signal will be made on media engine render thread. The clients 313 // of this signal should have no assumptions on what thread this signal 314 // come from. 315 capturer_->SignalVideoFrame(capturer_, frame); 316 return true; 317 } 318 319 private: 320 cricket::VideoCapturer* capturer_; 321 322 RTC_DISALLOW_COPY_AND_ASSIGN(FrameInputWrapper); 323}; 324 325} // anonymous namespace 326 327namespace webrtc { 328 329rtc::scoped_refptr<VideoSource> VideoSource::Create( 330 cricket::ChannelManager* channel_manager, 331 cricket::VideoCapturer* capturer, 332 const webrtc::MediaConstraintsInterface* constraints, 333 bool remote) { 334 ASSERT(channel_manager != NULL); 335 ASSERT(capturer != NULL); 336 rtc::scoped_refptr<VideoSource> source(new rtc::RefCountedObject<VideoSource>( 337 channel_manager, capturer, remote)); 338 source->Initialize(constraints); 339 return source; 340} 341 342VideoSource::VideoSource(cricket::ChannelManager* channel_manager, 343 cricket::VideoCapturer* capturer, 344 bool remote) 345 : channel_manager_(channel_manager), 346 video_capturer_(capturer), 347 state_(kInitializing), 348 remote_(remote) { 349 channel_manager_->SignalVideoCaptureStateChange.connect( 350 this, &VideoSource::OnStateChange); 351} 352 353VideoSource::~VideoSource() { 354 channel_manager_->StopVideoCapture(video_capturer_.get(), format_); 355 channel_manager_->SignalVideoCaptureStateChange.disconnect(this); 356} 357 358void VideoSource::Initialize( 359 const webrtc::MediaConstraintsInterface* constraints) { 360 361 std::vector<cricket::VideoFormat> formats = 362 channel_manager_->GetSupportedFormats(video_capturer_.get()); 363 if (formats.empty()) { 364 if (video_capturer_->IsScreencast()) { 365 // The screen capturer can accept any resolution and we will derive the 366 // format from the constraints if any. 367 // Note that this only affects tab capturing, not desktop capturing, 368 // since the desktop capturer does not respect the VideoFormat passed in. 369 formats.push_back(cricket::VideoFormat(kDefaultFormat)); 370 } else { 371 // The VideoCapturer implementation doesn't support capability 372 // enumeration. We need to guess what the camera supports. 373 for (int i = 0; i < arraysize(kVideoFormats); ++i) { 374 formats.push_back(cricket::VideoFormat(kVideoFormats[i])); 375 } 376 } 377 } 378 379 if (constraints) { 380 MediaConstraintsInterface::Constraints mandatory_constraints = 381 constraints->GetMandatory(); 382 MediaConstraintsInterface::Constraints optional_constraints; 383 optional_constraints = constraints->GetOptional(); 384 385 if (video_capturer_->IsScreencast()) { 386 // Use the maxWidth and maxHeight allowed by constraints for screencast. 387 FromConstraintsForScreencast(mandatory_constraints, &(formats[0])); 388 } 389 390 formats = FilterFormats(mandatory_constraints, optional_constraints, 391 formats); 392 } 393 394 if (formats.size() == 0) { 395 LOG(LS_WARNING) << "Failed to find a suitable video format."; 396 SetState(kEnded); 397 return; 398 } 399 400 cricket::VideoOptions options; 401 if (!ExtractVideoOptions(constraints, &options)) { 402 LOG(LS_WARNING) << "Could not satisfy mandatory options."; 403 SetState(kEnded); 404 return; 405 } 406 options_.SetAll(options); 407 408 format_ = GetBestCaptureFormat(formats); 409 // Start the camera with our best guess. 410 // TODO(perkj): Should we try again with another format it it turns out that 411 // the camera doesn't produce frames with the correct format? Or will 412 // cricket::VideCapturer be able to re-scale / crop to the requested 413 // resolution? 414 if (!channel_manager_->StartVideoCapture(video_capturer_.get(), format_)) { 415 SetState(kEnded); 416 return; 417 } 418 // Initialize hasn't succeeded until a successful state change has occurred. 419} 420 421cricket::VideoRenderer* VideoSource::FrameInput() { 422 // Defer creation of frame_input_ until it's needed, e.g. the local video 423 // sources will never need it. 424 if (!frame_input_) { 425 frame_input_.reset(new FrameInputWrapper(video_capturer_.get())); 426 } 427 return frame_input_.get(); 428} 429 430void VideoSource::Stop() { 431 channel_manager_->StopVideoCapture(video_capturer_.get(), format_); 432} 433 434void VideoSource::Restart() { 435 if (!channel_manager_->StartVideoCapture(video_capturer_.get(), format_)) { 436 SetState(kEnded); 437 return; 438 } 439 for(cricket::VideoRenderer* sink : sinks_) { 440 channel_manager_->AddVideoRenderer(video_capturer_.get(), sink); 441 } 442} 443 444void VideoSource::AddSink(cricket::VideoRenderer* output) { 445 sinks_.push_back(output); 446 channel_manager_->AddVideoRenderer(video_capturer_.get(), output); 447} 448 449void VideoSource::RemoveSink(cricket::VideoRenderer* output) { 450 sinks_.remove(output); 451 channel_manager_->RemoveVideoRenderer(video_capturer_.get(), output); 452} 453 454// OnStateChange listens to the ChannelManager::SignalVideoCaptureStateChange. 455// This signal is triggered for all video capturers. Not only the one we are 456// interested in. 457void VideoSource::OnStateChange(cricket::VideoCapturer* capturer, 458 cricket::CaptureState capture_state) { 459 if (capturer == video_capturer_.get()) { 460 SetState(GetReadyState(capture_state)); 461 } 462} 463 464void VideoSource::SetState(SourceState new_state) { 465 // TODO(hbos): Temporarily disabled VERIFY due to webrtc:4776. 466 // if (VERIFY(state_ != new_state)) { 467 if (state_ != new_state) { 468 state_ = new_state; 469 FireOnChanged(); 470 } 471} 472 473} // namespace webrtc 474