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