OLD | NEW |
| (Empty) |
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/media/base/capturemanager.h" | |
29 | |
30 #include <algorithm> | |
31 | |
32 #include "talk/media/base/videocapturer.h" | |
33 #include "talk/media/base/videorenderer.h" | |
34 #include "webrtc/base/checks.h" | |
35 #include "webrtc/base/logging.h" | |
36 | |
37 namespace cricket { | |
38 | |
39 // CaptureManager helper class. | |
40 class VideoCapturerState { | |
41 public: | |
42 static const VideoFormatPod kDefaultCaptureFormat; | |
43 | |
44 static VideoCapturerState* Create(VideoCapturer* video_capturer); | |
45 ~VideoCapturerState() {} | |
46 | |
47 void AddCaptureResolution(const VideoFormat& desired_format); | |
48 bool RemoveCaptureResolution(const VideoFormat& format); | |
49 VideoFormat GetHighestFormat(VideoCapturer* video_capturer) const; | |
50 | |
51 int IncCaptureStartRef(); | |
52 int DecCaptureStartRef(); | |
53 CaptureRenderAdapter* adapter() { | |
54 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
55 return adapter_.get(); | |
56 } | |
57 VideoCapturer* GetVideoCapturer() { | |
58 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
59 return adapter()->video_capturer(); | |
60 } | |
61 | |
62 int start_count() const { | |
63 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
64 return start_count_; | |
65 } | |
66 | |
67 private: | |
68 struct CaptureResolutionInfo { | |
69 VideoFormat video_format; | |
70 int format_ref_count; | |
71 }; | |
72 typedef std::vector<CaptureResolutionInfo> CaptureFormats; | |
73 | |
74 explicit VideoCapturerState(CaptureRenderAdapter* adapter); | |
75 | |
76 rtc::ThreadChecker thread_checker_; | |
77 rtc::scoped_ptr<CaptureRenderAdapter> adapter_; | |
78 | |
79 int start_count_; | |
80 CaptureFormats capture_formats_; | |
81 }; | |
82 | |
83 const VideoFormatPod VideoCapturerState::kDefaultCaptureFormat = { | |
84 640, 360, FPS_TO_INTERVAL(30), FOURCC_ANY | |
85 }; | |
86 | |
87 VideoCapturerState::VideoCapturerState(CaptureRenderAdapter* adapter) | |
88 : adapter_(adapter), start_count_(1) {} | |
89 | |
90 // static | |
91 VideoCapturerState* VideoCapturerState::Create(VideoCapturer* video_capturer) { | |
92 CaptureRenderAdapter* adapter = CaptureRenderAdapter::Create(video_capturer); | |
93 if (!adapter) { | |
94 return NULL; | |
95 } | |
96 return new VideoCapturerState(adapter); | |
97 } | |
98 | |
99 void VideoCapturerState::AddCaptureResolution( | |
100 const VideoFormat& desired_format) { | |
101 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
102 for (CaptureFormats::iterator iter = capture_formats_.begin(); | |
103 iter != capture_formats_.end(); ++iter) { | |
104 if (desired_format == iter->video_format) { | |
105 ++(iter->format_ref_count); | |
106 return; | |
107 } | |
108 } | |
109 CaptureResolutionInfo capture_resolution = { desired_format, 1 }; | |
110 capture_formats_.push_back(capture_resolution); | |
111 } | |
112 | |
113 bool VideoCapturerState::RemoveCaptureResolution(const VideoFormat& format) { | |
114 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
115 for (CaptureFormats::iterator iter = capture_formats_.begin(); | |
116 iter != capture_formats_.end(); ++iter) { | |
117 if (format == iter->video_format) { | |
118 --(iter->format_ref_count); | |
119 if (iter->format_ref_count == 0) { | |
120 capture_formats_.erase(iter); | |
121 } | |
122 return true; | |
123 } | |
124 } | |
125 return false; | |
126 } | |
127 | |
128 VideoFormat VideoCapturerState::GetHighestFormat( | |
129 VideoCapturer* video_capturer) const { | |
130 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
131 VideoFormat highest_format(0, 0, VideoFormat::FpsToInterval(1), FOURCC_ANY); | |
132 if (capture_formats_.empty()) { | |
133 VideoFormat default_format(kDefaultCaptureFormat); | |
134 return default_format; | |
135 } | |
136 for (CaptureFormats::const_iterator iter = capture_formats_.begin(); | |
137 iter != capture_formats_.end(); ++iter) { | |
138 if (iter->video_format.width > highest_format.width) { | |
139 highest_format.width = iter->video_format.width; | |
140 } | |
141 if (iter->video_format.height > highest_format.height) { | |
142 highest_format.height = iter->video_format.height; | |
143 } | |
144 if (iter->video_format.interval < highest_format.interval) { | |
145 highest_format.interval = iter->video_format.interval; | |
146 } | |
147 } | |
148 return highest_format; | |
149 } | |
150 | |
151 int VideoCapturerState::IncCaptureStartRef() { | |
152 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
153 return ++start_count_; | |
154 } | |
155 | |
156 int VideoCapturerState::DecCaptureStartRef() { | |
157 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
158 if (start_count_ > 0) { | |
159 // Start count may be 0 if a capturer was added but never started. | |
160 --start_count_; | |
161 } | |
162 return start_count_; | |
163 } | |
164 | |
165 CaptureManager::CaptureManager() { | |
166 // Allowing construction of manager in any thread as long as subsequent calls | |
167 // are all from the same thread. | |
168 thread_checker_.DetachFromThread(); | |
169 } | |
170 | |
171 CaptureManager::~CaptureManager() { | |
172 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
173 | |
174 // Since we don't own any of the capturers, all capturers should have been | |
175 // cleaned up before we get here. In fact, in the normal shutdown sequence, | |
176 // all capturers *will* be shut down by now, so trying to stop them here | |
177 // will crash. If we're still tracking any, it's a dangling pointer. | |
178 // TODO(hbos): RTC_DCHECK instead of RTC_CHECK until we figure out why | |
179 // capture_states_ is not always empty here. | |
180 RTC_DCHECK(capture_states_.empty()); | |
181 } | |
182 | |
183 bool CaptureManager::StartVideoCapture(VideoCapturer* video_capturer, | |
184 const VideoFormat& desired_format) { | |
185 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
186 if (desired_format.width == 0 || desired_format.height == 0) { | |
187 return false; | |
188 } | |
189 if (!video_capturer) { | |
190 return false; | |
191 } | |
192 VideoCapturerState* capture_state = GetCaptureState(video_capturer); | |
193 if (capture_state) { | |
194 const int ref_count = capture_state->IncCaptureStartRef(); | |
195 if (ref_count < 1) { | |
196 ASSERT(false); | |
197 } | |
198 // VideoCapturer has already been started. Don't start listening to | |
199 // callbacks since that has already been done. | |
200 capture_state->AddCaptureResolution(desired_format); | |
201 return true; | |
202 } | |
203 if (!RegisterVideoCapturer(video_capturer)) { | |
204 return false; | |
205 } | |
206 capture_state = GetCaptureState(video_capturer); | |
207 ASSERT(capture_state != NULL); | |
208 capture_state->AddCaptureResolution(desired_format); | |
209 if (!StartWithBestCaptureFormat(capture_state, video_capturer)) { | |
210 UnregisterVideoCapturer(capture_state); | |
211 return false; | |
212 } | |
213 return true; | |
214 } | |
215 | |
216 bool CaptureManager::StopVideoCapture(VideoCapturer* video_capturer, | |
217 const VideoFormat& format) { | |
218 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
219 VideoCapturerState* capture_state = GetCaptureState(video_capturer); | |
220 if (!capture_state) { | |
221 return false; | |
222 } | |
223 if (!capture_state->RemoveCaptureResolution(format)) { | |
224 return false; | |
225 } | |
226 | |
227 if (capture_state->DecCaptureStartRef() == 0) { | |
228 // Unregistering cannot fail as capture_state is not NULL. | |
229 UnregisterVideoCapturer(capture_state); | |
230 } | |
231 return true; | |
232 } | |
233 | |
234 bool CaptureManager::RestartVideoCapture( | |
235 VideoCapturer* video_capturer, | |
236 const VideoFormat& previous_format, | |
237 const VideoFormat& desired_format, | |
238 CaptureManager::RestartOptions options) { | |
239 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
240 if (!IsCapturerRegistered(video_capturer)) { | |
241 LOG(LS_ERROR) << "RestartVideoCapture: video_capturer is not registered."; | |
242 return false; | |
243 } | |
244 // Start the new format first. This keeps the capturer running. | |
245 if (!StartVideoCapture(video_capturer, desired_format)) { | |
246 LOG(LS_ERROR) << "RestartVideoCapture: unable to start video capture with " | |
247 "desired_format=" << desired_format.ToString(); | |
248 return false; | |
249 } | |
250 // Stop the old format. | |
251 if (!StopVideoCapture(video_capturer, previous_format)) { | |
252 LOG(LS_ERROR) << "RestartVideoCapture: unable to stop video capture with " | |
253 "previous_format=" << previous_format.ToString(); | |
254 // Undo the start request we just performed. | |
255 StopVideoCapture(video_capturer, desired_format); | |
256 return false; | |
257 } | |
258 | |
259 switch (options) { | |
260 case kForceRestart: { | |
261 VideoCapturerState* capture_state = GetCaptureState(video_capturer); | |
262 ASSERT(capture_state && capture_state->start_count() > 0); | |
263 // Try a restart using the new best resolution. | |
264 VideoFormat highest_asked_format = | |
265 capture_state->GetHighestFormat(video_capturer); | |
266 VideoFormat capture_format; | |
267 if (video_capturer->GetBestCaptureFormat(highest_asked_format, | |
268 &capture_format)) { | |
269 if (!video_capturer->Restart(capture_format)) { | |
270 LOG(LS_ERROR) << "RestartVideoCapture: Restart failed."; | |
271 } | |
272 } else { | |
273 LOG(LS_WARNING) | |
274 << "RestartVideoCapture: Couldn't find a best capture format for " | |
275 << highest_asked_format.ToString(); | |
276 } | |
277 break; | |
278 } | |
279 case kRequestRestart: | |
280 // TODO(ryanpetrie): Support restart requests. Should this | |
281 // to-be-implemented logic be used for {Start,Stop}VideoCapture as well? | |
282 break; | |
283 default: | |
284 LOG(LS_ERROR) << "Unknown/unimplemented RestartOption"; | |
285 break; | |
286 } | |
287 return true; | |
288 } | |
289 | |
290 bool CaptureManager::AddVideoRenderer(VideoCapturer* video_capturer, | |
291 VideoRenderer* video_renderer) { | |
292 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
293 if (!video_capturer || !video_renderer) { | |
294 return false; | |
295 } | |
296 CaptureRenderAdapter* adapter = GetAdapter(video_capturer); | |
297 if (!adapter) { | |
298 return false; | |
299 } | |
300 return adapter->AddRenderer(video_renderer); | |
301 } | |
302 | |
303 bool CaptureManager::RemoveVideoRenderer(VideoCapturer* video_capturer, | |
304 VideoRenderer* video_renderer) { | |
305 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
306 if (!video_capturer || !video_renderer) { | |
307 return false; | |
308 } | |
309 CaptureRenderAdapter* adapter = GetAdapter(video_capturer); | |
310 if (!adapter) { | |
311 return false; | |
312 } | |
313 return adapter->RemoveRenderer(video_renderer); | |
314 } | |
315 | |
316 bool CaptureManager::IsCapturerRegistered(VideoCapturer* video_capturer) const { | |
317 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
318 return GetCaptureState(video_capturer) != NULL; | |
319 } | |
320 | |
321 bool CaptureManager::RegisterVideoCapturer(VideoCapturer* video_capturer) { | |
322 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
323 VideoCapturerState* capture_state = | |
324 VideoCapturerState::Create(video_capturer); | |
325 if (!capture_state) { | |
326 return false; | |
327 } | |
328 capture_states_[video_capturer] = capture_state; | |
329 SignalCapturerStateChange.repeat(video_capturer->SignalStateChange); | |
330 return true; | |
331 } | |
332 | |
333 void CaptureManager::UnregisterVideoCapturer( | |
334 VideoCapturerState* capture_state) { | |
335 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
336 VideoCapturer* video_capturer = capture_state->GetVideoCapturer(); | |
337 capture_states_.erase(video_capturer); | |
338 delete capture_state; | |
339 | |
340 // When unregistering a VideoCapturer, the CaptureManager needs to unregister | |
341 // from all state change callbacks from the VideoCapturer. E.g. to avoid | |
342 // problems with multiple callbacks if registering the same VideoCapturer | |
343 // multiple times. The VideoCapturer will update the capturer state. However, | |
344 // this is done through Post-calls which means it may happen at any time. If | |
345 // the CaptureManager no longer is listening to the VideoCapturer it will not | |
346 // receive those callbacks. Here it is made sure that the the callback is | |
347 // indeed sent by letting the ChannelManager do the signaling. The downside is | |
348 // that the callback may happen before the VideoCapturer is stopped. However, | |
349 // for the CaptureManager it doesn't matter as it will no longer receive any | |
350 // frames from the VideoCapturer. | |
351 SignalCapturerStateChange.stop(video_capturer->SignalStateChange); | |
352 if (video_capturer->IsRunning()) { | |
353 video_capturer->Stop(); | |
354 SignalCapturerStateChange(video_capturer, CS_STOPPED); | |
355 } | |
356 } | |
357 | |
358 bool CaptureManager::StartWithBestCaptureFormat( | |
359 VideoCapturerState* capture_state, VideoCapturer* video_capturer) { | |
360 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
361 VideoFormat highest_asked_format = | |
362 capture_state->GetHighestFormat(video_capturer); | |
363 VideoFormat capture_format; | |
364 if (!video_capturer->GetBestCaptureFormat(highest_asked_format, | |
365 &capture_format)) { | |
366 LOG(LS_WARNING) << "Unsupported format:" | |
367 << " width=" << highest_asked_format.width | |
368 << " height=" << highest_asked_format.height | |
369 << ". Supported formats are:"; | |
370 const std::vector<VideoFormat>* formats = | |
371 video_capturer->GetSupportedFormats(); | |
372 ASSERT(formats != NULL); | |
373 for (std::vector<VideoFormat>::const_iterator i = formats->begin(); | |
374 i != formats->end(); ++i) { | |
375 const VideoFormat& format = *i; | |
376 LOG(LS_WARNING) << " " << GetFourccName(format.fourcc) | |
377 << ":" << format.width << "x" << format.height << "x" | |
378 << format.framerate(); | |
379 } | |
380 return false; | |
381 } | |
382 return video_capturer->StartCapturing(capture_format); | |
383 } | |
384 | |
385 VideoCapturerState* CaptureManager::GetCaptureState( | |
386 VideoCapturer* video_capturer) const { | |
387 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
388 CaptureStates::const_iterator iter = capture_states_.find(video_capturer); | |
389 if (iter == capture_states_.end()) { | |
390 return NULL; | |
391 } | |
392 return iter->second; | |
393 } | |
394 | |
395 CaptureRenderAdapter* CaptureManager::GetAdapter( | |
396 VideoCapturer* video_capturer) const { | |
397 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
398 VideoCapturerState* capture_state = GetCaptureState(video_capturer); | |
399 if (!capture_state) { | |
400 return NULL; | |
401 } | |
402 return capture_state->adapter(); | |
403 } | |
404 | |
405 } // namespace cricket | |
OLD | NEW |