1 /*
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 */
27
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
35 #include "talk/media/webrtc/webrtcvideoframe.h"
36 #include "talk/media/webrtc/webrtcvideoframefactory.h"
37 #include "webrtc/base/arraysize.h"
38 #include "webrtc/base/bind.h"
39 #include "webrtc/base/checks.h"
40 #include "webrtc/base/criticalsection.h"
41 #include "webrtc/base/logging.h"
42 #include "webrtc/base/safe_conversions.h"
43 #include "webrtc/base/thread.h"
44 #include "webrtc/base/timeutils.h"
45
46 #include "webrtc/base/win32.h" // Need this to #include the impl files.
47 #include "webrtc/modules/video_capture/video_capture_factory.h"
48 #include "webrtc/system_wrappers/include/field_trial.h"
49
50 namespace cricket {
51
52 struct kVideoFourCCEntry {
53 uint32_t fourcc;
54 webrtc::RawVideoType webrtc_type;
55 };
56
57 // This indicates our format preferences and defines a mapping between
58 // webrtc::RawVideoType (from video_capture_defines.h) to our FOURCCs.
59 static kVideoFourCCEntry kSupportedFourCCs[] = {
60 { FOURCC_I420, webrtc::kVideoI420 }, // 12 bpp, no conversion.
61 { FOURCC_YV12, webrtc::kVideoYV12 }, // 12 bpp, no conversion.
62 { FOURCC_YUY2, webrtc::kVideoYUY2 }, // 16 bpp, fast conversion.
63 { FOURCC_UYVY, webrtc::kVideoUYVY }, // 16 bpp, fast conversion.
64 { FOURCC_NV12, webrtc::kVideoNV12 }, // 12 bpp, fast conversion.
65 { FOURCC_NV21, webrtc::kVideoNV21 }, // 12 bpp, fast conversion.
66 { FOURCC_MJPG, webrtc::kVideoMJPEG }, // compressed, slow conversion.
67 { FOURCC_ARGB, webrtc::kVideoARGB }, // 32 bpp, slow conversion.
68 { FOURCC_24BG, webrtc::kVideoRGB24 }, // 24 bpp, slow conversion.
69 };
70
71 class WebRtcVcmFactory : public WebRtcVcmFactoryInterface {
72 public:
Create(int id,const char * device)73 virtual webrtc::VideoCaptureModule* Create(int id, const char* device) {
74 return webrtc::VideoCaptureFactory::Create(id, device);
75 }
CreateDeviceInfo(int id)76 virtual webrtc::VideoCaptureModule::DeviceInfo* CreateDeviceInfo(int id) {
77 return webrtc::VideoCaptureFactory::CreateDeviceInfo(id);
78 }
DestroyDeviceInfo(webrtc::VideoCaptureModule::DeviceInfo * info)79 virtual void DestroyDeviceInfo(webrtc::VideoCaptureModule::DeviceInfo* info) {
80 delete info;
81 }
82 };
83
CapabilityToFormat(const webrtc::VideoCaptureCapability & cap,VideoFormat * format)84 static bool CapabilityToFormat(const webrtc::VideoCaptureCapability& cap,
85 VideoFormat* format) {
86 uint32_t fourcc = 0;
87 for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
88 if (kSupportedFourCCs[i].webrtc_type == cap.rawType) {
89 fourcc = kSupportedFourCCs[i].fourcc;
90 break;
91 }
92 }
93 if (fourcc == 0) {
94 return false;
95 }
96
97 format->fourcc = fourcc;
98 format->width = cap.width;
99 format->height = cap.height;
100 format->interval = VideoFormat::FpsToInterval(cap.maxFPS);
101 return true;
102 }
103
FormatToCapability(const VideoFormat & format,webrtc::VideoCaptureCapability * cap)104 static bool FormatToCapability(const VideoFormat& format,
105 webrtc::VideoCaptureCapability* cap) {
106 webrtc::RawVideoType webrtc_type = webrtc::kVideoUnknown;
107 for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
108 if (kSupportedFourCCs[i].fourcc == format.fourcc) {
109 webrtc_type = kSupportedFourCCs[i].webrtc_type;
110 break;
111 }
112 }
113 if (webrtc_type == webrtc::kVideoUnknown) {
114 return false;
115 }
116
117 cap->width = format.width;
118 cap->height = format.height;
119 cap->maxFPS = VideoFormat::IntervalToFps(format.interval);
120 cap->expectedCaptureDelay = 0;
121 cap->rawType = webrtc_type;
122 cap->codecType = webrtc::kVideoCodecUnknown;
123 cap->interlaced = false;
124 return true;
125 }
126
127 ///////////////////////////////////////////////////////////////////////////
128 // Implementation of class WebRtcVideoCapturer
129 ///////////////////////////////////////////////////////////////////////////
130
WebRtcVideoCapturer()131 WebRtcVideoCapturer::WebRtcVideoCapturer()
132 : factory_(new WebRtcVcmFactory),
133 module_(nullptr),
134 captured_frames_(0),
135 start_thread_(nullptr),
136 async_invoker_(nullptr) {
137 set_frame_factory(new WebRtcVideoFrameFactory());
138 }
139
WebRtcVideoCapturer(WebRtcVcmFactoryInterface * factory)140 WebRtcVideoCapturer::WebRtcVideoCapturer(WebRtcVcmFactoryInterface* factory)
141 : factory_(factory),
142 module_(nullptr),
143 captured_frames_(0),
144 start_thread_(nullptr),
145 async_invoker_(nullptr) {
146 set_frame_factory(new WebRtcVideoFrameFactory());
147 }
148
~WebRtcVideoCapturer()149 WebRtcVideoCapturer::~WebRtcVideoCapturer() {
150 if (module_) {
151 module_->Release();
152 }
153 }
154
Init(const Device & device)155 bool WebRtcVideoCapturer::Init(const Device& device) {
156 RTC_DCHECK(!start_thread_);
157 if (module_) {
158 LOG(LS_ERROR) << "The capturer is already initialized";
159 return false;
160 }
161
162 webrtc::VideoCaptureModule::DeviceInfo* info = factory_->CreateDeviceInfo(0);
163 if (!info) {
164 return false;
165 }
166
167 // Find the desired camera, by name.
168 // In the future, comparing IDs will be more robust.
169 // TODO(juberti): Figure what's needed to allow this.
170 int num_cams = info->NumberOfDevices();
171 char vcm_id[256] = "";
172 bool found = false;
173 for (int index = 0; index < num_cams; ++index) {
174 char vcm_name[256];
175 if (info->GetDeviceName(index, vcm_name, arraysize(vcm_name), vcm_id,
176 arraysize(vcm_id)) != -1) {
177 if (device.name == reinterpret_cast<char*>(vcm_name)) {
178 found = true;
179 break;
180 }
181 }
182 }
183 if (!found) {
184 LOG(LS_WARNING) << "Failed to find capturer for id: " << device.id;
185 factory_->DestroyDeviceInfo(info);
186 return false;
187 }
188
189 // Enumerate the supported formats.
190 // TODO(juberti): Find out why this starts/stops the camera...
191 std::vector<VideoFormat> supported;
192 int32_t num_caps = info->NumberOfCapabilities(vcm_id);
193 for (int32_t i = 0; i < num_caps; ++i) {
194 webrtc::VideoCaptureCapability cap;
195 if (info->GetCapability(vcm_id, i, cap) != -1) {
196 VideoFormat format;
197 if (CapabilityToFormat(cap, &format)) {
198 supported.push_back(format);
199 } else {
200 LOG(LS_WARNING) << "Ignoring unsupported WebRTC capture format "
201 << cap.rawType;
202 }
203 }
204 }
205 factory_->DestroyDeviceInfo(info);
206
207 if (supported.empty()) {
208 LOG(LS_ERROR) << "Failed to find usable formats for id: " << device.id;
209 return false;
210 }
211
212 module_ = factory_->Create(0, vcm_id);
213 if (!module_) {
214 LOG(LS_ERROR) << "Failed to create capturer for id: " << device.id;
215 return false;
216 }
217
218 // It is safe to change member attributes now.
219 module_->AddRef();
220 SetId(device.id);
221 SetSupportedFormats(supported);
222
223 // Ensure these 2 have the same value.
224 SetApplyRotation(module_->GetApplyRotation());
225
226 return true;
227 }
228
Init(webrtc::VideoCaptureModule * module)229 bool WebRtcVideoCapturer::Init(webrtc::VideoCaptureModule* module) {
230 RTC_DCHECK(!start_thread_);
231 if (module_) {
232 LOG(LS_ERROR) << "The capturer is already initialized";
233 return false;
234 }
235 if (!module) {
236 LOG(LS_ERROR) << "Invalid VCM supplied";
237 return false;
238 }
239 // TODO(juberti): Set id and formats.
240 (module_ = module)->AddRef();
241 return true;
242 }
243
GetBestCaptureFormat(const VideoFormat & desired,VideoFormat * best_format)244 bool WebRtcVideoCapturer::GetBestCaptureFormat(const VideoFormat& desired,
245 VideoFormat* best_format) {
246 if (!best_format) {
247 return false;
248 }
249
250 if (!VideoCapturer::GetBestCaptureFormat(desired, best_format)) {
251 // We maybe using a manually injected VCM which doesn't support enum.
252 // Use the desired format as the best format.
253 best_format->width = desired.width;
254 best_format->height = desired.height;
255 best_format->fourcc = FOURCC_I420;
256 best_format->interval = desired.interval;
257 LOG(LS_INFO) << "Failed to find best capture format,"
258 << " fall back to the requested format "
259 << best_format->ToString();
260 }
261 return true;
262 }
SetApplyRotation(bool enable)263 bool WebRtcVideoCapturer::SetApplyRotation(bool enable) {
264 // Can't take lock here as this will cause deadlock with
265 // OnIncomingCapturedFrame. In fact, the whole method, including methods it
266 // calls, can't take lock.
267 RTC_DCHECK(module_);
268
269 const std::string group_name =
270 webrtc::field_trial::FindFullName("WebRTC-CVO");
271
272 if (group_name == "Disabled") {
273 return true;
274 }
275
276 if (!VideoCapturer::SetApplyRotation(enable)) {
277 return false;
278 }
279 return module_->SetApplyRotation(enable);
280 }
281
Start(const VideoFormat & capture_format)282 CaptureState WebRtcVideoCapturer::Start(const VideoFormat& capture_format) {
283 if (!module_) {
284 LOG(LS_ERROR) << "The capturer has not been initialized";
285 return CS_NO_DEVICE;
286 }
287 if (start_thread_) {
288 LOG(LS_ERROR) << "The capturer is already running";
289 RTC_DCHECK(start_thread_->IsCurrent())
290 << "Trying to start capturer on different threads";
291 return CS_FAILED;
292 }
293
294 start_thread_ = rtc::Thread::Current();
295 RTC_DCHECK(!async_invoker_);
296 async_invoker_.reset(new rtc::AsyncInvoker());
297 captured_frames_ = 0;
298
299 SetCaptureFormat(&capture_format);
300
301 webrtc::VideoCaptureCapability cap;
302 if (!FormatToCapability(capture_format, &cap)) {
303 LOG(LS_ERROR) << "Invalid capture format specified";
304 return CS_FAILED;
305 }
306
307 uint32_t start = rtc::Time();
308 module_->RegisterCaptureDataCallback(*this);
309 if (module_->StartCapture(cap) != 0) {
310 LOG(LS_ERROR) << "Camera '" << GetId() << "' failed to start";
311 module_->DeRegisterCaptureDataCallback();
312 async_invoker_.reset();
313 SetCaptureFormat(nullptr);
314 start_thread_ = nullptr;
315 return CS_FAILED;
316 }
317
318 LOG(LS_INFO) << "Camera '" << GetId() << "' started with format "
319 << capture_format.ToString() << ", elapsed time "
320 << rtc::TimeSince(start) << " ms";
321
322 SetCaptureState(CS_RUNNING);
323 return CS_STARTING;
324 }
325
Stop()326 void WebRtcVideoCapturer::Stop() {
327 if (!start_thread_) {
328 LOG(LS_ERROR) << "The capturer is already stopped";
329 return;
330 }
331 RTC_DCHECK(start_thread_);
332 RTC_DCHECK(start_thread_->IsCurrent());
333 RTC_DCHECK(async_invoker_);
334 if (IsRunning()) {
335 // The module is responsible for OnIncomingCapturedFrame being called, if
336 // we stop it we will get no further callbacks.
337 module_->StopCapture();
338 }
339 module_->DeRegisterCaptureDataCallback();
340
341 // TODO(juberti): Determine if the VCM exposes any drop stats we can use.
342 double drop_ratio = 0.0;
343 LOG(LS_INFO) << "Camera '" << GetId() << "' stopped after capturing "
344 << captured_frames_ << " frames and dropping "
345 << drop_ratio << "%";
346
347 // Clear any pending async invokes (that OnIncomingCapturedFrame may have
348 // caused).
349 async_invoker_.reset();
350
351 SetCaptureFormat(NULL);
352 start_thread_ = nullptr;
353 SetCaptureState(CS_STOPPED);
354 }
355
IsRunning()356 bool WebRtcVideoCapturer::IsRunning() {
357 return (module_ != NULL && module_->CaptureStarted());
358 }
359
GetPreferredFourccs(std::vector<uint32_t> * fourccs)360 bool WebRtcVideoCapturer::GetPreferredFourccs(std::vector<uint32_t>* fourccs) {
361 if (!fourccs) {
362 return false;
363 }
364
365 fourccs->clear();
366 for (size_t i = 0; i < arraysize(kSupportedFourCCs); ++i) {
367 fourccs->push_back(kSupportedFourCCs[i].fourcc);
368 }
369 return true;
370 }
371
OnIncomingCapturedFrame(const int32_t id,const webrtc::VideoFrame & sample)372 void WebRtcVideoCapturer::OnIncomingCapturedFrame(
373 const int32_t id,
374 const webrtc::VideoFrame& sample) {
375 // This can only happen between Start() and Stop().
376 RTC_DCHECK(start_thread_);
377 RTC_DCHECK(async_invoker_);
378 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 // Note that Stop() can cause the async invoke call to be cancelled.
387 async_invoker_->AsyncInvoke<void>(
388 start_thread_,
389 // Note that Bind captures by value, so there's an intermediate copy
390 // of sample.
391 rtc::Bind(&WebRtcVideoCapturer::SignalFrameCapturedOnStartThread, this,
392 sample));
393 }
394 }
395
OnCaptureDelayChanged(const int32_t id,const int32_t delay)396 void WebRtcVideoCapturer::OnCaptureDelayChanged(const int32_t id,
397 const int32_t delay) {
398 LOG(LS_INFO) << "Capture delay changed to " << delay << " ms";
399 }
400
SignalFrameCapturedOnStartThread(const webrtc::VideoFrame & frame)401 void WebRtcVideoCapturer::SignalFrameCapturedOnStartThread(
402 const webrtc::VideoFrame& frame) {
403 // This can only happen between Start() and Stop().
404 RTC_DCHECK(start_thread_);
405 RTC_DCHECK(start_thread_->IsCurrent());
406 RTC_DCHECK(async_invoker_);
407
408 ++captured_frames_;
409 // Log the size and pixel aspect ratio of the first captured frame.
410 if (1 == captured_frames_) {
411 LOG(LS_INFO) << "Captured frame size "
412 << frame.width() << "x" << frame.height()
413 << ". Expected format " << GetCaptureFormat()->ToString();
414 }
415
416 // Signal down stream components on captured frame.
417 // The CapturedFrame class doesn't support planes. We have to ExtractBuffer
418 // to one block for it.
419 size_t length =
420 webrtc::CalcBufferSize(webrtc::kI420, frame.width(), frame.height());
421 capture_buffer_.resize(length);
422 // TODO(magjed): Refactor the WebRtcCapturedFrame to avoid memory copy or
423 // take over ownership of the buffer held by |frame| if that's possible.
424 webrtc::ExtractBuffer(frame, length, &capture_buffer_[0]);
425 WebRtcCapturedFrame webrtc_frame(frame, &capture_buffer_[0], length);
426 SignalFrameCaptured(this, &webrtc_frame);
427 }
428
429 // WebRtcCapturedFrame
WebRtcCapturedFrame(const webrtc::VideoFrame & sample,void * buffer,size_t length)430 WebRtcCapturedFrame::WebRtcCapturedFrame(const webrtc::VideoFrame& sample,
431 void* buffer,
432 size_t length) {
433 width = sample.width();
434 height = sample.height();
435 fourcc = FOURCC_I420;
436 // TODO(hellner): Support pixel aspect ratio (for OSX).
437 pixel_width = 1;
438 pixel_height = 1;
439 // Convert units from VideoFrame RenderTimeMs to CapturedFrame (nanoseconds).
440 time_stamp = sample.render_time_ms() * rtc::kNumNanosecsPerMillisec;
441 data_size = rtc::checked_cast<uint32_t>(length);
442 data = buffer;
443 rotation = sample.rotation();
444 }
445
446 } // namespace cricket
447
448 #endif // HAVE_WEBRTC_VIDEO
449