blob: 75c3e6a7824cf3483a2501d487228f9b862d4b7b [file] [log] [blame]
// Copyright (c) 2012 The WebM 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 "encoder/video_encoder.h"
#include <new>
#include "glog/logging.h"
#include "libyuv/convert.h"
#include "libyuv/planar_functions.h"
#include "libyuv/video_common.h"
#if defined _MSC_VER
// Disable warning C4505(unreferenced local function has been removed) in MSVC.
// At the time this comment was written the warning is emitted 27 times for
// vp8.h and vp8cx.h (included by vpx_encoder.h).
#pragma warning(disable:4505)
#endif
#include "encoder/vpx_encoder.h"
namespace webmlive {
bool FourCCToVideoFormat(uint32 fourcc,
uint16 bits_per_pixel,
VideoFormat* ptr_format) {
bool converted = false;
if (ptr_format) {
switch (fourcc) {
case 0: // RGB formats.
if (bits_per_pixel == 24) {
*ptr_format = kVideoFormatRGB;
converted = true;
} else if (bits_per_pixel == 32) {
*ptr_format = kVideoFormatRGBA;
converted = true;
}
break;
case libyuv::FOURCC_I420:
if (bits_per_pixel == kI420BitCount) {
*ptr_format = kVideoFormatI420;
converted = true;
}
break;
case libyuv::FOURCC_YV12:
if (bits_per_pixel == kYV12BitCount) {
*ptr_format = kVideoFormatYV12;
converted = true;
}
break;
case libyuv::FOURCC_YUY2:
if (bits_per_pixel == kYUY2BitCount) {
*ptr_format = kVideoFormatYUY2;
converted = true;
}
break;
case libyuv::FOURCC_YUYV:
if (bits_per_pixel == kYUYVBitCount) {
*ptr_format = kVideoFormatYUYV;
converted = true;
}
break;
case libyuv::FOURCC_UYVY:
if (bits_per_pixel == kUYVYBitCount) {
*ptr_format = kVideoFormatUYVY;
converted = true;
}
break;
default:
LOG(WARNING) << "Unknown four char code.";
}
}
return converted;
}
VideoFrame::VideoFrame()
: keyframe_(false),
timestamp_(0),
duration_(0),
buffer_capacity_(0),
buffer_length_(0) {
}
VideoFrame::~VideoFrame() {
}
int VideoFrame::Init(const VideoConfig& config,
bool keyframe,
int64 timestamp,
int64 duration,
const uint8* ptr_data,
int32 data_length) {
if (!ptr_data) {
LOG(ERROR) << "VideoFrame can't Init with NULL data pointer.";
return kInvalidArg;
}
const bool needs_conversion =
(config.format != kVideoFormatI420 &&
config.format != kVideoFormatYV12 &&
config.format != kVideoFormatVP8 &&
config.format != kVideoFormatVP9);
if (needs_conversion) {
// Convert the video frame to I420.
const int32 status = ConvertToI420(config, ptr_data);
if (status) {
LOG(ERROR) << "Video format conversion failed " << status;
return status;
}
} else {
// Data does not need conversion: copy directly into |buffer_|.
if (data_length > buffer_capacity_) {
buffer_.reset(new (std::nothrow) uint8[data_length]); // NOLINT
if (!buffer_) {
LOG(ERROR) << "VideoFrame Init cannot allocate buffer.";
return kNoMemory;
}
buffer_capacity_ = data_length;
}
memcpy(buffer_.get(), ptr_data, data_length);
buffer_length_ = data_length;
config_ = config;
}
keyframe_ = keyframe;
timestamp_ = timestamp;
duration_ = duration;
return kSuccess;
}
int VideoFrame::Clone(VideoFrame* ptr_frame) const {
if (!ptr_frame) {
LOG(ERROR) << "cannot Clone to a NULL VideoFrame.";
return kInvalidArg;
}
if (buffer_.get() && buffer_capacity_ > 0) {
ptr_frame->buffer_.reset(
new (std::nothrow) uint8[buffer_capacity_]); // NOLINT
if (!ptr_frame->buffer_) {
LOG(ERROR) << "VideoFrame Clone cannot allocate buffer.";
return kNoMemory;
}
memcpy(ptr_frame->buffer_.get(), buffer_.get(), buffer_length_);
}
ptr_frame->buffer_capacity_ = buffer_capacity_;
ptr_frame->buffer_length_ = buffer_length_;
ptr_frame->config_ = config_;
ptr_frame->keyframe_ = keyframe_;
ptr_frame->timestamp_ = timestamp_;
ptr_frame->duration_ = duration_;
return kSuccess;
}
void VideoFrame::Swap(VideoFrame* ptr_frame) {
CHECK_NOTNULL(buffer_.get());
CHECK_NOTNULL(ptr_frame->buffer_.get());
const VideoConfig temp_config = config_;
config_ = ptr_frame->config_;
ptr_frame->config_ = temp_config;
const bool temp_keyframe = keyframe_;
keyframe_ = ptr_frame->keyframe_;
ptr_frame->keyframe_ = temp_keyframe;
int64 temp_time = timestamp_;
timestamp_ = ptr_frame->timestamp_;
ptr_frame->timestamp_ = temp_time;
temp_time = duration_;
duration_ = ptr_frame->duration_;
ptr_frame->duration_ = temp_time;
buffer_.swap(ptr_frame->buffer_);
int32 temp = buffer_capacity_;
buffer_capacity_ = ptr_frame->buffer_capacity_;
ptr_frame->buffer_capacity_ = temp;
temp = buffer_length_;
buffer_length_ = ptr_frame->buffer_length_;
ptr_frame->buffer_length_ = temp;
}
int VideoFrame::ConvertToI420(const VideoConfig& source_config,
const uint8* ptr_data) {
// Allocate storage for the I420 frame.
const int32 size_required =
source_config.width * source_config.height * 3 / 2;
if (size_required > buffer_capacity_) {
buffer_.reset(new (std::nothrow) uint8[size_required]); // NOLINT
if (!buffer_) {
LOG(ERROR) << "VideoFrame ConvertToI420 cannot allocate buffer.";
return kNoMemory;
}
buffer_capacity_ = size_required;
}
buffer_length_ = size_required;
VideoConfig& target_config = config_;
target_config.format = kVideoFormatI420;
target_config.width = source_config.width;
target_config.height = abs(source_config.height);
target_config.stride = source_config.width;
// Calculate length and stride for the I420 planes.
const int32 y_length = source_config.width * target_config.height;
const int32 uv_stride = target_config.stride / 2;
const int32 uv_length = uv_stride * (target_config.height / 2);
CHECK_EQ(buffer_length_, y_length + (uv_length * 2));
// Assign the pointers to the I420 planes.
uint8* const ptr_i420_y = buffer_.get();
uint8* const ptr_i420_u = ptr_i420_y + y_length;
uint8* const ptr_i420_v = ptr_i420_u + uv_length;
int status = kConversionFailed;
switch (source_config.format) {
case kVideoFormatYUY2:
case kVideoFormatYUYV:
status = libyuv::YUY2ToI420(ptr_data, source_config.stride,
ptr_i420_y, target_config.stride,
ptr_i420_u, uv_stride,
ptr_i420_v, uv_stride,
source_config.width, target_config.height);
break;
case kVideoFormatUYVY:
status = libyuv::UYVYToI420(ptr_data, source_config.stride,
ptr_i420_y, target_config.stride,
ptr_i420_u, uv_stride,
ptr_i420_v, uv_stride,
source_config.width, target_config.height);
break;
// Note that RGB conversions always negate the height to ensure correct
// image orientation.
case kVideoFormatRGB:
status = libyuv::RGB24ToI420(ptr_data, source_config.stride,
ptr_i420_y, target_config.stride,
ptr_i420_u, uv_stride,
ptr_i420_v, uv_stride,
source_config.width, -source_config.height);
break;
case kVideoFormatRGBA:
status = libyuv::BGRAToI420(ptr_data, source_config.stride,
ptr_i420_y, target_config.stride,
ptr_i420_u, uv_stride,
ptr_i420_v, uv_stride,
source_config.width, -source_config.height);
break;
case kVideoFormatI420:
case kVideoFormatVP8:
case kVideoFormatYV12:
case kVideoFormatCount:
LOG(ERROR) << "Cannot convert to I420: invalid video format.";
status = kInvalidArg;
}
return status;
}
///////////////////////////////////////////////////////////////////////////////
// VideoEncoder
//
VideoEncoder::VideoEncoder() {
}
VideoEncoder::~VideoEncoder() {
}
int VideoEncoder::Init(const WebmEncoderConfig& config) {
ptr_vpx_encoder_.reset(new (std::nothrow) VpxEncoder()); // NOLINT
if (!ptr_vpx_encoder_) {
return kNoMemory;
}
return ptr_vpx_encoder_->Init(config);
}
int VideoEncoder::EncodeFrame(const VideoFrame& raw_frame,
VideoFrame* ptr_vpx_frame) {
if (!ptr_vpx_encoder_) {
LOG(ERROR) << "VideoEncoder has NULL encoder, not Init'd";
return kEncoderError;
}
return ptr_vpx_encoder_->EncodeFrame(raw_frame, ptr_vpx_frame);
}
int64 VideoEncoder::frames_in() const {
return ptr_vpx_encoder_ ? ptr_vpx_encoder_->frames_in() : 0;
}
int64 VideoEncoder::frames_out() const {
return ptr_vpx_encoder_ ? ptr_vpx_encoder_->frames_out() : 0;
}
int64 VideoEncoder::last_keyframe_time() const {
return ptr_vpx_encoder_ ? ptr_vpx_encoder_->last_keyframe_time() : 0;
}
int64 VideoEncoder::last_timestamp() const {
return ptr_vpx_encoder_ ? ptr_vpx_encoder_->last_timestamp() : 0;
}
} // namespace webmlive