blob: 259f53ade0efac78a9eb0f04d14f70851ff58c5d [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"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000037#include "webrtc/base/criticalsection.h"
38#include "webrtc/base/logging.h"
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000039#include "webrtc/base/safe_conversions.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000040#include "webrtc/base/thread.h"
41#include "webrtc/base/timeutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000042
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000043#include "webrtc/base/win32.h" // Need this to #include the impl files.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000044#include "webrtc/modules/video_capture/include/video_capture_factory.h"
45
46namespace cricket {
47
48struct kVideoFourCCEntry {
49 uint32 fourcc;
50 webrtc::RawVideoType webrtc_type;
51};
52
53// This indicates our format preferences and defines a mapping between
54// webrtc::RawVideoType (from video_capture_defines.h) to our FOURCCs.
55static kVideoFourCCEntry kSupportedFourCCs[] = {
56 { FOURCC_I420, webrtc::kVideoI420 }, // 12 bpp, no conversion.
57 { FOURCC_YV12, webrtc::kVideoYV12 }, // 12 bpp, no conversion.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000058 { FOURCC_YUY2, webrtc::kVideoYUY2 }, // 16 bpp, fast conversion.
59 { FOURCC_UYVY, webrtc::kVideoUYVY }, // 16 bpp, fast conversion.
buildbot@webrtc.orgfa5fcd62014-07-21 21:55:43 +000060 { FOURCC_NV12, webrtc::kVideoNV12 }, // 12 bpp, fast conversion.
61 { FOURCC_NV21, webrtc::kVideoNV21 }, // 12 bpp, fast conversion.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000062 { FOURCC_MJPG, webrtc::kVideoMJPEG }, // compressed, slow conversion.
63 { FOURCC_ARGB, webrtc::kVideoARGB }, // 32 bpp, slow conversion.
64 { FOURCC_24BG, webrtc::kVideoRGB24 }, // 24 bpp, slow conversion.
65};
66
67class WebRtcVcmFactory : public WebRtcVcmFactoryInterface {
68 public:
69 virtual webrtc::VideoCaptureModule* Create(int id, const char* device) {
70 return webrtc::VideoCaptureFactory::Create(id, device);
71 }
72 virtual webrtc::VideoCaptureModule::DeviceInfo* CreateDeviceInfo(int id) {
73 return webrtc::VideoCaptureFactory::CreateDeviceInfo(id);
74 }
75 virtual void DestroyDeviceInfo(webrtc::VideoCaptureModule::DeviceInfo* info) {
76 delete info;
77 }
78};
79
80static bool CapabilityToFormat(const webrtc::VideoCaptureCapability& cap,
81 VideoFormat* format) {
82 uint32 fourcc = 0;
83 for (size_t i = 0; i < ARRAY_SIZE(kSupportedFourCCs); ++i) {
84 if (kSupportedFourCCs[i].webrtc_type == cap.rawType) {
85 fourcc = kSupportedFourCCs[i].fourcc;
86 break;
87 }
88 }
89 if (fourcc == 0) {
90 return false;
91 }
92
93 format->fourcc = fourcc;
94 format->width = cap.width;
95 format->height = cap.height;
96 format->interval = VideoFormat::FpsToInterval(cap.maxFPS);
97 return true;
98}
99
100static bool FormatToCapability(const VideoFormat& format,
101 webrtc::VideoCaptureCapability* cap) {
102 webrtc::RawVideoType webrtc_type = webrtc::kVideoUnknown;
103 for (size_t i = 0; i < ARRAY_SIZE(kSupportedFourCCs); ++i) {
104 if (kSupportedFourCCs[i].fourcc == format.fourcc) {
105 webrtc_type = kSupportedFourCCs[i].webrtc_type;
106 break;
107 }
108 }
109 if (webrtc_type == webrtc::kVideoUnknown) {
110 return false;
111 }
112
113 cap->width = format.width;
114 cap->height = format.height;
115 cap->maxFPS = VideoFormat::IntervalToFps(format.interval);
116 cap->expectedCaptureDelay = 0;
117 cap->rawType = webrtc_type;
118 cap->codecType = webrtc::kVideoCodecUnknown;
119 cap->interlaced = false;
120 return true;
121}
122
123///////////////////////////////////////////////////////////////////////////
124// Implementation of class WebRtcVideoCapturer
125///////////////////////////////////////////////////////////////////////////
126
127WebRtcVideoCapturer::WebRtcVideoCapturer()
128 : factory_(new WebRtcVcmFactory),
129 module_(NULL),
130 captured_frames_(0) {
buildbot@webrtc.org4f0d4012014-08-07 04:47:36 +0000131 set_frame_factory(new WebRtcVideoFrameFactory());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000132}
133
134WebRtcVideoCapturer::WebRtcVideoCapturer(WebRtcVcmFactoryInterface* factory)
135 : factory_(factory),
136 module_(NULL),
137 captured_frames_(0) {
buildbot@webrtc.org4f0d4012014-08-07 04:47:36 +0000138 set_frame_factory(new WebRtcVideoFrameFactory());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000139}
140
141WebRtcVideoCapturer::~WebRtcVideoCapturer() {
142 if (module_) {
143 module_->Release();
144 }
145}
146
147bool WebRtcVideoCapturer::Init(const Device& device) {
148 if (module_) {
149 LOG(LS_ERROR) << "The capturer is already initialized";
150 return false;
151 }
152
153 webrtc::VideoCaptureModule::DeviceInfo* info = factory_->CreateDeviceInfo(0);
154 if (!info) {
155 return false;
156 }
157
158 // Find the desired camera, by name.
159 // In the future, comparing IDs will be more robust.
160 // TODO(juberti): Figure what's needed to allow this.
161 int num_cams = info->NumberOfDevices();
162 char vcm_id[256] = "";
163 bool found = false;
164 for (int index = 0; index < num_cams; ++index) {
165 char vcm_name[256];
166 if (info->GetDeviceName(index, vcm_name, ARRAY_SIZE(vcm_name),
167 vcm_id, ARRAY_SIZE(vcm_id)) != -1) {
168 if (device.name == reinterpret_cast<char*>(vcm_name)) {
169 found = true;
170 break;
171 }
172 }
173 }
174 if (!found) {
175 LOG(LS_WARNING) << "Failed to find capturer for id: " << device.id;
176 factory_->DestroyDeviceInfo(info);
177 return false;
178 }
179
180 // Enumerate the supported formats.
181 // TODO(juberti): Find out why this starts/stops the camera...
182 std::vector<VideoFormat> supported;
183 int32_t num_caps = info->NumberOfCapabilities(vcm_id);
184 for (int32_t i = 0; i < num_caps; ++i) {
185 webrtc::VideoCaptureCapability cap;
186 if (info->GetCapability(vcm_id, i, cap) != -1) {
187 VideoFormat format;
188 if (CapabilityToFormat(cap, &format)) {
189 supported.push_back(format);
190 } else {
191 LOG(LS_WARNING) << "Ignoring unsupported WebRTC capture format "
192 << cap.rawType;
193 }
194 }
195 }
196 factory_->DestroyDeviceInfo(info);
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000197// TODO(fischman): Remove the following check
198// when capabilities for iOS are implemented
199// https://code.google.com/p/webrtc/issues/detail?id=2968
200#if !defined(IOS)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000201 if (supported.empty()) {
202 LOG(LS_ERROR) << "Failed to find usable formats for id: " << device.id;
203 return false;
204 }
henrike@webrtc.orgd3d6bce2014-03-10 20:41:22 +0000205#endif
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000206 module_ = factory_->Create(0, vcm_id);
207 if (!module_) {
208 LOG(LS_ERROR) << "Failed to create capturer for id: " << device.id;
209 return false;
210 }
211
212 // It is safe to change member attributes now.
213 module_->AddRef();
214 SetId(device.id);
215 SetSupportedFormats(supported);
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000216
217 // Ensure these 2 have the same value.
218 SetApplyRotation(module_->GetApplyRotation());
219
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000220 return true;
221}
222
223bool WebRtcVideoCapturer::Init(webrtc::VideoCaptureModule* module) {
224 if (module_) {
225 LOG(LS_ERROR) << "The capturer is already initialized";
226 return false;
227 }
228 if (!module) {
229 LOG(LS_ERROR) << "Invalid VCM supplied";
230 return false;
231 }
232 // TODO(juberti): Set id and formats.
233 (module_ = module)->AddRef();
234 return true;
235}
236
237bool WebRtcVideoCapturer::GetBestCaptureFormat(const VideoFormat& desired,
238 VideoFormat* best_format) {
239 if (!best_format) {
240 return false;
241 }
242
243 if (!VideoCapturer::GetBestCaptureFormat(desired, best_format)) {
244 // We maybe using a manually injected VCM which doesn't support enum.
245 // Use the desired format as the best format.
246 best_format->width = desired.width;
247 best_format->height = desired.height;
248 best_format->fourcc = FOURCC_I420;
249 best_format->interval = desired.interval;
250 LOG(LS_INFO) << "Failed to find best capture format,"
251 << " fall back to the requested format "
252 << best_format->ToString();
253 }
254 return true;
255}
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000256bool WebRtcVideoCapturer::SetApplyRotation(bool enable) {
257 rtc::CritScope cs(&critical_section_stopping_);
258
259 assert(module_);
260
261 if (!VideoCapturer::SetApplyRotation(enable)) {
262 return false;
263 }
264 return module_->SetApplyRotation(enable);
265}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000266
267CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) {
268 if (!module_) {
269 LOG(LS_ERROR) << "The capturer has not been initialized";
270 return CS_NO_DEVICE;
271 }
272
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000273 rtc::CritScope cs(&critical_section_stopping_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000274 // TODO(hellner): weird to return failure when it is in fact actually running.
275 if (IsRunning()) {
276 LOG(LS_ERROR) << "The capturer is already running";
277 return CS_FAILED;
278 }
279
280 SetCaptureFormat(&capture_format);
281
282 webrtc::VideoCaptureCapability cap;
283 if (!FormatToCapability(capture_format, &cap)) {
284 LOG(LS_ERROR) << "Invalid capture format specified";
285 return CS_FAILED;
286 }
287
288 std::string camera_id(GetId());
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000289 uint32 start = rtc::Time();
mallinath@webrtc.org7433a082014-01-29 00:56:02 +0000290 module_->RegisterCaptureDataCallback(*this);
291 if (module_->StartCapture(cap) != 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000292 LOG(LS_ERROR) << "Camera '" << camera_id << "' failed to start";
293 return CS_FAILED;
294 }
295
296 LOG(LS_INFO) << "Camera '" << camera_id << "' started with format "
297 << capture_format.ToString() << ", elapsed time "
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000298 << rtc::TimeSince(start) << " ms";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000299
300 captured_frames_ = 0;
301 SetCaptureState(CS_RUNNING);
302 return CS_STARTING;
303}
304
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000305// Critical section blocks Stop from shutting down during callbacks from capture
306// thread to OnIncomingCapturedFrame. Note that the crit is try-locked in
307// OnFrameCaptured, as the lock ordering between this and the system component
308// controlling the camera is reversed: system frame -> OnIncomingCapturedFrame;
309// Stop -> system stop camera).
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000310void WebRtcVideoCapturer::Stop() {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000311 rtc::CritScope cs(&critical_section_stopping_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000312 if (IsRunning()) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000313 rtc::Thread::Current()->Clear(this);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000314 module_->StopCapture();
315 module_->DeRegisterCaptureDataCallback();
316
317 // TODO(juberti): Determine if the VCM exposes any drop stats we can use.
318 double drop_ratio = 0.0;
319 std::string camera_id(GetId());
320 LOG(LS_INFO) << "Camera '" << camera_id << "' stopped after capturing "
321 << captured_frames_ << " frames and dropping "
322 << drop_ratio << "%";
323 }
324 SetCaptureFormat(NULL);
325}
326
327bool WebRtcVideoCapturer::IsRunning() {
328 return (module_ != NULL && module_->CaptureStarted());
329}
330
331bool WebRtcVideoCapturer::GetPreferredFourccs(
332 std::vector<uint32>* fourccs) {
333 if (!fourccs) {
334 return false;
335 }
336
337 fourccs->clear();
338 for (size_t i = 0; i < ARRAY_SIZE(kSupportedFourCCs); ++i) {
339 fourccs->push_back(kSupportedFourCCs[i].fourcc);
340 }
341 return true;
342}
343
344void WebRtcVideoCapturer::OnIncomingCapturedFrame(const int32_t id,
345 webrtc::I420VideoFrame& sample) {
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000346 // This would be a normal CritScope, except that it's possible that:
347 // (1) whatever system component producing this frame has taken a lock, and
348 // (2) Stop() probably calls back into that system component, which may take
349 // the same lock. Due to the reversed order, we have to try-lock in order to
350 // avoid a potential deadlock. Besides, if we can't enter because we're
351 // stopping, we may as well drop the frame.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000352 rtc::TryCritScope cs(&critical_section_stopping_);
wu@webrtc.orgf6d6ed02014-01-03 22:08:47 +0000353 if (!cs.locked() || !IsRunning()) {
354 // Capturer has been stopped or is in the process of stopping.
355 return;
356 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000357
358 ++captured_frames_;
359 // Log the size and pixel aspect ratio of the first captured frame.
360 if (1 == captured_frames_) {
361 LOG(LS_INFO) << "Captured frame size "
362 << sample.width() << "x" << sample.height()
363 << ". Expected format " << GetCaptureFormat()->ToString();
364 }
365
366 // Signal down stream components on captured frame.
367 // The CapturedFrame class doesn't support planes. We have to ExtractBuffer
368 // to one block for it.
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000369 size_t length =
370 webrtc::CalcBufferSize(webrtc::kI420, sample.width(), sample.height());
wu@webrtc.org16d62542013-11-05 23:45:14 +0000371 capture_buffer_.resize(length);
372 // TODO(ronghuawu): Refactor the WebRtcCapturedFrame to avoid memory copy.
373 webrtc::ExtractBuffer(sample, length, &capture_buffer_[0]);
374 WebRtcCapturedFrame frame(sample, &capture_buffer_[0], length);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000375 SignalFrameCaptured(this, &frame);
376}
377
378void WebRtcVideoCapturer::OnCaptureDelayChanged(const int32_t id,
379 const int32_t delay) {
380 LOG(LS_INFO) << "Capture delay changed to " << delay << " ms";
381}
382
383// WebRtcCapturedFrame
384WebRtcCapturedFrame::WebRtcCapturedFrame(const webrtc::I420VideoFrame& sample,
385 void* buffer,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000386 size_t length) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000387 width = sample.width();
388 height = sample.height();
389 fourcc = FOURCC_I420;
390 // TODO(hellner): Support pixel aspect ratio (for OSX).
391 pixel_width = 1;
392 pixel_height = 1;
393 // Convert units from VideoFrame RenderTimeMs to CapturedFrame (nanoseconds).
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000394 elapsed_time = sample.render_time_ms() * rtc::kNumNanosecsPerMillisec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000395 time_stamp = elapsed_time;
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +0000396 data_size = rtc::checked_cast<uint32>(length);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000397 data = buffer;
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000398 rotation = sample.rotation();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000399}
400
401} // namespace cricket
402
403#endif // HAVE_WEBRTC_VIDEO