blob: 7977a24f41e7657e00129b8e17c9230b69266a4b [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
mallinath@webrtc.org12984f02012-02-16 18:18:21 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
4 * 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.
9 */
10
elham@webrtc.org5f49dba2012-04-23 21:24:02 +000011#include <stdlib.h>
Yves Gerey3e707812018-11-28 16:47:49 +010012#include <string.h>
elham@webrtc.org5f49dba2012-04-23 21:24:02 +000013
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020014#include "api/video/i420_buffer.h"
Yves Gerey3e707812018-11-28 16:47:49 +010015#include "api/video/video_frame_buffer.h"
16#include "common_types.h" // NOLINT(build/include)
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "common_video/libyuv/include/webrtc_libyuv.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "modules/video_capture/video_capture_config.h"
Mirko Bonadei65432062017-12-11 09:32:13 +010019#include "modules/video_capture/video_capture_impl.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "rtc_base/logging.h"
Steve Anton10542f22019-01-11 09:11:00 -080021#include "rtc_base/ref_counted_object.h"
22#include "rtc_base/time_utils.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "rtc_base/trace_event.h"
Mirko Bonadei65432062017-12-11 09:32:13 +010024#include "third_party/libyuv/include/libyuv.h"
pbos@webrtc.orga9b74ad2013-07-12 10:03:52 +000025
Peter Boström1d194412016-03-21 16:44:31 +010026namespace webrtc {
27namespace videocapturemodule {
28rtc::scoped_refptr<VideoCaptureModule> VideoCaptureImpl::Create(
Peter Boström1d194412016-03-21 16:44:31 +010029 VideoCaptureExternal*& externalCapture) {
30 rtc::scoped_refptr<VideoCaptureImpl> implementation(
nisseb29b9c82016-12-12 00:22:56 -080031 new rtc::RefCountedObject<VideoCaptureImpl>());
Peter Boström1d194412016-03-21 16:44:31 +010032 externalCapture = implementation.get();
33 return implementation;
niklase@google.com470e71d2011-07-07 08:21:25 +000034}
35
ilnik00d802b2017-04-11 10:34:31 -070036const char* VideoCaptureImpl::CurrentDeviceName() const {
37 return _deviceUniqueId;
niklase@google.com470e71d2011-07-07 08:21:25 +000038}
39
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000040// static
41int32_t VideoCaptureImpl::RotationFromDegrees(int degrees,
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000042 VideoRotation* rotation) {
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000043 switch (degrees) {
44 case 0:
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000045 *rotation = kVideoRotation_0;
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000046 return 0;
47 case 90:
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000048 *rotation = kVideoRotation_90;
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000049 return 0;
50 case 180:
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000051 *rotation = kVideoRotation_180;
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000052 return 0;
53 case 270:
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000054 *rotation = kVideoRotation_270;
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000055 return 0;
56 default:
Mirko Bonadei72c42502017-11-09 09:33:23 +010057 return -1;
58 ;
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000059 }
60}
61
62// static
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000063int32_t VideoCaptureImpl::RotationInDegrees(VideoRotation rotation,
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000064 int* degrees) {
65 switch (rotation) {
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000066 case kVideoRotation_0:
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000067 *degrees = 0;
68 return 0;
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000069 case kVideoRotation_90:
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000070 *degrees = 90;
71 return 0;
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000072 case kVideoRotation_180:
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000073 *degrees = 180;
74 return 0;
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000075 case kVideoRotation_270:
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000076 *degrees = 270;
77 return 0;
78 }
79 return -1;
80}
81
nisseb29b9c82016-12-12 00:22:56 -080082VideoCaptureImpl::VideoCaptureImpl()
83 : _deviceUniqueId(NULL),
pbos@webrtc.org504af452013-07-02 10:15:43 +000084 _requestedCapability(),
Niels Möllerd28db7f2016-05-10 16:31:47 +020085 _lastProcessTimeNanos(rtc::TimeNanos()),
86 _lastFrameRateCallbackTimeNanos(rtc::TimeNanos()),
pbos@webrtc.org504af452013-07-02 10:15:43 +000087 _dataCallBack(NULL),
Niels Möllerd28db7f2016-05-10 16:31:47 +020088 _lastProcessFrameTimeNanos(rtc::TimeNanos()),
guoweis@webrtc.org59140d62015-03-09 17:07:31 +000089 _rotateFrame(kVideoRotation_0),
deadbeeff5629ad2016-03-18 11:38:26 -070090 apply_rotation_(false) {
Mirko Bonadei72c42502017-11-09 09:33:23 +010091 _requestedCapability.width = kDefaultWidth;
92 _requestedCapability.height = kDefaultHeight;
93 _requestedCapability.maxFPS = 30;
94 _requestedCapability.videoType = VideoType::kI420;
95 memset(_incomingFrameTimesNanos, 0, sizeof(_incomingFrameTimesNanos));
niklase@google.com470e71d2011-07-07 08:21:25 +000096}
97
Mirko Bonadei72c42502017-11-09 09:33:23 +010098VideoCaptureImpl::~VideoCaptureImpl() {
99 DeRegisterCaptureDataCallback();
100 if (_deviceUniqueId)
101 delete[] _deviceUniqueId;
niklase@google.com470e71d2011-07-07 08:21:25 +0000102}
103
mallinath@webrtc.org7433a082014-01-29 00:56:02 +0000104void VideoCaptureImpl::RegisterCaptureDataCallback(
nisseb29b9c82016-12-12 00:22:56 -0800105 rtc::VideoSinkInterface<VideoFrame>* dataCallBack) {
Mirko Bonadei72c42502017-11-09 09:33:23 +0100106 rtc::CritScope cs(&_apiCs);
107 _dataCallBack = dataCallBack;
niklase@google.com470e71d2011-07-07 08:21:25 +0000108}
109
mallinath@webrtc.org7433a082014-01-29 00:56:02 +0000110void VideoCaptureImpl::DeRegisterCaptureDataCallback() {
Mirko Bonadei72c42502017-11-09 09:33:23 +0100111 rtc::CritScope cs(&_apiCs);
112 _dataCallBack = NULL;
niklase@google.com470e71d2011-07-07 08:21:25 +0000113}
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700114int32_t VideoCaptureImpl::DeliverCapturedFrame(VideoFrame& captureFrame) {
mikhal@webrtc.org80f14d22012-10-11 15:03:53 +0000115 UpdateFrameCount(); // frame count used for local frame rate callback.
mikhal@webrtc.org80f14d22012-10-11 15:03:53 +0000116
mikhal@webrtc.org80f14d22012-10-11 15:03:53 +0000117 if (_dataCallBack) {
nisseb29b9c82016-12-12 00:22:56 -0800118 _dataCallBack->OnFrame(captureFrame);
mikhal@webrtc.org80f14d22012-10-11 15:03:53 +0000119 }
120
121 return 0;
122}
123
Mirko Bonadei72c42502017-11-09 09:33:23 +0100124int32_t VideoCaptureImpl::IncomingFrame(uint8_t* videoFrame,
125 size_t videoFrameLength,
126 const VideoCaptureCapability& frameInfo,
127 int64_t captureTime /*=0*/) {
128 rtc::CritScope cs(&_apiCs);
niklase@google.com470e71d2011-07-07 08:21:25 +0000129
Mirko Bonadei72c42502017-11-09 09:33:23 +0100130 const int32_t width = frameInfo.width;
131 const int32_t height = frameInfo.height;
niklase@google.com470e71d2011-07-07 08:21:25 +0000132
Mirko Bonadei72c42502017-11-09 09:33:23 +0100133 TRACE_EVENT1("webrtc", "VC::IncomingFrame", "capture_time", captureTime);
hclam@chromium.org806dc3b2013-04-09 19:54:10 +0000134
Mirko Bonadei72c42502017-11-09 09:33:23 +0100135 // Not encoded, convert to I420.
136 if (frameInfo.videoType != VideoType::kMJPEG &&
137 CalcBufferSize(frameInfo.videoType, width, abs(height)) !=
138 videoFrameLength) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100139 RTC_LOG(LS_ERROR) << "Wrong incoming frame length.";
Mirko Bonadei72c42502017-11-09 09:33:23 +0100140 return -1;
141 }
142
143 int stride_y = width;
144 int stride_uv = (width + 1) / 2;
145 int target_width = width;
Robert Bares0eb7d3ff2018-10-28 00:16:33 +0000146 int target_height = abs(height);
Mirko Bonadei72c42502017-11-09 09:33:23 +0100147
148 // SetApplyRotation doesn't take any lock. Make a local copy here.
149 bool apply_rotation = apply_rotation_;
150
151 if (apply_rotation) {
152 // Rotating resolution when for 90/270 degree rotations.
153 if (_rotateFrame == kVideoRotation_90 ||
154 _rotateFrame == kVideoRotation_270) {
155 target_width = abs(height);
156 target_height = width;
niklase@google.com470e71d2011-07-07 08:21:25 +0000157 }
Mirko Bonadei72c42502017-11-09 09:33:23 +0100158 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000159
Mirko Bonadei72c42502017-11-09 09:33:23 +0100160 // Setting absolute height (in case it was negative).
161 // In Windows, the image starts bottom left, instead of top left.
162 // Setting a negative source height, inverts the image (within LibYuv).
nisse1e321222017-02-20 23:27:37 -0800163
Mirko Bonadei72c42502017-11-09 09:33:23 +0100164 // TODO(nisse): Use a pool?
165 rtc::scoped_refptr<I420Buffer> buffer = I420Buffer::Create(
Robert Bares0eb7d3ff2018-10-28 00:16:33 +0000166 target_width, target_height, stride_y, stride_uv, stride_uv);
mallikarjun8212e555b2017-11-15 14:35:56 +0530167
168 libyuv::RotationMode rotation_mode = libyuv::kRotate0;
169 if (apply_rotation) {
170 switch (_rotateFrame) {
171 case kVideoRotation_0:
172 rotation_mode = libyuv::kRotate0;
173 break;
174 case kVideoRotation_90:
175 rotation_mode = libyuv::kRotate90;
176 break;
177 case kVideoRotation_180:
178 rotation_mode = libyuv::kRotate180;
179 break;
180 case kVideoRotation_270:
181 rotation_mode = libyuv::kRotate270;
182 break;
183 }
184 }
185
186 const int conversionResult = libyuv::ConvertToI420(
187 videoFrame, videoFrameLength, buffer.get()->MutableDataY(),
188 buffer.get()->StrideY(), buffer.get()->MutableDataU(),
189 buffer.get()->StrideU(), buffer.get()->MutableDataV(),
190 buffer.get()->StrideV(), 0, 0, // No Cropping
191 width, height, target_width, target_height, rotation_mode,
192 ConvertVideoType(frameInfo.videoType));
Mirko Bonadei72c42502017-11-09 09:33:23 +0100193 if (conversionResult < 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100194 RTC_LOG(LS_ERROR) << "Failed to convert capture frame from type "
195 << static_cast<int>(frameInfo.videoType) << "to I420.";
Mirko Bonadei72c42502017-11-09 09:33:23 +0100196 return -1;
197 }
nisse1e321222017-02-20 23:27:37 -0800198
Artem Titov1ebfb6a2019-01-03 23:49:37 +0100199 VideoFrame captureFrame =
200 VideoFrame::Builder()
201 .set_video_frame_buffer(buffer)
202 .set_timestamp_rtp(0)
203 .set_timestamp_ms(rtc::TimeMillis())
204 .set_rotation(!apply_rotation ? _rotateFrame : kVideoRotation_0)
205 .build();
Mirko Bonadei72c42502017-11-09 09:33:23 +0100206 captureFrame.set_ntp_time_ms(captureTime);
nisse1e321222017-02-20 23:27:37 -0800207
Mirko Bonadei72c42502017-11-09 09:33:23 +0100208 DeliverCapturedFrame(captureFrame);
nisse1e321222017-02-20 23:27:37 -0800209
Mirko Bonadei72c42502017-11-09 09:33:23 +0100210 return 0;
wu@webrtc.orgf10ea312011-10-14 17:16:04 +0000211}
niklase@google.com470e71d2011-07-07 08:21:25 +0000212
Mirko Bonadei5aec1f62018-08-29 13:27:15 +0200213int32_t VideoCaptureImpl::StartCapture(
214 const VideoCaptureCapability& capability) {
215 _requestedCapability = capability;
216 return -1;
217}
218
219int32_t VideoCaptureImpl::StopCapture() {
220 return -1;
221}
222
223bool VideoCaptureImpl::CaptureStarted() {
224 return false;
225}
226
227int32_t VideoCaptureImpl::CaptureSettings(
228 VideoCaptureCapability& /*settings*/) {
229 return -1;
230}
231
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +0000232int32_t VideoCaptureImpl::SetCaptureRotation(VideoRotation rotation) {
kthelgasonff046c72017-03-31 02:03:55 -0700233 rtc::CritScope cs(&_apiCs);
guoweis@webrtc.org59140d62015-03-09 17:07:31 +0000234 _rotateFrame = rotation;
mikhal@webrtc.org0f34fd72012-11-19 21:15:35 +0000235 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000236}
237
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000238bool VideoCaptureImpl::SetApplyRotation(bool enable) {
Guo-wei Shieh64c1e8c2015-04-01 15:33:06 -0700239 // We can't take any lock here as it'll cause deadlock with IncomingFrame.
240
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000241 // The effect of this is the last caller wins.
242 apply_rotation_ = enable;
243 return true;
244}
245
Mirko Bonadei5aec1f62018-08-29 13:27:15 +0200246bool VideoCaptureImpl::GetApplyRotation() {
247 return apply_rotation_;
248}
249
ilnik00d802b2017-04-11 10:34:31 -0700250void VideoCaptureImpl::UpdateFrameCount() {
251 if (_incomingFrameTimesNanos[0] / rtc::kNumNanosecsPerMicrosec == 0) {
252 // first no shift
253 } else {
254 // shift
255 for (int i = (kFrameRateCountHistorySize - 2); i >= 0; --i) {
256 _incomingFrameTimesNanos[i + 1] = _incomingFrameTimesNanos[i];
niklase@google.com470e71d2011-07-07 08:21:25 +0000257 }
ilnik00d802b2017-04-11 10:34:31 -0700258 }
259 _incomingFrameTimesNanos[0] = rtc::TimeNanos();
niklase@google.com470e71d2011-07-07 08:21:25 +0000260}
261
ilnik00d802b2017-04-11 10:34:31 -0700262uint32_t VideoCaptureImpl::CalculateFrameRate(int64_t now_ns) {
263 int32_t num = 0;
264 int32_t nrOfFrames = 0;
265 for (num = 1; num < (kFrameRateCountHistorySize - 1); ++num) {
266 if (_incomingFrameTimesNanos[num] <= 0 ||
267 (now_ns - _incomingFrameTimesNanos[num]) /
268 rtc::kNumNanosecsPerMillisec >
269 kFrameRateHistoryWindowMs) { // don't use data older than 2sec
270 break;
271 } else {
272 nrOfFrames++;
niklase@google.com470e71d2011-07-07 08:21:25 +0000273 }
ilnik00d802b2017-04-11 10:34:31 -0700274 }
275 if (num > 1) {
276 int64_t diff = (now_ns - _incomingFrameTimesNanos[num - 1]) /
277 rtc::kNumNanosecsPerMillisec;
278 if (diff > 0) {
279 return uint32_t((nrOfFrames * 1000.0f / diff) + 0.5f);
niklase@google.com470e71d2011-07-07 08:21:25 +0000280 }
ilnik00d802b2017-04-11 10:34:31 -0700281 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000282
ilnik00d802b2017-04-11 10:34:31 -0700283 return nrOfFrames;
niklase@google.com470e71d2011-07-07 08:21:25 +0000284}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000285} // namespace videocapturemodule
286} // namespace webrtc