1 /*
2  * libjingle
3  * Copyright 2011 Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *  1. Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *  3. The name of the author may not be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "talk/media/webrtc/webrtcvideoframe.h"
29 
30 #include "libyuv/convert.h"
31 #include "talk/media/base/videocapturer.h"
32 #include "talk/media/base/videocommon.h"
33 #include "webrtc/base/logging.h"
34 #include "webrtc/video_frame.h"
35 
36 using webrtc::kYPlane;
37 using webrtc::kUPlane;
38 using webrtc::kVPlane;
39 
40 namespace cricket {
41 
WebRtcVideoFrame()42 WebRtcVideoFrame::WebRtcVideoFrame():
43     pixel_width_(0),
44     pixel_height_(0),
45     time_stamp_ns_(0),
46     rotation_(webrtc::kVideoRotation_0) {}
47 
WebRtcVideoFrame(const rtc::scoped_refptr<webrtc::VideoFrameBuffer> & buffer,int64_t time_stamp_ns,webrtc::VideoRotation rotation)48 WebRtcVideoFrame::WebRtcVideoFrame(
49     const rtc::scoped_refptr<webrtc::VideoFrameBuffer>& buffer,
50     int64_t time_stamp_ns,
51     webrtc::VideoRotation rotation)
52     : video_frame_buffer_(buffer),
53       pixel_width_(1),
54       pixel_height_(1),
55       time_stamp_ns_(time_stamp_ns),
56       rotation_(rotation) {
57 }
58 
~WebRtcVideoFrame()59 WebRtcVideoFrame::~WebRtcVideoFrame() {}
60 
Init(uint32_t format,int w,int h,int dw,int dh,uint8_t * sample,size_t sample_size,size_t pixel_width,size_t pixel_height,int64_t time_stamp_ns,webrtc::VideoRotation rotation)61 bool WebRtcVideoFrame::Init(uint32_t format,
62                             int w,
63                             int h,
64                             int dw,
65                             int dh,
66                             uint8_t* sample,
67                             size_t sample_size,
68                             size_t pixel_width,
69                             size_t pixel_height,
70                             int64_t time_stamp_ns,
71                             webrtc::VideoRotation rotation) {
72   return Reset(format, w, h, dw, dh, sample, sample_size, pixel_width,
73                pixel_height, time_stamp_ns, rotation,
74                true /*apply_rotation*/);
75 }
76 
Init(const CapturedFrame * frame,int dw,int dh,bool apply_rotation)77 bool WebRtcVideoFrame::Init(const CapturedFrame* frame, int dw, int dh,
78                             bool apply_rotation) {
79   return Reset(frame->fourcc, frame->width, frame->height, dw, dh,
80                static_cast<uint8_t*>(frame->data), frame->data_size,
81                frame->pixel_width, frame->pixel_height, frame->time_stamp,
82                frame->rotation, apply_rotation);
83 }
84 
InitToBlack(int w,int h,size_t pixel_width,size_t pixel_height,int64_t time_stamp_ns)85 bool WebRtcVideoFrame::InitToBlack(int w, int h, size_t pixel_width,
86                                    size_t pixel_height, int64_t time_stamp_ns) {
87   InitToEmptyBuffer(w, h, pixel_width, pixel_height, time_stamp_ns);
88   return SetToBlack();
89 }
90 
GetWidth() const91 size_t WebRtcVideoFrame::GetWidth() const {
92   return video_frame_buffer_ ? video_frame_buffer_->width() : 0;
93 }
94 
GetHeight() const95 size_t WebRtcVideoFrame::GetHeight() const {
96   return video_frame_buffer_ ? video_frame_buffer_->height() : 0;
97 }
98 
GetYPlane() const99 const uint8_t* WebRtcVideoFrame::GetYPlane() const {
100   return video_frame_buffer_ ? video_frame_buffer_->data(kYPlane) : nullptr;
101 }
102 
GetUPlane() const103 const uint8_t* WebRtcVideoFrame::GetUPlane() const {
104   return video_frame_buffer_ ? video_frame_buffer_->data(kUPlane) : nullptr;
105 }
106 
GetVPlane() const107 const uint8_t* WebRtcVideoFrame::GetVPlane() const {
108   return video_frame_buffer_ ? video_frame_buffer_->data(kVPlane) : nullptr;
109 }
110 
GetYPlane()111 uint8_t* WebRtcVideoFrame::GetYPlane() {
112   return video_frame_buffer_ ? video_frame_buffer_->MutableData(kYPlane)
113                              : nullptr;
114 }
115 
GetUPlane()116 uint8_t* WebRtcVideoFrame::GetUPlane() {
117   return video_frame_buffer_ ? video_frame_buffer_->MutableData(kUPlane)
118                              : nullptr;
119 }
120 
GetVPlane()121 uint8_t* WebRtcVideoFrame::GetVPlane() {
122   return video_frame_buffer_ ? video_frame_buffer_->MutableData(kVPlane)
123                              : nullptr;
124 }
125 
GetYPitch() const126 int32_t WebRtcVideoFrame::GetYPitch() const {
127   return video_frame_buffer_ ? video_frame_buffer_->stride(kYPlane) : 0;
128 }
129 
GetUPitch() const130 int32_t WebRtcVideoFrame::GetUPitch() const {
131   return video_frame_buffer_ ? video_frame_buffer_->stride(kUPlane) : 0;
132 }
133 
GetVPitch() const134 int32_t WebRtcVideoFrame::GetVPitch() const {
135   return video_frame_buffer_ ? video_frame_buffer_->stride(kVPlane) : 0;
136 }
137 
IsExclusive() const138 bool WebRtcVideoFrame::IsExclusive() const {
139   return video_frame_buffer_->HasOneRef();
140 }
141 
GetNativeHandle() const142 void* WebRtcVideoFrame::GetNativeHandle() const {
143   return video_frame_buffer_ ? video_frame_buffer_->native_handle() : nullptr;
144 }
145 
146 rtc::scoped_refptr<webrtc::VideoFrameBuffer>
GetVideoFrameBuffer() const147 WebRtcVideoFrame::GetVideoFrameBuffer() const {
148   return video_frame_buffer_;
149 }
150 
Copy() const151 VideoFrame* WebRtcVideoFrame::Copy() const {
152   WebRtcVideoFrame* new_frame = new WebRtcVideoFrame(
153       video_frame_buffer_, time_stamp_ns_, rotation_);
154   new_frame->pixel_width_ = pixel_width_;
155   new_frame->pixel_height_ = pixel_height_;
156   return new_frame;
157 }
158 
MakeExclusive()159 bool WebRtcVideoFrame::MakeExclusive() {
160   RTC_DCHECK(video_frame_buffer_->native_handle() == nullptr);
161   if (IsExclusive())
162     return true;
163 
164   // Not exclusive already, need to copy buffer.
165   rtc::scoped_refptr<webrtc::VideoFrameBuffer> new_buffer =
166       new rtc::RefCountedObject<webrtc::I420Buffer>(
167           video_frame_buffer_->width(), video_frame_buffer_->height(),
168           video_frame_buffer_->stride(kYPlane),
169           video_frame_buffer_->stride(kUPlane),
170           video_frame_buffer_->stride(kVPlane));
171 
172   if (!CopyToPlanes(
173           new_buffer->MutableData(kYPlane), new_buffer->MutableData(kUPlane),
174           new_buffer->MutableData(kVPlane), new_buffer->stride(kYPlane),
175           new_buffer->stride(kUPlane), new_buffer->stride(kVPlane))) {
176     return false;
177   }
178 
179   video_frame_buffer_ = new_buffer;
180   return true;
181 }
182 
ConvertToRgbBuffer(uint32_t to_fourcc,uint8_t * buffer,size_t size,int stride_rgb) const183 size_t WebRtcVideoFrame::ConvertToRgbBuffer(uint32_t to_fourcc,
184                                             uint8_t* buffer,
185                                             size_t size,
186                                             int stride_rgb) const {
187   RTC_CHECK(video_frame_buffer_);
188   RTC_CHECK(video_frame_buffer_->native_handle() == nullptr);
189   return VideoFrame::ConvertToRgbBuffer(to_fourcc, buffer, size, stride_rgb);
190 }
191 
Reset(uint32_t format,int w,int h,int dw,int dh,uint8_t * sample,size_t sample_size,size_t pixel_width,size_t pixel_height,int64_t time_stamp_ns,webrtc::VideoRotation rotation,bool apply_rotation)192 bool WebRtcVideoFrame::Reset(uint32_t format,
193                              int w,
194                              int h,
195                              int dw,
196                              int dh,
197                              uint8_t* sample,
198                              size_t sample_size,
199                              size_t pixel_width,
200                              size_t pixel_height,
201                              int64_t time_stamp_ns,
202                              webrtc::VideoRotation rotation,
203                              bool apply_rotation) {
204   if (!Validate(format, w, h, sample, sample_size)) {
205     return false;
206   }
207   // Translate aliases to standard enums (e.g., IYUV -> I420).
208   format = CanonicalFourCC(format);
209 
210   // Set up a new buffer.
211   // TODO(fbarchard): Support lazy allocation.
212   int new_width = dw;
213   int new_height = dh;
214   // If rotated swap width, height.
215   if (apply_rotation && (rotation == 90 || rotation == 270)) {
216     new_width = dh;
217     new_height = dw;
218   }
219 
220   InitToEmptyBuffer(new_width, new_height, pixel_width, pixel_height,
221                     time_stamp_ns);
222   rotation_ = apply_rotation ? webrtc::kVideoRotation_0 : rotation;
223 
224   int horiz_crop = ((w - dw) / 2) & ~1;
225   // ARGB on Windows has negative height.
226   // The sample's layout in memory is normal, so just correct crop.
227   int vert_crop = ((abs(h) - dh) / 2) & ~1;
228   // Conversion functions expect negative height to flip the image.
229   int idh = (h < 0) ? -dh : dh;
230   int r = libyuv::ConvertToI420(
231       sample, sample_size,
232       GetYPlane(), GetYPitch(),
233       GetUPlane(), GetUPitch(),
234       GetVPlane(), GetVPitch(),
235       horiz_crop, vert_crop,
236       w, h,
237       dw, idh,
238       static_cast<libyuv::RotationMode>(
239           apply_rotation ? rotation : webrtc::kVideoRotation_0),
240       format);
241   if (r) {
242     LOG(LS_ERROR) << "Error parsing format: " << GetFourccName(format)
243                   << " return code : " << r;
244     return false;
245   }
246   return true;
247 }
248 
CreateEmptyFrame(int w,int h,size_t pixel_width,size_t pixel_height,int64_t time_stamp_ns) const249 VideoFrame* WebRtcVideoFrame::CreateEmptyFrame(
250     int w, int h, size_t pixel_width, size_t pixel_height,
251     int64_t time_stamp_ns) const {
252   WebRtcVideoFrame* frame = new WebRtcVideoFrame();
253   frame->InitToEmptyBuffer(w, h, pixel_width, pixel_height, time_stamp_ns);
254   return frame;
255 }
256 
InitToEmptyBuffer(int w,int h,size_t pixel_width,size_t pixel_height,int64_t time_stamp_ns)257 void WebRtcVideoFrame::InitToEmptyBuffer(int w, int h, size_t pixel_width,
258                                          size_t pixel_height,
259                                          int64_t time_stamp_ns) {
260   video_frame_buffer_ = new rtc::RefCountedObject<webrtc::I420Buffer>(w, h);
261   pixel_width_ = pixel_width;
262   pixel_height_ = pixel_height;
263   time_stamp_ns_ = time_stamp_ns;
264   rotation_ = webrtc::kVideoRotation_0;
265 }
266 
GetCopyWithRotationApplied() const267 const VideoFrame* WebRtcVideoFrame::GetCopyWithRotationApplied() const {
268   // If the frame is not rotated, the caller should reuse this frame instead of
269   // making a redundant copy.
270   if (GetVideoRotation() == webrtc::kVideoRotation_0) {
271     return this;
272   }
273 
274   // If the video frame is backed up by a native handle, it resides in the GPU
275   // memory which we can't rotate here. The assumption is that the renderers
276   // which uses GPU to render should be able to rotate themselves.
277   RTC_DCHECK(!GetNativeHandle());
278 
279   if (rotated_frame_) {
280     return rotated_frame_.get();
281   }
282 
283   int width = static_cast<int>(GetWidth());
284   int height = static_cast<int>(GetHeight());
285 
286   int rotated_width = width;
287   int rotated_height = height;
288   if (GetVideoRotation() == webrtc::kVideoRotation_90 ||
289       GetVideoRotation() == webrtc::kVideoRotation_270) {
290     rotated_width = height;
291     rotated_height = width;
292   }
293 
294   rotated_frame_.reset(CreateEmptyFrame(rotated_width, rotated_height,
295                                         GetPixelWidth(), GetPixelHeight(),
296                                         GetTimeStamp()));
297 
298   // TODO(guoweis): Add a function in webrtc_libyuv.cc to convert from
299   // VideoRotation to libyuv::RotationMode.
300   int ret = libyuv::I420Rotate(
301       GetYPlane(), GetYPitch(), GetUPlane(), GetUPitch(), GetVPlane(),
302       GetVPitch(), rotated_frame_->GetYPlane(), rotated_frame_->GetYPitch(),
303       rotated_frame_->GetUPlane(), rotated_frame_->GetUPitch(),
304       rotated_frame_->GetVPlane(), rotated_frame_->GetVPitch(), width, height,
305       static_cast<libyuv::RotationMode>(GetVideoRotation()));
306   if (ret == 0) {
307     return rotated_frame_.get();
308   }
309   return nullptr;
310 }
311 
312 }  // namespace cricket
313