Home | History | Annotate | Download | only in base
      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/base/videoframe.h"
     29 
     30 #include <string.h>
     31 
     32 #if !defined(DISABLE_YUV)
     33 #include "libyuv/compare.h"
     34 #include "libyuv/planar_functions.h"
     35 #include "libyuv/scale.h"
     36 #endif
     37 
     38 #include "talk/media/base/videocommon.h"
     39 #include "webrtc/base/logging.h"
     40 
     41 namespace cricket {
     42 
     43 // Round to 2 pixels because Chroma channels are half size.
     44 #define ROUNDTO2(v) (v & ~1)
     45 
     46 rtc::StreamResult VideoFrame::Write(rtc::StreamInterface* stream,
     47                                           int* error) {
     48   rtc::StreamResult result = rtc::SR_SUCCESS;
     49   const uint8* src_y = GetYPlane();
     50   const uint8* src_u = GetUPlane();
     51   const uint8* src_v = GetVPlane();
     52   if (!src_y || !src_u || !src_v) {
     53     return result;  // Nothing to write.
     54   }
     55   const int32 y_pitch = GetYPitch();
     56   const int32 u_pitch = GetUPitch();
     57   const int32 v_pitch = GetVPitch();
     58   const size_t width = GetWidth();
     59   const size_t height = GetHeight();
     60   const size_t half_width = (width + 1) >> 1;
     61   const size_t half_height = (height + 1) >> 1;
     62   // Write Y.
     63   for (size_t row = 0; row < height; ++row) {
     64     result = stream->Write(src_y + row * y_pitch, width, NULL, error);
     65     if (result != rtc::SR_SUCCESS) {
     66       return result;
     67     }
     68   }
     69   // Write U.
     70   for (size_t row = 0; row < half_height; ++row) {
     71     result = stream->Write(src_u + row * u_pitch, half_width, NULL, error);
     72     if (result != rtc::SR_SUCCESS) {
     73       return result;
     74     }
     75   }
     76   // Write V.
     77   for (size_t row = 0; row < half_height; ++row) {
     78     result = stream->Write(src_v + row * v_pitch, half_width, NULL, error);
     79     if (result != rtc::SR_SUCCESS) {
     80       return result;
     81     }
     82   }
     83   return result;
     84 }
     85 
     86 bool VideoFrame::CopyToPlanes(
     87     uint8* dst_y, uint8* dst_u, uint8* dst_v,
     88     int32 dst_pitch_y, int32 dst_pitch_u, int32 dst_pitch_v) const {
     89 #if !defined(DISABLE_YUV)
     90   int32 src_width = static_cast<int>(GetWidth());
     91   int32 src_height = static_cast<int>(GetHeight());
     92   return libyuv::I420Copy(GetYPlane(), GetYPitch(),
     93                           GetUPlane(), GetUPitch(),
     94                           GetVPlane(), GetVPitch(),
     95                           dst_y, dst_pitch_y,
     96                           dst_u, dst_pitch_u,
     97                           dst_v, dst_pitch_v,
     98                           src_width, src_height) == 0;
     99 #else
    100   int uv_size = GetUPitch() * GetChromaHeight();
    101   memcpy(dst_y, GetYPlane(), GetWidth() * GetHeight());
    102   memcpy(dst_u, GetUPlane(), uv_size);
    103   memcpy(dst_v, GetVPlane(), uv_size);
    104   return true;
    105 #endif
    106 }
    107 
    108 void VideoFrame::CopyToFrame(VideoFrame* dst) const {
    109   if (!dst) {
    110     LOG(LS_ERROR) << "NULL dst pointer.";
    111     return;
    112   }
    113 
    114   CopyToPlanes(dst->GetYPlane(), dst->GetUPlane(), dst->GetVPlane(),
    115                dst->GetYPitch(), dst->GetUPitch(), dst->GetVPitch());
    116 }
    117 
    118 // TODO(fbarchard): Handle odd width/height with rounding.
    119 void VideoFrame::StretchToPlanes(
    120     uint8* dst_y, uint8* dst_u, uint8* dst_v,
    121     int32 dst_pitch_y, int32 dst_pitch_u, int32 dst_pitch_v,
    122     size_t width, size_t height, bool interpolate, bool vert_crop) const {
    123   if (!GetYPlane() || !GetUPlane() || !GetVPlane()) {
    124     LOG(LS_ERROR) << "NULL plane pointer.";
    125     return;
    126   }
    127 
    128   size_t src_width = GetWidth();
    129   size_t src_height = GetHeight();
    130   if (width == src_width && height == src_height) {
    131     CopyToPlanes(dst_y, dst_u, dst_v, dst_pitch_y, dst_pitch_u, dst_pitch_v);
    132     return;
    133   }
    134   const uint8* src_y = GetYPlane();
    135   const uint8* src_u = GetUPlane();
    136   const uint8* src_v = GetVPlane();
    137 
    138   if (vert_crop) {
    139     // Adjust the input width:height ratio to be the same as the output ratio.
    140     if (src_width * height > src_height * width) {
    141       // Reduce the input width, but keep size/position aligned for YuvScaler
    142       src_width = ROUNDTO2(src_height * width / height);
    143       int32 iwidth_offset = ROUNDTO2((GetWidth() - src_width) / 2);
    144       src_y += iwidth_offset;
    145       src_u += iwidth_offset / 2;
    146       src_v += iwidth_offset / 2;
    147     } else if (src_width * height < src_height * width) {
    148       // Reduce the input height.
    149       src_height = src_width * height / width;
    150       int32 iheight_offset = static_cast<int32>(
    151           (GetHeight() - src_height) >> 2);
    152       iheight_offset <<= 1;  // Ensure that iheight_offset is even.
    153       src_y += iheight_offset * GetYPitch();
    154       src_u += iheight_offset / 2 * GetUPitch();
    155       src_v += iheight_offset / 2 * GetVPitch();
    156     }
    157   }
    158 
    159   // TODO(fbarchard): Implement a simple scale for non-libyuv.
    160 #if !defined(DISABLE_YUV)
    161   // Scale to the output I420 frame.
    162   libyuv::Scale(src_y, src_u, src_v,
    163                 GetYPitch(), GetUPitch(), GetVPitch(),
    164                 static_cast<int>(src_width), static_cast<int>(src_height),
    165                 dst_y, dst_u, dst_v, dst_pitch_y, dst_pitch_u, dst_pitch_v,
    166                 static_cast<int>(width), static_cast<int>(height), interpolate);
    167 #endif
    168 }
    169 
    170 size_t VideoFrame::StretchToBuffer(size_t dst_width, size_t dst_height,
    171                                    uint8* dst_buffer, size_t size,
    172                                    bool interpolate, bool vert_crop) const {
    173   if (!dst_buffer) {
    174     LOG(LS_ERROR) << "NULL dst_buffer pointer.";
    175     return 0;
    176   }
    177 
    178   size_t needed = SizeOf(dst_width, dst_height);
    179   if (needed <= size) {
    180     uint8* dst_y = dst_buffer;
    181     uint8* dst_u = dst_y + dst_width * dst_height;
    182     uint8* dst_v = dst_u + ((dst_width + 1) >> 1) * ((dst_height + 1) >> 1);
    183     StretchToPlanes(dst_y, dst_u, dst_v,
    184                     static_cast<int32>(dst_width),
    185                     static_cast<int32>((dst_width + 1) >> 1),
    186                     static_cast<int32>((dst_width + 1) >> 1),
    187                     dst_width, dst_height, interpolate, vert_crop);
    188   }
    189   return needed;
    190 }
    191 
    192 void VideoFrame::StretchToFrame(VideoFrame* dst,
    193                                 bool interpolate, bool vert_crop) const {
    194   if (!dst) {
    195     LOG(LS_ERROR) << "NULL dst pointer.";
    196     return;
    197   }
    198 
    199   StretchToPlanes(dst->GetYPlane(), dst->GetUPlane(), dst->GetVPlane(),
    200                   dst->GetYPitch(), dst->GetUPitch(), dst->GetVPitch(),
    201                   dst->GetWidth(), dst->GetHeight(),
    202                   interpolate, vert_crop);
    203   dst->SetElapsedTime(GetElapsedTime());
    204   dst->SetTimeStamp(GetTimeStamp());
    205 }
    206 
    207 VideoFrame* VideoFrame::Stretch(size_t dst_width, size_t dst_height,
    208                                 bool interpolate, bool vert_crop) const {
    209   VideoFrame* dest = CreateEmptyFrame(static_cast<int>(dst_width),
    210                                       static_cast<int>(dst_height),
    211                                       GetPixelWidth(), GetPixelHeight(),
    212                                       GetElapsedTime(), GetTimeStamp());
    213   if (dest) {
    214     StretchToFrame(dest, interpolate, vert_crop);
    215   }
    216   return dest;
    217 }
    218 
    219 bool VideoFrame::SetToBlack() {
    220 #if !defined(DISABLE_YUV)
    221   return libyuv::I420Rect(GetYPlane(), GetYPitch(),
    222                           GetUPlane(), GetUPitch(),
    223                           GetVPlane(), GetVPitch(),
    224                           0, 0,
    225                           static_cast<int>(GetWidth()),
    226                           static_cast<int>(GetHeight()),
    227                           16, 128, 128) == 0;
    228 #else
    229   int uv_size = GetUPitch() * GetChromaHeight();
    230   memset(GetYPlane(), 16, GetWidth() * GetHeight());
    231   memset(GetUPlane(), 128, uv_size);
    232   memset(GetVPlane(), 128, uv_size);
    233   return true;
    234 #endif
    235 }
    236 
    237 static const size_t kMaxSampleSize = 1000000000u;
    238 // Returns whether a sample is valid.
    239 bool VideoFrame::Validate(uint32 fourcc, int w, int h,
    240                           const uint8 *sample, size_t sample_size) {
    241   if (h < 0) {
    242     h = -h;
    243   }
    244   // 16384 is maximum resolution for VP8 codec.
    245   if (w < 1 || w > 16384 || h < 1 || h > 16384) {
    246     LOG(LS_ERROR) << "Invalid dimensions: " << w << "x" << h;
    247     return false;
    248   }
    249   uint32 format = CanonicalFourCC(fourcc);
    250   int expected_bpp = 8;
    251   switch (format) {
    252     case FOURCC_I400:
    253     case FOURCC_RGGB:
    254     case FOURCC_BGGR:
    255     case FOURCC_GRBG:
    256     case FOURCC_GBRG:
    257       expected_bpp = 8;
    258       break;
    259     case FOURCC_I420:
    260     case FOURCC_I411:
    261     case FOURCC_YU12:
    262     case FOURCC_YV12:
    263     case FOURCC_M420:
    264     case FOURCC_Q420:
    265     case FOURCC_NV21:
    266     case FOURCC_NV12:
    267       expected_bpp = 12;
    268       break;
    269     case FOURCC_I422:
    270     case FOURCC_YV16:
    271     case FOURCC_YUY2:
    272     case FOURCC_UYVY:
    273     case FOURCC_RGBP:
    274     case FOURCC_RGBO:
    275     case FOURCC_R444:
    276       expected_bpp = 16;
    277       break;
    278     case FOURCC_I444:
    279     case FOURCC_YV24:
    280     case FOURCC_24BG:
    281     case FOURCC_RAW:
    282       expected_bpp = 24;
    283       break;
    284 
    285     case FOURCC_ABGR:
    286     case FOURCC_BGRA:
    287     case FOURCC_ARGB:
    288       expected_bpp = 32;
    289       break;
    290 
    291     case FOURCC_MJPG:
    292     case FOURCC_H264:
    293       expected_bpp = 0;
    294       break;
    295     default:
    296       expected_bpp = 8;  // Expect format is at least 8 bits per pixel.
    297       break;
    298   }
    299   size_t expected_size = (w * expected_bpp + 7) / 8 * h;
    300   // For compressed formats, expect 4 bits per 16 x 16 macro.  I420 would be
    301   // 6 bits, but grey can be 4 bits.
    302   if (expected_bpp == 0) {
    303     expected_size = ((w + 15) / 16) * ((h + 15) / 16) * 4 / 8;
    304   }
    305   if (sample == NULL) {
    306     LOG(LS_ERROR) << "NULL sample pointer."
    307                   << " format: " << GetFourccName(format)
    308                   << " bpp: " << expected_bpp
    309                   << " size: " << w << "x" << h
    310                   << " expected: " << expected_size
    311                   << " " << sample_size;
    312     return false;
    313   }
    314   // TODO(fbarchard): Make function to dump information about frames.
    315   uint8 four_samples[4] = { 0, 0, 0, 0 };
    316   for (size_t i = 0; i < ARRAY_SIZE(four_samples) && i < sample_size; ++i) {
    317     four_samples[i] = sample[i];
    318   }
    319   if (sample_size < expected_size) {
    320     LOG(LS_ERROR) << "Size field is too small."
    321                   << " format: " << GetFourccName(format)
    322                   << " bpp: " << expected_bpp
    323                   << " size: " << w << "x" << h
    324                   << " " << sample_size
    325                   << " expected: " << expected_size
    326                   << " sample[0..3]: " << static_cast<int>(four_samples[0])
    327                   << ", " << static_cast<int>(four_samples[1])
    328                   << ", " << static_cast<int>(four_samples[2])
    329                   << ", " << static_cast<int>(four_samples[3]);
    330     return false;
    331   }
    332   if (sample_size > kMaxSampleSize) {
    333     LOG(LS_WARNING) << "Size field is invalid."
    334                     << " format: " << GetFourccName(format)
    335                     << " bpp: " << expected_bpp
    336                     << " size: " << w << "x" << h
    337                     << " " << sample_size
    338                     << " expected: " << 2 * expected_size
    339                     << " sample[0..3]: " << static_cast<int>(four_samples[0])
    340                     << ", " << static_cast<int>(four_samples[1])
    341                     << ", " << static_cast<int>(four_samples[2])
    342                     << ", " << static_cast<int>(four_samples[3]);
    343     return false;
    344   }
    345   // Show large size warning once every 100 frames.
    346   // TODO(fbarchard): Make frame counter atomic for thread safety.
    347   static int large_warn100 = 0;
    348   size_t large_expected_size = expected_size * 2;
    349   if (expected_bpp >= 8 &&
    350       (sample_size > large_expected_size || sample_size > kMaxSampleSize) &&
    351       large_warn100 % 100 == 0) {
    352     ++large_warn100;
    353     LOG(LS_WARNING) << "Size field is too large."
    354                     << " format: " << GetFourccName(format)
    355                     << " bpp: " << expected_bpp
    356                     << " size: " << w << "x" << h
    357                     << " bytes: " << sample_size
    358                     << " expected: " << large_expected_size
    359                     << " sample[0..3]: " << static_cast<int>(four_samples[0])
    360                     << ", " << static_cast<int>(four_samples[1])
    361                     << ", " << static_cast<int>(four_samples[2])
    362                     << ", " << static_cast<int>(four_samples[3]);
    363   }
    364 
    365   // TODO(fbarchard): Add duplicate pixel check.
    366   // TODO(fbarchard): Use frame counter atomic for thread safety.
    367   static bool valid_once = true;
    368   if (valid_once) {
    369     valid_once = false;
    370     LOG(LS_INFO) << "Validate frame passed."
    371                  << " format: " << GetFourccName(format)
    372                  << " bpp: " << expected_bpp
    373                  << " size: " << w << "x" << h
    374                  << " bytes: " << sample_size
    375                  << " expected: " << expected_size
    376                  << " sample[0..3]: " << static_cast<int>(four_samples[0])
    377                  << ", " << static_cast<int>(four_samples[1])
    378                  << ", " << static_cast<int>(four_samples[2])
    379                  << ", " << static_cast<int>(four_samples[3]);
    380   }
    381   return true;
    382 }
    383 
    384 }  // namespace cricket
    385