| OLD | NEW | 
|---|
| 1 /* | 1 /* | 
| 2  /* |  | 
| 3  *  Copyright 2012 The WebRTC project authors. All Rights Reserved. | 2  *  Copyright 2012 The WebRTC project authors. All Rights Reserved. | 
| 4  * | 3  * | 
| 5  *  Use of this source code is governed by a BSD-style license | 4  *  Use of this source code is governed by a BSD-style license | 
| 6  *  that can be found in the LICENSE file in the root of the source | 5  *  that can be found in the LICENSE file in the root of the source | 
| 7  *  tree. An additional intellectual property rights grant can be found | 6  *  tree. An additional intellectual property rights grant can be found | 
| 8  *  in the file PATENTS.  All contributing project authors may | 7  *  in the file PATENTS.  All contributing project authors may | 
| 9  *  be found in the AUTHORS file in the root of the source tree. | 8  *  be found in the AUTHORS file in the root of the source tree. | 
| 10  */ | 9  */ | 
| 11 | 10 | 
| 12 #include "webrtc/api/videocapturertracksource.h" | 11 #include "webrtc/api/videocapturertracksource.h" | 
| 13 | 12 | 
| 14 // TODO(perkj): This file is added to prepare for changing name of VideoSource | 13 #include <cstdlib> | 
| 15 // to VideoCapturerTrackSource. | 14 #include <string> | 
|  | 15 #include <vector> | 
|  | 16 | 
|  | 17 #include "webrtc/api/mediaconstraintsinterface.h" | 
|  | 18 #include "webrtc/base/arraysize.h" | 
|  | 19 | 
|  | 20 using cricket::CaptureState; | 
|  | 21 using webrtc::MediaConstraintsInterface; | 
|  | 22 using webrtc::MediaSourceInterface; | 
|  | 23 | 
|  | 24 namespace { | 
|  | 25 | 
|  | 26 const double kRoundingTruncation = 0.0005; | 
|  | 27 | 
|  | 28 // Default resolution. If no constraint is specified, this is the resolution we | 
|  | 29 // will use. | 
|  | 30 static const cricket::VideoFormatPod kDefaultFormat = { | 
|  | 31     640, 480, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}; | 
|  | 32 | 
|  | 33 // List of formats used if the camera doesn't support capability enumeration. | 
|  | 34 static const cricket::VideoFormatPod kVideoFormats[] = { | 
|  | 35     {1920, 1080, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, | 
|  | 36     {1280, 720, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, | 
|  | 37     {960, 720, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, | 
|  | 38     {640, 360, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, | 
|  | 39     {640, 480, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, | 
|  | 40     {320, 240, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}, | 
|  | 41     {320, 180, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY}}; | 
|  | 42 | 
|  | 43 MediaSourceInterface::SourceState GetReadyState(cricket::CaptureState state) { | 
|  | 44   switch (state) { | 
|  | 45     case cricket::CS_STARTING: | 
|  | 46       return MediaSourceInterface::kInitializing; | 
|  | 47     case cricket::CS_RUNNING: | 
|  | 48       return MediaSourceInterface::kLive; | 
|  | 49     case cricket::CS_FAILED: | 
|  | 50     case cricket::CS_STOPPED: | 
|  | 51       return MediaSourceInterface::kEnded; | 
|  | 52     case cricket::CS_PAUSED: | 
|  | 53       return MediaSourceInterface::kMuted; | 
|  | 54     default: | 
|  | 55       ASSERT(false && "GetReadyState unknown state"); | 
|  | 56   } | 
|  | 57   return MediaSourceInterface::kEnded; | 
|  | 58 } | 
|  | 59 | 
|  | 60 void SetUpperLimit(int new_limit, int* original_limit) { | 
|  | 61   if (*original_limit < 0 || new_limit < *original_limit) | 
|  | 62     *original_limit = new_limit; | 
|  | 63 } | 
|  | 64 | 
|  | 65 // Updates |format_upper_limit| from |constraint|. | 
|  | 66 // If constraint.maxFoo is smaller than format_upper_limit.foo, | 
|  | 67 // set format_upper_limit.foo to constraint.maxFoo. | 
|  | 68 void SetUpperLimitFromConstraint( | 
|  | 69     const MediaConstraintsInterface::Constraint& constraint, | 
|  | 70     cricket::VideoFormat* format_upper_limit) { | 
|  | 71   if (constraint.key == MediaConstraintsInterface::kMaxWidth) { | 
|  | 72     int value = rtc::FromString<int>(constraint.value); | 
|  | 73     SetUpperLimit(value, &(format_upper_limit->width)); | 
|  | 74   } else if (constraint.key == MediaConstraintsInterface::kMaxHeight) { | 
|  | 75     int value = rtc::FromString<int>(constraint.value); | 
|  | 76     SetUpperLimit(value, &(format_upper_limit->height)); | 
|  | 77   } | 
|  | 78 } | 
|  | 79 | 
|  | 80 // Fills |format_out| with the max width and height allowed by |constraints|. | 
|  | 81 void FromConstraintsForScreencast( | 
|  | 82     const MediaConstraintsInterface::Constraints& constraints, | 
|  | 83     cricket::VideoFormat* format_out) { | 
|  | 84   typedef MediaConstraintsInterface::Constraints::const_iterator | 
|  | 85       ConstraintsIterator; | 
|  | 86 | 
|  | 87   cricket::VideoFormat upper_limit(-1, -1, 0, 0); | 
|  | 88   for (ConstraintsIterator constraints_it = constraints.begin(); | 
|  | 89        constraints_it != constraints.end(); ++constraints_it) | 
|  | 90     SetUpperLimitFromConstraint(*constraints_it, &upper_limit); | 
|  | 91 | 
|  | 92   if (upper_limit.width >= 0) | 
|  | 93     format_out->width = upper_limit.width; | 
|  | 94   if (upper_limit.height >= 0) | 
|  | 95     format_out->height = upper_limit.height; | 
|  | 96 } | 
|  | 97 | 
|  | 98 // Returns true if |constraint| is fulfilled. |format_out| can differ from | 
|  | 99 // |format_in| if the format is changed by the constraint. Ie - the frame rate | 
|  | 100 // can be changed by setting maxFrameRate. | 
|  | 101 bool NewFormatWithConstraints( | 
|  | 102     const MediaConstraintsInterface::Constraint& constraint, | 
|  | 103     const cricket::VideoFormat& format_in, | 
|  | 104     bool mandatory, | 
|  | 105     cricket::VideoFormat* format_out) { | 
|  | 106   ASSERT(format_out != NULL); | 
|  | 107   *format_out = format_in; | 
|  | 108 | 
|  | 109   if (constraint.key == MediaConstraintsInterface::kMinWidth) { | 
|  | 110     int value = rtc::FromString<int>(constraint.value); | 
|  | 111     return (value <= format_in.width); | 
|  | 112   } else if (constraint.key == MediaConstraintsInterface::kMaxWidth) { | 
|  | 113     int value = rtc::FromString<int>(constraint.value); | 
|  | 114     return (value >= format_in.width); | 
|  | 115   } else if (constraint.key == MediaConstraintsInterface::kMinHeight) { | 
|  | 116     int value = rtc::FromString<int>(constraint.value); | 
|  | 117     return (value <= format_in.height); | 
|  | 118   } else if (constraint.key == MediaConstraintsInterface::kMaxHeight) { | 
|  | 119     int value = rtc::FromString<int>(constraint.value); | 
|  | 120     return (value >= format_in.height); | 
|  | 121   } else if (constraint.key == MediaConstraintsInterface::kMinFrameRate) { | 
|  | 122     int value = rtc::FromString<int>(constraint.value); | 
|  | 123     return (value <= cricket::VideoFormat::IntervalToFps(format_in.interval)); | 
|  | 124   } else if (constraint.key == MediaConstraintsInterface::kMaxFrameRate) { | 
|  | 125     int value = rtc::FromString<int>(constraint.value); | 
|  | 126     if (value == 0) { | 
|  | 127       if (mandatory) { | 
|  | 128         // TODO(ronghuawu): Convert the constraint value to float when sub-1fps | 
|  | 129         // is supported by the capturer. | 
|  | 130         return false; | 
|  | 131       } else { | 
|  | 132         value = 1; | 
|  | 133       } | 
|  | 134     } | 
|  | 135     if (value <= cricket::VideoFormat::IntervalToFps(format_in.interval)) | 
|  | 136       format_out->interval = cricket::VideoFormat::FpsToInterval(value); | 
|  | 137     return true; | 
|  | 138   } else if (constraint.key == MediaConstraintsInterface::kMinAspectRatio) { | 
|  | 139     double value = rtc::FromString<double>(constraint.value); | 
|  | 140     // The aspect ratio in |constraint.value| has been converted to a string and | 
|  | 141     // back to a double, so it may have a rounding error. | 
|  | 142     // E.g if the value 1/3 is converted to a string, the string will not have | 
|  | 143     // infinite length. | 
|  | 144     // We add a margin of 0.0005 which is high enough to detect the same aspect | 
|  | 145     // ratio but small enough to avoid matching wrong aspect ratios. | 
|  | 146     double ratio = static_cast<double>(format_in.width) / format_in.height; | 
|  | 147     return (value <= ratio + kRoundingTruncation); | 
|  | 148   } else if (constraint.key == MediaConstraintsInterface::kMaxAspectRatio) { | 
|  | 149     double value = rtc::FromString<double>(constraint.value); | 
|  | 150     double ratio = static_cast<double>(format_in.width) / format_in.height; | 
|  | 151     // Subtract 0.0005 to avoid rounding problems. Same as above. | 
|  | 152     const double kRoundingTruncation = 0.0005; | 
|  | 153     return (value >= ratio - kRoundingTruncation); | 
|  | 154   } else if (constraint.key == MediaConstraintsInterface::kNoiseReduction) { | 
|  | 155     // These are actually options, not constraints, so they can be satisfied | 
|  | 156     // regardless of the format. | 
|  | 157     return true; | 
|  | 158   } | 
|  | 159   LOG(LS_WARNING) << "Found unknown MediaStream constraint. Name:" | 
|  | 160                   << constraint.key << " Value:" << constraint.value; | 
|  | 161   return false; | 
|  | 162 } | 
|  | 163 | 
|  | 164 // Removes cricket::VideoFormats from |formats| that don't meet |constraint|. | 
|  | 165 void FilterFormatsByConstraint( | 
|  | 166     const MediaConstraintsInterface::Constraint& constraint, | 
|  | 167     bool mandatory, | 
|  | 168     std::vector<cricket::VideoFormat>* formats) { | 
|  | 169   std::vector<cricket::VideoFormat>::iterator format_it = formats->begin(); | 
|  | 170   while (format_it != formats->end()) { | 
|  | 171     // Modify the format_it to fulfill the constraint if possible. | 
|  | 172     // Delete it otherwise. | 
|  | 173     if (!NewFormatWithConstraints(constraint, (*format_it), mandatory, | 
|  | 174                                   &(*format_it))) { | 
|  | 175       format_it = formats->erase(format_it); | 
|  | 176     } else { | 
|  | 177       ++format_it; | 
|  | 178     } | 
|  | 179   } | 
|  | 180 } | 
|  | 181 | 
|  | 182 // Returns a vector of cricket::VideoFormat that best match |constraints|. | 
|  | 183 std::vector<cricket::VideoFormat> FilterFormats( | 
|  | 184     const MediaConstraintsInterface::Constraints& mandatory, | 
|  | 185     const MediaConstraintsInterface::Constraints& optional, | 
|  | 186     const std::vector<cricket::VideoFormat>& supported_formats) { | 
|  | 187   typedef MediaConstraintsInterface::Constraints::const_iterator | 
|  | 188       ConstraintsIterator; | 
|  | 189   std::vector<cricket::VideoFormat> candidates = supported_formats; | 
|  | 190 | 
|  | 191   for (ConstraintsIterator constraints_it = mandatory.begin(); | 
|  | 192        constraints_it != mandatory.end(); ++constraints_it) | 
|  | 193     FilterFormatsByConstraint(*constraints_it, true, &candidates); | 
|  | 194 | 
|  | 195   if (candidates.size() == 0) | 
|  | 196     return candidates; | 
|  | 197 | 
|  | 198   // Ok - all mandatory checked and we still have a candidate. | 
|  | 199   // Let's try filtering using the optional constraints. | 
|  | 200   for (ConstraintsIterator constraints_it = optional.begin(); | 
|  | 201        constraints_it != optional.end(); ++constraints_it) { | 
|  | 202     std::vector<cricket::VideoFormat> current_candidates = candidates; | 
|  | 203     FilterFormatsByConstraint(*constraints_it, false, ¤t_candidates); | 
|  | 204     if (current_candidates.size() > 0) { | 
|  | 205       candidates = current_candidates; | 
|  | 206     } | 
|  | 207   } | 
|  | 208 | 
|  | 209   // We have done as good as we can to filter the supported resolutions. | 
|  | 210   return candidates; | 
|  | 211 } | 
|  | 212 | 
|  | 213 // Find the format that best matches the default video size. | 
|  | 214 // Constraints are optional and since the performance of a video call | 
|  | 215 // might be bad due to bitrate limitations, CPU, and camera performance, | 
|  | 216 // it is better to select a resolution that is as close as possible to our | 
|  | 217 // default and still meets the contraints. | 
|  | 218 const cricket::VideoFormat& GetBestCaptureFormat( | 
|  | 219     const std::vector<cricket::VideoFormat>& formats) { | 
|  | 220   ASSERT(formats.size() > 0); | 
|  | 221 | 
|  | 222   int default_area = kDefaultFormat.width * kDefaultFormat.height; | 
|  | 223 | 
|  | 224   std::vector<cricket::VideoFormat>::const_iterator it = formats.begin(); | 
|  | 225   std::vector<cricket::VideoFormat>::const_iterator best_it = formats.begin(); | 
|  | 226   int best_diff_area = std::abs(default_area - it->width * it->height); | 
|  | 227   int64_t best_diff_interval = kDefaultFormat.interval; | 
|  | 228   for (; it != formats.end(); ++it) { | 
|  | 229     int diff_area = std::abs(default_area - it->width * it->height); | 
|  | 230     int64_t diff_interval = std::abs(kDefaultFormat.interval - it->interval); | 
|  | 231     if (diff_area < best_diff_area || | 
|  | 232         (diff_area == best_diff_area && diff_interval < best_diff_interval)) { | 
|  | 233       best_diff_area = diff_area; | 
|  | 234       best_diff_interval = diff_interval; | 
|  | 235       best_it = it; | 
|  | 236     } | 
|  | 237   } | 
|  | 238   return *best_it; | 
|  | 239 } | 
|  | 240 | 
|  | 241 // Set |option| to the highest-priority value of |key| in the constraints. | 
|  | 242 // Return false if the key is mandatory, and the value is invalid. | 
|  | 243 bool ExtractOption(const MediaConstraintsInterface* all_constraints, | 
|  | 244                    const std::string& key, | 
|  | 245                    rtc::Optional<bool>* option) { | 
|  | 246   size_t mandatory = 0; | 
|  | 247   bool value; | 
|  | 248   if (FindConstraint(all_constraints, key, &value, &mandatory)) { | 
|  | 249     *option = rtc::Optional<bool>(value); | 
|  | 250     return true; | 
|  | 251   } | 
|  | 252 | 
|  | 253   return mandatory == 0; | 
|  | 254 } | 
|  | 255 | 
|  | 256 // Search |all_constraints| for known video options.  Apply all options that are | 
|  | 257 // found with valid values, and return false if any mandatory video option was | 
|  | 258 // found with an invalid value. | 
|  | 259 bool ExtractVideoOptions(const MediaConstraintsInterface* all_constraints, | 
|  | 260                          cricket::VideoOptions* options) { | 
|  | 261   bool all_valid = true; | 
|  | 262 | 
|  | 263   all_valid &= | 
|  | 264       ExtractOption(all_constraints, MediaConstraintsInterface::kNoiseReduction, | 
|  | 265                     &(options->video_noise_reduction)); | 
|  | 266 | 
|  | 267   return all_valid; | 
|  | 268 } | 
|  | 269 | 
|  | 270 }  // anonymous namespace | 
|  | 271 | 
|  | 272 namespace webrtc { | 
|  | 273 | 
|  | 274 rtc::scoped_refptr<VideoTrackSourceInterface> VideoCapturerTrackSource::Create( | 
|  | 275     rtc::Thread* worker_thread, | 
|  | 276     cricket::VideoCapturer* capturer, | 
|  | 277     const webrtc::MediaConstraintsInterface* constraints, | 
|  | 278     bool remote) { | 
|  | 279   RTC_DCHECK(worker_thread != NULL); | 
|  | 280   RTC_DCHECK(capturer != NULL); | 
|  | 281   rtc::scoped_refptr<VideoCapturerTrackSource> source( | 
|  | 282       new rtc::RefCountedObject<VideoCapturerTrackSource>(worker_thread, | 
|  | 283                                                           capturer, remote)); | 
|  | 284   source->Initialize(constraints); | 
|  | 285   return source; | 
|  | 286 } | 
|  | 287 | 
|  | 288 rtc::scoped_refptr<VideoTrackSourceInterface> VideoCapturerTrackSource::Create( | 
|  | 289     rtc::Thread* worker_thread, | 
|  | 290     cricket::VideoCapturer* capturer, | 
|  | 291     bool remote) { | 
|  | 292   RTC_DCHECK(worker_thread != NULL); | 
|  | 293   RTC_DCHECK(capturer != NULL); | 
|  | 294   rtc::scoped_refptr<VideoCapturerTrackSource> source( | 
|  | 295       new rtc::RefCountedObject<VideoCapturerTrackSource>(worker_thread, | 
|  | 296                                                           capturer, remote)); | 
|  | 297   source->Initialize(nullptr); | 
|  | 298   return source; | 
|  | 299 } | 
|  | 300 | 
|  | 301 VideoCapturerTrackSource::VideoCapturerTrackSource( | 
|  | 302     rtc::Thread* worker_thread, | 
|  | 303     cricket::VideoCapturer* capturer, | 
|  | 304     bool remote) | 
|  | 305     : signaling_thread_(rtc::Thread::Current()), | 
|  | 306       worker_thread_(worker_thread), | 
|  | 307       video_capturer_(capturer), | 
|  | 308       started_(false), | 
|  | 309       state_(kInitializing), | 
|  | 310       remote_(remote) { | 
|  | 311   video_capturer_->SignalStateChange.connect( | 
|  | 312       this, &VideoCapturerTrackSource::OnStateChange); | 
|  | 313 } | 
|  | 314 | 
|  | 315 VideoCapturerTrackSource::~VideoCapturerTrackSource() { | 
|  | 316   video_capturer_->SignalStateChange.disconnect(this); | 
|  | 317   Stop(); | 
|  | 318 } | 
|  | 319 | 
|  | 320 void VideoCapturerTrackSource::Initialize( | 
|  | 321     const webrtc::MediaConstraintsInterface* constraints) { | 
|  | 322   std::vector<cricket::VideoFormat> formats = | 
|  | 323       *video_capturer_->GetSupportedFormats(); | 
|  | 324   if (formats.empty()) { | 
|  | 325     if (video_capturer_->IsScreencast()) { | 
|  | 326       // The screen capturer can accept any resolution and we will derive the | 
|  | 327       // format from the constraints if any. | 
|  | 328       // Note that this only affects tab capturing, not desktop capturing, | 
|  | 329       // since the desktop capturer does not respect the VideoFormat passed in. | 
|  | 330       formats.push_back(cricket::VideoFormat(kDefaultFormat)); | 
|  | 331     } else { | 
|  | 332       // The VideoCapturer implementation doesn't support capability | 
|  | 333       // enumeration. We need to guess what the camera supports. | 
|  | 334       for (int i = 0; i < arraysize(kVideoFormats); ++i) { | 
|  | 335         formats.push_back(cricket::VideoFormat(kVideoFormats[i])); | 
|  | 336       } | 
|  | 337     } | 
|  | 338   } | 
|  | 339 | 
|  | 340   if (constraints) { | 
|  | 341     MediaConstraintsInterface::Constraints mandatory_constraints = | 
|  | 342         constraints->GetMandatory(); | 
|  | 343     MediaConstraintsInterface::Constraints optional_constraints; | 
|  | 344     optional_constraints = constraints->GetOptional(); | 
|  | 345 | 
|  | 346     if (video_capturer_->IsScreencast()) { | 
|  | 347       // Use the maxWidth and maxHeight allowed by constraints for screencast. | 
|  | 348       FromConstraintsForScreencast(mandatory_constraints, &(formats[0])); | 
|  | 349     } | 
|  | 350 | 
|  | 351     formats = | 
|  | 352         FilterFormats(mandatory_constraints, optional_constraints, formats); | 
|  | 353   } | 
|  | 354 | 
|  | 355   if (formats.size() == 0) { | 
|  | 356     LOG(LS_WARNING) << "Failed to find a suitable video format."; | 
|  | 357     SetState(kEnded); | 
|  | 358     return; | 
|  | 359   } | 
|  | 360 | 
|  | 361   cricket::VideoOptions options; | 
|  | 362   if (!ExtractVideoOptions(constraints, &options)) { | 
|  | 363     LOG(LS_WARNING) << "Could not satisfy mandatory options."; | 
|  | 364     SetState(kEnded); | 
|  | 365     return; | 
|  | 366   } | 
|  | 367   options_.SetAll(options); | 
|  | 368   options_.is_screencast = rtc::Optional<bool>(video_capturer_->IsScreencast()); | 
|  | 369 | 
|  | 370   format_ = GetBestCaptureFormat(formats); | 
|  | 371   // Start the camera with our best guess. | 
|  | 372   // TODO(perkj): Should we try again with another format it it turns out that | 
|  | 373   // the camera doesn't produce frames with the correct format? Or will | 
|  | 374   // cricket::VideCapturer be able to re-scale / crop to the requested | 
|  | 375   // resolution? | 
|  | 376   if (!worker_thread_->Invoke<bool>( | 
|  | 377           rtc::Bind(&cricket::VideoCapturer::StartCapturing, | 
|  | 378                     video_capturer_.get(), format_))) { | 
|  | 379     SetState(kEnded); | 
|  | 380     return; | 
|  | 381   } | 
|  | 382   started_ = true; | 
|  | 383   // Initialize hasn't succeeded until a successful state change has occurred. | 
|  | 384 } | 
|  | 385 | 
|  | 386 void VideoCapturerTrackSource::Stop() { | 
|  | 387   if (!started_) { | 
|  | 388     return; | 
|  | 389   } | 
|  | 390   started_ = false; | 
|  | 391   worker_thread_->Invoke<void>( | 
|  | 392       rtc::Bind(&cricket::VideoCapturer::Stop, video_capturer_.get())); | 
|  | 393 } | 
|  | 394 | 
|  | 395 void VideoCapturerTrackSource::Restart() { | 
|  | 396   if (started_) { | 
|  | 397     return; | 
|  | 398   } | 
|  | 399   if (!worker_thread_->Invoke<bool>( | 
|  | 400           rtc::Bind(&cricket::VideoCapturer::StartCapturing, | 
|  | 401                     video_capturer_.get(), format_))) { | 
|  | 402     SetState(kEnded); | 
|  | 403     return; | 
|  | 404   } | 
|  | 405   started_ = true; | 
|  | 406 } | 
|  | 407 | 
|  | 408 void VideoCapturerTrackSource::AddOrUpdateSink( | 
|  | 409     rtc::VideoSinkInterface<cricket::VideoFrame>* sink, | 
|  | 410     const rtc::VideoSinkWants& wants) { | 
|  | 411   worker_thread_->Invoke<void>( | 
|  | 412       rtc::Bind(&cricket::VideoCapturer::AddOrUpdateSink, video_capturer_.get(), | 
|  | 413                 sink, wants)); | 
|  | 414 } | 
|  | 415 | 
|  | 416 void VideoCapturerTrackSource::RemoveSink( | 
|  | 417     rtc::VideoSinkInterface<cricket::VideoFrame>* output) { | 
|  | 418   worker_thread_->Invoke<void>(rtc::Bind(&cricket::VideoCapturer::RemoveSink, | 
|  | 419                                          video_capturer_.get(), output)); | 
|  | 420 } | 
|  | 421 | 
|  | 422 // OnStateChange listens to the cricket::VideoCapturer::SignalStateChange. | 
|  | 423 void VideoCapturerTrackSource::OnStateChange( | 
|  | 424     cricket::VideoCapturer* capturer, | 
|  | 425     cricket::CaptureState capture_state) { | 
|  | 426   if (rtc::Thread::Current() != signaling_thread_) { | 
|  | 427     invoker_.AsyncInvoke<void>( | 
|  | 428         signaling_thread_, rtc::Bind(&VideoCapturerTrackSource::OnStateChange, | 
|  | 429                                      this, capturer, capture_state)); | 
|  | 430     return; | 
|  | 431   } | 
|  | 432 | 
|  | 433   if (capturer == video_capturer_.get()) { | 
|  | 434     SetState(GetReadyState(capture_state)); | 
|  | 435   } | 
|  | 436 } | 
|  | 437 | 
|  | 438 void VideoCapturerTrackSource::SetState(SourceState new_state) { | 
|  | 439   if (state_ != new_state) { | 
|  | 440     state_ = new_state; | 
|  | 441     FireOnChanged(); | 
|  | 442   } | 
|  | 443 } | 
|  | 444 | 
|  | 445 }  // namespace webrtc | 
| OLD | NEW | 
|---|