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