blob: 66b167e1cf3d70b9debb88e1ed5f96d6d886072b [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(
50 int id,
51 const char* device) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000052 return webrtc::VideoCaptureFactory::Create(id, device);
53 }
54 virtual webrtc::VideoCaptureModule::DeviceInfo* CreateDeviceInfo(int id) {
55 return webrtc::VideoCaptureFactory::CreateDeviceInfo(id);
56 }
57 virtual void DestroyDeviceInfo(webrtc::VideoCaptureModule::DeviceInfo* info) {
58 delete info;
59 }
60};
61
62static bool CapabilityToFormat(const webrtc::VideoCaptureCapability& cap,
63 VideoFormat* format) {
Peter Boström0c4e06b2015-10-07 12:23:21 +020064 uint32_t fourcc = 0;
tfarina5237aaf2015-11-10 23:44:30 -080065 for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000066 if (kSupportedFourCCs[i].webrtc_type == cap.rawType) {
67 fourcc = kSupportedFourCCs[i].fourcc;
68 break;
69 }
70 }
71 if (fourcc == 0) {
72 return false;
73 }
74
75 format->fourcc = fourcc;
76 format->width = cap.width;
77 format->height = cap.height;
78 format->interval = VideoFormat::FpsToInterval(cap.maxFPS);
79 return true;
80}
81
82static bool FormatToCapability(const VideoFormat& format,
83 webrtc::VideoCaptureCapability* cap) {
84 webrtc::RawVideoType webrtc_type = webrtc::kVideoUnknown;
tfarina5237aaf2015-11-10 23:44:30 -080085 for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000086 if (kSupportedFourCCs[i].fourcc == format.fourcc) {
87 webrtc_type = kSupportedFourCCs[i].webrtc_type;
88 break;
89 }
90 }
91 if (webrtc_type == webrtc::kVideoUnknown) {
92 return false;
93 }
94
95 cap->width = format.width;
96 cap->height = format.height;
97 cap->maxFPS = VideoFormat::IntervalToFps(format.interval);
98 cap->expectedCaptureDelay = 0;
99 cap->rawType = webrtc_type;
100 cap->codecType = webrtc::kVideoCodecUnknown;
101 cap->interlaced = false;
102 return true;
103}
104
105///////////////////////////////////////////////////////////////////////////
106// Implementation of class WebRtcVideoCapturer
107///////////////////////////////////////////////////////////////////////////
108
109WebRtcVideoCapturer::WebRtcVideoCapturer()
110 : factory_(new WebRtcVcmFactory),
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200111 module_(nullptr),
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000112 captured_frames_(0),
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200113 start_thread_(nullptr),
nissef5297a02016-09-30 01:34:27 -0700114 async_invoker_(nullptr) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000115
116WebRtcVideoCapturer::WebRtcVideoCapturer(WebRtcVcmFactoryInterface* factory)
117 : factory_(factory),
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200118 module_(nullptr),
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000119 captured_frames_(0),
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200120 start_thread_(nullptr),
nissef5297a02016-09-30 01:34:27 -0700121 async_invoker_(nullptr) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000122
Peter Boström09c3a1e2016-03-22 17:17:39 +0100123WebRtcVideoCapturer::~WebRtcVideoCapturer() {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000124
125bool WebRtcVideoCapturer::Init(const Device& device) {
henrikg91d6ede2015-09-17 00:24:34 -0700126 RTC_DCHECK(!start_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000127 if (module_) {
128 LOG(LS_ERROR) << "The capturer is already initialized";
129 return false;
130 }
131
132 webrtc::VideoCaptureModule::DeviceInfo* info = factory_->CreateDeviceInfo(0);
133 if (!info) {
134 return false;
135 }
136
137 // Find the desired camera, by name.
138 // In the future, comparing IDs will be more robust.
139 // TODO(juberti): Figure what's needed to allow this.
140 int num_cams = info->NumberOfDevices();
141 char vcm_id[256] = "";
142 bool found = false;
143 for (int index = 0; index < num_cams; ++index) {
144 char vcm_name[256];
tfarina5237aaf2015-11-10 23:44:30 -0800145 if (info->GetDeviceName(index, vcm_name, arraysize(vcm_name), vcm_id,
146 arraysize(vcm_id)) != -1) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000147 if (device.name == reinterpret_cast<char*>(vcm_name)) {
148 found = true;
149 break;
150 }
151 }
152 }
153 if (!found) {
154 LOG(LS_WARNING) << "Failed to find capturer for id: " << device.id;
155 factory_->DestroyDeviceInfo(info);
156 return false;
157 }
158
159 // Enumerate the supported formats.
160 // TODO(juberti): Find out why this starts/stops the camera...
161 std::vector<VideoFormat> supported;
162 int32_t num_caps = info->NumberOfCapabilities(vcm_id);
163 for (int32_t i = 0; i < num_caps; ++i) {
164 webrtc::VideoCaptureCapability cap;
165 if (info->GetCapability(vcm_id, i, cap) != -1) {
166 VideoFormat format;
167 if (CapabilityToFormat(cap, &format)) {
168 supported.push_back(format);
169 } else {
170 LOG(LS_WARNING) << "Ignoring unsupported WebRTC capture format "
171 << cap.rawType;
172 }
173 }
174 }
175 factory_->DestroyDeviceInfo(info);
Yuriy Shevchuk39f2b0c2015-05-14 14:16:13 -0700176
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000177 if (supported.empty()) {
178 LOG(LS_ERROR) << "Failed to find usable formats for id: " << device.id;
179 return false;
180 }
Yuriy Shevchuk39f2b0c2015-05-14 14:16:13 -0700181
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000182 module_ = factory_->Create(0, vcm_id);
183 if (!module_) {
184 LOG(LS_ERROR) << "Failed to create capturer for id: " << device.id;
185 return false;
186 }
187
188 // It is safe to change member attributes now.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000189 SetId(device.id);
190 SetSupportedFormats(supported);
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000191
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000192 return true;
193}
194
Peter Boström09c3a1e2016-03-22 17:17:39 +0100195bool WebRtcVideoCapturer::Init(
196 const rtc::scoped_refptr<webrtc::VideoCaptureModule>& module) {
henrikg91d6ede2015-09-17 00:24:34 -0700197 RTC_DCHECK(!start_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000198 if (module_) {
199 LOG(LS_ERROR) << "The capturer is already initialized";
200 return false;
201 }
202 if (!module) {
203 LOG(LS_ERROR) << "Invalid VCM supplied";
204 return false;
205 }
206 // TODO(juberti): Set id and formats.
Peter Boström09c3a1e2016-03-22 17:17:39 +0100207 module_ = module;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000208 return true;
209}
210
211bool WebRtcVideoCapturer::GetBestCaptureFormat(const VideoFormat& desired,
212 VideoFormat* best_format) {
213 if (!best_format) {
214 return false;
215 }
216
217 if (!VideoCapturer::GetBestCaptureFormat(desired, best_format)) {
218 // We maybe using a manually injected VCM which doesn't support enum.
219 // Use the desired format as the best format.
220 best_format->width = desired.width;
221 best_format->height = desired.height;
222 best_format->fourcc = FOURCC_I420;
223 best_format->interval = desired.interval;
224 LOG(LS_INFO) << "Failed to find best capture format,"
225 << " fall back to the requested format "
226 << best_format->ToString();
227 }
228 return true;
229}
Pera5092412016-02-12 13:30:57 +0100230void WebRtcVideoCapturer::OnSinkWantsChanged(const rtc::VideoSinkWants& wants) {
Guo-wei Shieh64c1e8c2015-04-01 15:33:06 -0700231 // Can't take lock here as this will cause deadlock with
232 // OnIncomingCapturedFrame. In fact, the whole method, including methods it
233 // calls, can't take lock.
henrikg91d6ede2015-09-17 00:24:34 -0700234 RTC_DCHECK(module_);
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000235
jbauchbd384282015-07-16 04:05:52 -0700236 const std::string group_name =
Guo-wei Shieh64c1e8c2015-04-01 15:33:06 -0700237 webrtc::field_trial::FindFullName("WebRTC-CVO");
238
239 if (group_name == "Disabled") {
Pera5092412016-02-12 13:30:57 +0100240 return;
Guo-wei Shieh64c1e8c2015-04-01 15:33:06 -0700241 }
242
Pera5092412016-02-12 13:30:57 +0100243 VideoCapturer::OnSinkWantsChanged(wants);
244 bool result = module_->SetApplyRotation(wants.rotation_applied);
245 RTC_CHECK(result);
246
247 return;
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000248}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000249
250CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) {
251 if (!module_) {
252 LOG(LS_ERROR) << "The capturer has not been initialized";
Perfb45d172016-02-29 12:07:35 +0100253 return CS_FAILED;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254 }
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200255 if (start_thread_) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000256 LOG(LS_ERROR) << "The capturer is already running";
henrikg91d6ede2015-09-17 00:24:34 -0700257 RTC_DCHECK(start_thread_->IsCurrent())
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200258 << "Trying to start capturer on different threads";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000259 return CS_FAILED;
260 }
261
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000262 start_thread_ = rtc::Thread::Current();
henrikg91d6ede2015-09-17 00:24:34 -0700263 RTC_DCHECK(!async_invoker_);
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200264 async_invoker_.reset(new rtc::AsyncInvoker());
265 captured_frames_ = 0;
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000266
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000267 SetCaptureFormat(&capture_format);
268
269 webrtc::VideoCaptureCapability cap;
270 if (!FormatToCapability(capture_format, &cap)) {
271 LOG(LS_ERROR) << "Invalid capture format specified";
272 return CS_FAILED;
273 }
274
Honghai Zhang82d78622016-05-06 11:29:15 -0700275 int64_t start = rtc::TimeMillis();
mallinath@webrtc.org7433a082014-01-29 00:56:02 +0000276 module_->RegisterCaptureDataCallback(*this);
277 if (module_->StartCapture(cap) != 0) {
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200278 LOG(LS_ERROR) << "Camera '" << GetId() << "' failed to start";
279 module_->DeRegisterCaptureDataCallback();
280 async_invoker_.reset();
281 SetCaptureFormat(nullptr);
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000282 start_thread_ = nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000283 return CS_FAILED;
284 }
285
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200286 LOG(LS_INFO) << "Camera '" << GetId() << "' started with format "
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000287 << capture_format.ToString() << ", elapsed time "
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000288 << rtc::TimeSince(start) << " ms";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000289
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000290 SetCaptureState(CS_RUNNING);
291 return CS_STARTING;
292}
293
294void WebRtcVideoCapturer::Stop() {
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200295 if (!start_thread_) {
296 LOG(LS_ERROR) << "The capturer is already stopped";
297 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000298 }
henrikg91d6ede2015-09-17 00:24:34 -0700299 RTC_DCHECK(start_thread_);
300 RTC_DCHECK(start_thread_->IsCurrent());
301 RTC_DCHECK(async_invoker_);
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200302 if (IsRunning()) {
303 // The module is responsible for OnIncomingCapturedFrame being called, if
304 // we stop it we will get no further callbacks.
305 module_->StopCapture();
306 }
307 module_->DeRegisterCaptureDataCallback();
308
309 // TODO(juberti): Determine if the VCM exposes any drop stats we can use.
310 double drop_ratio = 0.0;
311 LOG(LS_INFO) << "Camera '" << GetId() << "' stopped after capturing "
312 << captured_frames_ << " frames and dropping "
313 << drop_ratio << "%";
314
315 // Clear any pending async invokes (that OnIncomingCapturedFrame may have
316 // caused).
317 async_invoker_.reset();
318
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000319 SetCaptureFormat(NULL);
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000320 start_thread_ = nullptr;
hbos0de97f12015-12-01 02:13:33 -0800321 SetCaptureState(CS_STOPPED);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000322}
323
324bool WebRtcVideoCapturer::IsRunning() {
325 return (module_ != NULL && module_->CaptureStarted());
326}
327
Peter Boström0c4e06b2015-10-07 12:23:21 +0200328bool WebRtcVideoCapturer::GetPreferredFourccs(std::vector<uint32_t>* fourccs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000329 if (!fourccs) {
330 return false;
331 }
332
333 fourccs->clear();
tfarina5237aaf2015-11-10 23:44:30 -0800334 for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000335 fourccs->push_back(kSupportedFourCCs[i].fourcc);
336 }
337 return true;
338}
339
perkj@webrtc.orgaf612d52015-03-18 09:51:05 +0000340void WebRtcVideoCapturer::OnIncomingCapturedFrame(
341 const int32_t id,
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700342 const webrtc::VideoFrame& sample) {
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200343 // This can only happen between Start() and Stop().
henrikg91d6ede2015-09-17 00:24:34 -0700344 RTC_DCHECK(start_thread_);
345 RTC_DCHECK(async_invoker_);
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200346
347 ++captured_frames_;
348 // Log the size and pixel aspect ratio of the first captured frame.
349 if (1 == captured_frames_) {
350 LOG(LS_INFO) << "Captured frame size "
nisse79246972016-08-23 05:50:09 -0700351 << sample.width() << "x" << sample.height()
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200352 << ". Expected format " << GetCaptureFormat()->ToString();
353 }
354
nisseacd935b2016-11-11 03:55:13 -0800355 OnFrame(sample, sample.width(), sample.height());
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000356}
357
nisse79246972016-08-23 05:50:09 -0700358void WebRtcVideoCapturer::OnCaptureDelayChanged(const int32_t id,
359 const int32_t delay) {
360 LOG(LS_INFO) << "Capture delay changed to " << delay << " ms";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000361}
362
363} // namespace cricket