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 42 WebRtcVideoFrame::WebRtcVideoFrame(): 43 pixel_width_(0), 44 pixel_height_(0), 45 time_stamp_ns_(0), 46 rotation_(webrtc::kVideoRotation_0) {} 47 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 59 WebRtcVideoFrame::~WebRtcVideoFrame() {} 60 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 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 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 91 size_t WebRtcVideoFrame::GetWidth() const { 92 return video_frame_buffer_ ? video_frame_buffer_->width() : 0; 93 } 94 95 size_t WebRtcVideoFrame::GetHeight() const { 96 return video_frame_buffer_ ? video_frame_buffer_->height() : 0; 97 } 98 99 const uint8_t* WebRtcVideoFrame::GetYPlane() const { 100 return video_frame_buffer_ ? video_frame_buffer_->data(kYPlane) : nullptr; 101 } 102 103 const uint8_t* WebRtcVideoFrame::GetUPlane() const { 104 return video_frame_buffer_ ? video_frame_buffer_->data(kUPlane) : nullptr; 105 } 106 107 const uint8_t* WebRtcVideoFrame::GetVPlane() const { 108 return video_frame_buffer_ ? video_frame_buffer_->data(kVPlane) : nullptr; 109 } 110 111 uint8_t* WebRtcVideoFrame::GetYPlane() { 112 return video_frame_buffer_ ? video_frame_buffer_->MutableData(kYPlane) 113 : nullptr; 114 } 115 116 uint8_t* WebRtcVideoFrame::GetUPlane() { 117 return video_frame_buffer_ ? video_frame_buffer_->MutableData(kUPlane) 118 : nullptr; 119 } 120 121 uint8_t* WebRtcVideoFrame::GetVPlane() { 122 return video_frame_buffer_ ? video_frame_buffer_->MutableData(kVPlane) 123 : nullptr; 124 } 125 126 int32_t WebRtcVideoFrame::GetYPitch() const { 127 return video_frame_buffer_ ? video_frame_buffer_->stride(kYPlane) : 0; 128 } 129 130 int32_t WebRtcVideoFrame::GetUPitch() const { 131 return video_frame_buffer_ ? video_frame_buffer_->stride(kUPlane) : 0; 132 } 133 134 int32_t WebRtcVideoFrame::GetVPitch() const { 135 return video_frame_buffer_ ? video_frame_buffer_->stride(kVPlane) : 0; 136 } 137 138 bool WebRtcVideoFrame::IsExclusive() const { 139 return video_frame_buffer_->HasOneRef(); 140 } 141 142 void* WebRtcVideoFrame::GetNativeHandle() const { 143 return video_frame_buffer_ ? video_frame_buffer_->native_handle() : nullptr; 144 } 145 146 rtc::scoped_refptr<webrtc::VideoFrameBuffer> 147 WebRtcVideoFrame::GetVideoFrameBuffer() const { 148 return video_frame_buffer_; 149 } 150 151 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 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 183 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 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 249 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 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 267 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