blob: 044cfcb6d0b81f0f11a8ebe59e1e699d900ca3c7 [file] [log] [blame]
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00001/*
kjellander1afca732016-02-07 20:46:45 -08002 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00003 *
kjellander1afca732016-02-07 20:46:45 -08004 * 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
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00009 */
henrike@webrtc.org28e20752013-07-10 00:45:36 +000010
kjellander@webrtc.org5ad12972016-02-12 06:39:40 +010011#include "webrtc/media/engine/webrtcvideocapturer.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
tfarina5237aaf2015-11-10 23:44:30 -080013#include "webrtc/base/arraysize.h"
tommi@webrtc.orge07710c2015-02-19 17:43:25 +000014#include "webrtc/base/bind.h"
15#include "webrtc/base/checks.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000016#include "webrtc/base/criticalsection.h"
17#include "webrtc/base/logging.h"
Tommid44c0772016-03-11 17:12:32 -080018#include "webrtc/base/safe_conversions.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000019#include "webrtc/base/thread.h"
20#include "webrtc/base/timeutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000021
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000022#include "webrtc/base/win32.h" // Need this to #include the impl files.
Henrik Kjellander5dda80a2015-11-12 12:46:09 +010023#include "webrtc/modules/video_capture/video_capture_factory.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010024#include "webrtc/system_wrappers/include/field_trial.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000025
26namespace cricket {
27
28struct kVideoFourCCEntry {
Peter Boström0c4e06b2015-10-07 12:23:21 +020029 uint32_t fourcc;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000030 webrtc::RawVideoType webrtc_type;
31};
32
33// This indicates our format preferences and defines a mapping between
34// webrtc::RawVideoType (from video_capture_defines.h) to our FOURCCs.
35static kVideoFourCCEntry kSupportedFourCCs[] = {
36 { FOURCC_I420, webrtc::kVideoI420 }, // 12 bpp, no conversion.
37 { FOURCC_YV12, webrtc::kVideoYV12 }, // 12 bpp, no conversion.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000038 { FOURCC_YUY2, webrtc::kVideoYUY2 }, // 16 bpp, fast conversion.
39 { FOURCC_UYVY, webrtc::kVideoUYVY }, // 16 bpp, fast conversion.
buildbot@webrtc.orgfa5fcd62014-07-21 21:55:43 +000040 { FOURCC_NV12, webrtc::kVideoNV12 }, // 12 bpp, fast conversion.
41 { FOURCC_NV21, webrtc::kVideoNV21 }, // 12 bpp, fast conversion.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000042 { FOURCC_MJPG, webrtc::kVideoMJPEG }, // compressed, slow conversion.
43 { FOURCC_ARGB, webrtc::kVideoARGB }, // 32 bpp, slow conversion.
44 { FOURCC_24BG, webrtc::kVideoRGB24 }, // 24 bpp, slow conversion.
45};
46
47class WebRtcVcmFactory : public WebRtcVcmFactoryInterface {
48 public:
Peter Boström09c3a1e2016-03-22 17:17:39 +010049 virtual rtc::scoped_refptr<webrtc::VideoCaptureModule> Create(
Peter Boström09c3a1e2016-03-22 17:17:39 +010050 const char* device) {
nisseb29b9c82016-12-12 00:22:56 -080051 return webrtc::VideoCaptureFactory::Create(device);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000052 }
nisseb29b9c82016-12-12 00:22:56 -080053 virtual webrtc::VideoCaptureModule::DeviceInfo* CreateDeviceInfo() {
54 return webrtc::VideoCaptureFactory::CreateDeviceInfo();
henrike@webrtc.org28e20752013-07-10 00:45:36 +000055 }
56 virtual void DestroyDeviceInfo(webrtc::VideoCaptureModule::DeviceInfo* info) {
57 delete info;
58 }
59};
60
61static bool CapabilityToFormat(const webrtc::VideoCaptureCapability& cap,
62 VideoFormat* format) {
Peter Boström0c4e06b2015-10-07 12:23:21 +020063 uint32_t fourcc = 0;
tfarina5237aaf2015-11-10 23:44:30 -080064 for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000065 if (kSupportedFourCCs[i].webrtc_type == cap.rawType) {
66 fourcc = kSupportedFourCCs[i].fourcc;
67 break;
68 }
69 }
70 if (fourcc == 0) {
71 return false;
72 }
73
74 format->fourcc = fourcc;
75 format->width = cap.width;
76 format->height = cap.height;
77 format->interval = VideoFormat::FpsToInterval(cap.maxFPS);
78 return true;
79}
80
81static bool FormatToCapability(const VideoFormat& format,
82 webrtc::VideoCaptureCapability* cap) {
83 webrtc::RawVideoType webrtc_type = webrtc::kVideoUnknown;
tfarina5237aaf2015-11-10 23:44:30 -080084 for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000085 if (kSupportedFourCCs[i].fourcc == format.fourcc) {
86 webrtc_type = kSupportedFourCCs[i].webrtc_type;
87 break;
88 }
89 }
90 if (webrtc_type == webrtc::kVideoUnknown) {
91 return false;
92 }
93
94 cap->width = format.width;
95 cap->height = format.height;
96 cap->maxFPS = VideoFormat::IntervalToFps(format.interval);
97 cap->expectedCaptureDelay = 0;
98 cap->rawType = webrtc_type;
99 cap->codecType = webrtc::kVideoCodecUnknown;
100 cap->interlaced = false;
101 return true;
102}
103
104///////////////////////////////////////////////////////////////////////////
105// Implementation of class WebRtcVideoCapturer
106///////////////////////////////////////////////////////////////////////////
107
108WebRtcVideoCapturer::WebRtcVideoCapturer()
109 : factory_(new WebRtcVcmFactory),
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200110 module_(nullptr),
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000111 captured_frames_(0),
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200112 start_thread_(nullptr),
nissef5297a02016-09-30 01:34:27 -0700113 async_invoker_(nullptr) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000114
115WebRtcVideoCapturer::WebRtcVideoCapturer(WebRtcVcmFactoryInterface* factory)
116 : factory_(factory),
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200117 module_(nullptr),
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000118 captured_frames_(0),
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200119 start_thread_(nullptr),
nissef5297a02016-09-30 01:34:27 -0700120 async_invoker_(nullptr) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000121
Peter Boström09c3a1e2016-03-22 17:17:39 +0100122WebRtcVideoCapturer::~WebRtcVideoCapturer() {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000123
124bool WebRtcVideoCapturer::Init(const Device& device) {
henrikg91d6ede2015-09-17 00:24:34 -0700125 RTC_DCHECK(!start_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000126 if (module_) {
127 LOG(LS_ERROR) << "The capturer is already initialized";
128 return false;
129 }
130
nisseb29b9c82016-12-12 00:22:56 -0800131 webrtc::VideoCaptureModule::DeviceInfo* info = factory_->CreateDeviceInfo();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000132 if (!info) {
133 return false;
134 }
135
136 // Find the desired camera, by name.
137 // In the future, comparing IDs will be more robust.
138 // TODO(juberti): Figure what's needed to allow this.
139 int num_cams = info->NumberOfDevices();
140 char vcm_id[256] = "";
141 bool found = false;
142 for (int index = 0; index < num_cams; ++index) {
143 char vcm_name[256];
tfarina5237aaf2015-11-10 23:44:30 -0800144 if (info->GetDeviceName(index, vcm_name, arraysize(vcm_name), vcm_id,
145 arraysize(vcm_id)) != -1) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000146 if (device.name == reinterpret_cast<char*>(vcm_name)) {
147 found = true;
148 break;
149 }
150 }
151 }
152 if (!found) {
153 LOG(LS_WARNING) << "Failed to find capturer for id: " << device.id;
154 factory_->DestroyDeviceInfo(info);
155 return false;
156 }
157
158 // Enumerate the supported formats.
159 // TODO(juberti): Find out why this starts/stops the camera...
160 std::vector<VideoFormat> supported;
161 int32_t num_caps = info->NumberOfCapabilities(vcm_id);
162 for (int32_t i = 0; i < num_caps; ++i) {
163 webrtc::VideoCaptureCapability cap;
164 if (info->GetCapability(vcm_id, i, cap) != -1) {
165 VideoFormat format;
166 if (CapabilityToFormat(cap, &format)) {
167 supported.push_back(format);
168 } else {
169 LOG(LS_WARNING) << "Ignoring unsupported WebRTC capture format "
170 << cap.rawType;
171 }
172 }
173 }
174 factory_->DestroyDeviceInfo(info);
Yuriy Shevchuk39f2b0c2015-05-14 14:16:13 -0700175
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000176 if (supported.empty()) {
177 LOG(LS_ERROR) << "Failed to find usable formats for id: " << device.id;
178 return false;
179 }
Yuriy Shevchuk39f2b0c2015-05-14 14:16:13 -0700180
nisseb29b9c82016-12-12 00:22:56 -0800181 module_ = factory_->Create(vcm_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000182 if (!module_) {
183 LOG(LS_ERROR) << "Failed to create capturer for id: " << device.id;
184 return false;
185 }
186
187 // It is safe to change member attributes now.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000188 SetId(device.id);
189 SetSupportedFormats(supported);
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000190
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000191 return true;
192}
193
Peter Boström09c3a1e2016-03-22 17:17:39 +0100194bool WebRtcVideoCapturer::Init(
195 const rtc::scoped_refptr<webrtc::VideoCaptureModule>& module) {
henrikg91d6ede2015-09-17 00:24:34 -0700196 RTC_DCHECK(!start_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000197 if (module_) {
198 LOG(LS_ERROR) << "The capturer is already initialized";
199 return false;
200 }
201 if (!module) {
202 LOG(LS_ERROR) << "Invalid VCM supplied";
203 return false;
204 }
205 // TODO(juberti): Set id and formats.
Peter Boström09c3a1e2016-03-22 17:17:39 +0100206 module_ = module;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000207 return true;
208}
209
210bool WebRtcVideoCapturer::GetBestCaptureFormat(const VideoFormat& desired,
211 VideoFormat* best_format) {
212 if (!best_format) {
213 return false;
214 }
215
216 if (!VideoCapturer::GetBestCaptureFormat(desired, best_format)) {
217 // We maybe using a manually injected VCM which doesn't support enum.
218 // Use the desired format as the best format.
219 best_format->width = desired.width;
220 best_format->height = desired.height;
221 best_format->fourcc = FOURCC_I420;
222 best_format->interval = desired.interval;
223 LOG(LS_INFO) << "Failed to find best capture format,"
224 << " fall back to the requested format "
225 << best_format->ToString();
226 }
227 return true;
228}
Pera5092412016-02-12 13:30:57 +0100229void WebRtcVideoCapturer::OnSinkWantsChanged(const rtc::VideoSinkWants& wants) {
Guo-wei Shieh64c1e8c2015-04-01 15:33:06 -0700230 // Can't take lock here as this will cause deadlock with
231 // OnIncomingCapturedFrame. In fact, the whole method, including methods it
232 // calls, can't take lock.
henrikg91d6ede2015-09-17 00:24:34 -0700233 RTC_DCHECK(module_);
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000234
jbauchbd384282015-07-16 04:05:52 -0700235 const std::string group_name =
Guo-wei Shieh64c1e8c2015-04-01 15:33:06 -0700236 webrtc::field_trial::FindFullName("WebRTC-CVO");
237
238 if (group_name == "Disabled") {
Pera5092412016-02-12 13:30:57 +0100239 return;
Guo-wei Shieh64c1e8c2015-04-01 15:33:06 -0700240 }
241
Pera5092412016-02-12 13:30:57 +0100242 VideoCapturer::OnSinkWantsChanged(wants);
243 bool result = module_->SetApplyRotation(wants.rotation_applied);
244 RTC_CHECK(result);
245
246 return;
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000247}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000248
249CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) {
250 if (!module_) {
251 LOG(LS_ERROR) << "The capturer has not been initialized";
Perfb45d172016-02-29 12:07:35 +0100252 return CS_FAILED;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000253 }
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200254 if (start_thread_) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000255 LOG(LS_ERROR) << "The capturer is already running";
henrikg91d6ede2015-09-17 00:24:34 -0700256 RTC_DCHECK(start_thread_->IsCurrent())
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200257 << "Trying to start capturer on different threads";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000258 return CS_FAILED;
259 }
260
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000261 start_thread_ = rtc::Thread::Current();
henrikg91d6ede2015-09-17 00:24:34 -0700262 RTC_DCHECK(!async_invoker_);
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200263 async_invoker_.reset(new rtc::AsyncInvoker());
264 captured_frames_ = 0;
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000265
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000266 SetCaptureFormat(&capture_format);
267
268 webrtc::VideoCaptureCapability cap;
269 if (!FormatToCapability(capture_format, &cap)) {
270 LOG(LS_ERROR) << "Invalid capture format specified";
271 return CS_FAILED;
272 }
273
Honghai Zhang82d78622016-05-06 11:29:15 -0700274 int64_t start = rtc::TimeMillis();
nisseb29b9c82016-12-12 00:22:56 -0800275 module_->RegisterCaptureDataCallback(this);
mallinath@webrtc.org7433a082014-01-29 00:56:02 +0000276 if (module_->StartCapture(cap) != 0) {
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200277 LOG(LS_ERROR) << "Camera '" << GetId() << "' failed to start";
278 module_->DeRegisterCaptureDataCallback();
279 async_invoker_.reset();
280 SetCaptureFormat(nullptr);
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000281 start_thread_ = nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000282 return CS_FAILED;
283 }
284
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200285 LOG(LS_INFO) << "Camera '" << GetId() << "' started with format "
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000286 << capture_format.ToString() << ", elapsed time "
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000287 << rtc::TimeSince(start) << " ms";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000288
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000289 SetCaptureState(CS_RUNNING);
290 return CS_STARTING;
291}
292
293void WebRtcVideoCapturer::Stop() {
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200294 if (!start_thread_) {
295 LOG(LS_ERROR) << "The capturer is already stopped";
296 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000297 }
henrikg91d6ede2015-09-17 00:24:34 -0700298 RTC_DCHECK(start_thread_);
299 RTC_DCHECK(start_thread_->IsCurrent());
300 RTC_DCHECK(async_invoker_);
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200301 if (IsRunning()) {
302 // The module is responsible for OnIncomingCapturedFrame being called, if
303 // we stop it we will get no further callbacks.
304 module_->StopCapture();
305 }
306 module_->DeRegisterCaptureDataCallback();
307
308 // TODO(juberti): Determine if the VCM exposes any drop stats we can use.
309 double drop_ratio = 0.0;
310 LOG(LS_INFO) << "Camera '" << GetId() << "' stopped after capturing "
311 << captured_frames_ << " frames and dropping "
312 << drop_ratio << "%";
313
314 // Clear any pending async invokes (that OnIncomingCapturedFrame may have
315 // caused).
316 async_invoker_.reset();
317
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000318 SetCaptureFormat(NULL);
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000319 start_thread_ = nullptr;
hbos0de97f12015-12-01 02:13:33 -0800320 SetCaptureState(CS_STOPPED);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000321}
322
323bool WebRtcVideoCapturer::IsRunning() {
324 return (module_ != NULL && module_->CaptureStarted());
325}
326
Peter Boström0c4e06b2015-10-07 12:23:21 +0200327bool WebRtcVideoCapturer::GetPreferredFourccs(std::vector<uint32_t>* fourccs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000328 if (!fourccs) {
329 return false;
330 }
331
332 fourccs->clear();
tfarina5237aaf2015-11-10 23:44:30 -0800333 for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000334 fourccs->push_back(kSupportedFourCCs[i].fourcc);
335 }
336 return true;
337}
338
nisseb29b9c82016-12-12 00:22:56 -0800339void WebRtcVideoCapturer::OnFrame(
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700340 const webrtc::VideoFrame& sample) {
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200341 // This can only happen between Start() and Stop().
henrikg91d6ede2015-09-17 00:24:34 -0700342 RTC_DCHECK(start_thread_);
343 RTC_DCHECK(async_invoker_);
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200344
345 ++captured_frames_;
346 // Log the size and pixel aspect ratio of the first captured frame.
347 if (1 == captured_frames_) {
348 LOG(LS_INFO) << "Captured frame size "
nisse79246972016-08-23 05:50:09 -0700349 << sample.width() << "x" << sample.height()
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200350 << ". Expected format " << GetCaptureFormat()->ToString();
351 }
352
nisseb29b9c82016-12-12 00:22:56 -0800353 VideoCapturer::OnFrame(sample, sample.width(), sample.height());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000354}
355
356} // namespace cricket