• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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