| /* |
| * libjingle |
| * Copyright 2011 Google Inc. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "talk/session/phone/webrtcvideoframe.h" |
| |
| #include "libyuv/planar_functions.h" |
| #include "talk/base/logging.h" |
| #include "talk/session/phone/videocapturer.h" |
| #include "talk/session/phone/videocommon.h" |
| |
| namespace cricket { |
| |
| static const int kWatermarkWidth = 8; |
| static const int kWatermarkHeight = 8; |
| static const int kWatermarkOffsetFromLeft = 8; |
| static const int kWatermarkOffsetFromBottom = 8; |
| static const unsigned char kWatermarkMaxYValue = 64; |
| |
| WebRtcVideoFrame::WebRtcVideoFrame() { |
| } |
| |
| WebRtcVideoFrame::~WebRtcVideoFrame() { |
| } |
| |
| bool WebRtcVideoFrame::Init(uint32 format, int w, int h, int dw, int dh, |
| uint8* sample, size_t sample_size, |
| size_t pixel_width, size_t pixel_height, |
| int64 elapsed_time, int64 time_stamp, |
| int rotation) { |
| // WebRtcVideoFrame currently doesn't support color conversion or rotation. |
| // TODO: Add horizontal cropping support. |
| if (format != FOURCC_I420 || dw != w || dh < 0 || dh > abs(h) || |
| rotation != 0) { |
| return false; |
| } |
| |
| size_t desired_size = SizeOf(dw, dh); |
| uint8* buffer = new uint8[desired_size]; |
| Attach(buffer, desired_size, dw, dh, pixel_width, pixel_height, |
| elapsed_time, time_stamp, rotation); |
| if (dh == h) { |
| // Uncropped |
| memcpy(buffer, sample, desired_size); |
| } else { |
| // Cropped |
| // TODO: use I420Copy which supports horizontal crop and vertical |
| // flip. |
| int horiz_crop = ((w - dw) / 2) & ~1; |
| int vert_crop = ((abs(h) - dh) / 2) & ~1; |
| int y_crop_offset = w * vert_crop + horiz_crop; |
| int halfwidth = (w + 1) / 2; |
| int halfheight = (h + 1) / 2; |
| int uv_size = GetChromaSize(); |
| int uv_crop_offset = (halfwidth * vert_crop + horiz_crop) / 2; |
| uint8* src_y = sample + y_crop_offset; |
| uint8* src_u = sample + w * h + uv_crop_offset; |
| uint8* src_v = sample + w * h + halfwidth * halfheight + uv_crop_offset; |
| memcpy(GetYPlane(), src_y, dw * dh); |
| memcpy(GetUPlane(), src_u, uv_size); |
| memcpy(GetVPlane(), src_v, uv_size); |
| } |
| return true; |
| } |
| |
| bool WebRtcVideoFrame::Init(const CapturedFrame* frame, int dw, int dh) { |
| return Init(frame->fourcc, frame->width, frame->height, dw, dh, |
| static_cast<uint8*>(frame->data), frame->data_size, |
| frame->pixel_width, frame->pixel_height, |
| frame->elapsed_time, frame->time_stamp, frame->rotation); |
| } |
| |
| bool WebRtcVideoFrame::InitToBlack(int w, int h, |
| size_t pixel_width, size_t pixel_height, |
| int64 elapsed_time, int64 time_stamp) { |
| CreateBuffer(w, h, pixel_width, pixel_height, elapsed_time, time_stamp); |
| return SetToBlack(); |
| } |
| |
| void WebRtcVideoFrame::Attach(uint8* buffer, size_t buffer_size, int w, int h, |
| size_t pixel_width, size_t pixel_height, |
| int64 elapsed_time, int64 time_stamp, |
| int rotation) { |
| video_frame_.Free(); |
| WebRtc_UWord8* new_memory = buffer; |
| WebRtc_UWord32 new_length = buffer_size; |
| WebRtc_UWord32 new_size = buffer_size; |
| video_frame_.Swap(new_memory, new_length, new_size); |
| video_frame_.SetWidth(w); |
| video_frame_.SetHeight(h); |
| pixel_width_ = pixel_width; |
| pixel_height_ = pixel_height; |
| elapsed_time_ = elapsed_time; |
| time_stamp_ = time_stamp; |
| rotation_ = rotation; |
| } |
| |
| void WebRtcVideoFrame::Detach(uint8** buffer, size_t* buffer_size) { |
| WebRtc_UWord8* new_memory = NULL; |
| WebRtc_UWord32 new_length = 0; |
| WebRtc_UWord32 new_size = 0; |
| video_frame_.Swap(new_memory, new_length, new_size); |
| *buffer = new_memory; |
| *buffer_size = new_size; |
| } |
| |
| size_t WebRtcVideoFrame::GetWidth() const { |
| return video_frame_.Width(); |
| } |
| |
| size_t WebRtcVideoFrame::GetHeight() const { |
| return video_frame_.Height(); |
| } |
| |
| const uint8* WebRtcVideoFrame::GetYPlane() const { |
| WebRtc_UWord8* buffer = video_frame_.Buffer(); |
| return buffer; |
| } |
| |
| const uint8* WebRtcVideoFrame::GetUPlane() const { |
| WebRtc_UWord8* buffer = video_frame_.Buffer(); |
| if (buffer) { |
| buffer += (video_frame_.Width() * video_frame_.Height()); |
| } |
| return buffer; |
| } |
| |
| const uint8* WebRtcVideoFrame::GetVPlane() const { |
| WebRtc_UWord8* buffer = video_frame_.Buffer(); |
| if (buffer) { |
| int uv_size = GetChromaSize(); |
| buffer += video_frame_.Width() * video_frame_.Height() + uv_size; |
| } |
| return buffer; |
| } |
| |
| uint8* WebRtcVideoFrame::GetYPlane() { |
| WebRtc_UWord8* buffer = video_frame_.Buffer(); |
| return buffer; |
| } |
| |
| uint8* WebRtcVideoFrame::GetUPlane() { |
| WebRtc_UWord8* buffer = video_frame_.Buffer(); |
| if (buffer) { |
| buffer += (video_frame_.Width() * video_frame_.Height()); |
| } |
| return buffer; |
| } |
| |
| uint8* WebRtcVideoFrame::GetVPlane() { |
| WebRtc_UWord8* buffer = video_frame_.Buffer(); |
| if (buffer) { |
| int uv_size = GetChromaSize(); |
| buffer += video_frame_.Width() * video_frame_.Height() + uv_size; |
| } |
| return buffer; |
| } |
| |
| VideoFrame* WebRtcVideoFrame::Copy() const { |
| WebRtc_UWord8* buffer = video_frame_.Buffer(); |
| if (!buffer) |
| return NULL; |
| |
| size_t new_buffer_size = video_frame_.Length(); |
| uint8* new_buffer = new uint8[new_buffer_size]; |
| memcpy(new_buffer, buffer, new_buffer_size); |
| WebRtcVideoFrame* copy = new WebRtcVideoFrame(); |
| copy->Attach(new_buffer, new_buffer_size, |
| video_frame_.Width(), video_frame_.Height(), |
| pixel_width_, pixel_height_, |
| elapsed_time_, time_stamp_, rotation_); |
| return copy; |
| } |
| |
| bool WebRtcVideoFrame::MakeExclusive() { |
| // WebRtcVideoFrame::Copy makes a deep copy of the frame buffer. No action |
| // is needed for MakeExclusive. |
| return true; |
| } |
| |
| size_t WebRtcVideoFrame::CopyToBuffer(uint8* buffer, size_t size) const { |
| if (!video_frame_.Buffer()) { |
| return 0; |
| } |
| |
| size_t needed = video_frame_.Length(); |
| if (needed <= size) { |
| memcpy(buffer, video_frame_.Buffer(), needed); |
| } |
| return needed; |
| } |
| |
| // TODO: Refactor into base class and share with lmi |
| size_t WebRtcVideoFrame::ConvertToRgbBuffer(uint32 to_fourcc, |
| uint8* buffer, |
| size_t size, |
| size_t stride_rgb) const { |
| if (!video_frame_.Buffer()) { |
| return 0; |
| } |
| |
| size_t width = video_frame_.Width(); |
| size_t height = video_frame_.Height(); |
| // See http://www.virtualdub.org/blog/pivot/entry.php?id=190 for a good |
| // explanation of pitch and why this is the amount of space we need. |
| // TODO: increase to stride * height to allow padding to be used |
| // to overwrite for efficiency. |
| size_t needed = stride_rgb * (height - 1) + 4 * width; |
| |
| if (needed > size) { |
| LOG(LS_WARNING) << "RGB buffer is not large enough"; |
| return needed; |
| } |
| |
| // TODO: Use libyuv::ConvertFromI420 |
| switch (to_fourcc) { |
| case FOURCC_ARGB: |
| libyuv::I420ToARGB( |
| GetYPlane(), GetYPitch(), |
| GetUPlane(), GetUPitch(), |
| GetVPlane(), GetVPitch(), |
| buffer, stride_rgb, width, height); |
| break; |
| |
| case FOURCC_BGRA: |
| libyuv::I420ToBGRA( |
| GetYPlane(), GetYPitch(), |
| GetUPlane(), GetUPitch(), |
| GetVPlane(), GetVPitch(), |
| buffer, stride_rgb, width, height); |
| break; |
| |
| case FOURCC_ABGR: |
| libyuv::I420ToABGR( |
| GetYPlane(), GetYPitch(), |
| GetUPlane(), GetUPitch(), |
| GetVPlane(), GetVPitch(), |
| buffer, stride_rgb, width, height); |
| break; |
| |
| default: |
| needed = 0; |
| LOG(LS_WARNING) << "RGB type not supported: " << to_fourcc; |
| break; |
| } |
| return needed; |
| } |
| |
| VideoFrame* WebRtcVideoFrame::Stretch(size_t w, size_t h, |
| bool interpolate, bool vert_crop) const { |
| WebRtcVideoFrame* frame = new WebRtcVideoFrame(); |
| frame->CreateBuffer(w, h, 1, 1, 0, 0); |
| StretchToFrame(frame, interpolate, vert_crop); |
| |
| return frame; |
| } |
| |
| void WebRtcVideoFrame::CreateBuffer(int w, int h, |
| size_t pixel_width, size_t pixel_height, |
| int64 elapsed_time, int64 time_stamp) { |
| size_t buffer_size = VideoFrame::SizeOf(w, h); |
| uint8* buffer = new uint8[buffer_size]; |
| Attach(buffer, buffer_size, w, h, pixel_width, pixel_height, |
| elapsed_time, time_stamp, 0); |
| } |
| |
| // Add a square watermark near the left-low corner. clamp Y. |
| // Returns false on error. |
| bool WebRtcVideoFrame::AddWatermark() { |
| size_t w = GetWidth(); |
| size_t h = GetHeight(); |
| |
| if (w < kWatermarkWidth + kWatermarkOffsetFromLeft || |
| h < kWatermarkHeight + kWatermarkOffsetFromBottom) { |
| return false; |
| } |
| |
| uint8* buffer = GetYPlane(); |
| for (size_t x = kWatermarkOffsetFromLeft; |
| x < kWatermarkOffsetFromLeft + kWatermarkWidth; ++x) { |
| for (size_t y = h - kWatermarkOffsetFromBottom - kWatermarkHeight; |
| y < h - kWatermarkOffsetFromBottom; ++y) { |
| buffer[y * w + x] = talk_base::_min(buffer[y * w + x], |
| kWatermarkMaxYValue); |
| } |
| } |
| return true; |
| } |
| |
| } // namespace cricket |