mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-14 14:20:45 +01:00

See design doc at https://docs.google.com/a/chromium.org/document/d/1I6nmE9D_BmCY-IoV6MDPY2V6WYpEI-dg2apWXTfZyUI/edit?usp=sharing for more information. This CL was reviewed and approved in pieces in the following CLs: https://webrtc-codereview.appspot.com/24209004/ https://webrtc-codereview.appspot.com/24229004/ https://webrtc-codereview.appspot.com/24259004/ https://webrtc-codereview.appspot.com/25109004/ https://webrtc-codereview.appspot.com/26099004/ https://webrtc-codereview.appspot.com/27069004/ https://webrtc-codereview.appspot.com/27969004/ https://webrtc-codereview.appspot.com/27989004/ https://webrtc-codereview.appspot.com/29009004/ https://webrtc-codereview.appspot.com/30929004/ https://webrtc-codereview.appspot.com/30939004/ https://webrtc-codereview.appspot.com/31999004/ Committing as TBR to the original reviewers. BUG=chromium:81439 TEST=none TBR=pthatcher,henrik.lundin,tina.legrand,stefan,tkchin,glaznev,kjellander,perkj,mflodman,henrika,asapersson,niklas.enbom Review URL: https://webrtc-codereview.appspot.com/23129004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7726 4adac7df-926f-26a2-2b94-8c16560cd09d
377 lines
13 KiB
C++
377 lines
13 KiB
C++
/*
|
|
* 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/media/webrtc/webrtcvideoframe.h"
|
|
|
|
#include "libyuv/convert.h"
|
|
#include "libyuv/convert_from.h"
|
|
#include "libyuv/planar_functions.h"
|
|
#include "talk/media/base/videocapturer.h"
|
|
#include "talk/media/base/videocommon.h"
|
|
#include "webrtc/base/logging.h"
|
|
|
|
namespace cricket {
|
|
|
|
// Class that wraps ownerhip semantics of a buffer passed to it.
|
|
// * Buffers passed using Attach() become owned by this FrameBuffer and will be
|
|
// destroyed on FrameBuffer destruction.
|
|
// * Buffers passed using Alias() are not owned and will not be destroyed on
|
|
// FrameBuffer destruction, The buffer then must outlive the FrameBuffer.
|
|
class WebRtcVideoFrame::FrameBuffer {
|
|
public:
|
|
FrameBuffer();
|
|
explicit FrameBuffer(size_t length);
|
|
~FrameBuffer();
|
|
|
|
void Attach(uint8* data, size_t length);
|
|
void Alias(uint8* data, size_t length);
|
|
uint8* data();
|
|
size_t length() const;
|
|
|
|
webrtc::VideoFrame* frame();
|
|
const webrtc::VideoFrame* frame() const;
|
|
|
|
private:
|
|
rtc::scoped_ptr<uint8[]> owned_data_;
|
|
webrtc::VideoFrame video_frame_;
|
|
};
|
|
|
|
WebRtcVideoFrame::FrameBuffer::FrameBuffer() {}
|
|
|
|
WebRtcVideoFrame::FrameBuffer::FrameBuffer(size_t length) {
|
|
uint8* buffer = new uint8[length];
|
|
Attach(buffer, length);
|
|
}
|
|
|
|
WebRtcVideoFrame::FrameBuffer::~FrameBuffer() {
|
|
// Make sure that |video_frame_| doesn't delete the buffer, as |owned_data_|
|
|
// will release the buffer if this FrameBuffer owns it.
|
|
uint8_t* new_memory = NULL;
|
|
size_t new_length = 0;
|
|
size_t new_size = 0;
|
|
video_frame_.Swap(new_memory, new_length, new_size);
|
|
}
|
|
|
|
void WebRtcVideoFrame::FrameBuffer::Attach(uint8* data, size_t length) {
|
|
Alias(data, length);
|
|
owned_data_.reset(data);
|
|
}
|
|
|
|
void WebRtcVideoFrame::FrameBuffer::Alias(uint8* data, size_t length) {
|
|
owned_data_.reset();
|
|
uint8_t* new_memory = reinterpret_cast<uint8_t*>(data);
|
|
size_t new_length = length;
|
|
size_t new_size = length;
|
|
video_frame_.Swap(new_memory, new_length, new_size);
|
|
}
|
|
|
|
uint8* WebRtcVideoFrame::FrameBuffer::data() {
|
|
return video_frame_.Buffer();
|
|
}
|
|
|
|
size_t WebRtcVideoFrame::FrameBuffer::length() const {
|
|
return video_frame_.Length();
|
|
}
|
|
|
|
webrtc::VideoFrame* WebRtcVideoFrame::FrameBuffer::frame() {
|
|
return &video_frame_;
|
|
}
|
|
|
|
const webrtc::VideoFrame* WebRtcVideoFrame::FrameBuffer::frame() const {
|
|
return &video_frame_;
|
|
}
|
|
|
|
WebRtcVideoFrame::WebRtcVideoFrame()
|
|
: video_buffer_(new RefCountedBuffer()), is_black_(false) {}
|
|
|
|
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) {
|
|
return Reset(format, w, h, dw, dh, sample, sample_size, pixel_width,
|
|
pixel_height, elapsed_time, time_stamp, rotation);
|
|
}
|
|
|
|
bool WebRtcVideoFrame::Init(const CapturedFrame* frame, int dw, int dh) {
|
|
return Reset(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::Alias(const CapturedFrame* frame, int dw, int dh) {
|
|
if (CanonicalFourCC(frame->fourcc) != FOURCC_I420 || frame->rotation != 0 ||
|
|
frame->width != dw || frame->height != dh) {
|
|
// TODO(fbarchard): Enable aliasing of more formats.
|
|
return Init(frame, dw, dh);
|
|
} else {
|
|
Alias(static_cast<uint8*>(frame->data),
|
|
frame->data_size,
|
|
frame->width,
|
|
frame->height,
|
|
frame->pixel_width,
|
|
frame->pixel_height,
|
|
frame->elapsed_time,
|
|
frame->time_stamp,
|
|
frame->rotation);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool WebRtcVideoFrame::InitToBlack(int w, int h, size_t pixel_width,
|
|
size_t pixel_height, int64 elapsed_time,
|
|
int64 time_stamp) {
|
|
InitToEmptyBuffer(w, h, pixel_width, pixel_height, elapsed_time, time_stamp);
|
|
if (!is_black_) {
|
|
return SetToBlack();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void WebRtcVideoFrame::Alias(
|
|
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) {
|
|
rtc::scoped_refptr<RefCountedBuffer> video_buffer(
|
|
new RefCountedBuffer());
|
|
video_buffer->Alias(buffer, buffer_size);
|
|
Attach(video_buffer.get(), buffer_size, w, h, pixel_width, pixel_height,
|
|
elapsed_time, time_stamp, rotation);
|
|
}
|
|
|
|
size_t WebRtcVideoFrame::GetWidth() const { return frame()->Width(); }
|
|
|
|
size_t WebRtcVideoFrame::GetHeight() const { return frame()->Height(); }
|
|
|
|
const uint8* WebRtcVideoFrame::GetYPlane() const {
|
|
uint8_t* buffer = frame()->Buffer();
|
|
return buffer;
|
|
}
|
|
|
|
const uint8* WebRtcVideoFrame::GetUPlane() const {
|
|
uint8_t* buffer = frame()->Buffer();
|
|
if (buffer) {
|
|
buffer += (frame()->Width() * frame()->Height());
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
const uint8* WebRtcVideoFrame::GetVPlane() const {
|
|
uint8_t* buffer = frame()->Buffer();
|
|
if (buffer) {
|
|
int uv_size = static_cast<int>(GetChromaSize());
|
|
buffer += frame()->Width() * frame()->Height() + uv_size;
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
uint8* WebRtcVideoFrame::GetYPlane() {
|
|
uint8_t* buffer = frame()->Buffer();
|
|
return buffer;
|
|
}
|
|
|
|
uint8* WebRtcVideoFrame::GetUPlane() {
|
|
uint8_t* buffer = frame()->Buffer();
|
|
if (buffer) {
|
|
buffer += (frame()->Width() * frame()->Height());
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
uint8* WebRtcVideoFrame::GetVPlane() {
|
|
uint8_t* buffer = frame()->Buffer();
|
|
if (buffer) {
|
|
int uv_size = static_cast<int>(GetChromaSize());
|
|
buffer += frame()->Width() * frame()->Height() + uv_size;
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
VideoFrame* WebRtcVideoFrame::Copy() const {
|
|
uint8* old_buffer = video_buffer_->data();
|
|
if (!old_buffer)
|
|
return NULL;
|
|
size_t new_buffer_size = video_buffer_->length();
|
|
|
|
WebRtcVideoFrame* ret_val = new WebRtcVideoFrame();
|
|
ret_val->Attach(video_buffer_.get(), new_buffer_size, frame()->Width(),
|
|
frame()->Height(), pixel_width_, pixel_height_, elapsed_time_,
|
|
time_stamp_, rotation_);
|
|
return ret_val;
|
|
}
|
|
|
|
bool WebRtcVideoFrame::MakeExclusive() {
|
|
const size_t length = video_buffer_->length();
|
|
RefCountedBuffer* exclusive_buffer = new RefCountedBuffer(length);
|
|
memcpy(exclusive_buffer->data(), video_buffer_->data(), length);
|
|
Attach(exclusive_buffer, length, frame()->Width(), frame()->Height(),
|
|
pixel_width_, pixel_height_, elapsed_time_, time_stamp_, rotation_);
|
|
return true;
|
|
}
|
|
|
|
size_t WebRtcVideoFrame::CopyToBuffer(uint8* buffer, size_t size) const {
|
|
if (!frame()->Buffer()) {
|
|
return 0;
|
|
}
|
|
|
|
size_t needed = frame()->Length();
|
|
if (needed <= size) {
|
|
memcpy(buffer, frame()->Buffer(), needed);
|
|
}
|
|
return needed;
|
|
}
|
|
|
|
// TODO(fbarchard): Refactor into base class and share with lmi
|
|
size_t WebRtcVideoFrame::ConvertToRgbBuffer(uint32 to_fourcc, uint8* buffer,
|
|
size_t size, int stride_rgb) const {
|
|
if (!frame()->Buffer()) {
|
|
return 0;
|
|
}
|
|
size_t width = frame()->Width();
|
|
size_t height = frame()->Height();
|
|
size_t needed = (stride_rgb >= 0 ? stride_rgb : -stride_rgb) * height;
|
|
if (size < needed) {
|
|
LOG(LS_WARNING) << "RGB buffer is not large enough";
|
|
return needed;
|
|
}
|
|
|
|
if (libyuv::ConvertFromI420(GetYPlane(), GetYPitch(), GetUPlane(),
|
|
GetUPitch(), GetVPlane(), GetVPitch(), buffer,
|
|
stride_rgb,
|
|
static_cast<int>(width),
|
|
static_cast<int>(height),
|
|
to_fourcc)) {
|
|
LOG(LS_WARNING) << "RGB type not supported: " << to_fourcc;
|
|
return 0; // 0 indicates error
|
|
}
|
|
return needed;
|
|
}
|
|
|
|
void WebRtcVideoFrame::Attach(
|
|
RefCountedBuffer* video_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) {
|
|
if (video_buffer_.get() == video_buffer) {
|
|
return;
|
|
}
|
|
is_black_ = false;
|
|
video_buffer_ = video_buffer;
|
|
frame()->SetWidth(w);
|
|
frame()->SetHeight(h);
|
|
pixel_width_ = pixel_width;
|
|
pixel_height_ = pixel_height;
|
|
elapsed_time_ = elapsed_time;
|
|
time_stamp_ = time_stamp;
|
|
rotation_ = rotation;
|
|
}
|
|
|
|
webrtc::VideoFrame* WebRtcVideoFrame::frame() {
|
|
return video_buffer_->frame();
|
|
}
|
|
|
|
const webrtc::VideoFrame* WebRtcVideoFrame::frame() const {
|
|
return video_buffer_->frame();
|
|
}
|
|
|
|
bool WebRtcVideoFrame::Reset(
|
|
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) {
|
|
if (!Validate(format, w, h, sample, sample_size)) {
|
|
return false;
|
|
}
|
|
// Translate aliases to standard enums (e.g., IYUV -> I420).
|
|
format = CanonicalFourCC(format);
|
|
|
|
// Round display width and height down to multiple of 4, to avoid webrtc
|
|
// size calculation error on odd sizes.
|
|
// TODO(Ronghua): Remove this once the webrtc allocator is fixed.
|
|
dw = (dw > 4) ? (dw & ~3) : dw;
|
|
dh = (dh > 4) ? (dh & ~3) : dh;
|
|
|
|
// Set up a new buffer.
|
|
// TODO(fbarchard): Support lazy allocation.
|
|
int new_width = dw;
|
|
int new_height = dh;
|
|
if (rotation == 90 || rotation == 270) { // If rotated swap width, height.
|
|
new_width = dh;
|
|
new_height = dw;
|
|
}
|
|
|
|
size_t desired_size = SizeOf(new_width, new_height);
|
|
rtc::scoped_refptr<RefCountedBuffer> video_buffer(
|
|
new RefCountedBuffer(desired_size));
|
|
// Since the libyuv::ConvertToI420 will handle the rotation, so the
|
|
// new frame's rotation should always be 0.
|
|
Attach(video_buffer.get(), desired_size, new_width, new_height, pixel_width,
|
|
pixel_height, elapsed_time, time_stamp, 0);
|
|
|
|
int horiz_crop = ((w - dw) / 2) & ~1;
|
|
// ARGB on Windows has negative height.
|
|
// The sample's layout in memory is normal, so just correct crop.
|
|
int vert_crop = ((abs(h) - dh) / 2) & ~1;
|
|
// Conversion functions expect negative height to flip the image.
|
|
int idh = (h < 0) ? -dh : dh;
|
|
uint8* y = GetYPlane();
|
|
int y_stride = GetYPitch();
|
|
uint8* u = GetUPlane();
|
|
int u_stride = GetUPitch();
|
|
uint8* v = GetVPlane();
|
|
int v_stride = GetVPitch();
|
|
int r = libyuv::ConvertToI420(
|
|
sample, sample_size, y, y_stride, u, u_stride, v, v_stride, horiz_crop,
|
|
vert_crop, w, h, dw, idh, static_cast<libyuv::RotationMode>(rotation),
|
|
format);
|
|
if (r) {
|
|
LOG(LS_ERROR) << "Error parsing format: " << GetFourccName(format)
|
|
<< " return code : " << r;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
VideoFrame* WebRtcVideoFrame::CreateEmptyFrame(
|
|
int w, int h, size_t pixel_width, size_t pixel_height, int64 elapsed_time,
|
|
int64 time_stamp) const {
|
|
WebRtcVideoFrame* frame = new WebRtcVideoFrame();
|
|
frame->InitToEmptyBuffer(w, h, pixel_width, pixel_height, elapsed_time,
|
|
time_stamp);
|
|
return frame;
|
|
}
|
|
|
|
void WebRtcVideoFrame::InitToEmptyBuffer(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);
|
|
rtc::scoped_refptr<RefCountedBuffer> video_buffer(
|
|
new RefCountedBuffer(buffer_size));
|
|
Attach(video_buffer.get(), buffer_size, w, h, pixel_width, pixel_height,
|
|
elapsed_time, time_stamp, 0);
|
|
}
|
|
|
|
} // namespace cricket
|