blob: 389180c82bdd9be4f2c7bace028e202fbb92beff [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "AVCEncoder"
#include <utils/Log.h>
#include "AVCEncoder.h"
#include "avcenc_api.h"
#include "avcenc_int.h"
#include "OMX_Video.h"
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
namespace android {
inline static void ConvertYUV420SemiPlanarToYUV420Planar(
uint8_t *inyuv, uint8_t* outyuv,
int32_t width, int32_t height) {
int32_t outYsize = width * height;
uint32_t *outy = (uint32_t *) outyuv;
uint16_t *outcb = (uint16_t *) (outyuv + outYsize);
uint16_t *outcr = (uint16_t *) (outyuv + outYsize + (outYsize >> 2));
/* Y copying */
memcpy(outy, inyuv, outYsize);
/* U & V copying */
uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize);
for (int32_t i = height >> 1; i > 0; --i) {
for (int32_t j = width >> 2; j > 0; --j) {
uint32_t temp = *inyuv_4++;
uint32_t tempU = temp & 0xFF;
tempU = tempU | ((temp >> 8) & 0xFF00);
uint32_t tempV = (temp >> 8) & 0xFF;
tempV = tempV | ((temp >> 16) & 0xFF00);
// Flip U and V
*outcb++ = tempV;
*outcr++ = tempU;
}
}
}
static int32_t MallocWrapper(
void *userData, int32_t size, int32_t attrs) {
return reinterpret_cast<int32_t>(malloc(size));
}
static void FreeWrapper(void *userData, int32_t ptr) {
free(reinterpret_cast<void *>(ptr));
}
static int32_t DpbAllocWrapper(void *userData,
unsigned int sizeInMbs, unsigned int numBuffers) {
AVCEncoder *encoder = static_cast<AVCEncoder *>(userData);
CHECK(encoder != NULL);
return encoder->allocOutputBuffers(sizeInMbs, numBuffers);
}
static int32_t BindFrameWrapper(
void *userData, int32_t index, uint8_t **yuv) {
AVCEncoder *encoder = static_cast<AVCEncoder *>(userData);
CHECK(encoder != NULL);
return encoder->bindOutputBuffer(index, yuv);
}
static void UnbindFrameWrapper(void *userData, int32_t index) {
AVCEncoder *encoder = static_cast<AVCEncoder *>(userData);
CHECK(encoder != NULL);
return encoder->unbindOutputBuffer(index);
}
AVCEncoder::AVCEncoder(
const sp<MediaSource>& source,
const sp<MetaData>& meta)
: mSource(source),
mMeta(meta),
mNumInputFrames(-1),
mPrevTimestampUs(-1),
mStarted(false),
mInputBuffer(NULL),
mInputFrameData(NULL),
mGroup(NULL) {
LOGV("Construct software AVCEncoder");
mHandle = new tagAVCHandle;
memset(mHandle, 0, sizeof(tagAVCHandle));
mHandle->AVCObject = NULL;
mHandle->userData = this;
mHandle->CBAVC_DPBAlloc = DpbAllocWrapper;
mHandle->CBAVC_FrameBind = BindFrameWrapper;
mHandle->CBAVC_FrameUnbind = UnbindFrameWrapper;
mHandle->CBAVC_Malloc = MallocWrapper;
mHandle->CBAVC_Free = FreeWrapper;
mInitCheck = initCheck(meta);
}
AVCEncoder::~AVCEncoder() {
LOGV("Destruct software AVCEncoder");
if (mStarted) {
stop();
}
delete mEncParams;
delete mHandle;
}
status_t AVCEncoder::initCheck(const sp<MetaData>& meta) {
LOGV("initCheck");
CHECK(meta->findInt32(kKeyWidth, &mVideoWidth));
CHECK(meta->findInt32(kKeyHeight, &mVideoHeight));
CHECK(meta->findInt32(kKeySampleRate, &mVideoFrameRate));
CHECK(meta->findInt32(kKeyBitRate, &mVideoBitRate));
// XXX: Add more color format support
CHECK(meta->findInt32(kKeyColorFormat, &mVideoColorFormat));
if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) {
if (mVideoColorFormat != OMX_COLOR_FormatYUV420SemiPlanar) {
LOGE("Color format %d is not supported", mVideoColorFormat);
return BAD_VALUE;
}
// Allocate spare buffer only when color conversion is needed.
// Assume the color format is OMX_COLOR_FormatYUV420SemiPlanar.
mInputFrameData =
(uint8_t *) malloc((mVideoWidth * mVideoHeight * 3 ) >> 1);
CHECK(mInputFrameData);
}
// XXX: Remove this restriction
if (mVideoWidth % 16 != 0 || mVideoHeight % 16 != 0) {
LOGE("Video frame size %dx%d must be a multiple of 16",
mVideoWidth, mVideoHeight);
return BAD_VALUE;
}
mEncParams = new tagAVCEncParam;
memset(mEncParams, 0, sizeof(mEncParams));
mEncParams->width = mVideoWidth;
mEncParams->height = mVideoHeight;
mEncParams->frame_rate = 1000 * mVideoFrameRate; // In frames/ms!
mEncParams->rate_control = AVC_ON;
mEncParams->bitrate = mVideoBitRate;
mEncParams->initQP = 0;
mEncParams->init_CBP_removal_delay = 1600;
mEncParams->CPB_size = (uint32_t) (mVideoBitRate >> 1);
mEncParams->intramb_refresh = 0;
mEncParams->auto_scd = AVC_ON;
mEncParams->out_of_band_param_set = AVC_ON;
mEncParams->poc_type = 2;
mEncParams->log2_max_poc_lsb_minus_4 = 12;
mEncParams->delta_poc_zero_flag = 0;
mEncParams->offset_poc_non_ref = 0;
mEncParams->offset_top_bottom = 0;
mEncParams->num_ref_in_cycle = 0;
mEncParams->offset_poc_ref = NULL;
mEncParams->num_ref_frame = 1;
mEncParams->num_slice_group = 1;
mEncParams->fmo_type = 0;
mEncParams->db_filter = AVC_ON;
mEncParams->disable_db_idc = 0;
mEncParams->alpha_offset = 0;
mEncParams->beta_offset = 0;
mEncParams->constrained_intra_pred = AVC_OFF;
mEncParams->data_par = AVC_OFF;
mEncParams->fullsearch = AVC_OFF;
mEncParams->search_range = 16;
mEncParams->sub_pel = AVC_OFF;
mEncParams->submb_pred = AVC_OFF;
mEncParams->rdopt_mode = AVC_OFF;
mEncParams->bidir_pred = AVC_OFF;
int32_t nMacroBlocks = ((((mVideoWidth + 15) >> 4) << 4) *
(((mVideoHeight + 15) >> 4) << 4)) >> 8;
uint32_t *sliceGroup = (uint32_t *) malloc(sizeof(uint32_t) * nMacroBlocks);
for (int ii = 0, idx = 0; ii < nMacroBlocks; ++ii) {
sliceGroup[ii] = idx++;
if (idx >= mEncParams->num_slice_group) {
idx = 0;
}
}
mEncParams->slice_group = sliceGroup;
mEncParams->use_overrun_buffer = AVC_OFF;
// Set IDR frame refresh interval
int32_t iFramesIntervalSec;
CHECK(meta->findInt32(kKeyIFramesInterval, &iFramesIntervalSec));
if (iFramesIntervalSec < 0) {
mEncParams->idr_period = -1;
} else if (iFramesIntervalSec == 0) {
mEncParams->idr_period = 1; // All I frames
} else {
mEncParams->idr_period =
(iFramesIntervalSec * mVideoFrameRate);
}
LOGV("idr_period: %d, I-frames interval: %d seconds, and frame rate: %d",
mEncParams->idr_period, iFramesIntervalSec, mVideoFrameRate);
// Set profile and level
// If profile and level setting is not correct, failure
// is reported when the encoder is initialized.
mEncParams->profile = AVC_BASELINE;
mEncParams->level = AVC_LEVEL3_2;
int32_t profile, level;
if (meta->findInt32(kKeyVideoProfile, &profile)) {
mEncParams->profile = (AVCProfile) profile;
}
if (meta->findInt32(kKeyVideoLevel, &level)) {
mEncParams->level = (AVCLevel) level;
}
mFormat = new MetaData;
mFormat->setInt32(kKeyWidth, mVideoWidth);
mFormat->setInt32(kKeyHeight, mVideoHeight);
mFormat->setInt32(kKeyBitRate, mVideoBitRate);
mFormat->setInt32(kKeySampleRate, mVideoFrameRate);
mFormat->setInt32(kKeyColorFormat, mVideoColorFormat);
mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
mFormat->setCString(kKeyDecoderComponent, "AVCEncoder");
return OK;
}
status_t AVCEncoder::start(MetaData *params) {
LOGV("start");
if (mInitCheck != OK) {
return mInitCheck;
}
if (mStarted) {
LOGW("Call start() when encoder already started");
return OK;
}
AVCEnc_Status err;
err = PVAVCEncInitialize(mHandle, mEncParams, NULL, NULL);
if (err != AVCENC_SUCCESS) {
LOGE("Failed to initialize the encoder: %d", err);
return UNKNOWN_ERROR;
}
mGroup = new MediaBufferGroup();
int32_t maxSize;
if (AVCENC_SUCCESS !=
PVAVCEncGetMaxOutputBufferSize(mHandle, &maxSize)) {
maxSize = 31584; // Magic #
}
mGroup->add_buffer(new MediaBuffer(maxSize));
mSource->start(params);
mNumInputFrames = -2; // 1st two buffers contain SPS and PPS
mStarted = true;
mSpsPpsHeaderReceived = false;
mReadyForNextFrame = true;
mIsIDRFrame = 0;
return OK;
}
status_t AVCEncoder::stop() {
LOGV("stop");
if (!mStarted) {
LOGW("Call stop() when encoder has not started");
return OK;
}
if (mInputBuffer) {
mInputBuffer->release();
mInputBuffer = NULL;
}
if (mGroup) {
delete mGroup;
mGroup = NULL;
}
if (mInputFrameData) {
delete mInputFrameData;
mInputFrameData = NULL;
}
PVAVCCleanUpEncoder(mHandle);
mSource->stop();
releaseOutputBuffers();
mStarted = false;
return OK;
}
void AVCEncoder::releaseOutputBuffers() {
LOGV("releaseOutputBuffers");
for (size_t i = 0; i < mOutputBuffers.size(); ++i) {
MediaBuffer *buffer = mOutputBuffers.editItemAt(i);
buffer->setObserver(NULL);
buffer->release();
}
mOutputBuffers.clear();
}
sp<MetaData> AVCEncoder::getFormat() {
LOGV("getFormat");
return mFormat;
}
status_t AVCEncoder::read(
MediaBuffer **out, const ReadOptions *options) {
CHECK(!options);
*out = NULL;
MediaBuffer *outputBuffer;
CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer));
// Add 4 bytes for the start code 0x00000001
uint8_t *outPtr = (uint8_t *) outputBuffer->data() + 4;
uint32_t dataLength = outputBuffer->size() - 4;
int32_t type;
AVCEnc_Status encoderStatus = AVCENC_SUCCESS;
// Combine SPS and PPS and place them in the very first output buffer
// SPS and PPS are separated by start code 0x00000001
// Assume that we have exactly one SPS and exactly one PPS.
while (!mSpsPpsHeaderReceived && mNumInputFrames <= 0) {
encoderStatus = PVAVCEncodeNAL(mHandle, outPtr, &dataLength, &type);
if (encoderStatus == AVCENC_WRONG_STATE) {
mSpsPpsHeaderReceived = true;
CHECK_EQ(0, mNumInputFrames); // 1st video frame is 0
} else {
switch (type) {
case AVC_NALTYPE_SPS:
++mNumInputFrames;
memcpy(outputBuffer->data(), "\x00\x00\x00\x01", 4);
outputBuffer->set_range(0, dataLength + 4);
outPtr += (dataLength + 4); // 4 bytes for next start code
dataLength = outputBuffer->size() -
(outputBuffer->range_length() + 4);
break;
case AVC_NALTYPE_PPS:
++mNumInputFrames;
memcpy(((uint8_t *) outputBuffer->data()) +
outputBuffer->range_length(),
"\x00\x00\x00\x01", 4);
outputBuffer->set_range(0,
dataLength + outputBuffer->range_length() + 4);
outputBuffer->meta_data()->setInt32(kKeyIsCodecConfig, 1);
outputBuffer->meta_data()->setInt64(kKeyTime, 0);
*out = outputBuffer;
return OK;
default:
LOGE("Nal type (%d) other than SPS/PPS is unexpected", type);
return UNKNOWN_ERROR;
}
}
}
// Get next input video frame
if (mReadyForNextFrame) {
if (mInputBuffer) {
mInputBuffer->release();
mInputBuffer = NULL;
}
status_t err = mSource->read(&mInputBuffer, options);
if (err != OK) {
LOGE("Failed to read input video frame: %d", err);
outputBuffer->release();
return err;
}
if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) {
outputBuffer->release();
mInputBuffer->release();
mInputBuffer = NULL;
return UNKNOWN_ERROR;
}
int64_t timeUs;
CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
outputBuffer->meta_data()->setInt64(kKeyTime, timeUs);
// When the timestamp of the current sample is the same as
// that of the previous sample, the encoding of the sample
// is bypassed, and the output length is set to 0.
if (mNumInputFrames >= 1 && mPrevTimestampUs == timeUs) {
// Frame arrives too late
mInputBuffer->release();
mInputBuffer = NULL;
outputBuffer->set_range(0, 0);
*out = outputBuffer;
return OK;
}
// Don't accept out-of-order samples
CHECK(mPrevTimestampUs < timeUs);
mPrevTimestampUs = timeUs;
AVCFrameIO videoInput;
memset(&videoInput, 0, sizeof(videoInput));
videoInput.height = ((mVideoHeight + 15) >> 4) << 4;
videoInput.pitch = ((mVideoWidth + 15) >> 4) << 4;
videoInput.coding_timestamp = (timeUs + 500) / 1000; // in ms
uint8_t *inputData = (uint8_t *) mInputBuffer->data();
if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) {
CHECK(mInputFrameData);
CHECK(mVideoColorFormat == OMX_COLOR_FormatYUV420SemiPlanar);
ConvertYUV420SemiPlanarToYUV420Planar(
inputData, mInputFrameData, mVideoWidth, mVideoHeight);
inputData = mInputFrameData;
}
CHECK(inputData != NULL);
videoInput.YCbCr[0] = inputData;
videoInput.YCbCr[1] = videoInput.YCbCr[0] + videoInput.height * videoInput.pitch;
videoInput.YCbCr[2] = videoInput.YCbCr[1] +
((videoInput.height * videoInput.pitch) >> 2);
videoInput.disp_order = mNumInputFrames;
encoderStatus = PVAVCEncSetInput(mHandle, &videoInput);
if (encoderStatus == AVCENC_SUCCESS ||
encoderStatus == AVCENC_NEW_IDR) {
mReadyForNextFrame = false;
++mNumInputFrames;
if (encoderStatus == AVCENC_NEW_IDR) {
mIsIDRFrame = 1;
}
} else {
if (encoderStatus < AVCENC_SUCCESS) {
outputBuffer->release();
return UNKNOWN_ERROR;
} else {
outputBuffer->set_range(0, 0);
*out = outputBuffer;
return OK;
}
}
}
// Encode an input video frame
CHECK(encoderStatus == AVCENC_SUCCESS ||
encoderStatus == AVCENC_NEW_IDR);
dataLength = outputBuffer->size(); // Reset the output buffer length
encoderStatus = PVAVCEncodeNAL(mHandle, outPtr, &dataLength, &type);
if (encoderStatus == AVCENC_SUCCESS) {
outputBuffer->meta_data()->setInt32(kKeyIsSyncFrame, mIsIDRFrame);
CHECK_EQ(NULL, PVAVCEncGetOverrunBuffer(mHandle));
} else if (encoderStatus == AVCENC_PICTURE_READY) {
CHECK_EQ(NULL, PVAVCEncGetOverrunBuffer(mHandle));
if (mIsIDRFrame) {
outputBuffer->meta_data()->setInt32(kKeyIsSyncFrame, mIsIDRFrame);
mIsIDRFrame = 0;
LOGV("Output an IDR frame");
}
mReadyForNextFrame = true;
AVCFrameIO recon;
if (PVAVCEncGetRecon(mHandle, &recon) == AVCENC_SUCCESS) {
PVAVCEncReleaseRecon(mHandle, &recon);
}
} else {
dataLength = 0;
mReadyForNextFrame = true;
}
if (encoderStatus < AVCENC_SUCCESS) {
outputBuffer->release();
return UNKNOWN_ERROR;
}
outputBuffer->set_range(0, dataLength);
*out = outputBuffer;
return OK;
}
int32_t AVCEncoder::allocOutputBuffers(
unsigned int sizeInMbs, unsigned int numBuffers) {
CHECK(mOutputBuffers.isEmpty());
size_t frameSize = (sizeInMbs << 7) * 3;
for (unsigned int i = 0; i < numBuffers; ++i) {
MediaBuffer *buffer = new MediaBuffer(frameSize);
buffer->setObserver(this);
mOutputBuffers.push(buffer);
}
return 1;
}
void AVCEncoder::unbindOutputBuffer(int32_t index) {
CHECK(index >= 0);
}
int32_t AVCEncoder::bindOutputBuffer(int32_t index, uint8_t **yuv) {
CHECK(index >= 0);
CHECK(index < (int32_t) mOutputBuffers.size());
int64_t timeUs;
CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
mOutputBuffers[index]->meta_data()->setInt64(kKeyTime, timeUs);
*yuv = (uint8_t *) mOutputBuffers[index]->data();
return 1;
}
void AVCEncoder::signalBufferReturned(MediaBuffer *buffer) {
}
} // namespace android