blob: aaa6f1e4800d7c2741e8420da85277b7fa48ae1f [file] [log] [blame]
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00001/*
2 * libjingle
3 * Copyright 2011 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 */
henrike@webrtc.org28e20752013-07-10 00:45:36 +000027
28#include "talk/media/webrtc/webrtcvideocapturer.h"
29
30#ifdef HAVE_CONFIG_H
31#include <config.h>
32#endif
33
34#ifdef HAVE_WEBRTC_VIDEO
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000035#include "talk/media/webrtc/webrtcvideoframe.h"
36#include "talk/media/webrtc/webrtcvideoframefactory.h"
tommi@webrtc.orge07710c2015-02-19 17:43:25 +000037#include "webrtc/base/bind.h"
38#include "webrtc/base/checks.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000039#include "webrtc/base/criticalsection.h"
40#include "webrtc/base/logging.h"
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000041#include "webrtc/base/safe_conversions.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000042#include "webrtc/base/thread.h"
43#include "webrtc/base/timeutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000044
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000045#include "webrtc/base/win32.h" // Need this to #include the impl files.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000046#include "webrtc/modules/video_capture/include/video_capture_factory.h"
47
48namespace cricket {
49
50struct kVideoFourCCEntry {
51 uint32 fourcc;
52 webrtc::RawVideoType webrtc_type;
53};
54
55// This indicates our format preferences and defines a mapping between
56// webrtc::RawVideoType (from video_capture_defines.h) to our FOURCCs.
57static kVideoFourCCEntry kSupportedFourCCs[] = {
58 { FOURCC_I420, webrtc::kVideoI420 }, // 12 bpp, no conversion.
59 { FOURCC_YV12, webrtc::kVideoYV12 }, // 12 bpp, no conversion.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000060 { FOURCC_YUY2, webrtc::kVideoYUY2 }, // 16 bpp, fast conversion.
61 { FOURCC_UYVY, webrtc::kVideoUYVY }, // 16 bpp, fast conversion.
buildbot@webrtc.orgfa5fcd62014-07-21 21:55:43 +000062 { FOURCC_NV12, webrtc::kVideoNV12 }, // 12 bpp, fast conversion.
63 { FOURCC_NV21, webrtc::kVideoNV21 }, // 12 bpp, fast conversion.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000064 { FOURCC_MJPG, webrtc::kVideoMJPEG }, // compressed, slow conversion.
65 { FOURCC_ARGB, webrtc::kVideoARGB }, // 32 bpp, slow conversion.
66 { FOURCC_24BG, webrtc::kVideoRGB24 }, // 24 bpp, slow conversion.
67};
68
69class WebRtcVcmFactory : public WebRtcVcmFactoryInterface {
70 public:
71 virtual webrtc::VideoCaptureModule* Create(int id, const char* device) {
72 return webrtc::VideoCaptureFactory::Create(id, device);
73 }
74 virtual webrtc::VideoCaptureModule::DeviceInfo* CreateDeviceInfo(int id) {
75 return webrtc::VideoCaptureFactory::CreateDeviceInfo(id);
76 }
77 virtual void DestroyDeviceInfo(webrtc::VideoCaptureModule::DeviceInfo* info) {
78 delete info;
79 }
80};
81
82static bool CapabilityToFormat(const webrtc::VideoCaptureCapability& cap,
83 VideoFormat* format) {
84 uint32 fourcc = 0;
85 for (size_t i = 0; i < ARRAY_SIZE(kSupportedFourCCs); ++i) {
86 if (kSupportedFourCCs[i].webrtc_type == cap.rawType) {
87 fourcc = kSupportedFourCCs[i].fourcc;
88 break;
89 }
90 }
91 if (fourcc == 0) {
92 return false;
93 }
94
95 format->fourcc = fourcc;
96 format->width = cap.width;
97 format->height = cap.height;
98 format->interval = VideoFormat::FpsToInterval(cap.maxFPS);
99 return true;
100}
101
102static bool FormatToCapability(const VideoFormat& format,
103 webrtc::VideoCaptureCapability* cap) {
104 webrtc::RawVideoType webrtc_type = webrtc::kVideoUnknown;
105 for (size_t i = 0; i < ARRAY_SIZE(kSupportedFourCCs); ++i) {
106 if (kSupportedFourCCs[i].fourcc == format.fourcc) {
107 webrtc_type = kSupportedFourCCs[i].webrtc_type;
108 break;
109 }
110 }
111 if (webrtc_type == webrtc::kVideoUnknown) {
112 return false;
113 }
114
115 cap->width = format.width;
116 cap->height = format.height;
117 cap->maxFPS = VideoFormat::IntervalToFps(format.interval);
118 cap->expectedCaptureDelay = 0;
119 cap->rawType = webrtc_type;
120 cap->codecType = webrtc::kVideoCodecUnknown;
121 cap->interlaced = false;
122 return true;
123}
124
125///////////////////////////////////////////////////////////////////////////
126// Implementation of class WebRtcVideoCapturer
127///////////////////////////////////////////////////////////////////////////
128
129WebRtcVideoCapturer::WebRtcVideoCapturer()
130 : factory_(new WebRtcVcmFactory),
131 module_(NULL),
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000132 captured_frames_(0),
133 start_thread_(nullptr) {
buildbot@webrtc.org4f0d4012014-08-07 04:47:36 +0000134 set_frame_factory(new WebRtcVideoFrameFactory());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000135}
136
137WebRtcVideoCapturer::WebRtcVideoCapturer(WebRtcVcmFactoryInterface* factory)
138 : factory_(factory),
139 module_(NULL),
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000140 captured_frames_(0),
141 start_thread_(nullptr) {
buildbot@webrtc.org4f0d4012014-08-07 04:47:36 +0000142 set_frame_factory(new WebRtcVideoFrameFactory());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000143}
144
145WebRtcVideoCapturer::~WebRtcVideoCapturer() {
146 if (module_) {
147 module_->Release();
148 }
149}
150
151bool WebRtcVideoCapturer::Init(const Device& device) {
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000152 DCHECK(!start_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000153 if (module_) {
154 LOG(LS_ERROR) << "The capturer is already initialized";
155 return false;
156 }
157
158 webrtc::VideoCaptureModule::DeviceInfo* info = factory_->CreateDeviceInfo(0);
159 if (!info) {
160 return false;
161 }
162
163 // Find the desired camera, by name.
164 // In the future, comparing IDs will be more robust.
165 // TODO(juberti): Figure what's needed to allow this.
166 int num_cams = info->NumberOfDevices();
167 char vcm_id[256] = "";
168 bool found = false;
169 for (int index = 0; index < num_cams; ++index) {
170 char vcm_name[256];
171 if (info->GetDeviceName(index, vcm_name, ARRAY_SIZE(vcm_name),
172 vcm_id, ARRAY_SIZE(vcm_id)) != -1) {
173 if (device.name == reinterpret_cast<char*>(vcm_name)) {
174 found = true;
175 break;
176 }
177 }
178 }
179 if (!found) {
180 LOG(LS_WARNING) << "Failed to find capturer for id: " << device.id;
181 factory_->DestroyDeviceInfo(info);
182 return false;
183 }
184
185 // Enumerate the supported formats.
186 // TODO(juberti): Find out why this starts/stops the camera...
187 std::vector<VideoFormat> supported;
188 int32_t num_caps = info->NumberOfCapabilities(vcm_id);
189 for (int32_t i = 0; i < num_caps; ++i) {
190 webrtc::VideoCaptureCapability cap;
191 if (info->GetCapability(vcm_id, i, cap) != -1) {
192 VideoFormat format;
193 if (CapabilityToFormat(cap, &format)) {
194 supported.push_back(format);
195 } else {
196 LOG(LS_WARNING) << "Ignoring unsupported WebRTC capture format "
197 << cap.rawType;
198 }
199 }
200 }
201 factory_->DestroyDeviceInfo(info);
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000202// TODO(fischman): Remove the following check
203// when capabilities for iOS are implemented
204// https://code.google.com/p/webrtc/issues/detail?id=2968
205#if !defined(IOS)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000206 if (supported.empty()) {
207 LOG(LS_ERROR) << "Failed to find usable formats for id: " << device.id;
208 return false;
209 }
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000210#endif
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000211 module_ = factory_->Create(0, vcm_id);
212 if (!module_) {
213 LOG(LS_ERROR) << "Failed to create capturer for id: " << device.id;
214 return false;
215 }
216
217 // It is safe to change member attributes now.
218 module_->AddRef();
219 SetId(device.id);
220 SetSupportedFormats(supported);
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000221
222 // Ensure these 2 have the same value.
223 SetApplyRotation(module_->GetApplyRotation());
224
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000225 return true;
226}
227
228bool WebRtcVideoCapturer::Init(webrtc::VideoCaptureModule* module) {
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000229 DCHECK(!start_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000230 if (module_) {
231 LOG(LS_ERROR) << "The capturer is already initialized";
232 return false;
233 }
234 if (!module) {
235 LOG(LS_ERROR) << "Invalid VCM supplied";
236 return false;
237 }
238 // TODO(juberti): Set id and formats.
239 (module_ = module)->AddRef();
240 return true;
241}
242
243bool WebRtcVideoCapturer::GetBestCaptureFormat(const VideoFormat& desired,
244 VideoFormat* best_format) {
245 if (!best_format) {
246 return false;
247 }
248
249 if (!VideoCapturer::GetBestCaptureFormat(desired, best_format)) {
250 // We maybe using a manually injected VCM which doesn't support enum.
251 // Use the desired format as the best format.
252 best_format->width = desired.width;
253 best_format->height = desired.height;
254 best_format->fourcc = FOURCC_I420;
255 best_format->interval = desired.interval;
256 LOG(LS_INFO) << "Failed to find best capture format,"
257 << " fall back to the requested format "
258 << best_format->ToString();
259 }
260 return true;
261}
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000262bool WebRtcVideoCapturer::SetApplyRotation(bool enable) {
263 rtc::CritScope cs(&critical_section_stopping_);
264
265 assert(module_);
266
267 if (!VideoCapturer::SetApplyRotation(enable)) {
268 return false;
269 }
270 return module_->SetApplyRotation(enable);
271}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000272
273CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) {
274 if (!module_) {
275 LOG(LS_ERROR) << "The capturer has not been initialized";
276 return CS_NO_DEVICE;
277 }
278
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000279 rtc::CritScope cs(&critical_section_stopping_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000280 if (IsRunning()) {
281 LOG(LS_ERROR) << "The capturer is already running";
282 return CS_FAILED;
283 }
284
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000285 DCHECK(!start_thread_);
286
287 start_thread_ = rtc::Thread::Current();
288
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000289 SetCaptureFormat(&capture_format);
290
291 webrtc::VideoCaptureCapability cap;
292 if (!FormatToCapability(capture_format, &cap)) {
293 LOG(LS_ERROR) << "Invalid capture format specified";
294 return CS_FAILED;
295 }
296
297 std::string camera_id(GetId());
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000298 uint32 start = rtc::Time();
mallinath@webrtc.org7433a082014-01-29 00:56:02 +0000299 module_->RegisterCaptureDataCallback(*this);
300 if (module_->StartCapture(cap) != 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000301 LOG(LS_ERROR) << "Camera '" << camera_id << "' failed to start";
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000302 start_thread_ = nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000303 return CS_FAILED;
304 }
305
306 LOG(LS_INFO) << "Camera '" << camera_id << "' started with format "
307 << capture_format.ToString() << ", elapsed time "
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000308 << rtc::TimeSince(start) << " ms";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000309
310 captured_frames_ = 0;
311 SetCaptureState(CS_RUNNING);
312 return CS_STARTING;
313}
314
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000315// Critical section blocks Stop from shutting down during callbacks from capture
316// thread to OnIncomingCapturedFrame. Note that the crit is try-locked in
317// OnFrameCaptured, as the lock ordering between this and the system component
318// controlling the camera is reversed: system frame -> OnIncomingCapturedFrame;
319// Stop -> system stop camera).
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000320void WebRtcVideoCapturer::Stop() {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000321 rtc::CritScope cs(&critical_section_stopping_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000322 if (IsRunning()) {
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000323 DCHECK(start_thread_);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000324 rtc::Thread::Current()->Clear(this);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000325 module_->StopCapture();
326 module_->DeRegisterCaptureDataCallback();
327
328 // TODO(juberti): Determine if the VCM exposes any drop stats we can use.
329 double drop_ratio = 0.0;
330 std::string camera_id(GetId());
331 LOG(LS_INFO) << "Camera '" << camera_id << "' stopped after capturing "
332 << captured_frames_ << " frames and dropping "
333 << drop_ratio << "%";
334 }
335 SetCaptureFormat(NULL);
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000336 start_thread_ = nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000337}
338
339bool WebRtcVideoCapturer::IsRunning() {
340 return (module_ != NULL && module_->CaptureStarted());
341}
342
343bool WebRtcVideoCapturer::GetPreferredFourccs(
344 std::vector<uint32>* fourccs) {
345 if (!fourccs) {
346 return false;
347 }
348
349 fourccs->clear();
350 for (size_t i = 0; i < ARRAY_SIZE(kSupportedFourCCs); ++i) {
351 fourccs->push_back(kSupportedFourCCs[i].fourcc);
352 }
353 return true;
354}
355
356void WebRtcVideoCapturer::OnIncomingCapturedFrame(const int32_t id,
357 webrtc::I420VideoFrame& sample) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000358 // This would be a normal CritScope, except that it's possible that:
359 // (1) whatever system component producing this frame has taken a lock, and
360 // (2) Stop() probably calls back into that system component, which may take
361 // the same lock. Due to the reversed order, we have to try-lock in order to
362 // avoid a potential deadlock. Besides, if we can't enter because we're
363 // stopping, we may as well drop the frame.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000364 rtc::TryCritScope cs(&critical_section_stopping_);
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000365 if (!cs.locked() || !IsRunning()) {
366 // Capturer has been stopped or is in the process of stopping.
367 return;
368 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000369
370 ++captured_frames_;
371 // Log the size and pixel aspect ratio of the first captured frame.
372 if (1 == captured_frames_) {
373 LOG(LS_INFO) << "Captured frame size "
374 << sample.width() << "x" << sample.height()
375 << ". Expected format " << GetCaptureFormat()->ToString();
376 }
377
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000378 if (start_thread_->IsCurrent()) {
379 SignalFrameCapturedOnStartThread(&sample);
380 } else {
381 // This currently happens on with at least VideoCaptureModuleV4L2 and
382 // possibly other implementations of WebRTC's VideoCaptureModule.
383 // In order to maintain the threading contract with the upper layers and
384 // consistency with other capturers such as in Chrome, we need to do a
385 // thread hop.
386 start_thread_->Invoke<void>(
387 rtc::Bind(&WebRtcVideoCapturer::SignalFrameCapturedOnStartThread,
388 this, &sample));
389 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000390}
391
392void WebRtcVideoCapturer::OnCaptureDelayChanged(const int32_t id,
393 const int32_t delay) {
394 LOG(LS_INFO) << "Capture delay changed to " << delay << " ms";
395}
396
tommi@webrtc.orge07710c2015-02-19 17:43:25 +0000397void WebRtcVideoCapturer::SignalFrameCapturedOnStartThread(
398 webrtc::I420VideoFrame* frame) {
399 DCHECK(start_thread_->IsCurrent());
400 // Signal down stream components on captured frame.
401 // The CapturedFrame class doesn't support planes. We have to ExtractBuffer
402 // to one block for it.
403 size_t length =
404 webrtc::CalcBufferSize(webrtc::kI420, frame->width(), frame->height());
405 capture_buffer_.resize(length);
406 // TODO(magjed): Refactor the WebRtcCapturedFrame to avoid memory copy or
407 // take over ownership of the buffer held by |frame| if that's possible.
408 webrtc::ExtractBuffer(*frame, length, &capture_buffer_[0]);
409 WebRtcCapturedFrame webrtc_frame(*frame, &capture_buffer_[0], length);
410 SignalFrameCaptured(this, &webrtc_frame);
411}
412
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000413// WebRtcCapturedFrame
414WebRtcCapturedFrame::WebRtcCapturedFrame(const webrtc::I420VideoFrame& sample,
415 void* buffer,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000416 size_t length) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000417 width = sample.width();
418 height = sample.height();
419 fourcc = FOURCC_I420;
420 // TODO(hellner): Support pixel aspect ratio (for OSX).
421 pixel_width = 1;
422 pixel_height = 1;
423 // Convert units from VideoFrame RenderTimeMs to CapturedFrame (nanoseconds).
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000424 elapsed_time = sample.render_time_ms() * rtc::kNumNanosecsPerMillisec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000425 time_stamp = elapsed_time;
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000426 data_size = rtc::checked_cast<uint32>(length);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000427 data = buffer;
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000428 rotation = sample.rotation();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000429}
430
431} // namespace cricket
432
433#endif // HAVE_WEBRTC_VIDEO