blob: 11458d1ed07f36d45ae95adcb15cb6d37397b772 [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);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000097 cap->rawType = webrtc_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000098 cap->interlaced = false;
99 return true;
100}
101
102///////////////////////////////////////////////////////////////////////////
103// Implementation of class WebRtcVideoCapturer
104///////////////////////////////////////////////////////////////////////////
105
106WebRtcVideoCapturer::WebRtcVideoCapturer()
107 : factory_(new WebRtcVcmFactory),
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200108 module_(nullptr),
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000109 captured_frames_(0),
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200110 start_thread_(nullptr),
nissef5297a02016-09-30 01:34:27 -0700111 async_invoker_(nullptr) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000112
113WebRtcVideoCapturer::WebRtcVideoCapturer(WebRtcVcmFactoryInterface* factory)
114 : factory_(factory),
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200115 module_(nullptr),
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000116 captured_frames_(0),
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200117 start_thread_(nullptr),
nissef5297a02016-09-30 01:34:27 -0700118 async_invoker_(nullptr) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000119
Peter Boström09c3a1e2016-03-22 17:17:39 +0100120WebRtcVideoCapturer::~WebRtcVideoCapturer() {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000121
122bool WebRtcVideoCapturer::Init(const Device& device) {
henrikg91d6ede2015-09-17 00:24:34 -0700123 RTC_DCHECK(!start_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000124 if (module_) {
125 LOG(LS_ERROR) << "The capturer is already initialized";
126 return false;
127 }
128
nisseb29b9c82016-12-12 00:22:56 -0800129 webrtc::VideoCaptureModule::DeviceInfo* info = factory_->CreateDeviceInfo();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000130 if (!info) {
131 return false;
132 }
133
134 // Find the desired camera, by name.
135 // In the future, comparing IDs will be more robust.
136 // TODO(juberti): Figure what's needed to allow this.
137 int num_cams = info->NumberOfDevices();
138 char vcm_id[256] = "";
139 bool found = false;
140 for (int index = 0; index < num_cams; ++index) {
141 char vcm_name[256];
tfarina5237aaf2015-11-10 23:44:30 -0800142 if (info->GetDeviceName(index, vcm_name, arraysize(vcm_name), vcm_id,
143 arraysize(vcm_id)) != -1) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000144 if (device.name == reinterpret_cast<char*>(vcm_name)) {
145 found = true;
146 break;
147 }
148 }
149 }
150 if (!found) {
151 LOG(LS_WARNING) << "Failed to find capturer for id: " << device.id;
152 factory_->DestroyDeviceInfo(info);
153 return false;
154 }
155
156 // Enumerate the supported formats.
157 // TODO(juberti): Find out why this starts/stops the camera...
158 std::vector<VideoFormat> supported;
159 int32_t num_caps = info->NumberOfCapabilities(vcm_id);
160 for (int32_t i = 0; i < num_caps; ++i) {
161 webrtc::VideoCaptureCapability cap;
162 if (info->GetCapability(vcm_id, i, cap) != -1) {
163 VideoFormat format;
164 if (CapabilityToFormat(cap, &format)) {
165 supported.push_back(format);
166 } else {
167 LOG(LS_WARNING) << "Ignoring unsupported WebRTC capture format "
168 << cap.rawType;
169 }
170 }
171 }
172 factory_->DestroyDeviceInfo(info);
Yuriy Shevchuk39f2b0c2015-05-14 14:16:13 -0700173
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000174 if (supported.empty()) {
175 LOG(LS_ERROR) << "Failed to find usable formats for id: " << device.id;
176 return false;
177 }
Yuriy Shevchuk39f2b0c2015-05-14 14:16:13 -0700178
nisseb29b9c82016-12-12 00:22:56 -0800179 module_ = factory_->Create(vcm_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000180 if (!module_) {
181 LOG(LS_ERROR) << "Failed to create capturer for id: " << device.id;
182 return false;
183 }
184
185 // It is safe to change member attributes now.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000186 SetId(device.id);
187 SetSupportedFormats(supported);
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000188
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000189 return true;
190}
191
Peter Boström09c3a1e2016-03-22 17:17:39 +0100192bool WebRtcVideoCapturer::Init(
193 const rtc::scoped_refptr<webrtc::VideoCaptureModule>& module) {
henrikg91d6ede2015-09-17 00:24:34 -0700194 RTC_DCHECK(!start_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000195 if (module_) {
196 LOG(LS_ERROR) << "The capturer is already initialized";
197 return false;
198 }
199 if (!module) {
200 LOG(LS_ERROR) << "Invalid VCM supplied";
201 return false;
202 }
203 // TODO(juberti): Set id and formats.
Peter Boström09c3a1e2016-03-22 17:17:39 +0100204 module_ = module;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000205 return true;
206}
207
208bool WebRtcVideoCapturer::GetBestCaptureFormat(const VideoFormat& desired,
209 VideoFormat* best_format) {
210 if (!best_format) {
211 return false;
212 }
213
214 if (!VideoCapturer::GetBestCaptureFormat(desired, best_format)) {
215 // We maybe using a manually injected VCM which doesn't support enum.
216 // Use the desired format as the best format.
217 best_format->width = desired.width;
218 best_format->height = desired.height;
219 best_format->fourcc = FOURCC_I420;
220 best_format->interval = desired.interval;
221 LOG(LS_INFO) << "Failed to find best capture format,"
222 << " fall back to the requested format "
223 << best_format->ToString();
224 }
225 return true;
226}
Pera5092412016-02-12 13:30:57 +0100227void WebRtcVideoCapturer::OnSinkWantsChanged(const rtc::VideoSinkWants& wants) {
Guo-wei Shieh64c1e8c2015-04-01 15:33:06 -0700228 // Can't take lock here as this will cause deadlock with
229 // OnIncomingCapturedFrame. In fact, the whole method, including methods it
230 // calls, can't take lock.
henrikg91d6ede2015-09-17 00:24:34 -0700231 RTC_DCHECK(module_);
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000232
sprangc1b57a12017-02-28 08:50:47 -0800233 if (webrtc::field_trial::FindFullName("WebRTC-CVO").find("Disabled") == 0)
Pera5092412016-02-12 13:30:57 +0100234 return;
Guo-wei Shieh64c1e8c2015-04-01 15:33:06 -0700235
Pera5092412016-02-12 13:30:57 +0100236 VideoCapturer::OnSinkWantsChanged(wants);
237 bool result = module_->SetApplyRotation(wants.rotation_applied);
238 RTC_CHECK(result);
239
240 return;
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000241}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000242
243CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) {
244 if (!module_) {
245 LOG(LS_ERROR) << "The capturer has not been initialized";
Perfb45d172016-02-29 12:07:35 +0100246 return CS_FAILED;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000247 }
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200248 if (start_thread_) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000249 LOG(LS_ERROR) << "The capturer is already running";
henrikg91d6ede2015-09-17 00:24:34 -0700250 RTC_DCHECK(start_thread_->IsCurrent())
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200251 << "Trying to start capturer on different threads";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000252 return CS_FAILED;
253 }
254
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000255 start_thread_ = rtc::Thread::Current();
henrikg91d6ede2015-09-17 00:24:34 -0700256 RTC_DCHECK(!async_invoker_);
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200257 async_invoker_.reset(new rtc::AsyncInvoker());
258 captured_frames_ = 0;
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000259
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000260 SetCaptureFormat(&capture_format);
261
262 webrtc::VideoCaptureCapability cap;
263 if (!FormatToCapability(capture_format, &cap)) {
264 LOG(LS_ERROR) << "Invalid capture format specified";
265 return CS_FAILED;
266 }
267
Honghai Zhang82d78622016-05-06 11:29:15 -0700268 int64_t start = rtc::TimeMillis();
nisseb29b9c82016-12-12 00:22:56 -0800269 module_->RegisterCaptureDataCallback(this);
mallinath@webrtc.org7433a082014-01-29 00:56:02 +0000270 if (module_->StartCapture(cap) != 0) {
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200271 LOG(LS_ERROR) << "Camera '" << GetId() << "' failed to start";
272 module_->DeRegisterCaptureDataCallback();
273 async_invoker_.reset();
274 SetCaptureFormat(nullptr);
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000275 start_thread_ = nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000276 return CS_FAILED;
277 }
278
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200279 LOG(LS_INFO) << "Camera '" << GetId() << "' started with format "
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000280 << capture_format.ToString() << ", elapsed time "
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000281 << rtc::TimeSince(start) << " ms";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000282
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000283 SetCaptureState(CS_RUNNING);
284 return CS_STARTING;
285}
286
287void WebRtcVideoCapturer::Stop() {
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200288 if (!start_thread_) {
289 LOG(LS_ERROR) << "The capturer is already stopped";
290 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000291 }
henrikg91d6ede2015-09-17 00:24:34 -0700292 RTC_DCHECK(start_thread_);
293 RTC_DCHECK(start_thread_->IsCurrent());
294 RTC_DCHECK(async_invoker_);
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200295 if (IsRunning()) {
296 // The module is responsible for OnIncomingCapturedFrame being called, if
297 // we stop it we will get no further callbacks.
298 module_->StopCapture();
299 }
300 module_->DeRegisterCaptureDataCallback();
301
302 // TODO(juberti): Determine if the VCM exposes any drop stats we can use.
303 double drop_ratio = 0.0;
304 LOG(LS_INFO) << "Camera '" << GetId() << "' stopped after capturing "
305 << captured_frames_ << " frames and dropping "
306 << drop_ratio << "%";
307
308 // Clear any pending async invokes (that OnIncomingCapturedFrame may have
309 // caused).
310 async_invoker_.reset();
311
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000312 SetCaptureFormat(NULL);
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000313 start_thread_ = nullptr;
hbos0de97f12015-12-01 02:13:33 -0800314 SetCaptureState(CS_STOPPED);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000315}
316
317bool WebRtcVideoCapturer::IsRunning() {
318 return (module_ != NULL && module_->CaptureStarted());
319}
320
Peter Boström0c4e06b2015-10-07 12:23:21 +0200321bool WebRtcVideoCapturer::GetPreferredFourccs(std::vector<uint32_t>* fourccs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000322 if (!fourccs) {
323 return false;
324 }
325
326 fourccs->clear();
tfarina5237aaf2015-11-10 23:44:30 -0800327 for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000328 fourccs->push_back(kSupportedFourCCs[i].fourcc);
329 }
330 return true;
331}
332
nisseb29b9c82016-12-12 00:22:56 -0800333void WebRtcVideoCapturer::OnFrame(
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700334 const webrtc::VideoFrame& sample) {
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200335 // This can only happen between Start() and Stop().
henrikg91d6ede2015-09-17 00:24:34 -0700336 RTC_DCHECK(start_thread_);
337 RTC_DCHECK(async_invoker_);
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200338
339 ++captured_frames_;
340 // Log the size and pixel aspect ratio of the first captured frame.
341 if (1 == captured_frames_) {
342 LOG(LS_INFO) << "Captured frame size "
nisse79246972016-08-23 05:50:09 -0700343 << sample.width() << "x" << sample.height()
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200344 << ". Expected format " << GetCaptureFormat()->ToString();
345 }
346
nisseb29b9c82016-12-12 00:22:56 -0800347 VideoCapturer::OnFrame(sample, sample.width(), sample.height());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000348}
349
350} // namespace cricket