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 |