xref: /aosp_15_r20/external/webrtc/modules/video_capture/video_capture_impl.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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