blob: 1eb77ee7c171f07dbca0236d8da151525c615e42 [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/video_capture/video_capture_impl.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
elham@webrtc.org5f49dba2012-04-23 21:24:02 +000013#include <stdlib.h>
14
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "api/video/i420_buffer.h"
16#include "common_video/libyuv/include/webrtc_libyuv.h"
mallikarjun8212e555b2017-11-15 14:35:56 +053017#include "libyuv.h" // NOLINT
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "modules/include/module_common_types.h"
19#include "modules/video_capture/video_capture_config.h"
20#include "rtc_base/logging.h"
21#include "rtc_base/refcount.h"
Niels Möller84255bb2017-10-06 13:43:23 +020022#include "rtc_base/refcountedobject.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "rtc_base/timeutils.h"
24#include "rtc_base/trace_event.h"
25#include "system_wrappers/include/clock.h"
pbos@webrtc.orga9b74ad2013-07-12 10:03:52 +000026
Peter Boström1d194412016-03-21 16:44:31 +010027namespace webrtc {
28namespace videocapturemodule {
29rtc::scoped_refptr<VideoCaptureModule> VideoCaptureImpl::Create(
Peter Boström1d194412016-03-21 16:44:31 +010030 VideoCaptureExternal*& externalCapture) {
31 rtc::scoped_refptr<VideoCaptureImpl> implementation(
nisseb29b9c82016-12-12 00:22:56 -080032 new rtc::RefCountedObject<VideoCaptureImpl>());
Peter Boström1d194412016-03-21 16:44:31 +010033 externalCapture = implementation.get();
34 return implementation;
niklase@google.com470e71d2011-07-07 08:21:25 +000035}
36
ilnik00d802b2017-04-11 10:34:31 -070037const char* VideoCaptureImpl::CurrentDeviceName() const {
38 return _deviceUniqueId;
niklase@google.com470e71d2011-07-07 08:21:25 +000039}
40
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000041// static
42int32_t VideoCaptureImpl::RotationFromDegrees(int degrees,
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000043 VideoRotation* rotation) {
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000044 switch (degrees) {
45 case 0:
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000046 *rotation = kVideoRotation_0;
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000047 return 0;
48 case 90:
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000049 *rotation = kVideoRotation_90;
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000050 return 0;
51 case 180:
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000052 *rotation = kVideoRotation_180;
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000053 return 0;
54 case 270:
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000055 *rotation = kVideoRotation_270;
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000056 return 0;
57 default:
Mirko Bonadei72c42502017-11-09 09:33:23 +010058 return -1;
59 ;
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000060 }
61}
62
63// static
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000064int32_t VideoCaptureImpl::RotationInDegrees(VideoRotation rotation,
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000065 int* degrees) {
66 switch (rotation) {
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000067 case kVideoRotation_0:
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000068 *degrees = 0;
69 return 0;
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000070 case kVideoRotation_90:
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000071 *degrees = 90;
72 return 0;
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000073 case kVideoRotation_180:
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000074 *degrees = 180;
75 return 0;
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +000076 case kVideoRotation_270:
fischman@webrtc.org4e65e072013-10-03 18:23:13 +000077 *degrees = 270;
78 return 0;
79 }
80 return -1;
81}
82
nisseb29b9c82016-12-12 00:22:56 -080083VideoCaptureImpl::VideoCaptureImpl()
84 : _deviceUniqueId(NULL),
pbos@webrtc.org504af452013-07-02 10:15:43 +000085 _requestedCapability(),
Niels Möllerd28db7f2016-05-10 16:31:47 +020086 _lastProcessTimeNanos(rtc::TimeNanos()),
87 _lastFrameRateCallbackTimeNanos(rtc::TimeNanos()),
pbos@webrtc.org504af452013-07-02 10:15:43 +000088 _dataCallBack(NULL),
Niels Möllerd28db7f2016-05-10 16:31:47 +020089 _lastProcessFrameTimeNanos(rtc::TimeNanos()),
guoweis@webrtc.org59140d62015-03-09 17:07:31 +000090 _rotateFrame(kVideoRotation_0),
deadbeeff5629ad2016-03-18 11:38:26 -070091 apply_rotation_(false) {
Mirko Bonadei72c42502017-11-09 09:33:23 +010092 _requestedCapability.width = kDefaultWidth;
93 _requestedCapability.height = kDefaultHeight;
94 _requestedCapability.maxFPS = 30;
95 _requestedCapability.videoType = VideoType::kI420;
96 memset(_incomingFrameTimesNanos, 0, sizeof(_incomingFrameTimesNanos));
niklase@google.com470e71d2011-07-07 08:21:25 +000097}
98
Mirko Bonadei72c42502017-11-09 09:33:23 +010099VideoCaptureImpl::~VideoCaptureImpl() {
100 DeRegisterCaptureDataCallback();
101 if (_deviceUniqueId)
102 delete[] _deviceUniqueId;
niklase@google.com470e71d2011-07-07 08:21:25 +0000103}
104
mallinath@webrtc.org7433a082014-01-29 00:56:02 +0000105void VideoCaptureImpl::RegisterCaptureDataCallback(
nisseb29b9c82016-12-12 00:22:56 -0800106 rtc::VideoSinkInterface<VideoFrame>* dataCallBack) {
Mirko Bonadei72c42502017-11-09 09:33:23 +0100107 rtc::CritScope cs(&_apiCs);
108 _dataCallBack = dataCallBack;
niklase@google.com470e71d2011-07-07 08:21:25 +0000109}
110
mallinath@webrtc.org7433a082014-01-29 00:56:02 +0000111void VideoCaptureImpl::DeRegisterCaptureDataCallback() {
Mirko Bonadei72c42502017-11-09 09:33:23 +0100112 rtc::CritScope cs(&_apiCs);
113 _dataCallBack = NULL;
niklase@google.com470e71d2011-07-07 08:21:25 +0000114}
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700115int32_t VideoCaptureImpl::DeliverCapturedFrame(VideoFrame& captureFrame) {
mikhal@webrtc.org80f14d22012-10-11 15:03:53 +0000116 UpdateFrameCount(); // frame count used for local frame rate callback.
mikhal@webrtc.org80f14d22012-10-11 15:03:53 +0000117
mikhal@webrtc.org80f14d22012-10-11 15:03:53 +0000118 if (_dataCallBack) {
nisseb29b9c82016-12-12 00:22:56 -0800119 _dataCallBack->OnFrame(captureFrame);
mikhal@webrtc.org80f14d22012-10-11 15:03:53 +0000120 }
121
122 return 0;
123}
124
Mirko Bonadei72c42502017-11-09 09:33:23 +0100125int32_t VideoCaptureImpl::IncomingFrame(uint8_t* videoFrame,
126 size_t videoFrameLength,
127 const VideoCaptureCapability& frameInfo,
128 int64_t captureTime /*=0*/) {
129 rtc::CritScope cs(&_apiCs);
niklase@google.com470e71d2011-07-07 08:21:25 +0000130
Mirko Bonadei72c42502017-11-09 09:33:23 +0100131 const int32_t width = frameInfo.width;
132 const int32_t height = frameInfo.height;
niklase@google.com470e71d2011-07-07 08:21:25 +0000133
Mirko Bonadei72c42502017-11-09 09:33:23 +0100134 TRACE_EVENT1("webrtc", "VC::IncomingFrame", "capture_time", captureTime);
hclam@chromium.org806dc3b2013-04-09 19:54:10 +0000135
Mirko Bonadei72c42502017-11-09 09:33:23 +0100136 // Not encoded, convert to I420.
137 if (frameInfo.videoType != VideoType::kMJPEG &&
138 CalcBufferSize(frameInfo.videoType, width, abs(height)) !=
139 videoFrameLength) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100140 RTC_LOG(LS_ERROR) << "Wrong incoming frame length.";
Mirko Bonadei72c42502017-11-09 09:33:23 +0100141 return -1;
142 }
143
144 int stride_y = width;
145 int stride_uv = (width + 1) / 2;
146 int target_width = width;
147 int target_height = height;
148
149 // SetApplyRotation doesn't take any lock. Make a local copy here.
150 bool apply_rotation = apply_rotation_;
151
152 if (apply_rotation) {
153 // Rotating resolution when for 90/270 degree rotations.
154 if (_rotateFrame == kVideoRotation_90 ||
155 _rotateFrame == kVideoRotation_270) {
156 target_width = abs(height);
157 target_height = width;
niklase@google.com470e71d2011-07-07 08:21:25 +0000158 }
Mirko Bonadei72c42502017-11-09 09:33:23 +0100159 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000160
Mirko Bonadei72c42502017-11-09 09:33:23 +0100161 // Setting absolute height (in case it was negative).
162 // In Windows, the image starts bottom left, instead of top left.
163 // Setting a negative source height, inverts the image (within LibYuv).
nisse1e321222017-02-20 23:27:37 -0800164
Mirko Bonadei72c42502017-11-09 09:33:23 +0100165 // TODO(nisse): Use a pool?
166 rtc::scoped_refptr<I420Buffer> buffer = I420Buffer::Create(
167 target_width, abs(target_height), stride_y, stride_uv, stride_uv);
mallikarjun8212e555b2017-11-15 14:35:56 +0530168
169 libyuv::RotationMode rotation_mode = libyuv::kRotate0;
170 if (apply_rotation) {
171 switch (_rotateFrame) {
172 case kVideoRotation_0:
173 rotation_mode = libyuv::kRotate0;
174 break;
175 case kVideoRotation_90:
176 rotation_mode = libyuv::kRotate90;
177 break;
178 case kVideoRotation_180:
179 rotation_mode = libyuv::kRotate180;
180 break;
181 case kVideoRotation_270:
182 rotation_mode = libyuv::kRotate270;
183 break;
184 }
185 }
186
187 const int conversionResult = libyuv::ConvertToI420(
188 videoFrame, videoFrameLength, buffer.get()->MutableDataY(),
189 buffer.get()->StrideY(), buffer.get()->MutableDataU(),
190 buffer.get()->StrideU(), buffer.get()->MutableDataV(),
191 buffer.get()->StrideV(), 0, 0, // No Cropping
192 width, height, target_width, target_height, rotation_mode,
193 ConvertVideoType(frameInfo.videoType));
Mirko Bonadei72c42502017-11-09 09:33:23 +0100194 if (conversionResult < 0) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100195 RTC_LOG(LS_ERROR) << "Failed to convert capture frame from type "
196 << static_cast<int>(frameInfo.videoType) << "to I420.";
Mirko Bonadei72c42502017-11-09 09:33:23 +0100197 return -1;
198 }
nisse1e321222017-02-20 23:27:37 -0800199
Mirko Bonadei72c42502017-11-09 09:33:23 +0100200 VideoFrame captureFrame(buffer, 0, rtc::TimeMillis(),
201 !apply_rotation ? _rotateFrame : kVideoRotation_0);
202 captureFrame.set_ntp_time_ms(captureTime);
nisse1e321222017-02-20 23:27:37 -0800203
Mirko Bonadei72c42502017-11-09 09:33:23 +0100204 DeliverCapturedFrame(captureFrame);
nisse1e321222017-02-20 23:27:37 -0800205
Mirko Bonadei72c42502017-11-09 09:33:23 +0100206 return 0;
wu@webrtc.orgf10ea312011-10-14 17:16:04 +0000207}
niklase@google.com470e71d2011-07-07 08:21:25 +0000208
guoweis@webrtc.org5a7dc392015-02-13 14:31:26 +0000209int32_t VideoCaptureImpl::SetCaptureRotation(VideoRotation rotation) {
kthelgasonff046c72017-03-31 02:03:55 -0700210 rtc::CritScope cs(&_apiCs);
guoweis@webrtc.org59140d62015-03-09 17:07:31 +0000211 _rotateFrame = rotation;
mikhal@webrtc.org0f34fd72012-11-19 21:15:35 +0000212 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000213}
214
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000215bool VideoCaptureImpl::SetApplyRotation(bool enable) {
Guo-wei Shieh64c1e8c2015-04-01 15:33:06 -0700216 // We can't take any lock here as it'll cause deadlock with IncomingFrame.
217
guoweis@webrtc.org1226e922015-02-11 18:37:54 +0000218 // The effect of this is the last caller wins.
219 apply_rotation_ = enable;
220 return true;
221}
222
ilnik00d802b2017-04-11 10:34:31 -0700223void VideoCaptureImpl::UpdateFrameCount() {
224 if (_incomingFrameTimesNanos[0] / rtc::kNumNanosecsPerMicrosec == 0) {
225 // first no shift
226 } else {
227 // shift
228 for (int i = (kFrameRateCountHistorySize - 2); i >= 0; --i) {
229 _incomingFrameTimesNanos[i + 1] = _incomingFrameTimesNanos[i];
niklase@google.com470e71d2011-07-07 08:21:25 +0000230 }
ilnik00d802b2017-04-11 10:34:31 -0700231 }
232 _incomingFrameTimesNanos[0] = rtc::TimeNanos();
niklase@google.com470e71d2011-07-07 08:21:25 +0000233}
234
ilnik00d802b2017-04-11 10:34:31 -0700235uint32_t VideoCaptureImpl::CalculateFrameRate(int64_t now_ns) {
236 int32_t num = 0;
237 int32_t nrOfFrames = 0;
238 for (num = 1; num < (kFrameRateCountHistorySize - 1); ++num) {
239 if (_incomingFrameTimesNanos[num] <= 0 ||
240 (now_ns - _incomingFrameTimesNanos[num]) /
241 rtc::kNumNanosecsPerMillisec >
242 kFrameRateHistoryWindowMs) { // don't use data older than 2sec
243 break;
244 } else {
245 nrOfFrames++;
niklase@google.com470e71d2011-07-07 08:21:25 +0000246 }
ilnik00d802b2017-04-11 10:34:31 -0700247 }
248 if (num > 1) {
249 int64_t diff = (now_ns - _incomingFrameTimesNanos[num - 1]) /
250 rtc::kNumNanosecsPerMillisec;
251 if (diff > 0) {
252 return uint32_t((nrOfFrames * 1000.0f / diff) + 0.5f);
niklase@google.com470e71d2011-07-07 08:21:25 +0000253 }
ilnik00d802b2017-04-11 10:34:31 -0700254 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000255
ilnik00d802b2017-04-11 10:34:31 -0700256 return nrOfFrames;
niklase@google.com470e71d2011-07-07 08:21:25 +0000257}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000258} // namespace videocapturemodule
259} // namespace webrtc