blob: 9b872fba047bd4252a698e72adb682d04776664a [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
Edward Lemurc20978e2017-07-06 19:44:34 +020013#include "webrtc/rtc_base/arraysize.h"
14#include "webrtc/rtc_base/bind.h"
15#include "webrtc/rtc_base/checks.h"
16#include "webrtc/rtc_base/criticalsection.h"
17#include "webrtc/rtc_base/logging.h"
18#include "webrtc/rtc_base/safe_conversions.h"
19#include "webrtc/rtc_base/thread.h"
20#include "webrtc/rtc_base/timeutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000021
Henrik Kjellander5dda80a2015-11-12 12:46:09 +010022#include "webrtc/modules/video_capture/video_capture_factory.h"
Edward Lemurc20978e2017-07-06 19:44:34 +020023#include "webrtc/rtc_base/win32.h" // Need this to #include the impl files.
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
nisseeb44b392017-04-28 07:18:05 -070028namespace {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000029struct kVideoFourCCEntry {
Peter Boström0c4e06b2015-10-07 12:23:21 +020030 uint32_t fourcc;
nisseeb44b392017-04-28 07:18:05 -070031 webrtc::VideoType webrtc_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000032};
33
34// This indicates our format preferences and defines a mapping between
35// webrtc::RawVideoType (from video_capture_defines.h) to our FOURCCs.
nisseeb44b392017-04-28 07:18:05 -070036kVideoFourCCEntry kSupportedFourCCs[] = {
37 {FOURCC_I420, webrtc::VideoType::kI420}, // 12 bpp, no conversion.
38 {FOURCC_YV12, webrtc::VideoType::kYV12}, // 12 bpp, no conversion.
39 {FOURCC_YUY2, webrtc::VideoType::kYUY2}, // 16 bpp, fast conversion.
40 {FOURCC_UYVY, webrtc::VideoType::kUYVY}, // 16 bpp, fast conversion.
41 {FOURCC_NV12, webrtc::VideoType::kNV12}, // 12 bpp, fast conversion.
42 {FOURCC_NV21, webrtc::VideoType::kNV21}, // 12 bpp, fast conversion.
43 {FOURCC_MJPG, webrtc::VideoType::kMJPEG}, // compressed, slow conversion.
44 {FOURCC_ARGB, webrtc::VideoType::kARGB}, // 32 bpp, slow conversion.
45 {FOURCC_24BG, webrtc::VideoType::kRGB24}, // 24 bpp, slow conversion.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000046};
47
nisseeb44b392017-04-28 07:18:05 -070048bool CapabilityToFormat(const webrtc::VideoCaptureCapability& cap,
49 VideoFormat* format) {
Peter Boström0c4e06b2015-10-07 12:23:21 +020050 uint32_t fourcc = 0;
tfarina5237aaf2015-11-10 23:44:30 -080051 for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
nisseeb44b392017-04-28 07:18:05 -070052 if (kSupportedFourCCs[i].webrtc_type == cap.videoType) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000053 fourcc = kSupportedFourCCs[i].fourcc;
54 break;
55 }
56 }
57 if (fourcc == 0) {
58 return false;
59 }
60
61 format->fourcc = fourcc;
62 format->width = cap.width;
63 format->height = cap.height;
64 format->interval = VideoFormat::FpsToInterval(cap.maxFPS);
65 return true;
66}
67
nisseeb44b392017-04-28 07:18:05 -070068bool FormatToCapability(const VideoFormat& format,
69 webrtc::VideoCaptureCapability* cap) {
70 webrtc::VideoType webrtc_type = webrtc::VideoType::kUnknown;
tfarina5237aaf2015-11-10 23:44:30 -080071 for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000072 if (kSupportedFourCCs[i].fourcc == format.fourcc) {
73 webrtc_type = kSupportedFourCCs[i].webrtc_type;
74 break;
75 }
76 }
nisseeb44b392017-04-28 07:18:05 -070077 if (webrtc_type == webrtc::VideoType::kUnknown) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000078 return false;
79 }
80
81 cap->width = format.width;
82 cap->height = format.height;
83 cap->maxFPS = VideoFormat::IntervalToFps(format.interval);
nisseeb44b392017-04-28 07:18:05 -070084 cap->videoType = webrtc_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000085 cap->interlaced = false;
86 return true;
87}
88
nisseeb44b392017-04-28 07:18:05 -070089} // namespace
90
91class WebRtcVcmFactory : public WebRtcVcmFactoryInterface {
92 public:
93 virtual rtc::scoped_refptr<webrtc::VideoCaptureModule> Create(
94 const char* device) {
95 return webrtc::VideoCaptureFactory::Create(device);
96 }
97 virtual webrtc::VideoCaptureModule::DeviceInfo* CreateDeviceInfo() {
98 return webrtc::VideoCaptureFactory::CreateDeviceInfo();
99 }
100 virtual void DestroyDeviceInfo(webrtc::VideoCaptureModule::DeviceInfo* info) {
101 delete info;
102 }
103};
104
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000105///////////////////////////////////////////////////////////////////////////
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),
srtef3238e42017-08-14 02:51:17 -0700113 start_thread_(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),
srtef3238e42017-08-14 02:51:17 -0700119 start_thread_(nullptr) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000120
Peter Boström09c3a1e2016-03-22 17:17:39 +0100121WebRtcVideoCapturer::~WebRtcVideoCapturer() {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000122
123bool WebRtcVideoCapturer::Init(const Device& device) {
henrikg91d6ede2015-09-17 00:24:34 -0700124 RTC_DCHECK(!start_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000125 if (module_) {
126 LOG(LS_ERROR) << "The capturer is already initialized";
127 return false;
128 }
129
nisseb29b9c82016-12-12 00:22:56 -0800130 webrtc::VideoCaptureModule::DeviceInfo* info = factory_->CreateDeviceInfo();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000131 if (!info) {
132 return false;
133 }
134
135 // Find the desired camera, by name.
136 // In the future, comparing IDs will be more robust.
137 // TODO(juberti): Figure what's needed to allow this.
138 int num_cams = info->NumberOfDevices();
139 char vcm_id[256] = "";
140 bool found = false;
141 for (int index = 0; index < num_cams; ++index) {
142 char vcm_name[256];
tfarina5237aaf2015-11-10 23:44:30 -0800143 if (info->GetDeviceName(index, vcm_name, arraysize(vcm_name), vcm_id,
144 arraysize(vcm_id)) != -1) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000145 if (device.name == reinterpret_cast<char*>(vcm_name)) {
146 found = true;
147 break;
148 }
149 }
150 }
151 if (!found) {
152 LOG(LS_WARNING) << "Failed to find capturer for id: " << device.id;
153 factory_->DestroyDeviceInfo(info);
154 return false;
155 }
156
157 // Enumerate the supported formats.
158 // TODO(juberti): Find out why this starts/stops the camera...
159 std::vector<VideoFormat> supported;
160 int32_t num_caps = info->NumberOfCapabilities(vcm_id);
161 for (int32_t i = 0; i < num_caps; ++i) {
162 webrtc::VideoCaptureCapability cap;
163 if (info->GetCapability(vcm_id, i, cap) != -1) {
164 VideoFormat format;
165 if (CapabilityToFormat(cap, &format)) {
166 supported.push_back(format);
167 } else {
168 LOG(LS_WARNING) << "Ignoring unsupported WebRTC capture format "
nisseeb44b392017-04-28 07:18:05 -0700169 << static_cast<int>(cap.videoType);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000170 }
171 }
172 }
173 factory_->DestroyDeviceInfo(info);
Yuriy Shevchuk39f2b0c2015-05-14 14:16:13 -0700174
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000175 if (supported.empty()) {
176 LOG(LS_ERROR) << "Failed to find usable formats for id: " << device.id;
177 return false;
178 }
Yuriy Shevchuk39f2b0c2015-05-14 14:16:13 -0700179
nisseb29b9c82016-12-12 00:22:56 -0800180 module_ = factory_->Create(vcm_id);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000181 if (!module_) {
182 LOG(LS_ERROR) << "Failed to create capturer for id: " << device.id;
183 return false;
184 }
185
186 // It is safe to change member attributes now.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000187 SetId(device.id);
188 SetSupportedFormats(supported);
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000189
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000190 return true;
191}
192
Peter Boström09c3a1e2016-03-22 17:17:39 +0100193bool WebRtcVideoCapturer::Init(
194 const rtc::scoped_refptr<webrtc::VideoCaptureModule>& module) {
henrikg91d6ede2015-09-17 00:24:34 -0700195 RTC_DCHECK(!start_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000196 if (module_) {
197 LOG(LS_ERROR) << "The capturer is already initialized";
198 return false;
199 }
200 if (!module) {
201 LOG(LS_ERROR) << "Invalid VCM supplied";
202 return false;
203 }
204 // TODO(juberti): Set id and formats.
Peter Boström09c3a1e2016-03-22 17:17:39 +0100205 module_ = module;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000206 return true;
207}
208
209bool WebRtcVideoCapturer::GetBestCaptureFormat(const VideoFormat& desired,
210 VideoFormat* best_format) {
211 if (!best_format) {
212 return false;
213 }
214
215 if (!VideoCapturer::GetBestCaptureFormat(desired, best_format)) {
216 // We maybe using a manually injected VCM which doesn't support enum.
217 // Use the desired format as the best format.
218 best_format->width = desired.width;
219 best_format->height = desired.height;
220 best_format->fourcc = FOURCC_I420;
221 best_format->interval = desired.interval;
222 LOG(LS_INFO) << "Failed to find best capture format,"
223 << " fall back to the requested format "
224 << best_format->ToString();
225 }
226 return true;
227}
Pera5092412016-02-12 13:30:57 +0100228void WebRtcVideoCapturer::OnSinkWantsChanged(const rtc::VideoSinkWants& wants) {
Guo-wei Shieh64c1e8c2015-04-01 15:33:06 -0700229 // Can't take lock here as this will cause deadlock with
230 // OnIncomingCapturedFrame. In fact, the whole method, including methods it
231 // calls, can't take lock.
henrikg91d6ede2015-09-17 00:24:34 -0700232 RTC_DCHECK(module_);
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000233
sprangc1b57a12017-02-28 08:50:47 -0800234 if (webrtc::field_trial::FindFullName("WebRTC-CVO").find("Disabled") == 0)
Pera5092412016-02-12 13:30:57 +0100235 return;
Guo-wei Shieh64c1e8c2015-04-01 15:33:06 -0700236
Pera5092412016-02-12 13:30:57 +0100237 VideoCapturer::OnSinkWantsChanged(wants);
238 bool result = module_->SetApplyRotation(wants.rotation_applied);
239 RTC_CHECK(result);
240
241 return;
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000242}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000243
244CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) {
245 if (!module_) {
246 LOG(LS_ERROR) << "The capturer has not been initialized";
Perfb45d172016-02-29 12:07:35 +0100247 return CS_FAILED;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000248 }
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200249 if (start_thread_) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000250 LOG(LS_ERROR) << "The capturer is already running";
henrikg91d6ede2015-09-17 00:24:34 -0700251 RTC_DCHECK(start_thread_->IsCurrent())
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200252 << "Trying to start capturer on different threads";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000253 return CS_FAILED;
254 }
255
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000256 start_thread_ = rtc::Thread::Current();
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200257 captured_frames_ = 0;
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000258
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000259 SetCaptureFormat(&capture_format);
260
261 webrtc::VideoCaptureCapability cap;
262 if (!FormatToCapability(capture_format, &cap)) {
263 LOG(LS_ERROR) << "Invalid capture format specified";
264 return CS_FAILED;
265 }
266
Honghai Zhang82d78622016-05-06 11:29:15 -0700267 int64_t start = rtc::TimeMillis();
nisseb29b9c82016-12-12 00:22:56 -0800268 module_->RegisterCaptureDataCallback(this);
mallinath@webrtc.org7433a082014-01-29 00:56:02 +0000269 if (module_->StartCapture(cap) != 0) {
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200270 LOG(LS_ERROR) << "Camera '" << GetId() << "' failed to start";
271 module_->DeRegisterCaptureDataCallback();
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200272 SetCaptureFormat(nullptr);
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000273 start_thread_ = nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000274 return CS_FAILED;
275 }
276
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200277 LOG(LS_INFO) << "Camera '" << GetId() << "' started with format "
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000278 << capture_format.ToString() << ", elapsed time "
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000279 << rtc::TimeSince(start) << " ms";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000280
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000281 SetCaptureState(CS_RUNNING);
282 return CS_STARTING;
283}
284
285void WebRtcVideoCapturer::Stop() {
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200286 if (!start_thread_) {
287 LOG(LS_ERROR) << "The capturer is already stopped";
288 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000289 }
henrikg91d6ede2015-09-17 00:24:34 -0700290 RTC_DCHECK(start_thread_);
291 RTC_DCHECK(start_thread_->IsCurrent());
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200292 if (IsRunning()) {
293 // The module is responsible for OnIncomingCapturedFrame being called, if
294 // we stop it we will get no further callbacks.
295 module_->StopCapture();
296 }
297 module_->DeRegisterCaptureDataCallback();
298
299 // TODO(juberti): Determine if the VCM exposes any drop stats we can use.
300 double drop_ratio = 0.0;
301 LOG(LS_INFO) << "Camera '" << GetId() << "' stopped after capturing "
302 << captured_frames_ << " frames and dropping "
303 << drop_ratio << "%";
304
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000305 SetCaptureFormat(NULL);
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000306 start_thread_ = nullptr;
hbos0de97f12015-12-01 02:13:33 -0800307 SetCaptureState(CS_STOPPED);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000308}
309
310bool WebRtcVideoCapturer::IsRunning() {
311 return (module_ != NULL && module_->CaptureStarted());
312}
313
Peter Boström0c4e06b2015-10-07 12:23:21 +0200314bool WebRtcVideoCapturer::GetPreferredFourccs(std::vector<uint32_t>* fourccs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000315 if (!fourccs) {
316 return false;
317 }
318
319 fourccs->clear();
tfarina5237aaf2015-11-10 23:44:30 -0800320 for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000321 fourccs->push_back(kSupportedFourCCs[i].fourcc);
322 }
323 return true;
324}
325
nisseb29b9c82016-12-12 00:22:56 -0800326void WebRtcVideoCapturer::OnFrame(
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700327 const webrtc::VideoFrame& sample) {
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200328 // This can only happen between Start() and Stop().
henrikg91d6ede2015-09-17 00:24:34 -0700329 RTC_DCHECK(start_thread_);
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200330
331 ++captured_frames_;
332 // Log the size and pixel aspect ratio of the first captured frame.
333 if (1 == captured_frames_) {
334 LOG(LS_INFO) << "Captured frame size "
nisse79246972016-08-23 05:50:09 -0700335 << sample.width() << "x" << sample.height()
Henrik Boströmcbe408a2015-05-27 10:11:34 +0200336 << ". Expected format " << GetCaptureFormat()->ToString();
337 }
338
nisseb29b9c82016-12-12 00:22:56 -0800339 VideoCapturer::OnFrame(sample, sample.width(), sample.height());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000340}
341
342} // namespace cricket