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 "libyuv/convert_from.h"
     32 #include "libyuv/planar_functions.h"
     33 #include "talk/base/logging.h"
     34 #include "talk/media/base/videocapturer.h"
     35 #include "talk/media/base/videocommon.h"
     36 
     37 namespace cricket {
     38 
     39 static const int kWatermarkWidth = 8;
     40 static const int kWatermarkHeight = 8;
     41 static const int kWatermarkOffsetFromLeft = 8;
     42 static const int kWatermarkOffsetFromBottom = 8;
     43 static const unsigned char kWatermarkMaxYValue = 64;
     44 
     45 FrameBuffer::FrameBuffer() : length_(0) {}
     46 
     47 FrameBuffer::FrameBuffer(size_t length) : length_(0) {
     48   char* buffer = new char[length];
     49   SetData(buffer, length);
     50 }
     51 
     52 FrameBuffer::~FrameBuffer() {
     53   // Make sure that the video_frame_ doesn't delete the buffer as it may be
     54   // shared between multiple WebRtcVideoFrame.
     55   uint8_t* new_memory = NULL;
     56   uint32_t new_length = 0;
     57   uint32_t new_size = 0;
     58   video_frame_.Swap(new_memory, new_length, new_size);
     59 }
     60 
     61 void FrameBuffer::SetData(char* data, size_t length) {
     62   data_.reset(data);
     63   length_ = length;
     64   uint8_t* new_memory = reinterpret_cast<uint8_t*>(data);
     65   uint32_t new_length = static_cast<int>(length);
     66   uint32_t new_size = static_cast<int>(length);
     67   video_frame_.Swap(new_memory, new_length, new_size);
     68 }
     69 
     70 void FrameBuffer::ReturnData(char** data, size_t* length) {
     71   uint8_t* old_memory = NULL;
     72   uint32_t old_length = 0;
     73   uint32_t old_size = 0;
     74   video_frame_.Swap(old_memory, old_length, old_size);
     75   data_.release();
     76   length_ = 0;
     77   *length = old_length;
     78   *data = reinterpret_cast<char*>(old_memory);
     79 }
     80 
     81 char* FrameBuffer::data() { return data_.get(); }
     82 
     83 size_t FrameBuffer::length() const { return length_; }
     84 
     85 webrtc::VideoFrame* FrameBuffer::frame() { return &video_frame_; }
     86 
     87 const webrtc::VideoFrame* FrameBuffer::frame() const { return &video_frame_; }
     88 
     89 WebRtcVideoFrame::WebRtcVideoFrame()
     90     : video_buffer_(new RefCountedBuffer()), is_black_(false) {}
     91 
     92 WebRtcVideoFrame::~WebRtcVideoFrame() {}
     93 
     94 bool WebRtcVideoFrame::Init(
     95     uint32 format, int w, int h, int dw, int dh, uint8* sample,
     96     size_t sample_size, size_t pixel_width, size_t pixel_height,
     97     int64 elapsed_time, int64 time_stamp, int rotation) {
     98   return Reset(format, w, h, dw, dh, sample, sample_size, pixel_width,
     99                pixel_height, elapsed_time, time_stamp, rotation);
    100 }
    101 
    102 bool WebRtcVideoFrame::Init(const CapturedFrame* frame, int dw, int dh) {
    103   return Reset(frame->fourcc, frame->width, frame->height, dw, dh,
    104                static_cast<uint8*>(frame->data), frame->data_size,
    105                frame->pixel_width, frame->pixel_height, frame->elapsed_time,
    106                frame->time_stamp, frame->rotation);
    107 }
    108 
    109 bool WebRtcVideoFrame::InitToBlack(int w, int h, size_t pixel_width,
    110                                    size_t pixel_height, int64 elapsed_time,
    111                                    int64 time_stamp) {
    112   InitToEmptyBuffer(w, h, pixel_width, pixel_height, elapsed_time, time_stamp);
    113   if (!is_black_) {
    114     return SetToBlack();
    115   }
    116   return true;
    117 }
    118 
    119 void WebRtcVideoFrame::Attach(
    120     uint8* buffer, size_t buffer_size, int w, int h, size_t pixel_width,
    121     size_t pixel_height, int64 elapsed_time, int64 time_stamp, int rotation) {
    122   talk_base::scoped_refptr<RefCountedBuffer> video_buffer(
    123       new RefCountedBuffer());
    124   video_buffer->SetData(reinterpret_cast<char*>(buffer), buffer_size);
    125   Attach(video_buffer.get(), buffer_size, w, h, pixel_width, pixel_height,
    126          elapsed_time, time_stamp, rotation);
    127 }
    128 
    129 void WebRtcVideoFrame::Detach(uint8** data, size_t* length) {
    130   video_buffer_->ReturnData(reinterpret_cast<char**>(data), length);
    131 }
    132 
    133 size_t WebRtcVideoFrame::GetWidth() const { return frame()->Width(); }
    134 
    135 size_t WebRtcVideoFrame::GetHeight() const { return frame()->Height(); }
    136 
    137 const uint8* WebRtcVideoFrame::GetYPlane() const {
    138   uint8_t* buffer = frame()->Buffer();
    139   return buffer;
    140 }
    141 
    142 const uint8* WebRtcVideoFrame::GetUPlane() const {
    143   uint8_t* buffer = frame()->Buffer();
    144   if (buffer) {
    145     buffer += (frame()->Width() * frame()->Height());
    146   }
    147   return buffer;
    148 }
    149 
    150 const uint8* WebRtcVideoFrame::GetVPlane() const {
    151   uint8_t* buffer = frame()->Buffer();
    152   if (buffer) {
    153     int uv_size = static_cast<int>(GetChromaSize());
    154     buffer += frame()->Width() * frame()->Height() + uv_size;
    155   }
    156   return buffer;
    157 }
    158 
    159 uint8* WebRtcVideoFrame::GetYPlane() {
    160   uint8_t* buffer = frame()->Buffer();
    161   return buffer;
    162 }
    163 
    164 uint8* WebRtcVideoFrame::GetUPlane() {
    165   uint8_t* buffer = frame()->Buffer();
    166   if (buffer) {
    167     buffer += (frame()->Width() * frame()->Height());
    168   }
    169   return buffer;
    170 }
    171 
    172 uint8* WebRtcVideoFrame::GetVPlane() {
    173   uint8_t* buffer = frame()->Buffer();
    174   if (buffer) {
    175     int uv_size = static_cast<int>(GetChromaSize());
    176     buffer += frame()->Width() * frame()->Height() + uv_size;
    177   }
    178   return buffer;
    179 }
    180 
    181 VideoFrame* WebRtcVideoFrame::Copy() const {
    182   const char* old_buffer = video_buffer_->data();
    183   if (!old_buffer)
    184     return NULL;
    185   size_t new_buffer_size = video_buffer_->length();
    186 
    187   WebRtcVideoFrame* ret_val = new WebRtcVideoFrame();
    188   ret_val->Attach(video_buffer_.get(), new_buffer_size, frame()->Width(),
    189                   frame()->Height(), pixel_width_, pixel_height_, elapsed_time_,
    190                   time_stamp_, rotation_);
    191   return ret_val;
    192 }
    193 
    194 bool WebRtcVideoFrame::MakeExclusive() {
    195   const int length = static_cast<int>(video_buffer_->length());
    196   RefCountedBuffer* exclusive_buffer = new RefCountedBuffer(length);
    197   memcpy(exclusive_buffer->data(), video_buffer_->data(), length);
    198   Attach(exclusive_buffer, length, frame()->Width(), frame()->Height(),
    199          pixel_width_, pixel_height_, elapsed_time_, time_stamp_, rotation_);
    200   return true;
    201 }
    202 
    203 size_t WebRtcVideoFrame::CopyToBuffer(uint8* buffer, size_t size) const {
    204   if (!frame()->Buffer()) {
    205     return 0;
    206   }
    207 
    208   size_t needed = frame()->Length();
    209   if (needed <= size) {
    210     memcpy(buffer, frame()->Buffer(), needed);
    211   }
    212   return needed;
    213 }
    214 
    215 // TODO(fbarchard): Refactor into base class and share with lmi
    216 size_t WebRtcVideoFrame::ConvertToRgbBuffer(uint32 to_fourcc, uint8* buffer,
    217                                             size_t size, int stride_rgb) const {
    218   if (!frame()->Buffer()) {
    219     return 0;
    220   }
    221   size_t width = frame()->Width();
    222   size_t height = frame()->Height();
    223   size_t needed = (stride_rgb >= 0 ? stride_rgb : -stride_rgb) * height;
    224   if (size < needed) {
    225     LOG(LS_WARNING) << "RGB buffer is not large enough";
    226     return needed;
    227   }
    228 
    229   if (libyuv::ConvertFromI420(GetYPlane(), GetYPitch(), GetUPlane(),
    230                               GetUPitch(), GetVPlane(), GetVPitch(), buffer,
    231                               stride_rgb,
    232                               static_cast<int>(width),
    233                               static_cast<int>(height),
    234                               to_fourcc)) {
    235     LOG(LS_WARNING) << "RGB type not supported: " << to_fourcc;
    236     return 0;  // 0 indicates error
    237   }
    238   return needed;
    239 }
    240 
    241 void WebRtcVideoFrame::Attach(
    242     RefCountedBuffer* video_buffer, size_t buffer_size, int w, int h,
    243     size_t pixel_width, size_t pixel_height, int64 elapsed_time,
    244     int64 time_stamp, int rotation) {
    245   if (video_buffer_.get() == video_buffer) {
    246     return;
    247   }
    248   is_black_ = false;
    249   video_buffer_ = video_buffer;
    250   frame()->SetWidth(w);
    251   frame()->SetHeight(h);
    252   pixel_width_ = pixel_width;
    253   pixel_height_ = pixel_height;
    254   elapsed_time_ = elapsed_time;
    255   time_stamp_ = time_stamp;
    256   rotation_ = rotation;
    257 }
    258 
    259 // Add a square watermark near the left-low corner. clamp Y.
    260 // Returns false on error.
    261 bool WebRtcVideoFrame::AddWatermark() {
    262   size_t w = GetWidth();
    263   size_t h = GetHeight();
    264 
    265   if (w < kWatermarkWidth + kWatermarkOffsetFromLeft ||
    266       h < kWatermarkHeight + kWatermarkOffsetFromBottom) {
    267     return false;
    268   }
    269 
    270   uint8* buffer = GetYPlane();
    271   for (size_t x = kWatermarkOffsetFromLeft;
    272        x < kWatermarkOffsetFromLeft + kWatermarkWidth; ++x) {
    273     for (size_t y = h - kWatermarkOffsetFromBottom - kWatermarkHeight;
    274          y < h - kWatermarkOffsetFromBottom; ++y) {
    275       buffer[y * w + x] =
    276           talk_base::_min(buffer[y * w + x], kWatermarkMaxYValue);
    277     }
    278   }
    279   return true;
    280 }
    281 
    282 bool WebRtcVideoFrame::Reset(
    283     uint32 format, int w, int h, int dw, int dh, uint8* sample,
    284     size_t sample_size, size_t pixel_width, size_t pixel_height,
    285     int64 elapsed_time, int64 time_stamp, int rotation) {
    286   if (!Validate(format, w, h, sample, sample_size)) {
    287     return false;
    288   }
    289   // Translate aliases to standard enums (e.g., IYUV -> I420).
    290   format = CanonicalFourCC(format);
    291 
    292   // Round display width and height down to multiple of 4, to avoid webrtc
    293   // size calculation error on odd sizes.
    294   // TODO(Ronghua): Remove this once the webrtc allocator is fixed.
    295   dw = (dw > 4) ? (dw & ~3) : dw;
    296   dh = (dh > 4) ? (dh & ~3) : dh;
    297 
    298   // Set up a new buffer.
    299   // TODO(fbarchard): Support lazy allocation.
    300   int new_width = dw;
    301   int new_height = dh;
    302   if (rotation == 90 || rotation == 270) {  // If rotated swap width, height.
    303     new_width = dh;
    304     new_height = dw;
    305   }
    306 
    307   size_t desired_size = SizeOf(new_width, new_height);
    308   talk_base::scoped_refptr<RefCountedBuffer> video_buffer(
    309       new RefCountedBuffer(desired_size));
    310   // Since the libyuv::ConvertToI420 will handle the rotation, so the
    311   // new frame's rotation should always be 0.
    312   Attach(video_buffer.get(), desired_size, new_width, new_height, pixel_width,
    313          pixel_height, elapsed_time, time_stamp, 0);
    314 
    315   int horiz_crop = ((w - dw) / 2) & ~1;
    316   // ARGB on Windows has negative height.
    317   // The sample's layout in memory is normal, so just correct crop.
    318   int vert_crop = ((abs(h) - dh) / 2) & ~1;
    319   // Conversion functions expect negative height to flip the image.
    320   int idh = (h < 0) ? -dh : dh;
    321   uint8* y = GetYPlane();
    322   int y_stride = GetYPitch();
    323   uint8* u = GetUPlane();
    324   int u_stride = GetUPitch();
    325   uint8* v = GetVPlane();
    326   int v_stride = GetVPitch();
    327   int r = libyuv::ConvertToI420(
    328       sample, sample_size, y, y_stride, u, u_stride, v, v_stride, horiz_crop,
    329       vert_crop, w, h, dw, idh, static_cast<libyuv::RotationMode>(rotation),
    330       format);
    331   if (r) {
    332     LOG(LS_ERROR) << "Error parsing format: " << GetFourccName(format)
    333                   << " return code : " << r;
    334     return false;
    335   }
    336   return true;
    337 }
    338 
    339 VideoFrame* WebRtcVideoFrame::CreateEmptyFrame(
    340     int w, int h, size_t pixel_width, size_t pixel_height, int64 elapsed_time,
    341     int64 time_stamp) const {
    342   WebRtcVideoFrame* frame = new WebRtcVideoFrame();
    343   frame->InitToEmptyBuffer(w, h, pixel_width, pixel_height, elapsed_time,
    344                            time_stamp);
    345   return frame;
    346 }
    347 
    348 void WebRtcVideoFrame::InitToEmptyBuffer(int w, int h, size_t pixel_width,
    349                                          size_t pixel_height,
    350                                          int64 elapsed_time, int64 time_stamp) {
    351   size_t buffer_size = VideoFrame::SizeOf(w, h);
    352   talk_base::scoped_refptr<RefCountedBuffer> video_buffer(
    353       new RefCountedBuffer(buffer_size));
    354   Attach(video_buffer.get(), buffer_size, w, h, pixel_width, pixel_height,
    355          elapsed_time, time_stamp, 0);
    356 }
    357 
    358 }  // namespace cricket
    359