1157 lines
39 KiB
C++
1157 lines
39 KiB
C++
/*
|
|
* Copyright (C) Texas Instruments - http://www.ti.com/
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "VTCLoopback.h"
|
|
#include "IOMXEncoder.h"
|
|
#define LOG_TAG "VTC_ENC"
|
|
#define LOG_NDEBUG 0
|
|
//#define NO_MEMCOPY 1
|
|
|
|
using namespace android;
|
|
|
|
static void PrintEncoderFPS() {
|
|
static int mFrameCount = 0;
|
|
static int mLastFrameCount = 0;
|
|
static nsecs_t mLastFpsTime = 0;
|
|
static float mFps = 0;
|
|
mFrameCount++;
|
|
if (!(mFrameCount & 0x1F)) {
|
|
nsecs_t now = systemTime();
|
|
nsecs_t diff = now - mLastFpsTime;
|
|
mFps = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff;
|
|
mLastFpsTime = now;
|
|
mLastFrameCount = mFrameCount;
|
|
VTC_LOGD("Encoder: %d Frames, %f FPS", mFrameCount, mFps);
|
|
}
|
|
// XXX: mFPS has the value we want
|
|
}
|
|
|
|
static void PrintEncoderLatency(nsecs_t ts) {
|
|
static int mFrameCount = 0;
|
|
static int64_t sum_latency_ms = 0;
|
|
int64_t now, diff_ms, avg_latency;
|
|
|
|
now = systemTime()/1000;
|
|
diff_ms = (now - ts) / 1000;
|
|
sum_latency_ms += diff_ms;
|
|
mFrameCount++;
|
|
if (!(mFrameCount & 0x1F)) {
|
|
avg_latency = sum_latency_ms / 32;
|
|
sum_latency_ms = 0;
|
|
VTC_LOGD("Avg Encoder Latency: %d Frames, %llu ms", mFrameCount, avg_latency);
|
|
}
|
|
}
|
|
|
|
static uint64_t get_time_of_day_ms() {
|
|
struct timeval t0;
|
|
gettimeofday(&t0,0);
|
|
return t0.tv_sec*1000 + t0.tv_usec/1000;
|
|
}
|
|
|
|
static void PrintEffectiveBitrate(OMX_U32 filledLen) {
|
|
static int framecount = 0;
|
|
static uint64_t bytecount = 0;
|
|
static uint64_t starttime = get_time_of_day_ms();
|
|
const uint64_t wallclock = get_time_of_day_ms();
|
|
framecount++;
|
|
bytecount+=filledLen;
|
|
int delta=wallclock-starttime;
|
|
if (delta>2000) {
|
|
int fps=framecount*10000/delta;
|
|
const uint64_t br=bytecount*8*1000/delta;
|
|
//VTC_LOGI("ENCODER FPS: %d.%d",fps/10,fps-(fps/10)*10);
|
|
VTC_LOGI("ENCODER EFFECTIVE BITRATE: %llu",br);
|
|
framecount=0;
|
|
bytecount=0;
|
|
starttime = wallclock;
|
|
}
|
|
}
|
|
|
|
|
|
bool OMXEncoder::OMXCallbackHandler::Handler() {
|
|
VTC_LOGV("\n OMXCallbackHandler::Handler \n");
|
|
Ti::Utils::Message msg;
|
|
volatile int forever = 1;
|
|
|
|
while(forever) {
|
|
Ti::Utils::MessageQueue::waitForMsg(&mCommandMsgQ, NULL, NULL, -1);
|
|
{
|
|
Mutex::Autolock lock(mLock);
|
|
mCommandMsgQ.get(&msg);
|
|
}
|
|
|
|
switch ( msg.command ) {
|
|
case OMXCallbackHandler::COMMAND_PROCESS_MSG: {
|
|
omx_message *om = (omx_message*)(msg.arg1);
|
|
omx_message omsg = *om;
|
|
mOMXEncoder->on_message(omsg);
|
|
break;
|
|
}
|
|
case OMXCallbackHandler::COMMAND_EXIT: {
|
|
VTC_LOGD("Exiting OMX callback handler");
|
|
forever = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
OMXEncoder::OMXEncoder(const sp<IOMX> &omx, IOMX::node_id node, sp<MyCameraClient> camera, int width, int height, int framerate, int bitrate, char *fname, int sliceHeight):
|
|
mOMX(omx),
|
|
mNode(node),
|
|
mCameraSource(camera) {
|
|
resetParameters(width, height, framerate, bitrate, fname, sliceHeight);
|
|
}
|
|
|
|
|
|
OMXEncoder::~OMXEncoder() {
|
|
status_t err = mOMX->freeNode(mNode);
|
|
CHECK_EQ(err, (status_t)OK);
|
|
VTC_LOGD("OMX_FreeHandle completed");
|
|
}
|
|
|
|
status_t OMXEncoder::resetParameters(int width, int height, int framerate, int bitrate, char *fname, int sliceHeight) {
|
|
mWidth = width;
|
|
mHeight = height;
|
|
mFrameRate= framerate;
|
|
mBitRate = bitrate;
|
|
mSliceHeight = sliceHeight;
|
|
mAcceptingBuffers = 0;
|
|
mOutputFD = NULL;
|
|
mBufferCount = 0;
|
|
mOutputBufferCount = 4;
|
|
mCallbackSet = false;
|
|
mState = OMX_StateLoaded;
|
|
return OK;
|
|
}
|
|
|
|
void OMXEncoder::on_message(const omx_message &msg) {
|
|
switch (msg.type) {
|
|
case omx_message::EVENT:
|
|
EventHandler(msg.u.event_data.event, msg.u.event_data.data1, msg.u.event_data.data2);
|
|
break;
|
|
case omx_message::EMPTY_BUFFER_DONE:
|
|
EmptyBufferDone((OMX_BUFFERHEADERTYPE*)msg.u.extended_buffer_data.buffer);
|
|
break;
|
|
case omx_message::FILL_BUFFER_DONE:
|
|
FillBufferDone((OMX_BUFFERHEADERTYPE*)msg.u.extended_buffer_data.buffer,
|
|
msg.u.extended_buffer_data.range_offset,
|
|
msg.u.extended_buffer_data.range_length,
|
|
msg.u.extended_buffer_data.timestamp);
|
|
break;
|
|
default:
|
|
CHECK(!"############ Corrupted Message !!! #############");
|
|
break;
|
|
}
|
|
}
|
|
|
|
status_t OMXEncoder::configure(OMX_VIDEO_AVCPROFILETYPE profile, OMX_VIDEO_AVCLEVELTYPE level, OMX_U32 refFrames) {
|
|
status_t err;
|
|
LOG_FUNCTION_NAME_ENTRY
|
|
|
|
VTC_LOGV("\n\nPROFILE=%d\nLEVEL=%d\nRefFrames=%d\nWidth=%d\nHeight=%d\nFramerate=%d\nBitrate=%d\nSliceHeight=%d\n\n",
|
|
profile, level, refFrames, mWidth, mHeight, mFrameRate, mBitRate, mSliceHeight);
|
|
|
|
if ((mCallbackSet == false) && ((mDebugFlags & ENCODER_NO_FILE_WRTIE) == 0)) {
|
|
mOutputFD = fopen("/mnt/sdcard/video_0.264","w");
|
|
if (mOutputFD == NULL) {
|
|
VTC_LOGE("\n fopen failed\n");
|
|
}
|
|
VTC_LOGD("\nCallback was NULL. Opened file for writing\n");
|
|
}
|
|
|
|
// initialize omx callback handling thread
|
|
if(mOMXCallbackHandler.get() == NULL) {
|
|
mOMXCallbackHandler = new OMXCallbackHandler(this);
|
|
}
|
|
|
|
if ( NULL == mOMXCallbackHandler.get() ) {
|
|
VTC_LOGE("Couldn't create omx callback handler");
|
|
return -1;
|
|
}
|
|
|
|
err = mOMXCallbackHandler->run("OMXCallbackThread", PRIORITY_URGENT_DISPLAY);
|
|
if ( err != NO_ERROR ) {
|
|
if( err == INVALID_OPERATION) {
|
|
VTC_LOGE("omx callback handler thread already runnning!!");
|
|
err = NO_ERROR;
|
|
} else {
|
|
VTC_LOGE("Couldn't run omx callback handler thread");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
mState = OMX_StateLoaded;
|
|
|
|
OMX_VIDEO_PARAM_PORTFORMATTYPE format;
|
|
INIT_OMX_STRUCT(&format, OMX_VIDEO_PARAM_PORTFORMATTYPE);
|
|
format.nPortIndex = INPUT_PORT;
|
|
format.nIndex = 0;
|
|
bool found = false;
|
|
OMX_U32 index = 0;
|
|
for (;;) {
|
|
format.nIndex = index;
|
|
err = mOMX->getParameter(mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format));
|
|
if (err != OK) {
|
|
VTC_LOGD( "get OMX_IndexParamVideoPortFormat InPort Error:0x%x. OMX_ErrorUnsupportedIndex=0x%x", err, OMX_ErrorUnsupportedIndex);
|
|
return -1;
|
|
}
|
|
|
|
if (format.eCompressionFormat == OMX_VIDEO_CodingUnused
|
|
&& format.eColorFormat == (OMX_COLOR_FORMATTYPE)OMX_TI_COLOR_FormatYUV420PackedSemiPlanar) {
|
|
found = true;
|
|
break;
|
|
}
|
|
++index;
|
|
}
|
|
|
|
if (!found) {
|
|
VTC_LOGE("Did not find a match.");
|
|
return -1;
|
|
}
|
|
|
|
VTC_LOGV("found a match.");
|
|
err = mOMX->setParameter(mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format));
|
|
if (err != OK) {
|
|
VTC_LOGD( "set OMX_IndexParamVideoPortFormat InPort Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
format.nPortIndex = OUTPUT_PORT;
|
|
format.nIndex = 0;
|
|
found = false;
|
|
index = 0;
|
|
for (;;) {
|
|
format.nIndex = index;
|
|
err = mOMX->getParameter(mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format));
|
|
if (err != OK) {
|
|
VTC_LOGD( "get OMX_IndexParamVideoPortFormat OutPort Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
if (format.eCompressionFormat == OMX_VIDEO_CodingAVC
|
|
&& format.eColorFormat == (OMX_COLOR_FormatUnused)) {
|
|
found = true;
|
|
break;
|
|
}
|
|
++index;
|
|
}
|
|
|
|
if (!found) {
|
|
VTC_LOGE("Did not find a match.");
|
|
return -1;
|
|
}
|
|
|
|
VTC_LOGV("found a match.");
|
|
err = mOMX->setParameter(mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format));
|
|
if (err != OK) {
|
|
VTC_LOGD( "set OMX_IndexParamVideoPortFormat OutPort Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Populate Video input Port
|
|
//
|
|
INIT_OMX_STRUCT(&tInPortDef, OMX_PARAM_PORTDEFINITIONTYPE);
|
|
|
|
tInPortDef.nPortIndex = INPUT_PORT;
|
|
err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef));
|
|
if (err != OK) {
|
|
VTC_LOGD( "get OMX_IndexParamPortDefinition InPort Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
tInPortDef.format.video.nFrameWidth = mWidth;
|
|
tInPortDef.format.video.nFrameHeight = mHeight;
|
|
tInPortDef.format.video.nStride = 4096;
|
|
tInPortDef.format.video.nSliceHeight = mHeight;
|
|
tInPortDef.format.video.xFramerate = (mFrameRate << 16);
|
|
tInPortDef.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
|
|
tInPortDef.format.video.eColorFormat = (OMX_COLOR_FORMATTYPE)OMX_TI_COLOR_FormatYUV420PackedSemiPlanar;
|
|
tInPortDef.nBufferSize = (mWidth * mHeight *3)/2;
|
|
err = mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef));
|
|
if (err != OK) {
|
|
VTC_LOGD( "set OMX_IndexParamPortDefinition InPort Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_IndexParamPortDefinition InPort Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Populate Video output Port
|
|
//
|
|
INIT_OMX_STRUCT(&tOutPortDef, OMX_PARAM_PORTDEFINITIONTYPE);
|
|
tOutPortDef.nPortIndex = OUTPUT_PORT;
|
|
err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition,&tOutPortDef, sizeof(tOutPortDef));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_IndexParamPortDefinition OutPort Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
tOutPortDef.format.video.nFrameWidth = mWidth;
|
|
tOutPortDef.format.video.nFrameHeight = mHeight;
|
|
tOutPortDef.format.video.xFramerate = 0;
|
|
tOutPortDef.format.video.nBitrate = mBitRate;
|
|
tOutPortDef.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC;
|
|
tOutPortDef.format.video.eColorFormat = OMX_COLOR_FormatUnused;
|
|
tOutPortDef.nBufferCountActual = mOutputBufferCount;
|
|
|
|
err = mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, &tOutPortDef, sizeof(tOutPortDef));
|
|
if (err != OK) {
|
|
VTC_LOGD( "set OMX_IndexParamPortDefinition OutPort Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition,&tOutPortDef, sizeof(tOutPortDef));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_IndexParamPortDefinition OutPort Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// setup (code specific) AVC Encoder paramters for OUTPUT port
|
|
//
|
|
OMX_VIDEO_PARAM_AVCTYPE h264type;
|
|
INIT_OMX_STRUCT(&h264type,OMX_VIDEO_PARAM_AVCTYPE);
|
|
|
|
h264type.nPortIndex = OUTPUT_PORT;
|
|
|
|
err = mOMX->getParameter(mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_IndexParamVideoAvc failed : %d", err);
|
|
return -1;
|
|
}
|
|
|
|
h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
|
|
h264type.eProfile = profile;
|
|
h264type.eLevel = level;
|
|
h264type.nSliceHeaderSpacing = 0;
|
|
h264type.bUseHadamard = OMX_TRUE;
|
|
h264type.nRefFrames = refFrames;
|
|
h264type.nBFrames = 0;
|
|
//h264type.nPFrames = 30; // assume iFrameInterval 1, frameRate
|
|
h264type.nPFrames = 0; // Let only the first frame be an I Frame. The rest will be P Frames. For VTC type of applications, you want to insert the IDR only when necessary.
|
|
h264type.nRefIdx10ActiveMinus1 = 0;
|
|
h264type.nRefIdx11ActiveMinus1 = 0;
|
|
h264type.bEntropyCodingCABAC = OMX_FALSE;
|
|
h264type.bWeightedPPrediction = OMX_FALSE;
|
|
h264type.bconstIpred = OMX_FALSE;
|
|
h264type.bDirect8x8Inference = OMX_FALSE;
|
|
h264type.bDirectSpatialTemporal = OMX_FALSE;
|
|
h264type.nCabacInitIdc = 0;
|
|
h264type.bEnableUEP = OMX_FALSE;
|
|
h264type.bEnableFMO = OMX_FALSE;
|
|
h264type.bEnableASO = OMX_FALSE;
|
|
h264type.bEnableRS = OMX_FALSE;
|
|
h264type.bFrameMBsOnly = OMX_TRUE;
|
|
h264type.bMBAFF = OMX_FALSE;
|
|
h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable;
|
|
|
|
err = mOMX->setParameter(mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
|
|
if (err != OK) {
|
|
VTC_LOGD("set OMX_IndexParamVideoAvc failed : %d", err);
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Set Profile and Level for OUTPUT
|
|
//
|
|
OMX_VIDEO_PARAM_PROFILELEVELTYPE profileLevel;
|
|
INIT_OMX_STRUCT(&profileLevel, OMX_VIDEO_PARAM_PROFILELEVELTYPE);
|
|
profileLevel.nPortIndex = OUTPUT_PORT;
|
|
err = mOMX->getParameter(mNode, OMX_IndexParamVideoProfileLevelCurrent, &profileLevel, sizeof(profileLevel));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_IndexParamVideoProfileLevelCurrent failed : %d", err);
|
|
return -1;
|
|
}
|
|
|
|
profileLevel.eProfile = profile;
|
|
profileLevel.eLevel = level;
|
|
err = mOMX->setParameter(mNode, OMX_IndexParamVideoProfileLevelCurrent, &profileLevel, sizeof(profileLevel));
|
|
if (err != OK) {
|
|
VTC_LOGD("set OMX_IndexParamVideoProfileLevelCurrent failed : %d", err);
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Set data content type for input port
|
|
//
|
|
OMX_TI_VIDEO_PARAM_FRAMEDATACONTENTTYPE dataContent;
|
|
INIT_OMX_STRUCT(&dataContent, OMX_TI_VIDEO_PARAM_FRAMEDATACONTENTTYPE);
|
|
dataContent.nPortIndex = INPUT_PORT;
|
|
err = mOMX->getParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexParamVideoFrameDataContentSettings, &dataContent, sizeof(dataContent));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_TI_IndexParamVideoFrameDataContentSettings failed : %d", err);
|
|
return -1;
|
|
}
|
|
|
|
dataContent.eContentType = OMX_TI_Video_Progressive; //appears to be the default value
|
|
err = mOMX->setParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexParamVideoFrameDataContentSettings, &dataContent, sizeof(dataContent));
|
|
if (err != OK) {
|
|
VTC_LOGD("set OMX_TI_IndexParamVideoFrameDataContentSettings failed : %d", err);
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// setupBitRate for OUTPUT
|
|
//
|
|
OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
|
|
INIT_OMX_STRUCT(&bitrateType,OMX_VIDEO_PARAM_BITRATETYPE);
|
|
bitrateType.nPortIndex = OUTPUT_PORT;
|
|
err = mOMX->getParameter(mNode, OMX_IndexParamVideoBitrate, &bitrateType, sizeof(bitrateType));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_IndexParamVideoBitrate failed : %d", err);
|
|
return -1;
|
|
}
|
|
|
|
bitrateType.eControlRate = OMX_Video_ControlRateVariable;
|
|
bitrateType.nTargetBitrate = mBitRate;
|
|
err = mOMX->setParameter(mNode, OMX_IndexParamVideoBitrate, &bitrateType, sizeof(bitrateType));
|
|
if (err != OK) {
|
|
VTC_LOGD("set OMX_IndexParamVideoBitrate failed : %d", err);
|
|
return -1;
|
|
}
|
|
|
|
err = mOMX->storeMetaDataInBuffers(mNode, INPUT_PORT, OMX_TRUE);
|
|
if (err != OK) {
|
|
VTC_LOGE("Storing meta data in video buffers is not supported");
|
|
return -1;
|
|
}
|
|
|
|
if (mSliceHeight == 0) {
|
|
LOG_FUNCTION_NAME_EXIT
|
|
return 0;
|
|
}
|
|
|
|
/**************** Configuration specific to Slice based processing ******************/
|
|
|
|
//
|
|
// setup data sync mode for INPUT
|
|
//
|
|
OMX_VIDEO_PARAM_DATASYNCMODETYPE syncMode;
|
|
INIT_OMX_STRUCT(&syncMode, OMX_VIDEO_PARAM_DATASYNCMODETYPE);
|
|
syncMode.nPortIndex = INPUT_PORT;
|
|
err = mOMX->getParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexParamVideoDataSyncMode, &syncMode, sizeof(syncMode));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_TI_IndexParamVideoDataSyncMode failed : %d", err);
|
|
return -1;
|
|
}
|
|
|
|
syncMode.eDataMode = OMX_Video_NumMBRows;
|
|
err = mOMX->setParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexParamVideoDataSyncMode, &syncMode, sizeof(syncMode));
|
|
if (err != OK) {
|
|
VTC_LOGD("set OMX_TI_IndexParamVideoDataSyncMode failed: %d", err);
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// setup data sync mode for OUTPUT
|
|
//
|
|
INIT_OMX_STRUCT(&syncMode, OMX_VIDEO_PARAM_DATASYNCMODETYPE);
|
|
syncMode.nPortIndex = OUTPUT_PORT;
|
|
err = mOMX->getParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexParamVideoDataSyncMode, &syncMode, sizeof(syncMode));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_TI_IndexParamVideoDataSyncMode failed : %d", err);
|
|
return -1;
|
|
}
|
|
|
|
syncMode.eDataMode = OMX_Video_EntireFrame;
|
|
syncMode.nNumDataUnits = 1;
|
|
err = mOMX->setParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexParamVideoDataSyncMode, &syncMode, sizeof(syncMode));
|
|
if (err != OK) {
|
|
VTC_LOGD("set OMX_TI_IndexParamVideoDataSyncMode failed: %d", err);
|
|
return -1;
|
|
}
|
|
|
|
LOG_FUNCTION_NAME_EXIT
|
|
return 0;
|
|
}
|
|
|
|
status_t OMXEncoder::prepare() {
|
|
status_t err;
|
|
|
|
LOG_FUNCTION_NAME_ENTRY
|
|
|
|
if (setCurrentState(OMX_StateIdle)) {
|
|
return -1;
|
|
}
|
|
|
|
INIT_OMX_STRUCT(&tInPortDef, OMX_PARAM_PORTDEFINITIONTYPE);
|
|
tInPortDef.nPortIndex = INPUT_PORT;
|
|
err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_IndexParamPortDefinition InPort Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
if (mSliceHeight == 0) { // non tunnel mode
|
|
|
|
mDealer[INPUT_PORT] = new MemoryDealer((tInPortDef.nBufferCountActual*tInPortDef.nBufferSize), "RECORD_INPUT");
|
|
for (OMX_U32 i = 0; i < tInPortDef.nBufferCountActual; ++i) {
|
|
mBufferInfo[INPUT_PORT][i].mEncMem= mDealer[INPUT_PORT]->allocate(tInPortDef.nBufferSize);
|
|
CHECK(mBufferInfo[INPUT_PORT][i].mEncMem.get() != NULL);
|
|
err = mOMX->allocateBufferWithBackup(mNode, INPUT_PORT, mBufferInfo[INPUT_PORT][i].mEncMem, (void**)(&(mBufferInfo[INPUT_PORT][i].mBufferHdr)));
|
|
if (err != OK) {
|
|
VTC_LOGE("OMX_AllocateBuffer for input port index:%d failed:%d",(int)i,err);
|
|
mBufferInfo[INPUT_PORT][i].mBufferHdr = NULL;
|
|
}
|
|
}
|
|
VTC_LOGD( "Allocated %d Input port Buffers. ", (int)tInPortDef.nBufferCountActual);
|
|
}
|
|
|
|
INIT_OMX_STRUCT(&tOutPortDef, OMX_PARAM_PORTDEFINITIONTYPE);
|
|
tOutPortDef.nPortIndex = OUTPUT_PORT;
|
|
err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tOutPortDef, sizeof(tOutPortDef));
|
|
if (err != OK) {
|
|
VTC_LOGE("get OMX_IndexParamPortDefinition OutPort Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
#ifdef NO_MEMCOPY
|
|
err = allocateOutputBuffers();
|
|
if (err != OK) return -1;
|
|
#else
|
|
|
|
mDealer[OUTPUT_PORT] = new MemoryDealer((tOutPortDef.nBufferCountActual*tOutPortDef.nBufferSize), "RECORD_OUTPUT");
|
|
for (OMX_U32 i = 0; i < tOutPortDef.nBufferCountActual; ++i) {
|
|
mBufferInfo[OUTPUT_PORT][i].mEncMem = mDealer[OUTPUT_PORT]->allocate(tOutPortDef.nBufferSize);
|
|
err = mOMX->allocateBufferWithBackup(mNode, OUTPUT_PORT, mBufferInfo[OUTPUT_PORT][i].mEncMem, (void**)(&(mBufferInfo[OUTPUT_PORT][i].mBufferHdr)));
|
|
if (err != OK) {
|
|
VTC_LOGD("OMX_UseBuffer for output port index:%d failed:%d",(int)i,err);
|
|
mBufferInfo[OUTPUT_PORT][i].mBufferHdr = NULL;
|
|
return -1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
VTC_LOGD( "Allocated %d Output port Buffers. ", (int)tOutPortDef.nBufferCountActual);
|
|
|
|
LOG_FUNCTION_NAME_EXIT
|
|
return 0;
|
|
}
|
|
|
|
status_t OMXEncoder::start(MetaData *params) {
|
|
status_t err;
|
|
|
|
LOG_FUNCTION_NAME_ENTRY
|
|
|
|
// now wait until state becomes idle.
|
|
if (waitForStateSet(OMX_StateIdle)) {
|
|
VTC_LOGD("state change to IDLE failed");
|
|
return -1;
|
|
}
|
|
|
|
// If we are going to reuse the node, then port enable is a MUST
|
|
// since we are disabling the port during stop
|
|
|
|
// now transition to exec
|
|
if (setCurrentState(OMX_StateExecuting)) {
|
|
return -1;
|
|
}
|
|
|
|
if (waitForStateSet(OMX_StateExecuting)) {
|
|
VTC_LOGD("state change to EXECUTING failed");
|
|
return -1;
|
|
}
|
|
|
|
mAcceptingBuffers = 1; // let OMX callbacks to handle buffers
|
|
|
|
INIT_OMX_STRUCT(&tInPortDef, OMX_PARAM_PORTDEFINITIONTYPE);
|
|
tInPortDef.nPortIndex = INPUT_PORT;
|
|
err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_IndexParamPortDefinition InPort Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
INIT_OMX_STRUCT(&tOutPortDef, OMX_PARAM_PORTDEFINITIONTYPE);
|
|
tOutPortDef.nPortIndex = OUTPUT_PORT;
|
|
err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tOutPortDef, sizeof(tOutPortDef));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_IndexParamPortDefinition OutPort Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
// give output buffers to the OMX
|
|
for (int i=0; i<(int)tOutPortDef.nBufferCountActual; i++) {
|
|
err = mOMX->fillBuffer(mNode, mBufferInfo[OUTPUT_PORT][i].mBufferHdr);
|
|
if (err != OK) {
|
|
VTC_LOGD("OMX_FillThisBuffer failed:%d", err);
|
|
} else {
|
|
VTC_LOGD("called fillBuffer(%d)",i);
|
|
}
|
|
}
|
|
|
|
// call camera encoder_is_ready
|
|
mCameraSource->encoderReady();
|
|
|
|
if (mSliceHeight) {
|
|
LOG_FUNCTION_NAME_EXIT
|
|
return 0;
|
|
}
|
|
|
|
/************** non tunnel / frame mode ***************/
|
|
|
|
// now wait for payload to be available. when it does, call OMX_EmptyThisBuffer
|
|
int64_t time[3];
|
|
sp<IMemory> payload[3];
|
|
|
|
// wait until camera returns a frame
|
|
for (int i=0; i<3; i++) {
|
|
payload[i] = mCameraSource->getCameraPayload(time[i]);
|
|
}
|
|
|
|
for (int i=0; i<3; i++) {
|
|
mBufferInfo[INPUT_PORT][i].mCamMem = payload[i];
|
|
memcpy((uint8_t *)mBufferInfo[INPUT_PORT][i].mEncMem->pointer(), payload[i]->pointer(), payload[i]->size());
|
|
err = mOMX->emptyBuffer(mNode, mBufferInfo[INPUT_PORT][i].mBufferHdr, 0, payload[i]->size(), OMX_BUFFERFLAG_ENDOFFRAME, (OMX_TICKS)time);
|
|
if (err != OK) {
|
|
VTC_LOGD("OMX_EmptyThisBuffer failed:%d", err);
|
|
} else {
|
|
VTC_LOGD("Called EmptyThisBuffer[%d] ",i);
|
|
}
|
|
}
|
|
|
|
LOG_FUNCTION_NAME_EXIT
|
|
return 0;
|
|
}
|
|
|
|
status_t OMXEncoder::stop() {
|
|
status_t err;
|
|
|
|
LOG_FUNCTION_NAME_ENTRY
|
|
|
|
// stop mAcceptingBuffers any callbacks
|
|
mAcceptingBuffers = 0;
|
|
mCameraSource->encoderNotReady();
|
|
|
|
if (mOutputFD) {
|
|
fclose(mOutputFD);
|
|
mOutputFD = NULL;
|
|
}
|
|
|
|
if (mSliceHeight == 0) { // non tunnel mode
|
|
// flush all the buffers since mAcceptingBuffers off they won't be returned.
|
|
err = mOMX->sendCommand(mNode,OMX_CommandFlush, INPUT_PORT);
|
|
if (err != OK) {
|
|
VTC_LOGD("OMX_CommandFlush for input port (0) failed:%d", err);
|
|
}
|
|
}
|
|
|
|
err = mOMX->sendCommand(mNode,OMX_CommandFlush, OUTPUT_PORT);
|
|
if (err != OK) {
|
|
VTC_LOGD("OMX_CommandFlush for output port (1) failed:%d", err);
|
|
}
|
|
|
|
// change state to Idle if not already
|
|
if (mState != OMX_StateIdle && mState != OMX_StateLoaded) {
|
|
if (setCurrentState(OMX_StateIdle)) {
|
|
VTC_LOGD("OMX_StateIdle failed");
|
|
}
|
|
|
|
if (waitForStateSet(OMX_StateIdle)) {
|
|
VTC_LOGD("state change to IDLE failed");
|
|
}
|
|
}
|
|
|
|
// disable ports
|
|
err = mOMX->sendCommand(mNode, OMX_CommandPortDisable, -1);
|
|
if (err != OK) {
|
|
VTC_LOGD("Error in SendCommand()-OMX_CommandPortDisable:");
|
|
} else {
|
|
VTC_LOGD("OMX_CommandPortDisable done");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
status_t OMXEncoder::deinit() {
|
|
status_t err;
|
|
|
|
LOG_FUNCTION_NAME_ENTRY
|
|
|
|
if (mSliceHeight == 0) { // non tunnel mode
|
|
// free input buffers
|
|
for (int i=0; i<(int)tInPortDef.nBufferCountActual; i++) {
|
|
if (mBufferInfo[INPUT_PORT][i].mBufferHdr) {
|
|
err = mOMX->freeBuffer(mNode, INPUT_PORT, mBufferInfo[INPUT_PORT][i].mBufferHdr);
|
|
if( (err != OK)) {
|
|
VTC_LOGD("Free Buffer for Input Port buffer:%d failed:%d",i,err);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// free output buffers
|
|
for (int i=0; i <(int)tOutPortDef.nBufferCountActual; i++) {
|
|
if (mBufferInfo[OUTPUT_PORT][i].mBufferHdr) {
|
|
err = mOMX->freeBuffer(mNode,OUTPUT_PORT,mBufferInfo[OUTPUT_PORT][i].mBufferHdr);
|
|
if( (err != OK)) {
|
|
VTC_LOGD("Free Buffer for Output Port buffer:%d failed:%d",i,err);
|
|
}
|
|
}
|
|
}
|
|
|
|
// change state to Loaded
|
|
if (mState != OMX_StateLoaded) {
|
|
if (setCurrentState(OMX_StateLoaded)) {
|
|
VTC_LOGD("OMX_StateLoaded failed");
|
|
}
|
|
if (waitForStateSet(OMX_StateLoaded)) {
|
|
VTC_LOGD("state change to LOADED failed");
|
|
}
|
|
} else {
|
|
VTC_LOGD("It was already OMX_StateLoaded???");
|
|
}
|
|
|
|
usleep(5000);
|
|
|
|
|
|
//Exit and free ref to callback handling thread
|
|
if ( NULL != mOMXCallbackHandler.get() ) {
|
|
Ti::Utils::Message msg;
|
|
msg.command = OMXCallbackHandler::COMMAND_EXIT;
|
|
//Clear all messages pending first
|
|
mOMXCallbackHandler->clearCommandQ();
|
|
mOMXCallbackHandler->put(&msg);
|
|
mOMXCallbackHandler->requestExitAndWait();
|
|
mOMXCallbackHandler.clear();
|
|
}
|
|
|
|
LOG_FUNCTION_NAME_EXIT
|
|
return 0;
|
|
}
|
|
|
|
sp<MetaData> OMXEncoder::getFormat() {
|
|
return NULL;
|
|
}
|
|
|
|
status_t OMXEncoder::read(MediaBuffer **buffer, const ReadOptions *options) {
|
|
return 0;
|
|
}
|
|
|
|
|
|
OMX_ERRORTYPE OMXEncoder::EventHandler(OMX_EVENTTYPE eEvent, OMX_U32 nData1,OMX_U32 nData2) {
|
|
OMX_ERRORTYPE errorType;
|
|
VTC_LOGV("########## EventHandler: eEvent:0x%x, nData1:%d, nData2:%d, pid=%d", (int)eEvent, (int)nData1, (int)nData2, getpid());
|
|
|
|
switch (eEvent) {
|
|
case OMX_EventCmdComplete:
|
|
if (nData1 == OMX_CommandPortDisable) {
|
|
if (nData2 == OMX_DirInput) {
|
|
VTC_LOGD( "Component OMX_EventCmdComplete OMX_CommandPortDisable OMX_DirInput");
|
|
}
|
|
if (nData2 == OMX_DirOutput) {
|
|
VTC_LOGD( "Component OMX_EventCmdComplete OMX_CommandPortDisable OMX_DirOutput");
|
|
}
|
|
} else if (nData1 == OMX_CommandStateSet) {
|
|
VTC_LOGD( "Component OMX_EventCmdComplete OMX_CommandStateSet new State:%s",OMXStateName((OMX_STATETYPE)nData2));
|
|
{
|
|
Mutex::Autolock autoLock(mLock);
|
|
mState = (OMX_STATETYPE)nData2;
|
|
}
|
|
mAsyncCompletion.signal();
|
|
|
|
} else if (nData1 == OMX_CommandFlush) {
|
|
VTC_LOGD( "Component OMX_EventCmdComplete OMX_CommandFlush port:%d",(int)nData2);
|
|
} else {
|
|
VTC_LOGD( "Component OMX_EventCmdComplete command:%d",(int)nData1);
|
|
}
|
|
break;
|
|
|
|
case OMX_EventError:
|
|
errorType = (OMX_ERRORTYPE) nData1;
|
|
VTC_LOGD( "\n\n\nComponent OMX_EventError error:%x\n\n\n",errorType);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
VTC_LOGV("EXIT EventHandler");
|
|
return errorType;
|
|
}
|
|
|
|
status_t OMXEncoder::FillBufferDone(OMX_BUFFERHEADERTYPE* pBufferHdr, OMX_U32 nOffset, OMX_U32 nFilledLen, OMX_TICKS nTimeStamp) {
|
|
status_t err;
|
|
OMX_U32 i=0;
|
|
|
|
// not mAcceptingBuffers, just return, eventually, decoder will stop
|
|
if (mAcceptingBuffers == 0) {
|
|
VTC_LOGV( " in non mAcceptingBuffers mode");
|
|
return OMX_ErrorNone;
|
|
}
|
|
|
|
for (i = 0; i < tOutPortDef.nBufferCountActual; i++) {
|
|
if (pBufferHdr == mBufferInfo[OUTPUT_PORT][i].mBufferHdr) {
|
|
break;
|
|
}
|
|
}
|
|
if (i == tOutPortDef.nBufferCountActual) {
|
|
VTC_LOGE("FillBufferDone returned unknown buffer header! i=%d",(int)i);
|
|
return -1;
|
|
}
|
|
//VTC_LOGD( "----- %d ----- ", (int)i);
|
|
|
|
if (mDebugFlags & DEBUG_DUMP_ENCODER_TIMESTAMP) VTC_LOGD("FBD TS: %lld", nTimeStamp);
|
|
|
|
if (mDebugFlags & FPS_ENCODER) PrintEncoderFPS();
|
|
|
|
if (mDebugFlags & ENCODER_LATENCY) PrintEncoderLatency(nTimeStamp);
|
|
|
|
if (mDebugFlags & ENCODER_EFFECTIVE_BITRATE) PrintEffectiveBitrate(nFilledLen);
|
|
|
|
if (mCallbackSet) {
|
|
mEncodedBufferCallback((mBufferInfo[OUTPUT_PORT][i].mEncMem->pointer() + nOffset), nFilledLen, nTimeStamp);
|
|
} else {
|
|
if (mOutputFD != NULL) {
|
|
i = fwrite((unsigned char *)(mBufferInfo[OUTPUT_PORT][i].mEncMem->pointer() + nOffset), 1, nFilledLen, mOutputFD);
|
|
if (i != nFilledLen) {
|
|
VTC_LOGD("fwrite failed:%d should have been:%d\n", i, nFilledLen);
|
|
return -1;
|
|
}
|
|
fflush(mOutputFD);
|
|
}
|
|
}
|
|
|
|
err = mOMX->fillBuffer(mNode, pBufferHdr);
|
|
if (err != OK) {
|
|
VTC_LOGE("OMX_FillThisBuffer failed:%d", err);
|
|
}
|
|
|
|
mBufferCount++;
|
|
VTC_LOGV("EXIT FillBufferDone: nOffset: %d, nFilledLen=%d, mBufferCount=%d", nOffset, nFilledLen, mBufferCount);
|
|
return OMX_ErrorNone;
|
|
}
|
|
|
|
status_t OMXEncoder::EmptyBufferDone(OMX_BUFFERHEADERTYPE* pBufferHdr)
|
|
{
|
|
status_t err;
|
|
OMX_U32 i=0;
|
|
//VTC_LOGV("ENTER EmptyBufferDone");
|
|
|
|
// not mAcceptingBuffers, just return, eventually, decoder will stop
|
|
if (mAcceptingBuffers == 0) {
|
|
VTC_LOGV( " in non mAcceptingBuffers mode");
|
|
return OMX_ErrorNone;
|
|
}
|
|
|
|
for (i = 0; i < tInPortDef.nBufferCountActual; i++) {
|
|
if (pBufferHdr == mBufferInfo[INPUT_PORT][i].mBufferHdr) break;
|
|
}
|
|
|
|
if (i == tInPortDef.nBufferCountActual) {
|
|
VTC_LOGE("EmptyBufferDone returned unknown buffer header! i=%d",(int)i);
|
|
return -1;
|
|
}
|
|
|
|
mCameraSource->releaseBuffer(mBufferInfo[INPUT_PORT][i].mCamMem);
|
|
|
|
if (mSliceHeight == 0) { // non tunnel mode
|
|
|
|
// now get the next buffer and feed the encoder
|
|
sp<IMemory> payload;
|
|
int64_t time;
|
|
|
|
// wait til buffer in the camera
|
|
payload = mCameraSource->getCameraPayload(time);
|
|
if (payload != NULL) {
|
|
mBufferInfo[INPUT_PORT][i].mCamMem = payload;
|
|
memcpy((uint8_t *)mBufferInfo[INPUT_PORT][i].mEncMem->pointer(), payload->pointer(), payload->size());
|
|
err = mOMX->emptyBuffer(mNode, mBufferInfo[INPUT_PORT][i].mBufferHdr, 0, payload->size(), OMX_BUFFERFLAG_ENDOFFRAME, time);
|
|
if (err != OK) {
|
|
VTC_LOGE("OMX_EmptyThisBuffer failed:%d", err);
|
|
}
|
|
}
|
|
}
|
|
|
|
//VTC_LOGV("EXIT EmptyBufferDone");
|
|
return err;
|
|
}
|
|
|
|
status_t OMXEncoder::setCurrentState(OMX_STATETYPE newState) {
|
|
VTC_LOGV("Attempting to set state to %s.", OMXStateName(newState));
|
|
|
|
status_t err = mOMX->sendCommand(mNode, OMX_CommandStateSet, newState);
|
|
if (err != OK) {
|
|
VTC_LOGD("setCurrentState: Error:%d", err);
|
|
return -1;
|
|
}
|
|
|
|
LOG_FUNCTION_NAME_EXIT
|
|
return 0;
|
|
}
|
|
|
|
status_t OMXEncoder::waitForStateSet(OMX_STATETYPE newState) {
|
|
VTC_LOGV("waitForStateSet: Waiting to move to state %s .....", OMXStateName(newState));
|
|
|
|
if (newState == mState) {
|
|
VTC_LOGD("New State [%s] already set!", OMXStateName(newState));
|
|
return 0;
|
|
}
|
|
|
|
status_t retval = mAsyncCompletion.waitRelative(mLock, TWO_SECOND);
|
|
if (retval) {
|
|
if (errno == ETIMEDOUT) {
|
|
VTC_LOGD("$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Waiting for State change timed out $$$$$$$$$$$$$$$$$$$$$$$$$$$$");
|
|
} else {
|
|
VTC_LOGD("$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Waiting for State errno :%d, retval:%d \n", errno, retval);
|
|
}
|
|
}
|
|
|
|
if (newState == mState) {
|
|
VTC_LOGD("State [%s] Set !!!!!!!!!!!!!!!!!", OMXStateName(newState));
|
|
return 0;
|
|
}
|
|
|
|
LOG_FUNCTION_NAME_EXIT
|
|
return -1;
|
|
}
|
|
|
|
//Function to set the slice mode for video encoder output port
|
|
status_t OMXEncoder::setEncoderOutputSlice(OMX_U32 nHeight, OMX_U32 nWidth, OMX_U32 sizeBytes, OMX_U32 sizeMB) {
|
|
status_t err = 0;
|
|
|
|
VTC_LOGV("Setting Video Output Slice Mode Size in Bytes:%d, Size in MB:%d\n",sizeBytes, sizeMB);
|
|
|
|
if ((!sizeBytes) && (!sizeMB)) {
|
|
//Enc o/p slice not set
|
|
return err;
|
|
}
|
|
|
|
if (sizeBytes) {
|
|
|
|
if (nWidth <= 320) {
|
|
VTC_LOGD ("Setting the Video Encoder output slice mode NOT supported for given Resolution(width should be > 320)\n");
|
|
return err;
|
|
}
|
|
|
|
if (sizeBytes < 256) {
|
|
VTC_LOGD ("Slice size provided for Video Encoder output port too small, should be atleast 256 bytes\n");
|
|
return err;
|
|
}
|
|
|
|
OMX_VIDEO_CONFIG_SLICECODINGTYPE slicetype;
|
|
INIT_OMX_STRUCT(&slicetype, OMX_VIDEO_CONFIG_SLICECODINGTYPE);
|
|
slicetype.nPortIndex = OUTPUT_PORT;
|
|
|
|
err = mOMX->getConfig(
|
|
mNode, (OMX_INDEXTYPE)OMX_TI_IndexConfigSliceSettings, &slicetype, sizeof(slicetype));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_TI_IndexConfigSliceSettings failed : 0x%x", err);
|
|
return -1;
|
|
}
|
|
|
|
slicetype.eSliceMode = OMX_VIDEO_SLICEMODE_AVCByteSlice;
|
|
slicetype.nSlicesize = sizeBytes;
|
|
|
|
err = mOMX->setConfig(
|
|
mNode, (OMX_INDEXTYPE)OMX_TI_IndexConfigSliceSettings, &slicetype, sizeof(slicetype));
|
|
if (err != OK) {
|
|
VTC_LOGD("set OMX_TI_IndexConfigSliceSettings OMX_VIDEO_SLICEMODE_AVCByteSlice failed : 0x%x", err);
|
|
return -1;
|
|
}
|
|
|
|
} else if (sizeMB) {
|
|
|
|
if (sizeMB <= 6) {
|
|
VTC_LOGD ("Macro Block set for the Video Encoder output slice mode NOT supported (very low should be > 6) \n");
|
|
return err;
|
|
}
|
|
|
|
/* Max # of MB
|
|
1080p=8160
|
|
720p=3600
|
|
VGA=1200
|
|
*/
|
|
if (sizeMB > (((nWidth+15)>> 4) * ((nHeight+15)>> 4))) {
|
|
VTC_LOGD ("Macro Block set for the Video Encoder output slice mode is too large, should be less then \
|
|
(((PreviewWidth+15)>> 4) * ((PreviewHeight+15)>> 4)) \n");
|
|
return err;
|
|
}
|
|
|
|
OMX_VIDEO_CONFIG_SLICECODINGTYPE slicetype;
|
|
INIT_OMX_STRUCT(&slicetype, OMX_VIDEO_CONFIG_SLICECODINGTYPE);
|
|
slicetype.nPortIndex = OUTPUT_PORT;
|
|
|
|
err = mOMX->getConfig(
|
|
mNode, (OMX_INDEXTYPE)OMX_TI_IndexConfigSliceSettings, &slicetype, sizeof(slicetype));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_TI_IndexConfigSliceSettings failed : 0x%x", err);
|
|
return -1;
|
|
}
|
|
|
|
slicetype.eSliceMode = OMX_VIDEO_SLICEMODE_AVCMBSlice;
|
|
slicetype.nSlicesize = sizeMB;
|
|
|
|
err = mOMX->setConfig(
|
|
mNode, (OMX_INDEXTYPE)OMX_TI_IndexConfigSliceSettings, &slicetype, sizeof(slicetype));
|
|
if (err != OK) {
|
|
VTC_LOGD("set OMX_TI_IndexConfigSliceSettings OMX_VIDEO_SLICEMODE_AVCMBSlice failed : 0x%x", err);
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
/* Other limitations:
|
|
- Input content type should be progressive (currently nPFrames = 0 by default)
|
|
- Changing parameters at run time will not have effect until next I-frame (hence setting IDR frame forcefully below)
|
|
- Incase of doing the initial setting of nPFrames = 0 (only initial frame is I-frame and all others P-frames),
|
|
you must request an I-frame to the codec after you have set nSlicesize to see your changes take place.
|
|
*/
|
|
|
|
VTC_LOGV ("Insert IDR frame for the Encoder o/p slice mode setting to get effective \n");
|
|
//Insert IDR frame for the setting to get effective
|
|
OMX_CONFIG_INTRAREFRESHVOPTYPE voptype;
|
|
INIT_OMX_STRUCT(&voptype, OMX_CONFIG_INTRAREFRESHVOPTYPE);
|
|
voptype.nPortIndex = OUTPUT_PORT;
|
|
|
|
err = mOMX->getConfig(
|
|
mNode, OMX_IndexConfigVideoIntraVOPRefresh, &voptype, sizeof(voptype));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_IndexConfigVideoIntraVOPRefresh failed : %d", err);
|
|
return -1;
|
|
}
|
|
|
|
voptype.IntraRefreshVOP = OMX_TRUE;
|
|
err = mOMX->setConfig(
|
|
mNode, OMX_IndexConfigVideoIntraVOPRefresh, &voptype, sizeof(voptype));
|
|
if (err != OK) {
|
|
VTC_LOGD("set OMX_IndexConfigVideoIntraVOPRefresh failed : %d", err);
|
|
return -1;
|
|
}
|
|
|
|
VTC_LOGV("Insert IDR Frame DONE!!!, Setting Video Output Slice Mode\n");
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
void OMXEncoder::setCallback(EncodedBufferCallback fp) {
|
|
mEncodedBufferCallback = fp;
|
|
mCallbackSet = true;
|
|
}
|
|
|
|
status_t OMXEncoder::changeFrameRate(int framerate) {
|
|
VTC_LOGV("setConfigVideoFrameRate: %d", framerate);
|
|
OMX_CONFIG_FRAMERATETYPE framerateType;
|
|
INIT_OMX_STRUCT(&framerateType, OMX_CONFIG_FRAMERATETYPE);
|
|
framerateType.nPortIndex = INPUT_PORT;
|
|
|
|
status_t err = mOMX->getConfig(
|
|
mNode, OMX_IndexConfigVideoFramerate,
|
|
&framerateType, sizeof(framerateType));
|
|
if (err != OK) {
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
framerateType.xEncodeFramerate = framerate << 16;
|
|
err = mOMX->setConfig(mNode, OMX_IndexConfigVideoFramerate, &framerateType, sizeof(framerateType));
|
|
if (err != OK) {
|
|
return BAD_VALUE;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
status_t OMXEncoder::changeBitRate(int bitrate) {
|
|
OMX_VIDEO_CONFIG_BITRATETYPE bitrateType;
|
|
INIT_OMX_STRUCT(&bitrateType,OMX_VIDEO_CONFIG_BITRATETYPE);
|
|
bitrateType.nPortIndex = OUTPUT_PORT;
|
|
status_t err = mOMX->getConfig(mNode, OMX_IndexConfigVideoBitrate, &bitrateType, sizeof(bitrateType));
|
|
if (err != OMX_ErrorNone) {
|
|
VTC_LOGE("get OMX_IndexConfigVideoBitrate failed err:%X", err);
|
|
return err;
|
|
}
|
|
|
|
VTC_LOGD("\nSet encoder bitrate to %d.\n\n", bitrate);
|
|
bitrateType.nEncodeBitrate = bitrate;
|
|
err = mOMX->setConfig(mNode, OMX_IndexConfigVideoBitrate, &bitrateType, sizeof(bitrateType));
|
|
if (err != OMX_ErrorNone) {
|
|
VTC_LOGE("set OMX_IndexConfigVideoBitrate failed error:%x", err);
|
|
return err;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
#ifdef NO_MEMCOPY
|
|
status_t OMXEncoder::allocateOutputBuffer() {
|
|
//USE_ION_BUFFERS_ALLOCATED_BY_DOMX
|
|
|
|
int ion_fd = ion_open();
|
|
if(ion_fd == 0) {
|
|
VTC_LOGE("ion_open failed!!!");
|
|
return -1;
|
|
}
|
|
|
|
struct ion_handle *importedHandle = NULL;
|
|
|
|
for (OMX_U32 i = 0; i < tOutPortDef.nBufferCountActual; ++i) {
|
|
|
|
IOMX::buffer_id buffer;
|
|
void *pBuffer;
|
|
err = mOMX->allocateBuffer(mNode, OUTPUT_PORT, tOutPortDef.nBufferSize, &buffer, &pBuffer);
|
|
if (err != OK) {
|
|
VTC_LOGD("OMX_UseBuffer for output port index:%d failed:%d",(int)i,err);
|
|
mBufferInfo[OUTPUT_PORT][i].mBufferHdr = NULL;
|
|
}
|
|
|
|
OMX_TI_ION_SHARE_FD shareFDParam;
|
|
INIT_OMX_STRUCT(&shareFDParam, OMX_TI_ION_SHARE_FD);
|
|
shareFDParam.nPortIndex = OUTPUT_PORT;
|
|
shareFDParam.nBufferIndex = i;
|
|
err = mOMX->getParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexIONBufferShareHandle, (void*)&shareFDParam, sizeof(shareFDParam));
|
|
if (err != OK) {
|
|
VTC_LOGD("get OMX_TI_IndexIONBufferShareHandle Error:%d", err);
|
|
return -1;
|
|
}
|
|
VTC_LOGD("ENC SHARE FD = %d", shareFDParam.nShareFD);
|
|
|
|
OMX_U32 nSize = (tOutPortDef.nBufferSize + LINUX_PAGE_SIZE - 1) & ~(LINUX_PAGE_SIZE - 1);
|
|
|
|
err = ion_import(ion_fd, shareFDParam.nShareFD, &importedHandle);
|
|
if (err != OK) {
|
|
VTC_LOGD("ion_import failed. ret = %d", err);
|
|
return -1;
|
|
}
|
|
VTC_LOGD("IMPORT SUCCEEDED");
|
|
|
|
int mmap_fd;
|
|
void *pIONBuffer;
|
|
err = ion_map(ion_fd, importedHandle, nSize, PROT_READ | PROT_WRITE, MAP_SHARED, 0, (unsigned char**)&pIONBuffer, &mmap_fd);
|
|
if (err) {
|
|
VTC_LOGE("\n\n$$$$$$$$$$$$$$$$ Userspace mapping of ION buffers returned error %d\n\n", err);
|
|
err = ion_free(ion_fd, h);
|
|
if (err) VTC_LOGE("\n ion_free failed err=%d.\n\n%s\n\n", err, strerror(errno));
|
|
return -1;
|
|
}
|
|
// TODO: More work needs to be done here.. This is just a skeleton for the moment.
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
|