blob: ef2df2b867a2964b4848d504b78960c6ad9fd2ad [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:251/*
mallinath@webrtc.org12984f02012-02-16 18:18:212 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:253 *
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
Jonas Olssona4d87372019-07-05 17:08:3311#include "modules/video_capture/video_capture_impl.h"
12
elham@webrtc.org5f49dba2012-04-23 21:24:0213#include <stdlib.h>
Yves Gerey3e707812018-11-28 15:47:4914#include <string.h>
elham@webrtc.org5f49dba2012-04-23 21:24:0215
Mirko Bonadei92ea95e2017-09-15 04:47:3116#include "api/video/i420_buffer.h"
Yves Gerey3e707812018-11-28 15:47:4917#include "api/video/video_frame_buffer.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3118#include "common_video/libyuv/include/webrtc_libyuv.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3119#include "modules/video_capture/video_capture_config.h"
20#include "rtc_base/logging.h"
Steve Anton10542f22019-01-11 17:11:0021#include "rtc_base/time_utils.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3122#include "rtc_base/trace_event.h"
Mirko Bonadei65432062017-12-11 08:32:1323#include "third_party/libyuv/include/libyuv.h"
pbos@webrtc.orga9b74ad2013-07-12 10:03:5224
Peter Boström1d194412016-03-21 15:44:3125namespace webrtc {
26namespace videocapturemodule {
niklase@google.com470e71d2011-07-07 08:21:2527
ilnik00d802b2017-04-11 17:34:3128const char* VideoCaptureImpl::CurrentDeviceName() const {
Andreas Pehrsoneee10392023-05-22 09:21:3329 RTC_DCHECK_RUN_ON(&api_checker_);
ilnik00d802b2017-04-11 17:34:3130 return _deviceUniqueId;
niklase@google.com470e71d2011-07-07 08:21:2531}
32
fischman@webrtc.org4e65e072013-10-03 18:23:1333// static
34int32_t VideoCaptureImpl::RotationFromDegrees(int degrees,
guoweis@webrtc.org5a7dc392015-02-13 14:31:2635 VideoRotation* rotation) {
fischman@webrtc.org4e65e072013-10-03 18:23:1336 switch (degrees) {
37 case 0:
guoweis@webrtc.org5a7dc392015-02-13 14:31:2638 *rotation = kVideoRotation_0;
fischman@webrtc.org4e65e072013-10-03 18:23:1339 return 0;
40 case 90:
guoweis@webrtc.org5a7dc392015-02-13 14:31:2641 *rotation = kVideoRotation_90;
fischman@webrtc.org4e65e072013-10-03 18:23:1342 return 0;
43 case 180:
guoweis@webrtc.org5a7dc392015-02-13 14:31:2644 *rotation = kVideoRotation_180;
fischman@webrtc.org4e65e072013-10-03 18:23:1345 return 0;
46 case 270:
guoweis@webrtc.org5a7dc392015-02-13 14:31:2647 *rotation = kVideoRotation_270;
fischman@webrtc.org4e65e072013-10-03 18:23:1348 return 0;
49 default:
Mirko Bonadei72c42502017-11-09 08:33:2350 return -1;
51 ;
fischman@webrtc.org4e65e072013-10-03 18:23:1352 }
53}
54
55// static
guoweis@webrtc.org5a7dc392015-02-13 14:31:2656int32_t VideoCaptureImpl::RotationInDegrees(VideoRotation rotation,
fischman@webrtc.org4e65e072013-10-03 18:23:1357 int* degrees) {
58 switch (rotation) {
guoweis@webrtc.org5a7dc392015-02-13 14:31:2659 case kVideoRotation_0:
fischman@webrtc.org4e65e072013-10-03 18:23:1360 *degrees = 0;
61 return 0;
guoweis@webrtc.org5a7dc392015-02-13 14:31:2662 case kVideoRotation_90:
fischman@webrtc.org4e65e072013-10-03 18:23:1363 *degrees = 90;
64 return 0;
guoweis@webrtc.org5a7dc392015-02-13 14:31:2665 case kVideoRotation_180:
fischman@webrtc.org4e65e072013-10-03 18:23:1366 *degrees = 180;
67 return 0;
guoweis@webrtc.org5a7dc392015-02-13 14:31:2668 case kVideoRotation_270:
fischman@webrtc.org4e65e072013-10-03 18:23:1369 *degrees = 270;
70 return 0;
71 }
72 return -1;
73}
74
nisseb29b9c82016-12-12 08:22:5675VideoCaptureImpl::VideoCaptureImpl()
76 : _deviceUniqueId(NULL),
pbos@webrtc.org504af452013-07-02 10:15:4377 _requestedCapability(),
Evan Shrubsole652c7712025-03-21 09:47:3578 _lastProcessTimeNanos(TimeNanos()),
79 _lastFrameRateCallbackTimeNanos(TimeNanos()),
pbos@webrtc.org504af452013-07-02 10:15:4380 _dataCallBack(NULL),
Michael Olbrich079e93d2022-12-06 15:27:4481 _rawDataCallBack(NULL),
Evan Shrubsole652c7712025-03-21 09:47:3582 _lastProcessFrameTimeNanos(TimeNanos()),
guoweis@webrtc.org59140d62015-03-09 17:07:3183 _rotateFrame(kVideoRotation_0),
deadbeeff5629ad2016-03-18 18:38:2684 apply_rotation_(false) {
Mirko Bonadei72c42502017-11-09 08:33:2385 _requestedCapability.width = kDefaultWidth;
86 _requestedCapability.height = kDefaultHeight;
87 _requestedCapability.maxFPS = 30;
88 _requestedCapability.videoType = VideoType::kI420;
89 memset(_incomingFrameTimesNanos, 0, sizeof(_incomingFrameTimesNanos));
niklase@google.com470e71d2011-07-07 08:21:2590}
91
Mirko Bonadei72c42502017-11-09 08:33:2392VideoCaptureImpl::~VideoCaptureImpl() {
Andreas Pehrsoneee10392023-05-22 09:21:3393 RTC_DCHECK_RUN_ON(&api_checker_);
Mirko Bonadei72c42502017-11-09 08:33:2394 DeRegisterCaptureDataCallback();
95 if (_deviceUniqueId)
96 delete[] _deviceUniqueId;
niklase@google.com470e71d2011-07-07 08:21:2597}
98
mallinath@webrtc.org7433a082014-01-29 00:56:0299void VideoCaptureImpl::RegisterCaptureDataCallback(
Evan Shrubsole8f7678f2025-04-01 14:23:55100 VideoSinkInterface<VideoFrame>* dataCallBack) {
Markus Handell4379a7d2020-07-13 11:09:15101 MutexLock lock(&api_lock_);
Michael Olbrich079e93d2022-12-06 15:27:44102 RTC_DCHECK(!_rawDataCallBack);
Mirko Bonadei72c42502017-11-09 08:33:23103 _dataCallBack = dataCallBack;
niklase@google.com470e71d2011-07-07 08:21:25104}
105
Michael Olbrich079e93d2022-12-06 15:27:44106void VideoCaptureImpl::RegisterCaptureDataCallback(
107 RawVideoSinkInterface* dataCallBack) {
108 MutexLock lock(&api_lock_);
109 RTC_DCHECK(!_dataCallBack);
110 _rawDataCallBack = dataCallBack;
111}
112
mallinath@webrtc.org7433a082014-01-29 00:56:02113void VideoCaptureImpl::DeRegisterCaptureDataCallback() {
Markus Handell4379a7d2020-07-13 11:09:15114 MutexLock lock(&api_lock_);
Mirko Bonadei72c42502017-11-09 08:33:23115 _dataCallBack = NULL;
Michael Olbrich079e93d2022-12-06 15:27:44116 _rawDataCallBack = NULL;
niklase@google.com470e71d2011-07-07 08:21:25117}
Miguel Casas-Sanchez47650702015-05-30 00:21:40118int32_t VideoCaptureImpl::DeliverCapturedFrame(VideoFrame& captureFrame) {
Andreas Pehrsoneee10392023-05-22 09:21:33119 RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
120
mikhal@webrtc.org80f14d22012-10-11 15:03:53121 UpdateFrameCount(); // frame count used for local frame rate callback.
mikhal@webrtc.org80f14d22012-10-11 15:03:53122
mikhal@webrtc.org80f14d22012-10-11 15:03:53123 if (_dataCallBack) {
nisseb29b9c82016-12-12 08:22:56124 _dataCallBack->OnFrame(captureFrame);
mikhal@webrtc.org80f14d22012-10-11 15:03:53125 }
126
127 return 0;
128}
129
Michael Olbrich079e93d2022-12-06 15:27:44130void VideoCaptureImpl::DeliverRawFrame(uint8_t* videoFrame,
131 size_t videoFrameLength,
132 const VideoCaptureCapability& frameInfo,
133 int64_t captureTime) {
Andreas Pehrsoneee10392023-05-22 09:21:33134 RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
135
Michael Olbrich079e93d2022-12-06 15:27:44136 UpdateFrameCount();
137 _rawDataCallBack->OnRawFrame(videoFrame, videoFrameLength, frameInfo,
138 _rotateFrame, captureTime);
139}
140
Mirko Bonadei72c42502017-11-09 08:33:23141int32_t VideoCaptureImpl::IncomingFrame(uint8_t* videoFrame,
142 size_t videoFrameLength,
143 const VideoCaptureCapability& frameInfo,
144 int64_t captureTime /*=0*/) {
Andreas Pehrsoneee10392023-05-22 09:21:33145 RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
Markus Handell4379a7d2020-07-13 11:09:15146 MutexLock lock(&api_lock_);
niklase@google.com470e71d2011-07-07 08:21:25147
Mirko Bonadei72c42502017-11-09 08:33:23148 const int32_t width = frameInfo.width;
149 const int32_t height = frameInfo.height;
niklase@google.com470e71d2011-07-07 08:21:25150
Mirko Bonadei72c42502017-11-09 08:33:23151 TRACE_EVENT1("webrtc", "VC::IncomingFrame", "capture_time", captureTime);
hclam@chromium.org806dc3b2013-04-09 19:54:10152
Michael Olbrich079e93d2022-12-06 15:27:44153 if (_rawDataCallBack) {
154 DeliverRawFrame(videoFrame, videoFrameLength, frameInfo, captureTime);
155 return 0;
156 }
157
Mirko Bonadei72c42502017-11-09 08:33:23158 // Not encoded, convert to I420.
Andreas Pehrsonb1a17402023-01-27 14:59:44159 if (frameInfo.videoType != VideoType::kMJPEG) {
160 // Allow buffers larger than expected. On linux gstreamer allocates buffers
161 // page-aligned and v4l2loopback passes us the buffer size verbatim which
162 // for most cases is larger than expected.
163 // See https://github.com/umlaeute/v4l2loopback/issues/190.
164 if (auto size = CalcBufferSize(frameInfo.videoType, width, abs(height));
165 videoFrameLength < size) {
166 RTC_LOG(LS_ERROR) << "Wrong incoming frame length. Expected " << size
167 << ", Got " << videoFrameLength << ".";
168 return -1;
169 }
Mirko Bonadei72c42502017-11-09 08:33:23170 }
171
172 int stride_y = width;
173 int stride_uv = (width + 1) / 2;
174 int target_width = width;
Robert Bares0eb7d3ff2018-10-28 00:16:33175 int target_height = abs(height);
Mirko Bonadei72c42502017-11-09 08:33:23176
Andreas Pehrson8f1903e2023-05-17 12:03:17177 if (apply_rotation_) {
Mirko Bonadei72c42502017-11-09 08:33:23178 // Rotating resolution when for 90/270 degree rotations.
179 if (_rotateFrame == kVideoRotation_90 ||
180 _rotateFrame == kVideoRotation_270) {
181 target_width = abs(height);
182 target_height = width;
niklase@google.com470e71d2011-07-07 08:21:25183 }
Mirko Bonadei72c42502017-11-09 08:33:23184 }
niklase@google.com470e71d2011-07-07 08:21:25185
Mirko Bonadei72c42502017-11-09 08:33:23186 // Setting absolute height (in case it was negative).
187 // In Windows, the image starts bottom left, instead of top left.
188 // Setting a negative source height, inverts the image (within LibYuv).
Mirko Bonadei72c42502017-11-09 08:33:23189 rtc::scoped_refptr<I420Buffer> buffer = I420Buffer::Create(
Robert Bares0eb7d3ff2018-10-28 00:16:33190 target_width, target_height, stride_y, stride_uv, stride_uv);
mallikarjun8212e555b2017-11-15 09:05:56191
192 libyuv::RotationMode rotation_mode = libyuv::kRotate0;
Andreas Pehrson8f1903e2023-05-17 12:03:17193 if (apply_rotation_) {
mallikarjun8212e555b2017-11-15 09:05:56194 switch (_rotateFrame) {
195 case kVideoRotation_0:
196 rotation_mode = libyuv::kRotate0;
197 break;
198 case kVideoRotation_90:
199 rotation_mode = libyuv::kRotate90;
200 break;
201 case kVideoRotation_180:
202 rotation_mode = libyuv::kRotate180;
203 break;
204 case kVideoRotation_270:
205 rotation_mode = libyuv::kRotate270;
206 break;
207 }
208 }
209
210 const int conversionResult = libyuv::ConvertToI420(
211 videoFrame, videoFrameLength, buffer.get()->MutableDataY(),
212 buffer.get()->StrideY(), buffer.get()->MutableDataU(),
213 buffer.get()->StrideU(), buffer.get()->MutableDataV(),
214 buffer.get()->StrideV(), 0, 0, // No Cropping
215 width, height, target_width, target_height, rotation_mode,
216 ConvertVideoType(frameInfo.videoType));
Michael Froman3e1484e2023-08-16 21:31:19217 if (conversionResult != 0) {
Mirko Bonadei675513b2017-11-09 10:09:25218 RTC_LOG(LS_ERROR) << "Failed to convert capture frame from type "
219 << static_cast<int>(frameInfo.videoType) << "to I420.";
Mirko Bonadei72c42502017-11-09 08:33:23220 return -1;
221 }
nisse1e321222017-02-21 07:27:37222
Artem Titov1ebfb6a2019-01-03 22:49:37223 VideoFrame captureFrame =
224 VideoFrame::Builder()
225 .set_video_frame_buffer(buffer)
Per K0fa90882024-03-13 08:52:41226 .set_rtp_timestamp(0)
Evan Shrubsole652c7712025-03-21 09:47:35227 .set_timestamp_ms(TimeMillis())
Andreas Pehrson8f1903e2023-05-17 12:03:17228 .set_rotation(!apply_rotation_ ? _rotateFrame : kVideoRotation_0)
Artem Titov1ebfb6a2019-01-03 22:49:37229 .build();
Mirko Bonadei72c42502017-11-09 08:33:23230 captureFrame.set_ntp_time_ms(captureTime);
nisse1e321222017-02-21 07:27:37231
Mirko Bonadei72c42502017-11-09 08:33:23232 DeliverCapturedFrame(captureFrame);
nisse1e321222017-02-21 07:27:37233
Mirko Bonadei72c42502017-11-09 08:33:23234 return 0;
wu@webrtc.orgf10ea312011-10-14 17:16:04235}
niklase@google.com470e71d2011-07-07 08:21:25236
Mirko Bonadei5aec1f62018-08-29 11:27:15237int32_t VideoCaptureImpl::StartCapture(
238 const VideoCaptureCapability& capability) {
Andreas Pehrsoneee10392023-05-22 09:21:33239 RTC_DCHECK_RUN_ON(&api_checker_);
Mirko Bonadei5aec1f62018-08-29 11:27:15240 _requestedCapability = capability;
241 return -1;
242}
243
244int32_t VideoCaptureImpl::StopCapture() {
245 return -1;
246}
247
248bool VideoCaptureImpl::CaptureStarted() {
249 return false;
250}
251
252int32_t VideoCaptureImpl::CaptureSettings(
253 VideoCaptureCapability& /*settings*/) {
254 return -1;
255}
256
guoweis@webrtc.org5a7dc392015-02-13 14:31:26257int32_t VideoCaptureImpl::SetCaptureRotation(VideoRotation rotation) {
Markus Handell4379a7d2020-07-13 11:09:15258 MutexLock lock(&api_lock_);
guoweis@webrtc.org59140d62015-03-09 17:07:31259 _rotateFrame = rotation;
mikhal@webrtc.org0f34fd72012-11-19 21:15:35260 return 0;
niklase@google.com470e71d2011-07-07 08:21:25261}
262
guoweis@webrtc.org1226e922015-02-11 18:37:54263bool VideoCaptureImpl::SetApplyRotation(bool enable) {
Andreas Pehrson8f1903e2023-05-17 12:03:17264 MutexLock lock(&api_lock_);
guoweis@webrtc.org1226e922015-02-11 18:37:54265 apply_rotation_ = enable;
266 return true;
267}
268
Mirko Bonadei5aec1f62018-08-29 11:27:15269bool VideoCaptureImpl::GetApplyRotation() {
Andreas Pehrson8f1903e2023-05-17 12:03:17270 MutexLock lock(&api_lock_);
Mirko Bonadei5aec1f62018-08-29 11:27:15271 return apply_rotation_;
272}
273
ilnik00d802b2017-04-11 17:34:31274void VideoCaptureImpl::UpdateFrameCount() {
Andreas Pehrsoneee10392023-05-22 09:21:33275 RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
276
Evan Shrubsole652c7712025-03-21 09:47:35277 if (_incomingFrameTimesNanos[0] / kNumNanosecsPerMicrosec == 0) {
ilnik00d802b2017-04-11 17:34:31278 // first no shift
279 } else {
280 // shift
281 for (int i = (kFrameRateCountHistorySize - 2); i >= 0; --i) {
282 _incomingFrameTimesNanos[i + 1] = _incomingFrameTimesNanos[i];
niklase@google.com470e71d2011-07-07 08:21:25283 }
ilnik00d802b2017-04-11 17:34:31284 }
Evan Shrubsole652c7712025-03-21 09:47:35285 _incomingFrameTimesNanos[0] = TimeNanos();
niklase@google.com470e71d2011-07-07 08:21:25286}
287
ilnik00d802b2017-04-11 17:34:31288uint32_t VideoCaptureImpl::CalculateFrameRate(int64_t now_ns) {
Andreas Pehrsoneee10392023-05-22 09:21:33289 RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
290
ilnik00d802b2017-04-11 17:34:31291 int32_t num = 0;
292 int32_t nrOfFrames = 0;
293 for (num = 1; num < (kFrameRateCountHistorySize - 1); ++num) {
294 if (_incomingFrameTimesNanos[num] <= 0 ||
Evan Shrubsole652c7712025-03-21 09:47:35295 (now_ns - _incomingFrameTimesNanos[num]) / kNumNanosecsPerMillisec >
ilnik00d802b2017-04-11 17:34:31296 kFrameRateHistoryWindowMs) { // don't use data older than 2sec
297 break;
298 } else {
299 nrOfFrames++;
niklase@google.com470e71d2011-07-07 08:21:25300 }
ilnik00d802b2017-04-11 17:34:31301 }
302 if (num > 1) {
Evan Shrubsole652c7712025-03-21 09:47:35303 int64_t diff =
304 (now_ns - _incomingFrameTimesNanos[num - 1]) / kNumNanosecsPerMillisec;
ilnik00d802b2017-04-11 17:34:31305 if (diff > 0) {
306 return uint32_t((nrOfFrames * 1000.0f / diff) + 0.5f);
niklase@google.com470e71d2011-07-07 08:21:25307 }
ilnik00d802b2017-04-11 17:34:31308 }
niklase@google.com470e71d2011-07-07 08:21:25309
ilnik00d802b2017-04-11 17:34:31310 return nrOfFrames;
niklase@google.com470e71d2011-07-07 08:21:25311}
pbos@webrtc.orgd900e8b2013-07-03 15:12:26312} // namespace videocapturemodule
313} // namespace webrtc