blob: 4f89c8b85d100562d14cb891c6b11e8f67f247d1 [file] [log] [blame]
/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/media/engine/webrtcvideoframe.h"
#include "libyuv/convert.h"
#include "webrtc/base/logging.h"
#include "webrtc/media/base/videocapturer.h"
#include "webrtc/media/base/videocommon.h"
#include "webrtc/video_frame.h"
using webrtc::kYPlane;
using webrtc::kUPlane;
using webrtc::kVPlane;
namespace cricket {
WebRtcVideoFrame::WebRtcVideoFrame()
: timestamp_us_(0), rotation_(webrtc::kVideoRotation_0) {}
WebRtcVideoFrame::WebRtcVideoFrame(
const rtc::scoped_refptr<webrtc::VideoFrameBuffer>& buffer,
webrtc::VideoRotation rotation,
int64_t timestamp_us)
: video_frame_buffer_(buffer),
timestamp_us_(timestamp_us),
rotation_(rotation) {}
WebRtcVideoFrame::WebRtcVideoFrame(
const rtc::scoped_refptr<webrtc::VideoFrameBuffer>& buffer,
int64_t time_stamp_ns,
webrtc::VideoRotation rotation)
: WebRtcVideoFrame(buffer,
rotation,
time_stamp_ns / rtc::kNumNanosecsPerMicrosec) {}
WebRtcVideoFrame::~WebRtcVideoFrame() {}
bool WebRtcVideoFrame::Init(uint32_t format,
int w,
int h,
int dw,
int dh,
uint8_t* sample,
size_t sample_size,
int64_t time_stamp_ns,
webrtc::VideoRotation rotation) {
return Reset(format, w, h, dw, dh, sample, sample_size,
time_stamp_ns / rtc::kNumNanosecsPerMicrosec, rotation,
true /*apply_rotation*/);
}
bool WebRtcVideoFrame::Init(const CapturedFrame* frame, int dw, int dh,
bool apply_rotation) {
return Reset(frame->fourcc, frame->width, frame->height, dw, dh,
static_cast<uint8_t*>(frame->data), frame->data_size,
frame->time_stamp / rtc::kNumNanosecsPerMicrosec,
frame->rotation, apply_rotation);
}
int WebRtcVideoFrame::width() const {
return video_frame_buffer_ ? video_frame_buffer_->width() : 0;
}
int WebRtcVideoFrame::height() const {
return video_frame_buffer_ ? video_frame_buffer_->height() : 0;
}
const rtc::scoped_refptr<webrtc::VideoFrameBuffer>&
WebRtcVideoFrame::video_frame_buffer() const {
return video_frame_buffer_;
}
VideoFrame* WebRtcVideoFrame::Copy() const {
return new WebRtcVideoFrame(video_frame_buffer_, rotation_, timestamp_us_);
}
size_t WebRtcVideoFrame::ConvertToRgbBuffer(uint32_t to_fourcc,
uint8_t* buffer,
size_t size,
int stride_rgb) const {
RTC_CHECK(video_frame_buffer_);
RTC_CHECK(video_frame_buffer_->native_handle() == nullptr);
return VideoFrame::ConvertToRgbBuffer(to_fourcc, buffer, size, stride_rgb);
}
bool WebRtcVideoFrame::Reset(uint32_t format,
int w,
int h,
int dw,
int dh,
uint8_t* sample,
size_t sample_size,
int64_t timestamp_us,
webrtc::VideoRotation rotation,
bool apply_rotation) {
if (!Validate(format, w, h, sample, sample_size)) {
return false;
}
// Translate aliases to standard enums (e.g., IYUV -> I420).
format = CanonicalFourCC(format);
// Set up a new buffer.
// TODO(fbarchard): Support lazy allocation.
int new_width = dw;
int new_height = dh;
// If rotated swap width, height.
if (apply_rotation && (rotation == 90 || rotation == 270)) {
new_width = dh;
new_height = dw;
}
InitToEmptyBuffer(new_width, new_height);
rotation_ = apply_rotation ? webrtc::kVideoRotation_0 : rotation;
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;
int r = libyuv::ConvertToI420(
sample, sample_size,
video_frame_buffer_->MutableDataY(),
video_frame_buffer_->StrideY(),
video_frame_buffer_->MutableDataU(),
video_frame_buffer_->StrideU(),
video_frame_buffer_->MutableDataV(),
video_frame_buffer_->StrideV(),
horiz_crop, vert_crop,
w, h,
dw, idh,
static_cast<libyuv::RotationMode>(
apply_rotation ? rotation : webrtc::kVideoRotation_0),
format);
if (r) {
LOG(LS_ERROR) << "Error parsing format: " << GetFourccName(format)
<< " return code : " << r;
return false;
}
timestamp_us_ = timestamp_us;
return true;
}
VideoFrame* WebRtcVideoFrame::CreateEmptyFrame(int w,
int h,
int64_t timestamp_us) const {
WebRtcVideoFrame* frame = new WebRtcVideoFrame();
frame->InitToEmptyBuffer(w, h, rtc::kNumNanosecsPerMicrosec * timestamp_us);
return frame;
}
void WebRtcVideoFrame::InitToEmptyBuffer(int w, int h) {
video_frame_buffer_ = new rtc::RefCountedObject<webrtc::I420Buffer>(w, h);
rotation_ = webrtc::kVideoRotation_0;
}
void WebRtcVideoFrame::InitToEmptyBuffer(int w, int h,
int64_t time_stamp_ns) {
video_frame_buffer_ = new rtc::RefCountedObject<webrtc::I420Buffer>(w, h);
SetTimeStamp(time_stamp_ns);
rotation_ = webrtc::kVideoRotation_0;
}
const VideoFrame* WebRtcVideoFrame::GetCopyWithRotationApplied() const {
// If the frame is not rotated, the caller should reuse this frame instead of
// making a redundant copy.
if (rotation() == webrtc::kVideoRotation_0) {
return this;
}
// If the video frame is backed up by a native handle, it resides in the GPU
// memory which we can't rotate here. The assumption is that the renderers
// which uses GPU to render should be able to rotate themselves.
RTC_DCHECK(!video_frame_buffer()->native_handle());
if (rotated_frame_) {
return rotated_frame_.get();
}
int orig_width = width();
int orig_height = height();
int rotated_width = orig_width;
int rotated_height = orig_height;
if (rotation() == webrtc::kVideoRotation_90 ||
rotation() == webrtc::kVideoRotation_270) {
rotated_width = orig_height;
rotated_height = orig_width;
}
rotated_frame_.reset(
CreateEmptyFrame(rotated_width, rotated_height, timestamp_us_));
// TODO(guoweis): Add a function in webrtc_libyuv.cc to convert from
// VideoRotation to libyuv::RotationMode.
int ret = libyuv::I420Rotate(
video_frame_buffer_->DataY(), video_frame_buffer_->StrideY(),
video_frame_buffer_->DataU(), video_frame_buffer_->StrideU(),
video_frame_buffer_->DataV(), video_frame_buffer_->StrideV(),
rotated_frame_->video_frame_buffer()->MutableDataY(),
rotated_frame_->video_frame_buffer()->StrideY(),
rotated_frame_->video_frame_buffer()->MutableDataU(),
rotated_frame_->video_frame_buffer()->StrideU(),
rotated_frame_->video_frame_buffer()->MutableDataV(),
rotated_frame_->video_frame_buffer()->StrideV(),
orig_width, orig_height,
static_cast<libyuv::RotationMode>(rotation()));
if (ret == 0) {
return rotated_frame_.get();
}
return nullptr;
}
} // namespace cricket