1 /*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
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
11 #include "modules/video_capture/video_capture_impl.h"
12
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "api/video/i420_buffer.h"
17 #include "api/video/video_frame_buffer.h"
18 #include "common_video/libyuv/include/webrtc_libyuv.h"
19 #include "modules/video_capture/video_capture_config.h"
20 #include "rtc_base/logging.h"
21 #include "rtc_base/ref_counted_object.h"
22 #include "rtc_base/time_utils.h"
23 #include "rtc_base/trace_event.h"
24 #include "third_party/libyuv/include/libyuv.h"
25
26 namespace webrtc {
27 namespace videocapturemodule {
28
CurrentDeviceName() const29 const char* VideoCaptureImpl::CurrentDeviceName() const {
30 return _deviceUniqueId;
31 }
32
33 // static
RotationFromDegrees(int degrees,VideoRotation * rotation)34 int32_t VideoCaptureImpl::RotationFromDegrees(int degrees,
35 VideoRotation* rotation) {
36 switch (degrees) {
37 case 0:
38 *rotation = kVideoRotation_0;
39 return 0;
40 case 90:
41 *rotation = kVideoRotation_90;
42 return 0;
43 case 180:
44 *rotation = kVideoRotation_180;
45 return 0;
46 case 270:
47 *rotation = kVideoRotation_270;
48 return 0;
49 default:
50 return -1;
51 ;
52 }
53 }
54
55 // static
RotationInDegrees(VideoRotation rotation,int * degrees)56 int32_t VideoCaptureImpl::RotationInDegrees(VideoRotation rotation,
57 int* degrees) {
58 switch (rotation) {
59 case kVideoRotation_0:
60 *degrees = 0;
61 return 0;
62 case kVideoRotation_90:
63 *degrees = 90;
64 return 0;
65 case kVideoRotation_180:
66 *degrees = 180;
67 return 0;
68 case kVideoRotation_270:
69 *degrees = 270;
70 return 0;
71 }
72 return -1;
73 }
74
VideoCaptureImpl()75 VideoCaptureImpl::VideoCaptureImpl()
76 : _deviceUniqueId(NULL),
77 _requestedCapability(),
78 _lastProcessTimeNanos(rtc::TimeNanos()),
79 _lastFrameRateCallbackTimeNanos(rtc::TimeNanos()),
80 _dataCallBack(NULL),
81 _lastProcessFrameTimeNanos(rtc::TimeNanos()),
82 _rotateFrame(kVideoRotation_0),
83 apply_rotation_(false) {
84 _requestedCapability.width = kDefaultWidth;
85 _requestedCapability.height = kDefaultHeight;
86 _requestedCapability.maxFPS = 30;
87 _requestedCapability.videoType = VideoType::kI420;
88 memset(_incomingFrameTimesNanos, 0, sizeof(_incomingFrameTimesNanos));
89 }
90
~VideoCaptureImpl()91 VideoCaptureImpl::~VideoCaptureImpl() {
92 DeRegisterCaptureDataCallback();
93 if (_deviceUniqueId)
94 delete[] _deviceUniqueId;
95 }
96
RegisterCaptureDataCallback(rtc::VideoSinkInterface<VideoFrame> * dataCallBack)97 void VideoCaptureImpl::RegisterCaptureDataCallback(
98 rtc::VideoSinkInterface<VideoFrame>* dataCallBack) {
99 MutexLock lock(&api_lock_);
100 _dataCallBack = dataCallBack;
101 }
102
DeRegisterCaptureDataCallback()103 void VideoCaptureImpl::DeRegisterCaptureDataCallback() {
104 MutexLock lock(&api_lock_);
105 _dataCallBack = NULL;
106 }
DeliverCapturedFrame(VideoFrame & captureFrame)107 int32_t VideoCaptureImpl::DeliverCapturedFrame(VideoFrame& captureFrame) {
108 UpdateFrameCount(); // frame count used for local frame rate callback.
109
110 if (_dataCallBack) {
111 _dataCallBack->OnFrame(captureFrame);
112 }
113
114 return 0;
115 }
116
IncomingFrame(uint8_t * videoFrame,size_t videoFrameLength,const VideoCaptureCapability & frameInfo,int64_t captureTime)117 int32_t VideoCaptureImpl::IncomingFrame(uint8_t* videoFrame,
118 size_t videoFrameLength,
119 const VideoCaptureCapability& frameInfo,
120 int64_t captureTime /*=0*/) {
121 MutexLock lock(&api_lock_);
122
123 const int32_t width = frameInfo.width;
124 const int32_t height = frameInfo.height;
125
126 TRACE_EVENT1("webrtc", "VC::IncomingFrame", "capture_time", captureTime);
127
128 // Not encoded, convert to I420.
129 if (frameInfo.videoType != VideoType::kMJPEG &&
130 CalcBufferSize(frameInfo.videoType, width, abs(height)) !=
131 videoFrameLength) {
132 RTC_LOG(LS_ERROR) << "Wrong incoming frame length.";
133 return -1;
134 }
135
136 int stride_y = width;
137 int stride_uv = (width + 1) / 2;
138 int target_width = width;
139 int target_height = abs(height);
140
141 // SetApplyRotation doesn't take any lock. Make a local copy here.
142 bool apply_rotation = apply_rotation_;
143
144 if (apply_rotation) {
145 // Rotating resolution when for 90/270 degree rotations.
146 if (_rotateFrame == kVideoRotation_90 ||
147 _rotateFrame == kVideoRotation_270) {
148 target_width = abs(height);
149 target_height = width;
150 }
151 }
152
153 // Setting absolute height (in case it was negative).
154 // In Windows, the image starts bottom left, instead of top left.
155 // Setting a negative source height, inverts the image (within LibYuv).
156
157 // TODO(nisse): Use a pool?
158 rtc::scoped_refptr<I420Buffer> buffer = I420Buffer::Create(
159 target_width, target_height, stride_y, stride_uv, stride_uv);
160
161 libyuv::RotationMode rotation_mode = libyuv::kRotate0;
162 if (apply_rotation) {
163 switch (_rotateFrame) {
164 case kVideoRotation_0:
165 rotation_mode = libyuv::kRotate0;
166 break;
167 case kVideoRotation_90:
168 rotation_mode = libyuv::kRotate90;
169 break;
170 case kVideoRotation_180:
171 rotation_mode = libyuv::kRotate180;
172 break;
173 case kVideoRotation_270:
174 rotation_mode = libyuv::kRotate270;
175 break;
176 }
177 }
178
179 const int conversionResult = libyuv::ConvertToI420(
180 videoFrame, videoFrameLength, buffer.get()->MutableDataY(),
181 buffer.get()->StrideY(), buffer.get()->MutableDataU(),
182 buffer.get()->StrideU(), buffer.get()->MutableDataV(),
183 buffer.get()->StrideV(), 0, 0, // No Cropping
184 width, height, target_width, target_height, rotation_mode,
185 ConvertVideoType(frameInfo.videoType));
186 if (conversionResult < 0) {
187 RTC_LOG(LS_ERROR) << "Failed to convert capture frame from type "
188 << static_cast<int>(frameInfo.videoType) << "to I420.";
189 return -1;
190 }
191
192 VideoFrame captureFrame =
193 VideoFrame::Builder()
194 .set_video_frame_buffer(buffer)
195 .set_timestamp_rtp(0)
196 .set_timestamp_ms(rtc::TimeMillis())
197 .set_rotation(!apply_rotation ? _rotateFrame : kVideoRotation_0)
198 .build();
199 captureFrame.set_ntp_time_ms(captureTime);
200
201 DeliverCapturedFrame(captureFrame);
202
203 return 0;
204 }
205
StartCapture(const VideoCaptureCapability & capability)206 int32_t VideoCaptureImpl::StartCapture(
207 const VideoCaptureCapability& capability) {
208 _requestedCapability = capability;
209 return -1;
210 }
211
StopCapture()212 int32_t VideoCaptureImpl::StopCapture() {
213 return -1;
214 }
215
CaptureStarted()216 bool VideoCaptureImpl::CaptureStarted() {
217 return false;
218 }
219
CaptureSettings(VideoCaptureCapability &)220 int32_t VideoCaptureImpl::CaptureSettings(
221 VideoCaptureCapability& /*settings*/) {
222 return -1;
223 }
224
SetCaptureRotation(VideoRotation rotation)225 int32_t VideoCaptureImpl::SetCaptureRotation(VideoRotation rotation) {
226 MutexLock lock(&api_lock_);
227 _rotateFrame = rotation;
228 return 0;
229 }
230
SetApplyRotation(bool enable)231 bool VideoCaptureImpl::SetApplyRotation(bool enable) {
232 // We can't take any lock here as it'll cause deadlock with IncomingFrame.
233
234 // The effect of this is the last caller wins.
235 apply_rotation_ = enable;
236 return true;
237 }
238
GetApplyRotation()239 bool VideoCaptureImpl::GetApplyRotation() {
240 return apply_rotation_;
241 }
242
UpdateFrameCount()243 void VideoCaptureImpl::UpdateFrameCount() {
244 if (_incomingFrameTimesNanos[0] / rtc::kNumNanosecsPerMicrosec == 0) {
245 // first no shift
246 } else {
247 // shift
248 for (int i = (kFrameRateCountHistorySize - 2); i >= 0; --i) {
249 _incomingFrameTimesNanos[i + 1] = _incomingFrameTimesNanos[i];
250 }
251 }
252 _incomingFrameTimesNanos[0] = rtc::TimeNanos();
253 }
254
CalculateFrameRate(int64_t now_ns)255 uint32_t VideoCaptureImpl::CalculateFrameRate(int64_t now_ns) {
256 int32_t num = 0;
257 int32_t nrOfFrames = 0;
258 for (num = 1; num < (kFrameRateCountHistorySize - 1); ++num) {
259 if (_incomingFrameTimesNanos[num] <= 0 ||
260 (now_ns - _incomingFrameTimesNanos[num]) /
261 rtc::kNumNanosecsPerMillisec >
262 kFrameRateHistoryWindowMs) { // don't use data older than 2sec
263 break;
264 } else {
265 nrOfFrames++;
266 }
267 }
268 if (num > 1) {
269 int64_t diff = (now_ns - _incomingFrameTimesNanos[num - 1]) /
270 rtc::kNumNanosecsPerMillisec;
271 if (diff > 0) {
272 return uint32_t((nrOfFrames * 1000.0f / diff) + 0.5f);
273 }
274 }
275
276 return nrOfFrames;
277 }
278 } // namespace videocapturemodule
279 } // namespace webrtc
280