Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(227)

Side by Side Diff: talk/media/base/capturemanager.cc

Issue 1587193006: Move talk/media to webrtc/media (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Rebased to b647aca12a884a13c1728118586245399b55fa3d (#11493) Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « talk/media/base/capturemanager.h ('k') | talk/media/base/capturemanager_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « talk/media/base/capturemanager.h ('k') | talk/media/base/capturemanager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698