Home | History | Annotate | Download | only in webrtc
      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