/*-------------------------------------------------------------------------- | |
Copyright (c) 2010, Code Aurora Forum. 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 Code Aurora 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 <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" | |
////////////////////////// | |
// MACROS | |
////////////////////////// | |
#define CHK(result) if (result != OMX_ErrorNone) { E("*************** error *************"); exit(0); } | |
#define TEST_LOG | |
#ifdef VENC_SYSLOG | |
#include "cutils/log.h" | |
/// Debug message macro | |
#define D(fmt, ...) LOGE("venc_test Debug %s::%d "fmt"\n", \ | |
__FUNCTION__, __LINE__, \ | |
## __VA_ARGS__) | |
/// Error message macro | |
#define E(fmt, ...) LOGE("venc_test Error %s::%d "fmt"\n", \ | |
__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,128000,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,14000000,OMX_VIDEO_MPEG4Level5,OMX_VIDEO_MPEG4ProfileSimple}, | |
{99,2970,128000,OMX_VIDEO_MPEG4Level0,OMX_VIDEO_MPEG4ProfileAdvancedSimple}, | |
{99,2970,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}, | |
{3600,108000,14000000,OMX_VIDEO_MPEG4Level5,OMX_VIDEO_MPEG4ProfileAdvancedSimple}, | |
{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}, | |
{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}, | |
{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}, | |
{0 , 0 , 0 , 0 } | |
}; | |
////////////////////////// | |
// 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; | |
OMX_U32 nBitrate; | |
OMX_U32 nFramerate; | |
char* cInFileName; | |
char* cOutFileName; | |
}; | |
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 | |
}; | |
////////////////////////// | |
// 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; | |
/* Statistics Logging */ | |
static long long tot_bufsize = 0; | |
int ebd_cnt=0, fbd_cnt=0; | |
////////////////////////// | |
// MODULE FUNCTIONS | |
////////////////////////// | |
void* PmemMalloc(OMX_QCOM_PLATFORM_PRIVATE_PMEM_INFO* pMem, int nSize) | |
{ | |
void *pvirt = NULL; | |
if (!pMem) | |
return NULL; | |
pMem->pmem_fd = open("/dev/pmem_adsp", O_RDWR); | |
if ((int)(pMem->pmem_fd) < 0) | |
return NULL; | |
nSize = (nSize + 4095) & (~4095); | |
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; | |
return NULL; | |
} | |
D("allocated pMem->fd = %d pvirt=0x%x, pMem->phys=0x%x, 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; | |
return 0; | |
} | |
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 = NULL; | |
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 %d width %d bit rate %d",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); | |
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; | |
portdef.format.video.xFramerate = m_sProfile.nFramerate << 16; | |
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 | |
//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; | |
} | |
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 <= (int)profile_tbl[0]) | |
{ | |
if(mb_per_sec <= (int)profile_tbl[1]) | |
{ | |
if(m_sProfile.nBitrate <= (int)profile_tbl[2]) | |
{ | |
eLevel = (int)profile_tbl[3]; | |
eProfile = (int)profile_tbl[4]; | |
E("\n profile and level found \n"); | |
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; | |
} | |
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; ///@todo determine what this should be | |
h263.nPictureHeaderRepetition = 0; ///@todo determine what this should be | |
h263.nGOBHeaderInterval = 0; ///@todo determine what this should be | |
result = OMX_SetParameter(m_hHandle, | |
OMX_IndexParamVideoH263, | |
&h263); | |
} | |
else | |
{ | |
D("Configuring MP4/H264..."); | |
OMX_VIDEO_PARAM_PROFILELEVELTYPE profileLevel; // OMX_IndexParamVideoProfileLevelCurrent | |
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 = %d level = %d",profileLevel.eProfile,profileLevel.eLevel); | |
CHK(result); | |
/*OMX_VIDEO_PARAM_MPEG4TYPE mp4; | |
result = OMX_GetParameter(m_hHandle, | |
OMX_IndexParamVideoMpeg4, | |
&mp4); | |
E("\n OMX_IndexParamVideoMpeg4 Set Paramter port"); | |
CHK(result); | |
mp4.nTimeIncRes = m_sProfile.nFramerate * 2; | |
mp4.nPFrames = mp4.nTimeIncRes - 1; // intra period | |
result = OMX_SetParameter(m_hHandle, | |
OMX_IndexParamVideoMpeg4, | |
&mp4); | |
CHK(result);*/ | |
// 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.eProfile = OMX_VIDEO_MPEG4ProfileSimple; | |
// mp4.eLevel = m_sProfile.eLevel; | |
// mp4.nSliceHeaderSpacing = 0; | |
// mp4.bSVH = OMX_FALSE; | |
// mp4.bGov = OMX_FALSE; | |
// mp4.nPFrames = m_sProfile.nFramerate * 2 - 1; // intra period | |
// mp4.bACPred = OMX_TRUE; | |
// mp4.nTimeIncRes = m_sProfile.nFramerate * 2; // delta = 2 @ 15 fps | |
// mp4.nAllowedPictureTypes = 2; // pframe and iframe | |
// result = OMX_SetParameter(m_hHandle, | |
// OMX_IndexParamVideoMpeg4, | |
// &mp4); | |
// CHK(result); | |
} | |
if (m_sProfile.eCodec == OMX_VIDEO_CodingAVC) | |
{ | |
OMX_VIDEO_PARAM_AVCSLICEFMO avcslicefmo; | |
avcslicefmo.nPortIndex = (OMX_U32)PORT_INDEX_OUT; | |
result = OMX_GetParameter(m_hHandle, | |
OMX_IndexParamVideoSliceFMO, | |
&avcslicefmo); | |
E("\n OMX_IndexParamVideoSliceFMO Get Paramter port"); | |
CHK(result); | |
avcslicefmo.eSliceMode = m_sProfile.eSliceMode; | |
result = OMX_SetParameter(m_hHandle, | |
OMX_IndexParamVideoSliceFMO, | |
&avcslicefmo); | |
E("\n OMX_IndexParamVideoSliceFMO Set Paramter port"); | |
CHK(result); | |
} | |
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); | |
framerate.xFramerate = m_sProfile.nFramerate << 16; | |
result = OMX_SetParameter(m_hHandle, | |
OMX_IndexParamVideoPortFormat, | |
&framerate); | |
E("\n OMX_IndexParamVideoPortFormat Set Paramter port"); | |
CHK(result); | |
#if 1 | |
OMX_CONFIG_FRAMERATETYPE enc_framerate; // OMX_IndexConfigVideoFramerate | |
enc_framerate.nPortIndex = (OMX_U32)PORT_INDEX_OUT; | |
result = OMX_GetConfig(m_hHandle, | |
OMX_IndexConfigVideoFramerate, | |
&enc_framerate); | |
E("\n OMX_IndexConfigVideoFramerate Get config port"); | |
CHK(result); | |
enc_framerate.xEncodeFramerate = m_sProfile.nFramerate << 16; | |
result = OMX_SetConfig(m_hHandle, | |
OMX_IndexConfigVideoFramerate, | |
&enc_framerate); | |
E("\n OMX_IndexConfigVideoFramerate Set config port"); | |
CHK(result); | |
#endif | |
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); | |
} | |
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; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
OMX_ERRORTYPE VencTest_ReadAndEmpty(OMX_BUFFERHEADERTYPE* pYUVBuffer) | |
{ | |
OMX_ERRORTYPE result = OMX_ErrorNone; | |
#ifdef T_ARM | |
if (read(m_nInFd, | |
pYUVBuffer->pBuffer, | |
m_sProfile.nFrameBytes) != m_sProfile.nFrameBytes) | |
{ | |
return OMX_ErrorUndefined; | |
} | |
#else | |
{ | |
char * pInputbuf = (char *)(pYUVBuffer->pBuffer) ; | |
read(m_nInFd,pInputbuf,m_sProfile.nFrameBytes) ; | |
} | |
#endif | |
D("about to call VencTest_EncodeFrame..."); | |
pthread_mutex_lock(&m_mutex); | |
++m_nFrameIn; | |
pYUVBuffer->nFilledLen = m_sProfile.nFrameBytes; | |
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, "<Rate Control - optional> <AVC Slice Mode - optional\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); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
void parseArgs(int argc, char** argv) | |
{ | |
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 > 11) | |
{ | |
usage(argv[0]); | |
} | |
else | |
{ | |
if ((argc == 10)) | |
{ | |
m_sProfile.eControlRate = OMX_Video_ControlRateVariable; | |
int RC = atoi(argv[9]); | |
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) | |
{ | |
if(!strcmp(argv[3], "H264") || !strcmp(argv[3], "h264")) | |
{ | |
E("\nSetting AVCSliceMode ... "); | |
int AVCSliceMode = atoi(argv[10]); | |
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; | |
} | |
} | |
else | |
{ | |
E("SliceMode support only for H.264 codec"); | |
usage(argv[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 || | |
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 | |
{ | |
usage(argv[0]); | |
} | |
if (m_eMode == MODE_DISPLAY || | |
m_eMode == MODE_PREVIEW) | |
{ | |
m_sProfile.nFramerate = atoi(argv[3]); | |
m_nFramePlay = atoi(argv[4]); | |
} | |
else if (m_eMode == MODE_LIVE_ENCODE || | |
m_eMode == MODE_FILE_ENCODE || | |
m_eMode == MODE_PROFILE) | |
{//263 | |
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; | |
} | |
else | |
{ | |
usage(argv[0]); | |
} | |
m_sProfile.nFramerate = atoi(argv[4]); | |
m_sProfile.nBitrate = atoi(argv[5]); | |
// m_sProfile.eControlRate = OMX_Video_ControlRateVariable; | |
m_nFramePlay = atoi(argv[6]); | |
} | |
} | |
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)); | |
parseArgs(argc, argv); | |
D("fps=%d, bitrate=%d, width=%d, height=%d", | |
m_sProfile.nFramerate, | |
m_sProfile.nBitrate, | |
m_sProfile.nFrameWidth, | |
m_sProfile.nFrameHeight); | |
//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 output buffers"); | |
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 output buffers"); | |
D("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); | |
read(m_nInFd, | |
m_pInBuffers[i]->pBuffer, | |
m_sProfile.nFrameBytes); | |
} | |
// 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) | |
} // 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_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; | |
} |