blob: b14f807bc5562a73b68c884a8cbe0e83f188459d [file] [log] [blame]
/*--------------------------------------------------------------------------
Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of The Linux Foundation nor
the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS 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.
--------------------------------------------------------------------------*/
/*============================================================================
V E N C _ T E S T. C P P
DESCRIPTION
This is the OMX test app .
REFERENCES
============================================================================*/
//usage
// FILE QVGA MP4 24 384000 100 enc_qvga.yuv QVGA_24.m4v
// FILE QCIF MP4 15 96000 0 foreman.qcif.yuv output_qcif.m4v
// FILE VGA MP4 24 1200000 218 enc_vga.yuv vga_output.m4v
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/mman.h>
//#include <sys/time.h>
#include <time.h>
#include <sys/ioctl.h>
#include <limits.h>
#include <string.h>
//#include <sys/stat.h>
#include "OMX_QCOMExtns.h"
#include "OMX_Core.h"
#define QCOM_EXT 1
#include "OMX_Core.h"
#include "OMX_Video.h"
#include "OMX_Component.h"
#include "camera_test.h"
#include "fb_test.h"
#include "venc_util.h"
#include "extra_data_handler.h"
#ifdef USE_ION
#include <linux/msm_ion.h>
#endif
#ifdef _MSM8974_
#include <media/msm_media_info.h>
#endif
//////////////////////////
// MACROS
//////////////////////////
#define CHK(result) if ((result != OMX_ErrorNone) && (result != OMX_ErrorNoMore)) { E("*************** error *************"); exit(0); }
#define TEST_LOG
#ifdef VENC_SYSLOG
#include <cutils/log.h>
/// Debug message macro
#define D(fmt, ...) ALOGE("venc_test Debug %s::%d "fmt, \
__FUNCTION__, __LINE__, \
## __VA_ARGS__)
/// Error message macro
#define E(fmt, ...) ALOGE("venc_test Error %s::%d "fmt, \
__FUNCTION__, __LINE__, \
## __VA_ARGS__)
#else
#ifdef TEST_LOG
#define D(fmt, ...) fprintf(stderr, "venc_test Debug %s::%d "fmt"\n", \
__FUNCTION__, __LINE__, \
## __VA_ARGS__)
/// Error message macro
#define E(fmt, ...) fprintf(stderr, "venc_test Error %s::%d "fmt"\n", \
__FUNCTION__, __LINE__, \
## __VA_ARGS__)
#else
#define D(fmt, ...)
#define E(fmt, ...)
#endif
#endif
//////////////////////////
// CONSTANTS
//////////////////////////
static const int MAX_MSG = 100;
//#warning do not hardcode these use port definition
static const int PORT_INDEX_IN = 0;
static const int PORT_INDEX_OUT = 1;
static const int NUM_IN_BUFFERS = 10;
static const int NUM_OUT_BUFFERS = 10;
unsigned int num_in_buffers = 0;
unsigned int num_out_buffers = 0;
//////////////////////////
/* MPEG4 profile and level table*/
static const unsigned int mpeg4_profile_level_table[][5]= {
/*max mb per frame, max mb per sec, max bitrate, level, profile*/
{99,1485,64000,OMX_VIDEO_MPEG4Level0,OMX_VIDEO_MPEG4ProfileSimple},
{99,1485,64000,OMX_VIDEO_MPEG4Level1,OMX_VIDEO_MPEG4ProfileSimple},
{396,5940,128000,OMX_VIDEO_MPEG4Level2,OMX_VIDEO_MPEG4ProfileSimple},
{396,11880,384000,OMX_VIDEO_MPEG4Level3,OMX_VIDEO_MPEG4ProfileSimple},
{1200,36000,4000000,OMX_VIDEO_MPEG4Level4a,OMX_VIDEO_MPEG4ProfileSimple},
{1620,40500,8000000,OMX_VIDEO_MPEG4Level5,OMX_VIDEO_MPEG4ProfileSimple},
{3600,108000,12000000,OMX_VIDEO_MPEG4Level5,OMX_VIDEO_MPEG4ProfileSimple},
#ifdef _MSM8974_
{32400,972000,20000000,OMX_VIDEO_MPEG4Level5,OMX_VIDEO_MPEG4ProfileSimple},
{34560,1036800,20000000,OMX_VIDEO_MPEG4Level5,OMX_VIDEO_MPEG4ProfileSimple},
#endif
{0,0,0,0,0},
{99,1485,128000,OMX_VIDEO_MPEG4Level0,OMX_VIDEO_MPEG4ProfileAdvancedSimple},
{99,1485,128000,OMX_VIDEO_MPEG4Level1,OMX_VIDEO_MPEG4ProfileAdvancedSimple},
{396,5940,384000,OMX_VIDEO_MPEG4Level2,OMX_VIDEO_MPEG4ProfileAdvancedSimple},
{396,11880,768000,OMX_VIDEO_MPEG4Level3,OMX_VIDEO_MPEG4ProfileAdvancedSimple},
{792,23760,3000000,OMX_VIDEO_MPEG4Level4,OMX_VIDEO_MPEG4ProfileAdvancedSimple},
{1620,48600,8000000,OMX_VIDEO_MPEG4Level5,OMX_VIDEO_MPEG4ProfileAdvancedSimple},
#ifdef _MSM8974_
{32400,972000,20000000,OMX_VIDEO_MPEG4Level5,OMX_VIDEO_MPEG4ProfileAdvancedSimple},
{34560,1036800,20000000,OMX_VIDEO_MPEG4Level5,OMX_VIDEO_MPEG4ProfileAdvancedSimple},
#endif
{0,0,0,0,0},
};
/* H264 profile and level table*/
static const unsigned int h264_profile_level_table[][5]= {
/*max mb per frame, max mb per sec, max bitrate, level, profile*/
{99,1485,64000,OMX_VIDEO_AVCLevel1,OMX_VIDEO_AVCProfileBaseline},
{99,1485,128000,OMX_VIDEO_AVCLevel1b,OMX_VIDEO_AVCProfileBaseline},
{396,3000,192000,OMX_VIDEO_AVCLevel11,OMX_VIDEO_AVCProfileBaseline},
{396,6000,384000,OMX_VIDEO_AVCLevel12,OMX_VIDEO_AVCProfileBaseline},
{396,11880,768000,OMX_VIDEO_AVCLevel13,OMX_VIDEO_AVCProfileBaseline},
{396,11880,2000000,OMX_VIDEO_AVCLevel2,OMX_VIDEO_AVCProfileBaseline},
{792,19800,4000000,OMX_VIDEO_AVCLevel21,OMX_VIDEO_AVCProfileBaseline},
{1620,20250,4000000,OMX_VIDEO_AVCLevel22,OMX_VIDEO_AVCProfileBaseline},
{1620,40500,10000000,OMX_VIDEO_AVCLevel3,OMX_VIDEO_AVCProfileBaseline},
{3600,108000,14000000,OMX_VIDEO_AVCLevel31,OMX_VIDEO_AVCProfileBaseline},
{5120,216000,20000000,OMX_VIDEO_AVCLevel32,OMX_VIDEO_AVCProfileBaseline},
{8192,245760,20000000,OMX_VIDEO_AVCLevel4,OMX_VIDEO_AVCProfileBaseline},
#ifdef _MSM8974_
{32400,972000,20000000,OMX_VIDEO_AVCLevel4,OMX_VIDEO_AVCProfileBaseline},
{34560,1036800,20000000,OMX_VIDEO_AVCLevel4,OMX_VIDEO_AVCProfileBaseline},
#endif
{0,0,0,0,0},
{99,1485,64000,OMX_VIDEO_AVCLevel1,OMX_VIDEO_AVCProfileHigh},
{99,1485,160000,OMX_VIDEO_AVCLevel1b,OMX_VIDEO_AVCProfileHigh},
{396,3000,240000,OMX_VIDEO_AVCLevel11,OMX_VIDEO_AVCProfileHigh},
{396,6000,480000,OMX_VIDEO_AVCLevel12,OMX_VIDEO_AVCProfileHigh},
{396,11880,960000,OMX_VIDEO_AVCLevel13,OMX_VIDEO_AVCProfileHigh},
{396,11880,2500000,OMX_VIDEO_AVCLevel2,OMX_VIDEO_AVCProfileHigh},
{792,19800,5000000,OMX_VIDEO_AVCLevel21,OMX_VIDEO_AVCProfileHigh},
{1620,20250,5000000,OMX_VIDEO_AVCLevel22,OMX_VIDEO_AVCProfileHigh},
{1620,40500,12500000,OMX_VIDEO_AVCLevel3,OMX_VIDEO_AVCProfileHigh},
{3600,108000,17500000,OMX_VIDEO_AVCLevel31,OMX_VIDEO_AVCProfileHigh},
{5120,216000,25000000,OMX_VIDEO_AVCLevel32,OMX_VIDEO_AVCProfileHigh},
{8192,245760,25000000,OMX_VIDEO_AVCLevel4,OMX_VIDEO_AVCProfileHigh},
#ifdef _MSM8974_
{32400,972000,20000000,OMX_VIDEO_AVCLevel4,OMX_VIDEO_AVCProfileHigh},
{34560,1036800,20000000,OMX_VIDEO_AVCLevel4,OMX_VIDEO_AVCProfileHigh},
#endif
{0,0,0,0,0},
{99,1485,64000,OMX_VIDEO_AVCLevel1,OMX_VIDEO_AVCProfileMain},
{99,1485,128000,OMX_VIDEO_AVCLevel1b,OMX_VIDEO_AVCProfileMain},
{396,3000,192000,OMX_VIDEO_AVCLevel11,OMX_VIDEO_AVCProfileMain},
{396,6000,384000,OMX_VIDEO_AVCLevel12,OMX_VIDEO_AVCProfileMain},
{396,11880,768000,OMX_VIDEO_AVCLevel13,OMX_VIDEO_AVCProfileMain},
{396,11880,2000000,OMX_VIDEO_AVCLevel2,OMX_VIDEO_AVCProfileMain},
{792,19800,4000000,OMX_VIDEO_AVCLevel21,OMX_VIDEO_AVCProfileMain},
{1620,20250,4000000,OMX_VIDEO_AVCLevel22,OMX_VIDEO_AVCProfileMain},
{1620,40500,10000000,OMX_VIDEO_AVCLevel3,OMX_VIDEO_AVCProfileMain},
{3600,108000,14000000,OMX_VIDEO_AVCLevel31,OMX_VIDEO_AVCProfileMain},
{5120,216000,20000000,OMX_VIDEO_AVCLevel32,OMX_VIDEO_AVCProfileMain},
{8192,245760,20000000,OMX_VIDEO_AVCLevel4,OMX_VIDEO_AVCProfileMain},
#ifdef _MSM8974_
{32400,972000,20000000,OMX_VIDEO_AVCLevel4,OMX_VIDEO_AVCProfileMain},
{34560,1036800,20000000,OMX_VIDEO_AVCLevel4,OMX_VIDEO_AVCProfileMain},
#endif
{0,0,0,0,0}
};
/* H263 profile and level table*/
static const unsigned int h263_profile_level_table[][5]= {
/*max mb per frame, max mb per sec, max bitrate, level, profile*/
{99,1485,64000,OMX_VIDEO_H263Level10,OMX_VIDEO_H263ProfileBaseline},
{396,5940,128000,OMX_VIDEO_H263Level20,OMX_VIDEO_H263ProfileBaseline},
{396,11880,384000,OMX_VIDEO_H263Level30,OMX_VIDEO_H263ProfileBaseline},
{396,11880,2048000,OMX_VIDEO_H263Level40,OMX_VIDEO_H263ProfileBaseline},
{99,1485,128000,OMX_VIDEO_H263Level45,OMX_VIDEO_H263ProfileBaseline},
{396,19800,4096000,OMX_VIDEO_H263Level50,OMX_VIDEO_H263ProfileBaseline},
{810,40500,8192000,OMX_VIDEO_H263Level60,OMX_VIDEO_H263ProfileBaseline},
{1620,81000,16384000,OMX_VIDEO_H263Level70,OMX_VIDEO_H263ProfileBaseline},
#ifdef _MSM8974_
{32400,972000,20000000,OMX_VIDEO_H263Level60,OMX_VIDEO_H263ProfileBaseline},
{34560,1036800,20000000,OMX_VIDEO_H263Level70,OMX_VIDEO_H263ProfileBaseline},
#endif
{0,0,0,0,0}
};
#ifdef _MSM8974_
static const unsigned int VP8_profile_level_table[][5]= {
/*max mb per frame, max mb per sec, max bitrate, level, profile*/
{99,1485,64000,OMX_VIDEO_H263Level10,OMX_VIDEO_H263ProfileBaseline},
{396,5940,128000,OMX_VIDEO_H263Level20,OMX_VIDEO_H263ProfileBaseline},
{396,11880,384000,OMX_VIDEO_H263Level30,OMX_VIDEO_H263ProfileBaseline},
{396,11880,2048000,OMX_VIDEO_H263Level40,OMX_VIDEO_H263ProfileBaseline},
{99,1485,128000,OMX_VIDEO_H263Level45,OMX_VIDEO_H263ProfileBaseline},
{396,19800,4096000,OMX_VIDEO_H263Level50,OMX_VIDEO_H263ProfileBaseline},
{810,40500,8192000,OMX_VIDEO_H263Level60,OMX_VIDEO_H263ProfileBaseline},
{1620,81000,16384000,OMX_VIDEO_H263Level70,OMX_VIDEO_H263ProfileBaseline},
{32400,972000,20000000,OMX_VIDEO_H263Level70,OMX_VIDEO_H263ProfileBaseline},
{34560,1036800,20000000,OMX_VIDEO_H263Level70,OMX_VIDEO_H263ProfileBaseline},
{0,0,0,0,0}
};
#endif
#define Log2(number, power) { OMX_U32 temp = number; power = 0; while( (0 == (temp & 0x1)) && power < 16) { temp >>=0x1; power++; } }
#define FractionToQ16(q,num,den) { OMX_U32 power; Log2(den,power); q = num << (16 - power); }
//////////////////////////
// TYPES
//////////////////////////
struct ProfileType {
OMX_VIDEO_CODINGTYPE eCodec;
OMX_VIDEO_MPEG4LEVELTYPE eLevel;
OMX_VIDEO_CONTROLRATETYPE eControlRate;
OMX_VIDEO_AVCSLICEMODETYPE eSliceMode;
OMX_U32 nFrameWidth;
OMX_U32 nFrameHeight;
OMX_U32 nFrameBytes;
#ifdef _MSM8974_
OMX_U32 nFramestride;
OMX_U32 nFrameScanlines;
OMX_U32 nFrameRead;
#endif
OMX_U32 nBitrate;
float nFramerate;
char* cInFileName;
char* cOutFileName;
OMX_U32 nUserProfile;
};
enum MsgId {
MSG_ID_OUTPUT_FRAME_DONE,
MSG_ID_INPUT_FRAME_DONE,
MSG_ID_MAX
};
union MsgData {
struct {
OMX_BUFFERHEADERTYPE* pBuffer;
} sBitstreamData;
};
struct Msg {
MsgId id;
MsgData data;
};
struct MsgQ {
Msg q[MAX_MSG];
int head;
int size;
};
enum Mode {
MODE_PREVIEW,
MODE_DISPLAY,
MODE_PROFILE,
MODE_FILE_ENCODE,
MODE_LIVE_ENCODE
};
enum ResyncMarkerType {
RESYNC_MARKER_NONE, ///< No resync marker
RESYNC_MARKER_BYTE, ///< BYTE Resync marker for MPEG4, H.264
RESYNC_MARKER_MB, ///< MB resync marker for MPEG4, H.264
RESYNC_MARKER_GOB ///< GOB resync marker for H.263
};
union DynamicConfigData {
OMX_VIDEO_CONFIG_BITRATETYPE bitrate;
OMX_CONFIG_FRAMERATETYPE framerate;
QOMX_VIDEO_INTRAPERIODTYPE intraperiod;
OMX_CONFIG_INTRAREFRESHVOPTYPE intravoprefresh;
OMX_CONFIG_ROTATIONTYPE rotation;
float f_framerate;
};
struct DynamicConfig {
bool pending;
unsigned frame_num;
OMX_INDEXTYPE config_param;
union DynamicConfigData config_data;
};
#ifdef USE_ION
struct enc_ion {
int ion_device_fd;
struct ion_allocation_data alloc_data;
struct ion_fd_data ion_alloc_fd;
};
#endif
//////////////////////////
// MODULE VARS
//////////////////////////
static pthread_mutex_t m_mutex;
static pthread_cond_t m_signal;
static MsgQ m_sMsgQ;
//#warning determine how many buffers we really have
OMX_STATETYPE m_eState = OMX_StateInvalid;
OMX_COMPONENTTYPE m_sComponent;
OMX_HANDLETYPE m_hHandle = NULL;
OMX_BUFFERHEADERTYPE* m_pOutBuffers[NUM_OUT_BUFFERS] = {NULL};
OMX_BUFFERHEADERTYPE* m_pInBuffers[NUM_IN_BUFFERS] = {NULL};
OMX_BOOL m_bInFrameFree[NUM_IN_BUFFERS];
ProfileType m_sProfile;
static int m_nFramePlay = 0;
static int m_eMode = MODE_PREVIEW;
static int m_nInFd = -1;
static int m_nOutFd = -1;
static int m_nTimeStamp = 0;
static int m_nFrameIn = 0; // frames pushed to encoder
static int m_nFrameOut = 0; // frames returned by encoder
static int m_nAVCSliceMode = 0;
static bool m_bWatchDogKicked = false;
FILE *m_pDynConfFile = NULL;
static struct DynamicConfig dynamic_config;
/* Statistics Logging */
static long long tot_bufsize = 0;
int ebd_cnt=0, fbd_cnt=0;
#ifdef USE_ION
static const char* PMEM_DEVICE = "/dev/ion";
#elif MAX_RES_720P
static const char* PMEM_DEVICE = "/dev/pmem_adsp";
#elif MAX_RES_1080P_EBI
static const char* PMEM_DEVICE = "/dev/pmem_adsp";
#elif MAX_RES_1080P
static const char* PMEM_DEVICE = "/dev/pmem_smipool";
#else
#error PMEM_DEVICE cannot be determined.
#endif
#ifdef USE_ION
struct enc_ion ion_data;
#endif
//////////////////////////
// MODULE FUNCTIONS
//////////////////////////
void* PmemMalloc(OMX_QCOM_PLATFORM_PRIVATE_PMEM_INFO* pMem, int nSize)
{
void *pvirt = NULL;
int rc = 0;
if (!pMem)
return NULL;
#ifdef USE_ION
ion_data.ion_device_fd = open (PMEM_DEVICE, O_RDONLY);
if (ion_data.ion_device_fd < 0) {
E("\nERROR: ION Device open() Failed");
return NULL;
}
nSize = (nSize + 4095) & (~4095);
ion_data.alloc_data.len = nSize;
ion_data.alloc_data.heap_mask = 0x1 << ION_CP_MM_HEAP_ID;
ion_data.alloc_data.align = 4096;
ion_data.alloc_data.flags = ION_SECURE;
rc = ioctl(ion_data.ion_device_fd,ION_IOC_ALLOC,&ion_data.alloc_data);
if (rc || !ion_data.alloc_data.handle) {
E("\n ION ALLOC memory failed rc: %d, handle: %p", rc, ion_data.alloc_data.handle);
ion_data.alloc_data.handle=NULL;
return NULL;
}
ion_data.ion_alloc_fd.handle = ion_data.alloc_data.handle;
rc = ioctl(ion_data.ion_device_fd,ION_IOC_MAP,&ion_data.ion_alloc_fd);
if (rc) {
E("\n ION MAP failed ");
ion_data.ion_alloc_fd.fd =-1;
ion_data.ion_alloc_fd.fd =-1;
return NULL;
}
pMem->pmem_fd = ion_data.ion_alloc_fd.fd;
#else
pMem->pmem_fd = open(PMEM_DEVICE, O_RDWR);
if ((int)(pMem->pmem_fd) < 0)
return NULL;
nSize = (nSize + 4095) & (~4095);
#endif
pMem->offset = 0;
pvirt = mmap(NULL, nSize,
PROT_READ | PROT_WRITE,
MAP_SHARED, pMem->pmem_fd, pMem->offset);
if (pvirt == (void*) MAP_FAILED) {
close(pMem->pmem_fd);
pMem->pmem_fd = -1;
#ifdef USE_ION
if (ioctl(ion_data.ion_device_fd,ION_IOC_FREE,
&ion_data.alloc_data.handle)) {
E("ion recon buffer free failed");
}
ion_data.alloc_data.handle = NULL;
ion_data.ion_alloc_fd.fd =-1;
close(ion_data.ion_device_fd);
ion_data.ion_device_fd =-1;
#endif
return NULL;
}
D("allocated pMem->fd = %lu pvirt=0x%p, pMem->phys=0x%lx, size = %d", pMem->pmem_fd,
pvirt, pMem->offset, nSize);
return pvirt;
}
int PmemFree(OMX_QCOM_PLATFORM_PRIVATE_PMEM_INFO* pMem, void* pvirt, int nSize)
{
if (!pMem || !pvirt)
return -1;
nSize = (nSize + 4095) & (~4095);
munmap(pvirt, nSize);
close(pMem->pmem_fd);
pMem->pmem_fd = -1;
#ifdef USE_ION
if (ioctl(ion_data.ion_device_fd,ION_IOC_FREE,
&ion_data.alloc_data.handle)) {
E("ion recon buffer free failed");
}
ion_data.alloc_data.handle = NULL;
ion_data.ion_alloc_fd.fd =-1;
close(ion_data.ion_device_fd);
ion_data.ion_device_fd =-1;
#endif
return 0;
}
void PrintFramePackArrangement(OMX_QCOM_FRAME_PACK_ARRANGEMENT framePackingArrangement)
{
printf("id (%lu)\n",
framePackingArrangement.id);
printf("cancel_flag (%lu)\n",
framePackingArrangement.cancel_flag);
printf("type (%lu)\n",
framePackingArrangement.type);
printf("quincunx_sampling_flag (%lu)\n",
framePackingArrangement.quincunx_sampling_flag);
printf("content_interpretation_type (%lu)\n",
framePackingArrangement.content_interpretation_type);
printf("spatial_flipping_flag (%lu)\n",
framePackingArrangement.spatial_flipping_flag);
printf("frame0_flipped_flag (%lu)\n",
framePackingArrangement.frame0_flipped_flag);
printf("field_views_flag (%lu)\n",
framePackingArrangement.field_views_flag);
printf("current_frame_is_frame0_flag (%lu)\n",
framePackingArrangement.current_frame_is_frame0_flag);
printf("frame0_self_contained_flag (%lu)\n",
framePackingArrangement.frame0_self_contained_flag);
printf("frame1_self_contained_flag (%lu)\n",
framePackingArrangement.frame1_self_contained_flag);
printf("frame0_grid_position_x (%lu)\n",
framePackingArrangement.frame0_grid_position_x);
printf("frame0_grid_position_y (%lu)\n",
framePackingArrangement.frame0_grid_position_y);
printf("frame1_grid_position_x (%lu)\n",
framePackingArrangement.frame1_grid_position_x);
printf("frame1_grid_position_y (%lu)\n",
framePackingArrangement.frame1_grid_position_y);
printf("reserved_byte (%lu)\n",
framePackingArrangement.reserved_byte);
printf("repetition_period (%lu)\n",
framePackingArrangement.repetition_period);
printf("extension_flag (%lu)\n",
framePackingArrangement.extension_flag);
}
void SetState(OMX_STATETYPE eState)
{
#define GOTO_STATE(eState) \
case eState: \
{ \
D("Going to state " # eState"..."); \
OMX_SendCommand(m_hHandle, \
OMX_CommandStateSet, \
(OMX_U32) eState, \
NULL); \
while (m_eState != eState) \
{ \
sleep(1); \
} \
D("Now in state " # eState); \
break; \
}
switch (eState) {
GOTO_STATE(OMX_StateLoaded);
GOTO_STATE(OMX_StateIdle);
GOTO_STATE(OMX_StateExecuting);
GOTO_STATE(OMX_StateInvalid);
GOTO_STATE(OMX_StateWaitForResources);
GOTO_STATE(OMX_StatePause);
}
}
////////////////////////////////////////////////////////////////////////////////
OMX_ERRORTYPE ConfigureEncoder()
{
OMX_ERRORTYPE result = OMX_ErrorNone;
unsigned const int *profile_tbl = (unsigned int const *)mpeg4_profile_level_table;
OMX_U32 mb_per_sec, mb_per_frame;
bool profile_level_found = false;
OMX_U32 eProfile,eLevel;
OMX_PARAM_PORTDEFINITIONTYPE portdef; // OMX_IndexParamPortDefinition
#ifdef QCOM_EXT
OMX_QCOM_PARAM_PORTDEFINITIONTYPE qPortDefnType;
#endif
portdef.nPortIndex = (OMX_U32) 0; // input
result = OMX_GetParameter(m_hHandle,
OMX_IndexParamPortDefinition,
&portdef);
E("\n OMX_IndexParamPortDefinition Get Paramter on input port");
CHK(result);
portdef.format.video.nFrameWidth = m_sProfile.nFrameWidth;
portdef.format.video.nFrameHeight = m_sProfile.nFrameHeight;
E ("\n Height %lu width %lu bit rate %lu",portdef.format.video.nFrameHeight
,portdef.format.video.nFrameWidth,portdef.format.video.nBitrate);
result = OMX_SetParameter(m_hHandle,
OMX_IndexParamPortDefinition,
&portdef);
E("\n OMX_IndexParamPortDefinition Set Paramter on input port");
CHK(result);
// once more to get proper buffer size
result = OMX_GetParameter(m_hHandle,
OMX_IndexParamPortDefinition,
&portdef);
E("\n OMX_IndexParamPortDefinition Get Paramter on input port, 2nd pass");
CHK(result);
// update size accordingly
m_sProfile.nFrameBytes = portdef.nBufferSize;
portdef.nPortIndex = (OMX_U32) 1; // output
result = OMX_GetParameter(m_hHandle,
OMX_IndexParamPortDefinition,
&portdef);
E("\n OMX_IndexParamPortDefinition Get Paramter on output port");
CHK(result);
portdef.format.video.nFrameWidth = m_sProfile.nFrameWidth;
portdef.format.video.nFrameHeight = m_sProfile.nFrameHeight;
portdef.format.video.nBitrate = m_sProfile.nBitrate;
FractionToQ16(portdef.format.video.xFramerate,(int) (m_sProfile.nFramerate * 2),2);
result = OMX_SetParameter(m_hHandle,
OMX_IndexParamPortDefinition,
&portdef);
E("\n OMX_IndexParamPortDefinition Set Paramter on output port");
CHK(result);
#ifdef QCOM_EXT
qPortDefnType.nPortIndex = PORT_INDEX_IN;
qPortDefnType.nMemRegion = OMX_QCOM_MemRegionEBI1;
qPortDefnType.nSize = sizeof(OMX_QCOM_PARAM_PORTDEFINITIONTYPE);
result = OMX_SetParameter(m_hHandle,
(OMX_INDEXTYPE)OMX_QcomIndexPortDefn,
&qPortDefnType);
#endif
if (!m_sProfile.nUserProfile) { // profile not set by user, go ahead with table calculation
//validate the ht,width,fps,bitrate and set the appropriate profile and level
if (m_sProfile.eCodec == OMX_VIDEO_CodingMPEG4) {
profile_tbl = (unsigned int const *)mpeg4_profile_level_table;
} else if (m_sProfile.eCodec == OMX_VIDEO_CodingAVC) {
profile_tbl = (unsigned int const *)h264_profile_level_table;
} else if (m_sProfile.eCodec == OMX_VIDEO_CodingH263) {
profile_tbl = (unsigned int const *)h263_profile_level_table;
}
#ifdef _MSM8974_
else if (m_sProfile.eCodec == OMX_VIDEO_CodingVPX) {
profile_tbl = (unsigned int const *)VP8_profile_level_table;
}
#endif
mb_per_frame = ((m_sProfile.nFrameHeight+15)>>4)*
((m_sProfile.nFrameWidth+15)>>4);
mb_per_sec = mb_per_frame*(m_sProfile.nFramerate);
do {
if (mb_per_frame <= (unsigned int)profile_tbl[0]) {
if (mb_per_sec <= (unsigned int)profile_tbl[1]) {
if (m_sProfile.nBitrate <= (unsigned int)profile_tbl[2]) {
eLevel = (int)profile_tbl[3];
eProfile = (int)profile_tbl[4];
E("\n profile/level found: %lu/%lu\n",eProfile, eLevel);
profile_level_found = true;
break;
}
}
}
profile_tbl = profile_tbl + 5;
} while (profile_tbl[0] != 0);
if ( profile_level_found != true ) {
E("\n Error: Unsupported profile/level\n");
return OMX_ErrorNone;
}
} else { // Profile set by user!
eProfile = m_sProfile.nUserProfile;
eLevel = 0;
}
if (m_sProfile.eCodec == OMX_VIDEO_CodingH263) {
D("Configuring H263...");
OMX_VIDEO_PARAM_H263TYPE h263;
result = OMX_GetParameter(m_hHandle,
OMX_IndexParamVideoH263,
&h263);
CHK(result);
h263.nPortIndex = (OMX_U32) PORT_INDEX_OUT;
h263.nPFrames = m_sProfile.nFramerate * 2 - 1; // intra period
h263.nBFrames = 0;
h263.eProfile = (OMX_VIDEO_H263PROFILETYPE)eProfile;
h263.eLevel = (OMX_VIDEO_H263LEVELTYPE)eLevel;
h263.bPLUSPTYPEAllowed = OMX_FALSE;
h263.nAllowedPictureTypes = 2;
h263.bForceRoundingTypeToZero = OMX_TRUE;
h263.nPictureHeaderRepetition = 0;
h263.nGOBHeaderInterval = 1;
result = OMX_SetParameter(m_hHandle,
OMX_IndexParamVideoH263,
&h263);
} else {
D("Configuring MP4/H264...");
OMX_VIDEO_PARAM_PROFILELEVELTYPE profileLevel; // OMX_IndexParamVideoProfileLevelCurrent
profileLevel.nPortIndex = (OMX_U32) PORT_INDEX_OUT;
profileLevel.eProfile = eProfile;
profileLevel.eLevel = eLevel;
result = OMX_SetParameter(m_hHandle,
OMX_IndexParamVideoProfileLevelCurrent,
&profileLevel);
E("\n OMX_IndexParamVideoProfileLevelCurrent Set Paramter port");
CHK(result);
//profileLevel.eLevel = (OMX_U32) m_sProfile.eLevel;
result = OMX_GetParameter(m_hHandle,
OMX_IndexParamVideoProfileLevelCurrent,
&profileLevel);
E("\n OMX_IndexParamVideoProfileLevelCurrent Get Paramter port");
D ("\n Profile = %lu level = %lu",profileLevel.eProfile,profileLevel.eLevel);
CHK(result);
if (m_sProfile.eCodec == OMX_VIDEO_CodingMPEG4) {
OMX_VIDEO_PARAM_MPEG4TYPE mp4; // OMX_IndexParamVideoMpeg4
result = OMX_GetParameter(m_hHandle,
OMX_IndexParamVideoMpeg4,
&mp4);
CHK(result);
mp4.nPortIndex = (OMX_U32) PORT_INDEX_OUT;
mp4.nTimeIncRes = 1000;
result = OMX_SetParameter(m_hHandle,
OMX_IndexParamVideoMpeg4,
&mp4);
CHK(result);
}
}
if (m_sProfile.eCodec == OMX_VIDEO_CodingAVC) {
#if 1
/////////////C A B A C ///A N D/////D E B L O C K I N G /////////////////
OMX_VIDEO_PARAM_AVCTYPE avcdata;
avcdata.nPortIndex = (OMX_U32)PORT_INDEX_OUT;
result = OMX_GetParameter(m_hHandle,
OMX_IndexParamVideoAvc,
&avcdata);
CHK(result);
// TEST VALUES (CHANGE FOR DIFF CONFIG's)
avcdata.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable;
// avcdata.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterDisable;
// avcdata.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterDisableSliceBoundary;
avcdata.bEntropyCodingCABAC = OMX_FALSE;
// avcdata.bEntropyCodingCABAC = OMX_TRUE;
avcdata.nCabacInitIdc = 1;
///////////////////////////////////////////////
result = OMX_SetParameter(m_hHandle,
OMX_IndexParamVideoAvc,
&avcdata);
CHK(result);
/////////////C A B A C ///A N D/////D E B L O C K I N G /////////////////
#endif
}
OMX_VIDEO_PARAM_BITRATETYPE bitrate; // OMX_IndexParamVideoBitrate
bitrate.nPortIndex = (OMX_U32)PORT_INDEX_OUT;
result = OMX_GetParameter(m_hHandle,
OMX_IndexParamVideoBitrate,
&bitrate);
E("\n OMX_IndexParamVideoBitrate Get Paramter port");
CHK(result);
bitrate.eControlRate = m_sProfile.eControlRate;
bitrate.nTargetBitrate = m_sProfile.nBitrate;
result = OMX_SetParameter(m_hHandle,
OMX_IndexParamVideoBitrate,
&bitrate);
E("\n OMX_IndexParamVideoBitrate Set Paramter port");
CHK(result);
OMX_VIDEO_PARAM_PORTFORMATTYPE framerate; // OMX_IndexParamVidePortFormat
framerate.nPortIndex = 0;
result = OMX_GetParameter(m_hHandle,
OMX_IndexParamVideoPortFormat,
&framerate);
E("\n OMX_IndexParamVideoPortFormat Get Paramter port");
CHK(result);
FractionToQ16(framerate.xFramerate,(int) (m_sProfile.nFramerate * 2),2);
result = OMX_SetParameter(m_hHandle,
OMX_IndexParamVideoPortFormat,
&framerate);
E("\n OMX_IndexParamVideoPortFormat Set Paramter port");
CHK(result);
#if 1
///////////////////I N T R A P E R I O D ///////////////////
QOMX_VIDEO_INTRAPERIODTYPE intra;
intra.nPortIndex = (OMX_U32) PORT_INDEX_OUT; // output
result = OMX_GetConfig(m_hHandle,
(OMX_INDEXTYPE) QOMX_IndexConfigVideoIntraperiod,
(OMX_PTR) &intra);
if (result == OMX_ErrorNone) {
intra.nPFrames = (OMX_U32) (2 * m_sProfile.nFramerate - 1); //setting I
//frame interval to
//2 x framerate
intra.nIDRPeriod = 1; //every I frame is an IDR
intra.nPortIndex = (OMX_U32) PORT_INDEX_OUT;
result = OMX_SetConfig(m_hHandle,
(OMX_INDEXTYPE) QOMX_IndexConfigVideoIntraperiod,
(OMX_PTR) &intra);
} else {
E("failed to get state", 0, 0, 0);
}
///////////////////I N T R A P E R I O D ///////////////////
#endif
#if 1
///////////////////E R R O R C O R R E C T I O N ///////////////////
ResyncMarkerType eResyncMarkerType = RESYNC_MARKER_NONE;
unsigned long int nResyncMarkerSpacing = 0;
OMX_BOOL enableHEC = OMX_FALSE;
//For Testing ONLY
if (m_sProfile.eCodec == OMX_VIDEO_CodingMPEG4) {
// MPEG4
// eResyncMarkerType = RESYNC_MARKER_BYTE;
// nResyncMarkerSpacing = 1920;
eResyncMarkerType = RESYNC_MARKER_MB;
nResyncMarkerSpacing = 50;
enableHEC = OMX_TRUE;
} else if (m_sProfile.eCodec == OMX_VIDEO_CodingH263) {
//H263
//eResyncMarkerType = RESYNC_MARKER_GOB;
eResyncMarkerType = RESYNC_MARKER_NONE;
nResyncMarkerSpacing = 0;
} else if (m_sProfile.eCodec == OMX_VIDEO_CodingAVC) {
//H264
// eResyncMarkerType = RESYNC_MARKER_BYTE;
// nResyncMarkerSpacing = 1920;
//nResyncMarkerSpacing sets the slice size in venc_set_multislice_cfg
//
//As of 9/24/10, it is known that the firmware has a bitstream
//corruption issue when RateControl and multislice are enabled for 720P
//So, disabling multislice for 720P when ratecontrol is enabled until
//the firmware issue is resolved.
if ( ( (m_sProfile.nFrameWidth == 1280) && (m_sProfile.nFrameHeight = 720) ) &&
(m_sProfile.eControlRate != OMX_Video_ControlRateDisable) ) {
eResyncMarkerType = RESYNC_MARKER_NONE;
nResyncMarkerSpacing = 0;
} else {
eResyncMarkerType = RESYNC_MARKER_NONE;
nResyncMarkerSpacing = 0;
}
}
OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrection; //OMX_IndexParamVideoErrorCorrection
errorCorrection.nPortIndex = (OMX_U32) PORT_INDEX_OUT; // output
result = OMX_GetParameter(m_hHandle,
(OMX_INDEXTYPE) OMX_IndexParamVideoErrorCorrection,
(OMX_PTR) &errorCorrection);
errorCorrection.bEnableRVLC = OMX_FALSE;
errorCorrection.bEnableDataPartitioning = OMX_FALSE;
if ((eResyncMarkerType == RESYNC_MARKER_BYTE) &&
(m_sProfile.eCodec == OMX_VIDEO_CodingMPEG4)) {
errorCorrection.bEnableResync = OMX_TRUE;
errorCorrection.nResynchMarkerSpacing = nResyncMarkerSpacing;
errorCorrection.bEnableHEC = enableHEC;
} else if ((eResyncMarkerType == RESYNC_MARKER_BYTE) &&
(m_sProfile.eCodec == OMX_VIDEO_CodingAVC)) {
errorCorrection.bEnableResync = OMX_TRUE;
errorCorrection.nResynchMarkerSpacing = nResyncMarkerSpacing;
} else if ((eResyncMarkerType == RESYNC_MARKER_GOB) &&
(m_sProfile.eCodec == OMX_VIDEO_CodingH263)) {
errorCorrection.bEnableResync = OMX_FALSE;
errorCorrection.nResynchMarkerSpacing = nResyncMarkerSpacing;
errorCorrection.bEnableDataPartitioning = OMX_TRUE;
}
result = OMX_SetParameter(m_hHandle,
(OMX_INDEXTYPE) OMX_IndexParamVideoErrorCorrection,
(OMX_PTR) &errorCorrection);
CHK(result);
if (eResyncMarkerType == RESYNC_MARKER_MB) {
if (m_sProfile.eCodec == OMX_VIDEO_CodingAVC) {
OMX_VIDEO_PARAM_AVCTYPE avcdata;
avcdata.nPortIndex = (OMX_U32) PORT_INDEX_OUT; // output
result = OMX_GetParameter(m_hHandle,
OMX_IndexParamVideoAvc,
(OMX_PTR) &avcdata);
CHK(result);
if (result == OMX_ErrorNone) {
avcdata.nSliceHeaderSpacing = nResyncMarkerSpacing;
result = OMX_SetParameter(m_hHandle,
OMX_IndexParamVideoAvc,
(OMX_PTR) &avcdata);
CHK(result);
}
} else if (m_sProfile.eCodec == OMX_VIDEO_CodingMPEG4) {
OMX_VIDEO_PARAM_MPEG4TYPE mp4;
mp4.nPortIndex = (OMX_U32) PORT_INDEX_OUT; // output
result = OMX_GetParameter(m_hHandle,
OMX_IndexParamVideoMpeg4,
(OMX_PTR) &mp4);
CHK(result);
if (result == OMX_ErrorNone) {
mp4.nSliceHeaderSpacing = nResyncMarkerSpacing;
result = OMX_SetParameter(m_hHandle,
OMX_IndexParamVideoMpeg4,
(OMX_PTR) &mp4);
CHK(result);
}
}
}
///////////////////E R R O R C O R R E C T I O N ///////////////////
#endif
#if 1
///////////////////I N T R A R E F R E S H///////////////////
bool bEnableIntraRefresh = OMX_TRUE;
if (result == OMX_ErrorNone) {
OMX_VIDEO_PARAM_INTRAREFRESHTYPE ir; // OMX_IndexParamVideoIntraRefresh
ir.nPortIndex = (OMX_U32) PORT_INDEX_OUT; // output
result = OMX_GetParameter(m_hHandle,
OMX_IndexParamVideoIntraRefresh,
(OMX_PTR) &ir);
if (result == OMX_ErrorNone) {
if (bEnableIntraRefresh) {
ir.eRefreshMode = OMX_VIDEO_IntraRefreshCyclic;
ir.nCirMBs = 5;
result = OMX_SetParameter(m_hHandle,
OMX_IndexParamVideoIntraRefresh,
(OMX_PTR) &ir);
CHK(result);
}
}
}
#endif
#if 1
///////////////////FRAMEPACKING DATA///////////////////
OMX_QCOM_FRAME_PACK_ARRANGEMENT framePackingArrangement;
FILE *m_pConfigFile;
char m_configFilename [128] = "/data/configFile.cfg";
memset(&framePackingArrangement, 0, sizeof(framePackingArrangement));
m_pConfigFile = fopen(m_configFilename, "r");
if (m_pConfigFile != NULL) {
//read all frame packing data
framePackingArrangement.nPortIndex = (OMX_U32)PORT_INDEX_OUT;
int totalSizeToRead = FRAME_PACK_SIZE * sizeof(OMX_U32);
char *pFramePack = (char *) &(framePackingArrangement.id);
while ( ( (fscanf(m_pConfigFile, "%d", pFramePack)) != EOF ) &&
(totalSizeToRead != 0) ) {
//printf("Addr = %p, Value read = %d, sizeToRead remaining=%d\n",
// pFramePack, *pFramePack, totalSizeToRead);
pFramePack += sizeof(OMX_U32);
totalSizeToRead -= sizeof(OMX_U32);
}
//close the file.
fclose(m_pConfigFile);
printf("Frame Packing data from config file:\n");
PrintFramePackArrangement(framePackingArrangement);
} else {
D("\n Config file does not exist or could not be opened.");
//set the default values
framePackingArrangement.nPortIndex = (OMX_U32)PORT_INDEX_OUT;
framePackingArrangement.id = 123;
framePackingArrangement.cancel_flag = false;
framePackingArrangement.type = 3;
framePackingArrangement.quincunx_sampling_flag = false;
framePackingArrangement.content_interpretation_type = 0;
framePackingArrangement.spatial_flipping_flag = true;
framePackingArrangement.frame0_flipped_flag = false;
framePackingArrangement.field_views_flag = false;
framePackingArrangement.current_frame_is_frame0_flag = false;
framePackingArrangement.frame0_self_contained_flag = true;
framePackingArrangement.frame1_self_contained_flag = false;
framePackingArrangement.frame0_grid_position_x = 3;
framePackingArrangement.frame0_grid_position_y = 15;
framePackingArrangement.frame1_grid_position_x = 11;
framePackingArrangement.frame1_grid_position_y = 7;
framePackingArrangement.reserved_byte = 0;
framePackingArrangement.repetition_period = 16381;
framePackingArrangement.extension_flag = false;
printf("Frame Packing Defaults :\n");
PrintFramePackArrangement(framePackingArrangement);
}
result = OMX_SetConfig(m_hHandle,
(OMX_INDEXTYPE)OMX_QcomIndexConfigVideoFramePackingArrangement,
(OMX_PTR) &framePackingArrangement);
CHK(result);
//////////////////////OMX_VIDEO_PARAM_INTRAREFRESHTYPE///////////////////
#endif
OMX_CONFIG_FRAMERATETYPE enc_framerate; // OMX_IndexConfigVideoFramerate
enc_framerate.nPortIndex = (OMX_U32)PORT_INDEX_OUT;
result = OMX_GetConfig(m_hHandle,
OMX_IndexConfigVideoFramerate,
&enc_framerate);
CHK(result);
FractionToQ16(enc_framerate.xEncodeFramerate,(int) (m_sProfile.nFramerate * 2),2);
result = OMX_SetConfig(m_hHandle,
OMX_IndexConfigVideoFramerate,
&enc_framerate);
CHK(result);
return OMX_ErrorNone;
}
////////////////////////////////////////////////////////////////////////////////
void SendMessage(MsgId id, MsgData* data)
{
pthread_mutex_lock(&m_mutex);
if (m_sMsgQ.size >= MAX_MSG) {
E("main msg m_sMsgQ is full");
return;
}
m_sMsgQ.q[(m_sMsgQ.head + m_sMsgQ.size) % MAX_MSG].id = id;
if (data)
m_sMsgQ.q[(m_sMsgQ.head + m_sMsgQ.size) % MAX_MSG].data = *data;
++m_sMsgQ.size;
pthread_cond_signal(&m_signal);
pthread_mutex_unlock(&m_mutex);
}
////////////////////////////////////////////////////////////////////////////////
void PopMessage(Msg* msg)
{
pthread_mutex_lock(&m_mutex);
while (m_sMsgQ.size == 0) {
pthread_cond_wait(&m_signal, &m_mutex);
}
*msg = m_sMsgQ.q[m_sMsgQ.head];
--m_sMsgQ.size;
m_sMsgQ.head = (m_sMsgQ.head + 1) % MAX_MSG;
pthread_mutex_unlock(&m_mutex);
}
////////////////////////////////////////////////////////////////////////////////
OMX_ERRORTYPE EVT_CB(OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_EVENTTYPE eEvent,
OMX_IN OMX_U32 nData1,
OMX_IN OMX_U32 nData2,
OMX_IN OMX_PTR pEventData)
{
#define SET_STATE(eState) \
case eState: \
{ \
D("" # eState " complete"); \
m_eState = eState; \
break; \
}
if (eEvent == OMX_EventCmdComplete) {
if ((OMX_COMMANDTYPE) nData1 == OMX_CommandStateSet) {
switch ((OMX_STATETYPE) nData2) {
SET_STATE(OMX_StateLoaded);
SET_STATE(OMX_StateIdle);
SET_STATE(OMX_StateExecuting);
SET_STATE(OMX_StateInvalid);
SET_STATE(OMX_StateWaitForResources);
SET_STATE(OMX_StatePause);
default:
E("invalid state %d", (int) nData2);
}
}
}
else if (eEvent == OMX_EventError) {
E("OMX_EventError");
}
else {
E("unexpected event %d", (int) eEvent);
}
return OMX_ErrorNone;
}
////////////////////////////////////////////////////////////////////////////////
OMX_ERRORTYPE EBD_CB(OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer)
{
D("Got EBD callback ts=%lld", pBuffer->nTimeStamp);
for (int i = 0; i < num_in_buffers; i++) {
// mark this buffer ready for use again
if (m_pInBuffers[i] == pBuffer) {
D("Marked input buffer idx %d as free, buf %p", i, pBuffer->pBuffer);
m_bInFrameFree[i] = OMX_TRUE;
break;
}
}
if (m_eMode == MODE_LIVE_ENCODE) {
CameraTest_ReleaseFrame(pBuffer->pBuffer,
((OMX_QCOM_PLATFORM_PRIVATE_PMEM_INFO*)pBuffer->pAppPrivate));
} else {
// wake up main thread and tell it to send next frame
MsgData data;
data.sBitstreamData.pBuffer = pBuffer;
SendMessage(MSG_ID_INPUT_FRAME_DONE,
&data);
}
return OMX_ErrorNone;
}
////////////////////////////////////////////////////////////////////////////////
OMX_ERRORTYPE FBD_CB(OMX_OUT OMX_HANDLETYPE hComponent,
OMX_OUT OMX_PTR pAppData,
OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer)
{
D("Got FBD callback ts=%lld", pBuffer->nTimeStamp);
static long long prevTime = 0;
long long currTime = GetTimeStamp();
m_bWatchDogKicked = true;
/* Empty Buffers should not be counted */
if (pBuffer->nFilledLen !=0) {
/* Counting Buffers supplied from OpneMax Encoder */
fbd_cnt++;
tot_bufsize += pBuffer->nFilledLen;
}
if (prevTime != 0) {
long long currTime = GetTimeStamp();
D("FBD_DELTA = %lld\n", currTime - prevTime);
}
prevTime = currTime;
if (m_eMode == MODE_PROFILE) {
// if we are profiling we are not doing file I/O
// so just give back to encoder
if (OMX_FillThisBuffer(m_hHandle, pBuffer) != OMX_ErrorNone) {
E("empty buffer failed for profiling");
}
} else {
// wake up main thread and tell it to write to file
MsgData data;
data.sBitstreamData.pBuffer = pBuffer;
SendMessage(MSG_ID_OUTPUT_FRAME_DONE,
&data);
}
return OMX_ErrorNone;
}
////////////////////////////////////////////////////////////////////////////////
OMX_ERRORTYPE VencTest_Initialize()
{
OMX_ERRORTYPE result = OMX_ErrorNone;
static OMX_CALLBACKTYPE sCallbacks = {EVT_CB, EBD_CB, FBD_CB};
int i;
for (i = 0; i < num_in_buffers; i++) {
m_pInBuffers[i] = NULL;
}
result = OMX_Init();
CHK(result);
if (m_sProfile.eCodec == OMX_VIDEO_CodingMPEG4) {
result = OMX_GetHandle(&m_hHandle,
"OMX.qcom.video.encoder.mpeg4",
NULL,
&sCallbacks);
// CHK(result);
} else if (m_sProfile.eCodec == OMX_VIDEO_CodingH263) {
result = OMX_GetHandle(&m_hHandle,
"OMX.qcom.video.encoder.h263",
NULL,
&sCallbacks);
CHK(result);
}
#ifdef _MSM8974_
else if (m_sProfile.eCodec == OMX_VIDEO_CodingVPX) {
result = OMX_GetHandle(&m_hHandle,
"OMX.qcom.video.encoder.vp8",
NULL,
&sCallbacks);
CHK(result);
}
#endif
else {
result = OMX_GetHandle(&m_hHandle,
"OMX.qcom.video.encoder.avc",
NULL,
&sCallbacks);
CHK(result);
}
result = ConfigureEncoder();
CHK(result);
return result;
}
////////////////////////////////////////////////////////////////////////////////
OMX_ERRORTYPE VencTest_RegisterYUVBuffer(OMX_BUFFERHEADERTYPE** ppBufferHeader,
OMX_U8 *pBuffer,
OMX_PTR pAppPrivate)
{
OMX_ERRORTYPE result = OMX_ErrorNone;
#if 0
D("register buffer");
if ((result = OMX_AllocateBuffer(m_hHandle,
ppBufferHeader,
(OMX_U32) PORT_INDEX_IN,
pAppPrivate,
m_sProfile.nFrameBytes
)) != OMX_ErrorNone) {
E("use buffer failed");
} else {
E("Allocate Buffer Success %x", (*ppBufferHeader)->pBuffer);
}
#endif
D("register buffer");
D("Calling UseBuffer for Input port");
if ((result = OMX_UseBuffer(m_hHandle,
ppBufferHeader,
(OMX_U32) PORT_INDEX_IN,
pAppPrivate,
m_sProfile.nFrameBytes,
pBuffer)) != OMX_ErrorNone) {
E("use buffer failed");
}
return result;
}
////////////////////////////////////////////////////////////////////////////////
OMX_ERRORTYPE VencTest_EncodeFrame(void* pYUVBuff,
long long nTimeStamp)
{
OMX_ERRORTYPE result = OMX_ErrorUndefined;
D("calling OMX empty this buffer");
for (int i = 0; i < num_in_buffers; i++) {
if (pYUVBuff == m_pInBuffers[i]->pBuffer) {
m_pInBuffers[i]->nTimeStamp = nTimeStamp;
D("Sending Buffer - %x", m_pInBuffers[i]->pBuffer);
result = OMX_EmptyThisBuffer(m_hHandle,
m_pInBuffers[i]);
/* Counting Buffers supplied to OpenMax Encoder */
if (OMX_ErrorNone == result)
ebd_cnt++;
CHK(result);
break;
}
}
return result;
}
////////////////////////////////////////////////////////////////////////////////
OMX_ERRORTYPE VencTest_Exit(void)
{
int i;
OMX_ERRORTYPE result = OMX_ErrorNone;
D("trying to exit venc");
D("going to idle state");
SetState(OMX_StateIdle);
D("going to loaded state");
//SetState(OMX_StateLoaded);
OMX_SendCommand(m_hHandle,
OMX_CommandStateSet,
(OMX_U32) OMX_StateLoaded,
NULL);
for (i = 0; i < num_in_buffers; i++) {
D("free buffer");
if (m_pInBuffers[i]->pBuffer) {
// free(m_pInBuffers[i]->pBuffer);
result = OMX_FreeBuffer(m_hHandle,
PORT_INDEX_IN,
m_pInBuffers[i]);
CHK(result);
} else {
E("buffer %d is null", i);
result = OMX_ErrorUndefined;
CHK(result);
}
}
for (i = 0; i < num_out_buffers; i++) {
D("free buffer");
if (m_pOutBuffers[i]->pBuffer) {
free(m_pOutBuffers[i]->pBuffer);
result = OMX_FreeBuffer(m_hHandle,
PORT_INDEX_OUT,
m_pOutBuffers[i]);
CHK(result);
} else {
E("buffer %d is null", i);
result = OMX_ErrorUndefined;
CHK(result);
}
}
while (m_eState != OMX_StateLoaded) {
sleep(1);
}
D("component_deinit...");
result = OMX_Deinit();
CHK(result);
D("venc is exiting...");
return result;
}
////////////////////////////////////////////////////////////////////////////////
void VencTest_ReadDynamicConfigMsg()
{
char frame_n[8], config[16], param[8];
char *dest = frame_n;
bool end = false;
int cntr, nparam = 0;
memset(&dynamic_config, 0, sizeof(struct DynamicConfig));
do {
cntr = -1;
do {
dest[++cntr] = fgetc(m_pDynConfFile);
} while (dest[cntr] != ' ' && dest[cntr] != '\t' && dest[cntr] != '\n' && dest[cntr] != '\r' && !feof(m_pDynConfFile));
if (dest[cntr] == '\n' || dest[cntr] == '\r')
end = true;
dest[cntr] = NULL;
if (dest == frame_n)
dest = config;
else if (dest == config)
dest = param;
else
end = true;
nparam++;
} while (!end && !feof(m_pDynConfFile));
if (nparam > 1) {
dynamic_config.pending = true;
dynamic_config.frame_num = atoi(frame_n);
if (!strcmp(config, "bitrate")) {
dynamic_config.config_param = OMX_IndexConfigVideoBitrate;
dynamic_config.config_data.bitrate.nPortIndex = PORT_INDEX_OUT;
dynamic_config.config_data.bitrate.nEncodeBitrate = strtoul(param, NULL, 10);
} else if (!strcmp(config, "framerate")) {
dynamic_config.config_param = OMX_IndexConfigVideoFramerate;
dynamic_config.config_data.framerate.nPortIndex = PORT_INDEX_OUT;
dynamic_config.config_data.f_framerate = atof(param);
} else if (!strcmp(config, "iperiod")) {
dynamic_config.config_param = (OMX_INDEXTYPE)QOMX_IndexConfigVideoIntraperiod;
dynamic_config.config_data.intraperiod.nPortIndex = PORT_INDEX_OUT;
dynamic_config.config_data.intraperiod.nPFrames = strtoul(param, NULL, 10) - 1;
dynamic_config.config_data.intraperiod.nIDRPeriod = 1; // This value is ignored in OMX component
} else if (!strcmp(config, "ivoprefresh")) {
dynamic_config.config_param = OMX_IndexConfigVideoIntraVOPRefresh;
dynamic_config.config_data.intravoprefresh.nPortIndex = PORT_INDEX_OUT;
dynamic_config.config_data.intravoprefresh.IntraRefreshVOP = OMX_TRUE;
} else if (!strcmp(config, "rotation")) {
dynamic_config.config_param = OMX_IndexConfigCommonRotate;
dynamic_config.config_data.rotation.nPortIndex = PORT_INDEX_OUT;
dynamic_config.config_data.rotation.nRotation = strtoul(param, NULL, 10);
} else {
E("UNKNOWN CONFIG PARAMETER: %s!", config);
dynamic_config.pending = false;
}
} else if (feof(m_pDynConfFile)) {
fclose(m_pDynConfFile);
m_pDynConfFile = NULL;
}
}
void VencTest_ProcessDynamicConfigurationFile()
{
do {
if (dynamic_config.pending) {
if (m_nFrameIn == dynamic_config.frame_num) {
if (dynamic_config.config_param == OMX_IndexConfigVideoFramerate) {
m_sProfile.nFramerate = dynamic_config.config_data.f_framerate;
FractionToQ16(dynamic_config.config_data.framerate.xEncodeFramerate,
(int)(m_sProfile.nFramerate * 2), 2);
}
if (OMX_SetConfig(m_hHandle, dynamic_config.config_param,
&dynamic_config.config_data) != OMX_ErrorNone)
E("ERROR: Setting dynamic config to OMX param[0x%x]", dynamic_config.config_param);
dynamic_config.pending = false;
} else if (m_nFrameIn > dynamic_config.frame_num) {
E("WARNING: Config change requested in passed frame(%d)", dynamic_config.frame_num);
dynamic_config.pending = false;
}
}
if (!dynamic_config.pending)
VencTest_ReadDynamicConfigMsg();
} while (!dynamic_config.pending && m_pDynConfFile);
}
////////////////////////////////////////////////////////////////////////////////
OMX_ERRORTYPE VencTest_ReadAndEmpty(OMX_BUFFERHEADERTYPE* pYUVBuffer)
{
OMX_ERRORTYPE result = OMX_ErrorNone;
#ifdef T_ARM
#if defined(MAX_RES_720P) && !defined(_MSM8974_)
if (read(m_nInFd,
pYUVBuffer->pBuffer,
m_sProfile.nFrameBytes) != m_sProfile.nFrameBytes) {
return OMX_ErrorUndefined;
}
#elif _MSM8974_
int i, lscanl, lstride, cscanl, cstride, height, width;
int bytes = 0, read_bytes = 0;
OMX_U8 *yuv = pYUVBuffer->pBuffer;
height = m_sProfile.nFrameHeight;
width = m_sProfile.nFrameWidth;
lstride = VENUS_Y_STRIDE(COLOR_FMT_NV12, width);
lscanl = VENUS_Y_SCANLINES(COLOR_FMT_NV12, height);
cstride = VENUS_UV_STRIDE(COLOR_FMT_NV12, width);
cscanl = VENUS_UV_SCANLINES(COLOR_FMT_NV12, height);
for (i = 0; i < height; i++) {
bytes = read(m_nInFd, yuv, width);
if (bytes != width) {
E("read failed: %d != %d\n", read, width);
return OMX_ErrorUndefined;
}
read_bytes += bytes;
yuv += lstride;
}
yuv = pYUVBuffer->pBuffer + (lscanl * lstride);
for (i = 0; i < ((height + 1) >> 1); i++) {
bytes = read(m_nInFd, yuv, width);
if (bytes != width) {
E("read failed: %d != %d\n", read, width);
return OMX_ErrorUndefined;
}
read_bytes += bytes;
yuv += cstride;
}
m_sProfile.nFrameRead = VENUS_BUFFER_SIZE(COLOR_FMT_NV12, width, height);
E("\n\nActual read bytes: %d, NV12 buffer size: %d\n\n\n", read_bytes, m_sProfile.nFrameRead);
#else
OMX_U32 bytestoread = m_sProfile.nFrameWidth*m_sProfile.nFrameHeight;
// read Y first
if (read(m_nInFd,
pYUVBuffer->pBuffer,
bytestoread) != bytestoread)
return OMX_ErrorUndefined;
// check alignment for offset to C
OMX_U32 offset_to_c = m_sProfile.nFrameWidth * m_sProfile.nFrameHeight;
const OMX_U32 C_2K = (1024*2),
MASK_2K = C_2K-1,
IMASK_2K = ~MASK_2K;
if (offset_to_c & MASK_2K) {
// offset to C is not 2k aligned, adjustment is required
offset_to_c = (offset_to_c & IMASK_2K) + C_2K;
}
bytestoread = m_sProfile.nFrameWidth*m_sProfile.nFrameHeight/2;
// read C
if (read(m_nInFd,
pYUVBuffer->pBuffer + offset_to_c,
bytestoread)!= bytestoread)
return OMX_ErrorUndefined;
#endif
#else
{
char * pInputbuf = (char *)(pYUVBuffer->pBuffer) ;
read(m_nInFd,pInputbuf,m_sProfile.nFrameBytes) ;
}
#endif
if (m_pDynConfFile)
VencTest_ProcessDynamicConfigurationFile();
D("about to call VencTest_EncodeFrame...");
pthread_mutex_lock(&m_mutex);
++m_nFrameIn;
#ifdef _MSM8974_
pYUVBuffer->nFilledLen = m_sProfile.nFrameRead;
#else
pYUVBuffer->nFilledLen = m_sProfile.nFrameBytes;
#endif
D("Called Buffer with Data filled length %d",pYUVBuffer->nFilledLen);
result = VencTest_EncodeFrame(pYUVBuffer->pBuffer,
m_nTimeStamp);
m_nTimeStamp += (1000000) / m_sProfile.nFramerate;
CHK(result);
pthread_mutex_unlock(&m_mutex);
return result;
}
////////////////////////////////////////////////////////////////////////////////
void PreviewCallback(int nFD,
int nOffset,
void* pPhys,
void* pVirt,
long long nTimeStamp)
{
D("================= preview frame %d, phys=0x%x, nTimeStamp(millis)=%lld",
m_nFrameIn+1, pPhys, (nTimeStamp / 1000));
if (m_nFrameIn == m_nFramePlay &&
m_nFramePlay != 0) {
// we will stop camera after last frame is encoded.
// for now just ignore input frames
CameraTest_ReleaseFrame(pPhys, pVirt);
return;
}
// see if we should stop
pthread_mutex_lock(&m_mutex);
++m_nFrameIn;
pthread_mutex_unlock(&m_mutex);
if (m_eMode == MODE_LIVE_ENCODE) {
OMX_ERRORTYPE result;
// register new camera buffers with encoder
int i;
for (i = 0; i < num_in_buffers; i++) {
if (m_pInBuffers[i] != NULL &&
m_pInBuffers[i]->pBuffer == pPhys) {
break;
} else if (m_pInBuffers[i] == NULL) {
D("registering buffer...");
result = VencTest_RegisterYUVBuffer(&m_pInBuffers[i],
(OMX_U8*) pPhys,
(OMX_PTR) pVirt); // store virt in app private field
D("register done");
CHK(result);
break;
}
}
if (i == num_in_buffers) {
E("There are more camera buffers than we thought");
CHK(1);
}
// encode the yuv frame
D("StartEncodeTime=%lld", GetTimeStamp());
result = VencTest_EncodeFrame(pPhys,
nTimeStamp);
CHK(result);
// FBTest_DisplayImage(nFD, nOffset);
} else {
// FBTest_DisplayImage(nFD, nOffset);
CameraTest_ReleaseFrame(pPhys, pVirt);
}
}
////////////////////////////////////////////////////////////////////////////////
void usage(char* filename)
{
char* fname = strrchr(filename, (int) '/');
fname = (fname == NULL) ? filename : fname;
fprintf(stderr, "usage: %s LIVE <QCIF|QVGA> <MP4|H263> <FPS> <BITRATE> <NFRAMES> <OUTFILE>\n", fname);
fprintf(stderr, "usage: %s FILE <QCIF|QVGA> <MP4|H263 <FPS> <BITRATE> <NFRAMES> <INFILE> <OUTFILE> ", fname);
fprintf(stderr, "<Dynamic config file - opt> <Rate Control - opt> <AVC Slice Mode - opt>\n", fname);
fprintf(stderr, "usage: %s PROFILE <QCIF|QVGA> <MP4|H263 <FPS> <BITRATE> <NFRAMES> <INFILE>\n", fname);
fprintf(stderr, "usage: %s PREVIEW <QCIF|QVGA> <FPS> <NFRAMES>\n", fname);
fprintf(stderr, "usage: %s DISPLAY <QCIF|QVGA> <FPS> <NFRAMES> <INFILE>\n", fname);
fprintf(stderr, "\n BITRATE - bitrate in kbps\n");
fprintf(stderr, " FPS - frames per second\n");
fprintf(stderr, " NFRAMES - number of frames to play, 0 for infinite\n");
fprintf(stderr, " RateControl (Values 0 - 4 for RC_OFF, RC_CBR_CFR, RC_CBR_VFR, RC_VBR_CFR, RC_VBR_VFR\n");
exit(1);
}
bool parseWxH(char *str, OMX_U32 *width, OMX_U32 *height)
{
bool parseOK = false;
const char delimiters[] = " x*,";
char *token, *dupstr, *temp;
OMX_U32 w, h;
dupstr = strdup(str);
token = strtok_r(dupstr, delimiters, &temp);
if (token) {
w = strtoul(token, NULL, 10);
token = strtok_r(NULL, delimiters, &temp);
if (token) {
h = strtoul(token, NULL, 10);
if (w != ULONG_MAX && h != ULONG_MAX) {
#ifdef MAX_RES_720P
if ((w * h >> 8) <= 3600) {
parseOK = true;
*width = w;
*height = h;
}
#else
if ((w * h >> 8) <= 8160) {
parseOK = true;
*width = w;
*height = h;
}
#endif
else
E("\nInvalid dimensions %dx%d",w,h);
}
}
}
free(dupstr);
return parseOK;
}
////////////////////////////////////////////////////////////////////////////////
void parseArgs(int argc, char** argv)
{
int dyn_file_arg = argc;
if (argc == 1) {
usage(argv[0]);
} else if (strcmp("PREVIEW", argv[1]) == 0 ||
strcmp("preview", argv[1]) == 0) {
m_eMode = MODE_PREVIEW;
if (argc != 5) {
usage(argv[0]);
}
} else if (strcmp("DISPLAY", argv[1]) == 0 ||
strcmp("display", argv[1]) == 0) {
m_eMode = MODE_DISPLAY;
if (argc != 6) {
usage(argv[0]);
}
m_sProfile.cInFileName = argv[5];
m_sProfile.cOutFileName = NULL;
} else if (strcmp("LIVE", argv[1]) == 0 ||
strcmp("live", argv[1]) == 0) {//263
m_eMode = MODE_LIVE_ENCODE;
if (argc != 8) {
usage(argv[0]);
}
m_sProfile.cInFileName = NULL;
m_sProfile.cOutFileName = argv[7];
} else if (strcmp("FILE", argv[1]) == 0 ||
strcmp("file", argv[1]) == 0) {//263
m_eMode = MODE_FILE_ENCODE;
if (argc < 9 || argc > 13) {
usage(argv[0]);
} else {
if (argc > 9)
dyn_file_arg = 9;
if (argc > 10) {
m_sProfile.eControlRate = OMX_Video_ControlRateVariable;
int RC = atoi(argv[10]);
switch (RC) {
case 0:
m_sProfile.eControlRate = OMX_Video_ControlRateDisable ;//VENC_RC_NONE
break;
case 1:
m_sProfile.eControlRate = OMX_Video_ControlRateConstant;//VENC_RC_CBR_CFR
break;
case 2:
m_sProfile.eControlRate = OMX_Video_ControlRateConstantSkipFrames;//VENC_RC_CBR_VFR
break;
case 3:
m_sProfile.eControlRate =OMX_Video_ControlRateVariable ;//VENC_RC_VBR_CFR
break;
case 4:
m_sProfile.eControlRate = OMX_Video_ControlRateVariableSkipFrames;//VENC_RC_VBR_VFR
break;
default:
E("invalid rate control selection");
m_sProfile.eControlRate = OMX_Video_ControlRateVariable; //VENC_RC_VBR_CFR
break;
}
}
if (argc > 11) {
int profile_argi = 11;
if (!strcmp(argv[3], "H264") || !strcmp(argv[3], "h264")) {
profile_argi = 12;
D("\nSetting AVCSliceMode ... ");
int AVCSliceMode = atoi(argv[11]);
switch (AVCSliceMode) {
case 0:
m_sProfile.eSliceMode = OMX_VIDEO_SLICEMODE_AVCDefault;
break;
case 1:
m_sProfile.eSliceMode = OMX_VIDEO_SLICEMODE_AVCMBSlice;
break;
case 2:
m_sProfile.eSliceMode = OMX_VIDEO_SLICEMODE_AVCByteSlice;
break;
default:
E("invalid Slice Mode");
m_sProfile.eSliceMode = OMX_VIDEO_SLICEMODE_AVCDefault;
break;
}
}
if (profile_argi < argc) {
if (!strncmp(argv[profile_argi], "0x", 2) || !strncmp(argv[profile_argi], "0x", 2)) {
m_sProfile.nUserProfile = strtoul(argv[profile_argi], NULL, 16);
} else {
m_sProfile.nUserProfile = strtoul(argv[profile_argi], NULL, 10);
}
if (!m_sProfile.nUserProfile || m_sProfile.nUserProfile == ULONG_MAX) {
E("invalid specified Profile %s, using default", argv[profile_argi]);
m_sProfile.nUserProfile = 0;
}
}
}
}
m_sProfile.cInFileName = argv[7];
m_sProfile.cOutFileName = argv[8];
} else if (strcmp("PROFILE", argv[1]) == 0 ||
strcmp("profile", argv[1]) == 0) {//263
m_eMode = MODE_PROFILE;
if (argc != 8) {
usage(argv[0]);
}
m_sProfile.cInFileName = argv[7];
m_sProfile.cOutFileName = NULL;
} else {
usage(argv[0]);
}
if (strcmp("QCIF", argv[2]) == 0 ||
strcmp("qcif", argv[2]) == 0) {
m_sProfile.nFrameWidth = 176;
m_sProfile.nFrameHeight = 144;
m_sProfile.nFrameBytes = 176*144*3/2;
m_sProfile.eLevel = OMX_VIDEO_MPEG4Level0;
} else if (strcmp("QVGA", argv[2]) == 0 ||
strcmp("qvga", argv[2]) == 0) {
m_sProfile.nFrameWidth = 320;
m_sProfile.nFrameHeight = 240;
m_sProfile.nFrameBytes = 320*240*3/2;
m_sProfile.eLevel = OMX_VIDEO_MPEG4Level1;
}
else if (strcmp("VGA", argv[2]) == 0 ||
strcmp("vga", argv[2]) == 0) {
m_sProfile.nFrameWidth = 640;
m_sProfile.nFrameHeight = 480;
m_sProfile.nFrameBytes = 640*480*3/2;
m_sProfile.eLevel = OMX_VIDEO_MPEG4Level1;
}
else if (strcmp("WVGA", argv[2]) == 0 ||
strcmp("wvga", argv[2]) == 0) {
m_sProfile.nFrameWidth = 800;
m_sProfile.nFrameHeight = 480;
m_sProfile.nFrameBytes = 800*480*3/2;
m_sProfile.eLevel = OMX_VIDEO_MPEG4Level1;
} else if (strcmp("CIF", argv[2]) == 0 ||
strcmp("cif", argv[2]) == 0) {
m_sProfile.nFrameWidth = 352;
m_sProfile.nFrameHeight = 288;
m_sProfile.nFrameBytes = 352*288*3/2;
m_sProfile.eLevel = OMX_VIDEO_MPEG4Level1;
} else if (strcmp("720", argv[2]) == 0) {
m_sProfile.nFrameWidth = 1280;
m_sProfile.nFrameHeight = 720;
m_sProfile.nFrameBytes = 720*1280*3/2;
m_sProfile.eLevel = OMX_VIDEO_MPEG4Level1;
} else if (strcmp("1080", argv[2]) == 0) {
m_sProfile.nFrameWidth = 1920;
m_sProfile.nFrameHeight = 1080;
m_sProfile.nFrameBytes = 1920*1080*3/2;
m_sProfile.eLevel = OMX_VIDEO_MPEG4Level1;
}
#ifdef _MSM8974_
else if (strcmp("4K2K", argv[2]) == 0) {
m_sProfile.nFrameWidth = 4096;
m_sProfile.nFrameHeight = 2160;
m_sProfile.nFrameBytes = 4096*2160*3/2;
m_sProfile.eLevel = OMX_VIDEO_MPEG4Level1;
} else if (strcmp("2160P", argv[2]) == 0) {
m_sProfile.nFrameWidth = 3840;
m_sProfile.nFrameHeight = 2160;
m_sProfile.nFrameBytes = 3840*2160*3/2;
m_sProfile.eLevel = OMX_VIDEO_MPEG4Level1;
}
#endif
else if (parseWxH(argv[2], &m_sProfile.nFrameWidth, &m_sProfile.nFrameHeight)) {
m_sProfile.nFrameBytes = m_sProfile.nFrameWidth*m_sProfile.nFrameHeight*3/2;
m_sProfile.eLevel = OMX_VIDEO_MPEG4Level1;
} else {
usage(argv[0]);
}
#ifdef _MSM8974_
m_sProfile.nFramestride = (m_sProfile.nFrameWidth + 31) & (~31);
m_sProfile.nFrameScanlines = (m_sProfile.nFrameHeight + 31) & (~31);
m_sProfile.nFrameBytes = ((m_sProfile.nFramestride * m_sProfile.nFrameScanlines * 3/2) + 4095) & (~4095);
m_sProfile.nFrameRead = m_sProfile.nFramestride * m_sProfile.nFrameScanlines * 3/2;
#endif
if (m_eMode == MODE_DISPLAY ||
m_eMode == MODE_PREVIEW) {
m_sProfile.nFramerate = atof(argv[3]);
m_nFramePlay = atoi(argv[4]);
} else if (m_eMode == MODE_LIVE_ENCODE ||
m_eMode == MODE_FILE_ENCODE ||
m_eMode == MODE_PROFILE) {
if ((!strcmp(argv[3], "MP4")) || (!strcmp(argv[3], "mp4"))) {
m_sProfile.eCodec = OMX_VIDEO_CodingMPEG4;
} else if ((!strcmp(argv[3], "H263")) || (!strcmp(argv[3], "h263"))) {
m_sProfile.eCodec = OMX_VIDEO_CodingH263;
} else if ((!strcmp(argv[3], "H264")) || (!strcmp(argv[3], "h264"))) {
m_sProfile.eCodec = OMX_VIDEO_CodingAVC;
}
#ifdef _MSM8974_
else if ((!strcmp(argv[3], "VP8")) || (!strcmp(argv[3], "vp8"))) {
m_sProfile.eCodec = OMX_VIDEO_CodingVPX;
}
#endif
else {
usage(argv[0]);
}
m_sProfile.nFramerate = atof(argv[4]);
m_sProfile.nBitrate = atoi(argv[5]);
// m_sProfile.eControlRate = OMX_Video_ControlRateVariable;
m_nFramePlay = atoi(argv[6]);
if (dyn_file_arg < argc) {
m_pDynConfFile = fopen(argv[dyn_file_arg], "r");
if (!m_pDynConfFile)
E("ERROR: Cannot open dynamic config file: %s", argv[dyn_file_arg]);
else {
memset(&dynamic_config, 0, sizeof(struct DynamicConfig));
}
}
}
}
void* Watchdog(void* data)
{
while (1) {
sleep(1000);
if (m_bWatchDogKicked == true)
m_bWatchDogKicked = false;
else
E("watchdog has not been kicked. we may have a deadlock");
}
return NULL;
}
int main(int argc, char** argv)
{
OMX_U8* pvirt = NULL;
int result;
float enc_time_sec=0.0,enc_time_usec=0.0;
m_nInFd = -1;
m_nOutFd = -1;
m_nTimeStamp = 0;
m_nFrameIn = 0;
m_nFrameOut = 0;
memset(&m_sMsgQ, 0, sizeof(MsgQ));
memset(&m_sProfile, 0, sizeof(m_sProfile));
parseArgs(argc, argv);
D("fps=%f, bitrate=%u, width=%u, height=%u, frame bytes=%u",
m_sProfile.nFramerate,
m_sProfile.nBitrate,
m_sProfile.nFrameWidth,
m_sProfile.nFrameHeight,
m_sProfile.nFrameBytes);
#ifdef _MSM8974_
D("Frame stride=%u, scanlines=%u, read=%u",
m_sProfile.nFramestride,
m_sProfile.nFrameScanlines,
m_sProfile.nFrameRead);
#endif
//if (m_eMode != MODE_PREVIEW && m_eMode != MODE_DISPLAY)
//{
// pthread_t wd;
// pthread_create(&wd, NULL, Watchdog, NULL);
//}
for (int x = 0; x < num_in_buffers; x++) {
// mark all buffers as ready to use
m_bInFrameFree[x] = OMX_TRUE;
}
if (m_eMode != MODE_PROFILE) {
#if T_ARM
m_nOutFd = open(m_sProfile.cOutFileName, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU | S_IRWXG | S_IRWXO);
#else
m_nOutFd = open(m_sProfile.cOutFileName,0);
#endif
if (m_nOutFd < 0) {
E("could not open output file %s", m_sProfile.cOutFileName);
CHK(1);
}
}
pthread_mutex_init(&m_mutex, NULL);
pthread_cond_init(&m_signal, NULL);
if (m_eMode != MODE_PREVIEW) {
VencTest_Initialize();
}
////////////////////////////////////////
// Camera + Encode
////////////////////////////////////////
if (m_eMode == MODE_LIVE_ENCODE) {
CameraTest_Initialize(m_sProfile.nFramerate,
m_sProfile.nFrameWidth,
m_sProfile.nFrameHeight,
PreviewCallback);
CameraTest_Run();
}
if (m_eMode == MODE_FILE_ENCODE ||
m_eMode == MODE_PROFILE) {
int i;
#if T_ARM
m_nInFd = open(m_sProfile.cInFileName, O_RDONLY);
#else
m_nInFd = open(m_sProfile.cInFileName,1);
#endif
if (m_nInFd < 0) {
E("could not open input file");
CHK(1);
}
D("going to idle state");
//SetState(OMX_StateIdle);
OMX_SendCommand(m_hHandle,
OMX_CommandStateSet,
(OMX_U32) OMX_StateIdle,
NULL);
OMX_PARAM_PORTDEFINITIONTYPE portDef;
portDef.nPortIndex = 0;
result = OMX_GetParameter(m_hHandle, OMX_IndexParamPortDefinition, &portDef);
CHK(result);
D("allocating Input buffers");
num_in_buffers = portDef.nBufferCountActual;
for (i = 0; i < portDef.nBufferCountActual; i++) {
OMX_QCOM_PLATFORM_PRIVATE_PMEM_INFO* pMem = new OMX_QCOM_PLATFORM_PRIVATE_PMEM_INFO;
pvirt = (OMX_U8*)PmemMalloc(pMem, m_sProfile.nFrameBytes);
if (pvirt == NULL) {
CHK(1);
}
result = VencTest_RegisterYUVBuffer(&m_pInBuffers[i],
(OMX_U8*) pvirt,
(OMX_PTR) pMem);
CHK(result);
}
} else if (m_eMode == MODE_LIVE_ENCODE) {
D("going to idle state");
//SetState(OMX_StateIdle);
OMX_SendCommand(m_hHandle,
OMX_CommandStateSet,
(OMX_U32) OMX_StateIdle,
NULL);
}
int i;
OMX_PARAM_PORTDEFINITIONTYPE portDef;
portDef.nPortIndex = 1;
result = OMX_GetParameter(m_hHandle, OMX_IndexParamPortDefinition, &portDef);
CHK(result);
D("allocating & calling usebuffer for Output port");
num_out_buffers = portDef.nBufferCountActual;
for (i = 0; i < portDef.nBufferCountActual; i++) {
void* pBuff;
pBuff = malloc(portDef.nBufferSize);
D("portDef.nBufferSize = %d ",portDef.nBufferSize);
result = OMX_UseBuffer(m_hHandle,
&m_pOutBuffers[i],
(OMX_U32) PORT_INDEX_OUT,
NULL,
portDef.nBufferSize,
(OMX_U8*) pBuff);
CHK(result);
}
D("allocate done");
// D("Going to state " # eState"...");
while (m_eState != OMX_StateIdle) {
sleep(1);
}
//D("Now in state " # eState);
D("going to executing state");
SetState(OMX_StateExecuting);
for (i = 0; i < num_out_buffers; i++) {
D("filling buffer %d", i);
result = OMX_FillThisBuffer(m_hHandle, m_pOutBuffers[i]);
//sleep(1000);
CHK(result);
}
if (m_eMode == MODE_FILE_ENCODE) {
// encode the first frame to kick off the whole process
VencTest_ReadAndEmpty(m_pInBuffers[0]);
// FBTest_DisplayImage(((PmemBuffer*) m_pInBuffers[0]->pAppPrivate)->fd,0);
}
if (m_eMode == MODE_PROFILE) {
int i;
// read several frames into memory
D("reading frames into memory");
for (i = 0; i < num_in_buffers; i++) {
D("[%d] address 0x%x",i, m_pInBuffers[i]->pBuffer);
#ifdef MAX_RES_720P
read(m_nInFd,
m_pInBuffers[i]->pBuffer,
m_sProfile.nFrameBytes);
#else
// read Y first
read(m_nInFd,
m_pInBuffers[i]->pBuffer,
m_sProfile.nFrameWidth*m_sProfile.nFrameHeight);
// check alignment for offset to C
OMX_U32 offset_to_c = m_sProfile.nFrameWidth * m_sProfile.nFrameHeight;
const OMX_U32 C_2K = (1024*2),
MASK_2K = C_2K-1,
IMASK_2K = ~MASK_2K;
if (offset_to_c & MASK_2K) {
// offset to C is not 2k aligned, adjustment is required
offset_to_c = (offset_to_c & IMASK_2K) + C_2K;
}
// read C
read(m_nInFd,
m_pInBuffers[i]->pBuffer + offset_to_c,
m_sProfile.nFrameWidth*m_sProfile.nFrameHeight/2);
#endif
}
// FBTest_Initialize(m_sProfile.nFrameWidth, m_sProfile.nFrameHeight);
// loop over the mem-resident frames and encode them
D("beging playing mem-resident frames...");
for (i = 0; m_nFramePlay == 0 || i < m_nFramePlay; i++) {
int idx = i % num_in_buffers;
if (m_bInFrameFree[idx] == OMX_FALSE) {
int j;
E("the expected buffer is not free, but lets find another");
idx = -1;
// lets see if we can find another free buffer
for (j = 0; j < num_in_buffers; j++) {
if (m_bInFrameFree[j]) {
idx = j;
break;
}
}
}
// if we have a free buffer let's encode it
if (idx >= 0) {
D("encode frame %d...m_pInBuffers[idx]->pBuffer=0x%x", i,m_pInBuffers[idx]->pBuffer);
m_bInFrameFree[idx] = OMX_FALSE;
VencTest_EncodeFrame(m_pInBuffers[idx]->pBuffer,
m_nTimeStamp);
D("display frame %d...", i);
// FBTest_DisplayImage(((PmemBuffer*) m_pInBuffers[idx]->pAppPrivate)->fd,0);
m_nTimeStamp += 1000000 / m_sProfile.nFramerate;
} else {
E("wow, no buffers are free, performance "
"is not so good. lets just sleep some more");
}
D("sleep for %d microsec", 1000000/m_sProfile.nFramerate);
sleep (1000000 / m_sProfile.nFramerate);
}
// FBTest_Exit();
}
Msg msg;
bool bQuit = false;
while ((m_eMode == MODE_FILE_ENCODE || m_eMode == MODE_LIVE_ENCODE) &&
!bQuit) {
PopMessage(&msg);
switch (msg.id) {
//////////////////////////////////
// FRAME IS ENCODED
//////////////////////////////////
case MSG_ID_INPUT_FRAME_DONE:
/*pthread_mutex_lock(&m_mutex);
++m_nFrameOut;
if (m_nFrameOut == m_nFramePlay && m_nFramePlay != 0)
{
bQuit = true;
}
pthread_mutex_unlock(&m_mutex);*/
if (!bQuit && m_eMode == MODE_FILE_ENCODE) {
D("pushing another frame down to encoder");
if (VencTest_ReadAndEmpty(msg.data.sBitstreamData.pBuffer)) {
// we have read the last frame
D("main is exiting...");
bQuit = true;
}
}
break;
case MSG_ID_OUTPUT_FRAME_DONE:
D("================ writing frame %d = %d bytes to output file",
m_nFrameOut+1,
msg.data.sBitstreamData.pBuffer->nFilledLen);
D("StopEncodeTime=%lld", GetTimeStamp());
write(m_nOutFd,
msg.data.sBitstreamData.pBuffer->pBuffer,
msg.data.sBitstreamData.pBuffer->nFilledLen);
result = OMX_FillThisBuffer(m_hHandle,
msg.data.sBitstreamData.pBuffer);
if (result != OMX_ErrorNone) {
CHK(result);
}
pthread_mutex_lock(&m_mutex);
++m_nFrameOut;
if (m_nFrameOut == m_nFramePlay && m_nFramePlay != 0) {
bQuit = true;
}
pthread_mutex_unlock(&m_mutex);
break;
default:
E("invalid msg id %d", (int) msg.id);
} // end switch (msg.id)
/* // TO UNCOMMENT FOR PAUSE TESTINGS
if(m_nFrameOut == 10)
{
E("\nGoing to Pause state\n");
SetState(OMX_StatePause);
sleep(3);
//REQUEST AN I FRAME AFTER PAUSE
OMX_CONFIG_INTRAREFRESHVOPTYPE voprefresh;
voprefresh.nPortIndex = (OMX_U32)PORT_INDEX_OUT;
voprefresh.IntraRefreshVOP = OMX_TRUE;
result = OMX_SetConfig(m_hHandle,
OMX_IndexConfigVideoIntraVOPRefresh,
&voprefresh);
E("\n OMX_IndexConfigVideoIntraVOPRefresh Set Paramter port");
CHK(result);
E("\nGoing to executing state\n");
SetState(OMX_StateExecuting);
}
*/
} // end while (!bQuit)
if (m_eMode == MODE_LIVE_ENCODE) {
CameraTest_Exit();
close(m_nOutFd);
} else if (m_eMode == MODE_FILE_ENCODE ||
m_eMode == MODE_PROFILE) {
// deallocate pmem buffers
for (int i = 0; i < num_in_buffers; i++) {
PmemFree((OMX_QCOM_PLATFORM_PRIVATE_PMEM_INFO*)m_pInBuffers[i]->pAppPrivate,
m_pInBuffers[i]->pBuffer,
m_sProfile.nFrameBytes);
delete (OMX_QCOM_PLATFORM_PRIVATE_PMEM_INFO*) m_pInBuffers[i]->pAppPrivate;
}
close(m_nInFd);
if (m_eMode == MODE_FILE_ENCODE) {
close(m_nOutFd);
}
if (m_pDynConfFile) {
fclose(m_pDynConfFile);
m_pDynConfFile = NULL;
}
}
if (m_eMode != MODE_PREVIEW) {
D("exit encoder test");
VencTest_Exit();
}
pthread_mutex_destroy(&m_mutex);
pthread_cond_destroy(&m_signal);
/* Time Statistics Logging */
if (0 != m_sProfile.nFramerate) {
enc_time_usec = m_nTimeStamp - (1000000 / m_sProfile.nFramerate);
enc_time_sec =enc_time_usec/1000000;
if (0 != enc_time_sec) {
printf("Total Frame Rate: %f",ebd_cnt/enc_time_sec);
printf("\nEncoder Bitrate :%lf Kbps",(tot_bufsize*8)/(enc_time_sec*1000));
}
} else {
printf("\n\n Encode Time is zero");
}
printf("\nTotal Number of Frames :%d",ebd_cnt);
printf("\nNumber of dropped frames during encoding:%d\n",ebd_cnt-fbd_cnt);
/* End of Time Statistics Logging */
D("main has exited");
return 0;
}