| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * 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 |
| 5 * 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 |
| 6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * 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. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 #include "webrtc/media/base/capturemanager.h" | 11 #include "webrtc/media/base/capturemanager.h" |
| 12 | 12 |
| 13 #include <algorithm> | 13 // TODO(perkj): Remove this file once Chrome's gyp file doesn't depend on it. |
| 14 | |
| 15 #include "webrtc/base/checks.h" | |
| 16 #include "webrtc/base/logging.h" | |
| 17 #include "webrtc/media/base/videocapturer.h" | |
| 18 | |
| 19 namespace cricket { | |
| 20 | |
| 21 // CaptureManager helper class. | |
| 22 class VideoCapturerState { | |
| 23 public: | |
| 24 static const VideoFormatPod kDefaultCaptureFormat; | |
| 25 | |
| 26 explicit VideoCapturerState(VideoCapturer* capturer); | |
| 27 ~VideoCapturerState() {} | |
| 28 | |
| 29 void AddCaptureResolution(const VideoFormat& desired_format); | |
| 30 bool RemoveCaptureResolution(const VideoFormat& format); | |
| 31 VideoFormat GetHighestFormat(VideoCapturer* video_capturer) const; | |
| 32 | |
| 33 int IncCaptureStartRef(); | |
| 34 int DecCaptureStartRef(); | |
| 35 VideoCapturer* GetVideoCapturer() { | |
| 36 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 37 return video_capturer_; | |
| 38 } | |
| 39 | |
| 40 int start_count() const { | |
| 41 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 42 return start_count_; | |
| 43 } | |
| 44 | |
| 45 private: | |
| 46 struct CaptureResolutionInfo { | |
| 47 VideoFormat video_format; | |
| 48 int format_ref_count; | |
| 49 }; | |
| 50 typedef std::vector<CaptureResolutionInfo> CaptureFormats; | |
| 51 | |
| 52 rtc::ThreadChecker thread_checker_; | |
| 53 | |
| 54 VideoCapturer* video_capturer_; | |
| 55 int start_count_; | |
| 56 CaptureFormats capture_formats_; | |
| 57 }; | |
| 58 | |
| 59 const VideoFormatPod VideoCapturerState::kDefaultCaptureFormat = { | |
| 60 640, 360, FPS_TO_INTERVAL(30), FOURCC_ANY | |
| 61 }; | |
| 62 | |
| 63 VideoCapturerState::VideoCapturerState(VideoCapturer* capturer) | |
| 64 : video_capturer_(capturer), start_count_(1) {} | |
| 65 | |
| 66 void VideoCapturerState::AddCaptureResolution( | |
| 67 const VideoFormat& desired_format) { | |
| 68 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 69 for (CaptureFormats::iterator iter = capture_formats_.begin(); | |
| 70 iter != capture_formats_.end(); ++iter) { | |
| 71 if (desired_format == iter->video_format) { | |
| 72 ++(iter->format_ref_count); | |
| 73 return; | |
| 74 } | |
| 75 } | |
| 76 CaptureResolutionInfo capture_resolution = { desired_format, 1 }; | |
| 77 capture_formats_.push_back(capture_resolution); | |
| 78 } | |
| 79 | |
| 80 bool VideoCapturerState::RemoveCaptureResolution(const VideoFormat& format) { | |
| 81 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 82 for (CaptureFormats::iterator iter = capture_formats_.begin(); | |
| 83 iter != capture_formats_.end(); ++iter) { | |
| 84 if (format == iter->video_format) { | |
| 85 --(iter->format_ref_count); | |
| 86 if (iter->format_ref_count == 0) { | |
| 87 capture_formats_.erase(iter); | |
| 88 } | |
| 89 return true; | |
| 90 } | |
| 91 } | |
| 92 return false; | |
| 93 } | |
| 94 | |
| 95 VideoFormat VideoCapturerState::GetHighestFormat( | |
| 96 VideoCapturer* video_capturer) const { | |
| 97 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 98 VideoFormat highest_format(0, 0, VideoFormat::FpsToInterval(1), FOURCC_ANY); | |
| 99 if (capture_formats_.empty()) { | |
| 100 VideoFormat default_format(kDefaultCaptureFormat); | |
| 101 return default_format; | |
| 102 } | |
| 103 for (CaptureFormats::const_iterator iter = capture_formats_.begin(); | |
| 104 iter != capture_formats_.end(); ++iter) { | |
| 105 if (iter->video_format.width > highest_format.width) { | |
| 106 highest_format.width = iter->video_format.width; | |
| 107 } | |
| 108 if (iter->video_format.height > highest_format.height) { | |
| 109 highest_format.height = iter->video_format.height; | |
| 110 } | |
| 111 if (iter->video_format.interval < highest_format.interval) { | |
| 112 highest_format.interval = iter->video_format.interval; | |
| 113 } | |
| 114 } | |
| 115 return highest_format; | |
| 116 } | |
| 117 | |
| 118 int VideoCapturerState::IncCaptureStartRef() { | |
| 119 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 120 return ++start_count_; | |
| 121 } | |
| 122 | |
| 123 int VideoCapturerState::DecCaptureStartRef() { | |
| 124 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 125 if (start_count_ > 0) { | |
| 126 // Start count may be 0 if a capturer was added but never started. | |
| 127 --start_count_; | |
| 128 } | |
| 129 return start_count_; | |
| 130 } | |
| 131 | |
| 132 CaptureManager::CaptureManager() { | |
| 133 // Allowing construction of manager in any thread as long as subsequent calls | |
| 134 // are all from the same thread. | |
| 135 thread_checker_.DetachFromThread(); | |
| 136 } | |
| 137 | |
| 138 CaptureManager::~CaptureManager() { | |
| 139 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 140 | |
| 141 // Since we don't own any of the capturers, all capturers should have been | |
| 142 // cleaned up before we get here. In fact, in the normal shutdown sequence, | |
| 143 // all capturers *will* be shut down by now, so trying to stop them here | |
| 144 // will crash. If we're still tracking any, it's a dangling pointer. | |
| 145 // TODO(hbos): RTC_DCHECK instead of RTC_CHECK until we figure out why | |
| 146 // capture_states_ is not always empty here. | |
| 147 RTC_DCHECK(capture_states_.empty()); | |
| 148 } | |
| 149 | |
| 150 bool CaptureManager::StartVideoCapture(VideoCapturer* video_capturer, | |
| 151 const VideoFormat& desired_format) { | |
| 152 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 153 if (desired_format.width == 0 || desired_format.height == 0) { | |
| 154 return false; | |
| 155 } | |
| 156 if (!video_capturer) { | |
| 157 return false; | |
| 158 } | |
| 159 VideoCapturerState* capture_state = GetCaptureState(video_capturer); | |
| 160 if (capture_state) { | |
| 161 const int ref_count = capture_state->IncCaptureStartRef(); | |
| 162 if (ref_count < 1) { | |
| 163 ASSERT(false); | |
| 164 } | |
| 165 // VideoCapturer has already been started. Don't start listening to | |
| 166 // callbacks since that has already been done. | |
| 167 capture_state->AddCaptureResolution(desired_format); | |
| 168 return true; | |
| 169 } | |
| 170 if (!RegisterVideoCapturer(video_capturer)) { | |
| 171 return false; | |
| 172 } | |
| 173 capture_state = GetCaptureState(video_capturer); | |
| 174 ASSERT(capture_state != NULL); | |
| 175 capture_state->AddCaptureResolution(desired_format); | |
| 176 if (!StartWithBestCaptureFormat(capture_state, video_capturer)) { | |
| 177 UnregisterVideoCapturer(capture_state); | |
| 178 return false; | |
| 179 } | |
| 180 return true; | |
| 181 } | |
| 182 | |
| 183 bool CaptureManager::StopVideoCapture(VideoCapturer* video_capturer, | |
| 184 const VideoFormat& format) { | |
| 185 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 186 VideoCapturerState* capture_state = GetCaptureState(video_capturer); | |
| 187 if (!capture_state) { | |
| 188 return false; | |
| 189 } | |
| 190 if (!capture_state->RemoveCaptureResolution(format)) { | |
| 191 return false; | |
| 192 } | |
| 193 | |
| 194 if (capture_state->DecCaptureStartRef() == 0) { | |
| 195 // Unregistering cannot fail as capture_state is not NULL. | |
| 196 UnregisterVideoCapturer(capture_state); | |
| 197 } | |
| 198 return true; | |
| 199 } | |
| 200 | |
| 201 void CaptureManager::AddVideoSink(VideoCapturer* video_capturer, | |
| 202 rtc::VideoSinkInterface<VideoFrame>* sink) { | |
| 203 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 204 // TODO(nisse): Do we really need to tolerate NULL inputs? | |
| 205 if (!video_capturer || !sink) { | |
| 206 return; | |
| 207 } | |
| 208 rtc::VideoSinkWants wants; | |
| 209 // Renderers must be able to apply rotation. | |
| 210 wants.rotation_applied = false; | |
| 211 video_capturer->AddOrUpdateSink(sink, wants); | |
| 212 } | |
| 213 | |
| 214 void CaptureManager::RemoveVideoSink( | |
| 215 VideoCapturer* video_capturer, | |
| 216 rtc::VideoSinkInterface<VideoFrame>* sink) { | |
| 217 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 218 if (!video_capturer || !sink) { | |
| 219 return; | |
| 220 } | |
| 221 video_capturer->RemoveSink(sink); | |
| 222 } | |
| 223 | |
| 224 bool CaptureManager::IsCapturerRegistered(VideoCapturer* video_capturer) const { | |
| 225 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 226 return GetCaptureState(video_capturer) != NULL; | |
| 227 } | |
| 228 | |
| 229 bool CaptureManager::RegisterVideoCapturer(VideoCapturer* video_capturer) { | |
| 230 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 231 VideoCapturerState* capture_state = new VideoCapturerState(video_capturer); | |
| 232 capture_states_[video_capturer] = capture_state; | |
| 233 SignalCapturerStateChange.repeat(video_capturer->SignalStateChange); | |
| 234 return true; | |
| 235 } | |
| 236 | |
| 237 void CaptureManager::UnregisterVideoCapturer( | |
| 238 VideoCapturerState* capture_state) { | |
| 239 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 240 VideoCapturer* video_capturer = capture_state->GetVideoCapturer(); | |
| 241 capture_states_.erase(video_capturer); | |
| 242 delete capture_state; | |
| 243 | |
| 244 // When unregistering a VideoCapturer, the CaptureManager needs to unregister | |
| 245 // from all state change callbacks from the VideoCapturer. E.g. to avoid | |
| 246 // problems with multiple callbacks if registering the same VideoCapturer | |
| 247 // multiple times. The VideoCapturer will update the capturer state. However, | |
| 248 // this is done through Post-calls which means it may happen at any time. If | |
| 249 // the CaptureManager no longer is listening to the VideoCapturer it will not | |
| 250 // receive those callbacks. Here it is made sure that the the callback is | |
| 251 // indeed sent by letting the ChannelManager do the signaling. The downside is | |
| 252 // that the callback may happen before the VideoCapturer is stopped. However, | |
| 253 // for the CaptureManager it doesn't matter as it will no longer receive any | |
| 254 // frames from the VideoCapturer. | |
| 255 SignalCapturerStateChange.stop(video_capturer->SignalStateChange); | |
| 256 if (video_capturer->IsRunning()) { | |
| 257 video_capturer->Stop(); | |
| 258 SignalCapturerStateChange(video_capturer, CS_STOPPED); | |
| 259 } | |
| 260 } | |
| 261 | |
| 262 bool CaptureManager::StartWithBestCaptureFormat( | |
| 263 VideoCapturerState* capture_state, VideoCapturer* video_capturer) { | |
| 264 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 265 VideoFormat highest_asked_format = | |
| 266 capture_state->GetHighestFormat(video_capturer); | |
| 267 VideoFormat capture_format; | |
| 268 if (!video_capturer->GetBestCaptureFormat(highest_asked_format, | |
| 269 &capture_format)) { | |
| 270 LOG(LS_WARNING) << "Unsupported format:" | |
| 271 << " width=" << highest_asked_format.width | |
| 272 << " height=" << highest_asked_format.height | |
| 273 << ". Supported formats are:"; | |
| 274 const std::vector<VideoFormat>* formats = | |
| 275 video_capturer->GetSupportedFormats(); | |
| 276 ASSERT(formats != NULL); | |
| 277 for (std::vector<VideoFormat>::const_iterator i = formats->begin(); | |
| 278 i != formats->end(); ++i) { | |
| 279 const VideoFormat& format = *i; | |
| 280 LOG(LS_WARNING) << " " << GetFourccName(format.fourcc) | |
| 281 << ":" << format.width << "x" << format.height << "x" | |
| 282 << format.framerate(); | |
| 283 } | |
| 284 return false; | |
| 285 } | |
| 286 return video_capturer->StartCapturing(capture_format); | |
| 287 } | |
| 288 | |
| 289 VideoCapturerState* CaptureManager::GetCaptureState( | |
| 290 VideoCapturer* video_capturer) const { | |
| 291 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 292 CaptureStates::const_iterator iter = capture_states_.find(video_capturer); | |
| 293 if (iter == capture_states_.end()) { | |
| 294 return NULL; | |
| 295 } | |
| 296 return iter->second; | |
| 297 } | |
| 298 | |
| 299 } // namespace cricket | |
| OLD | NEW |