diff options
author | Prasanna Kumar M.R <prasanna.kumarmr@ti.com> | 2012-04-03 16:25:11 -0500 |
---|---|---|
committer | Daniel Levin <dendy@ti.com> | 2012-07-25 08:56:29 -0500 |
commit | 5d732514084b76d6d047b89066ad679a098c89d6 (patch) | |
tree | 343076ca3f219e4b16edeffefef47efaf28962e9 /test/VTC | |
parent | 286e1a5ed9bb6113b754921229f94cf18d2b31d5 (diff) | |
download | hardware_ti_omap4-5d732514084b76d6d047b89066ad679a098c89d6.zip hardware_ti_omap4-5d732514084b76d6d047b89066ad679a098c89d6.tar.gz hardware_ti_omap4-5d732514084b76d6d047b89066ad679a098c89d6.tar.bz2 |
New VTC loopback application
In this application:
- Camera Preview is started.
- Preview Frames are sent to encoder for encoding.
- The encoded frames are then sent to the decoder for decoding.
- The decoded frames are then rendered along side the preview window.
The application can be run in frame mode or slice mode
Slice mode support is included at:
- Camera->encoder level (OMX tunneling)
- Encoder output data
Run "VTCLoopbackTest -h" to see all the supported features.
Change-Id: I4462adad8dde32d70d6c8ba13197296b96f96827
Signed-off-by: Prasanna Kumar M.R. <prasanna.kumarmr@ti.com>
Signed-off-by: Jorge E. Solano <jsolano@ti.com>
Signed-off-by: Anu Sundararajan <sanuradha@ti.com>
Diffstat (limited to 'test/VTC')
-rw-r--r-- | test/VTC/Android.mk | 71 | ||||
-rw-r--r-- | test/VTC/CameraHardwareInterfaceTest.cpp | 304 | ||||
-rw-r--r-- | test/VTC/IOMXDecoder.cpp | 1077 | ||||
-rw-r--r-- | test/VTC/IOMXDecoder.h | 252 | ||||
-rw-r--r-- | test/VTC/IOMXEncoder.cpp | 1158 | ||||
-rw-r--r-- | test/VTC/IOMXEncoder.h | 193 | ||||
-rw-r--r-- | test/VTC/VTCLoopback.cpp | 883 | ||||
-rw-r--r-- | test/VTC/VTCLoopback.h | 145 | ||||
-rw-r--r-- | test/VTC/VTCTestApp.cpp | 80 |
9 files changed, 4112 insertions, 51 deletions
diff --git a/test/VTC/Android.mk b/test/VTC/Android.mk index 337b13c..ef344f3 100644 --- a/test/VTC/Android.mk +++ b/test/VTC/Android.mk @@ -29,3 +29,74 @@ LOCAL_MODULE_TAGS:= optional LOCAL_MODULE := VTCTestApp include $(BUILD_EXECUTABLE) +############################################################################### + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := VTCLoopback.cpp IOMXEncoder.cpp IOMXDecoder.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcamera_client \ + libstagefright \ + libmedia \ + libbinder \ + libtiutils \ + libcutils \ + libutils \ + liblog \ + libgui \ + libui \ + +LOCAL_C_INCLUDES += \ + $(TOP)/frameworks/base/include/ui \ + $(TOP)/frameworks/base/include/surfaceflinger \ + $(TOP)/frameworks/base/include/camera \ + $(TOP)/frameworks/base/include/media \ + $(TOP)/frameworks/base/include/media/stagefright \ + $(TOP)/hardware/ti/domx/omx_core/inc \ + $(TOP)/hardware/ti/omap4xxx/libtiutils + +LOCAL_CFLAGS +=-Wall -fno-short-enums -O0 -g + +LOCAL_PRELINK_MODULE := false +LOCAL_MODULE_TAGS:= optional +LOCAL_MODULE := VTCLoopbackTest +include $(BUILD_EXECUTABLE) + +############################################################################### + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= CameraHardwareInterfaceTest.cpp + +LOCAL_SHARED_LIBRARIES:= \ + libdl \ + libui \ + libutils \ + libcutils \ + libbinder \ + libmedia \ + libui \ + libgui \ + libcamera_client \ + libhardware + +LOCAL_C_INCLUDES += \ + frameworks/base/include/ui \ + frameworks/base/include/surfaceflinger \ + frameworks/base/include/camera \ + frameworks/base/include/media \ + frameworks/base/services/camera/libcameraservice \ + $(PV_INCLUDES) + +LOCAL_MODULE:= CameraHardwareInterfaceTest +LOCAL_MODULE_TAGS:= tests + +LOCAL_CFLAGS += -Wall -fno-short-enums -O0 -g -D___ANDROID___ + +ifeq ($(TARGET_BOARD_PLATFORM),omap4) + LOCAL_CFLAGS += -DTARGET_OMAP4 +endif + +include $(BUILD_HEAPTRACKED_EXECUTABLE) + diff --git a/test/VTC/CameraHardwareInterfaceTest.cpp b/test/VTC/CameraHardwareInterfaceTest.cpp new file mode 100644 index 0000000..70d94dc --- /dev/null +++ b/test/VTC/CameraHardwareInterfaceTest.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) Texas Instruments - http://www.ti.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#define LOG_NDEBUG 0 +#define LOG_TAG "CamHalTest" + +#include <fcntl.h> +#include <getopt.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#include <semaphore.h> +#include <pthread.h> + + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> + +#include <camera/Camera.h> +#include <camera/ICamera.h> + +#include <cutils/log.h> +#include <media/stagefright/CameraSource.h> +#include <media/stagefright/MetaData.h> + +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/ISurface.h> +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/ISurfaceComposerClient.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +#include <cutils/properties.h> + +#include <hardware/camera.h> +#include <hardware/hardware.h> +#include "CameraHardwareInterface.h" + +using namespace android; + +int startPreviewNow = 1; +int mWidth = 640; +int mHeight = 480; + +static int cameraId = 0; +static int mPriority = 0; +camera_module_t *mModule; +sp<CameraHardwareInterface> mHardware = NULL; + +static int surface_setup_complete = 0; +sp<Surface> mSurface; +sp<SurfaceComposerClient> mComposerClient; +sp<SurfaceControl> mSurfaceControl; +sp<ANativeWindow> mWindow; + +static pthread_cond_t mCond; +static pthread_mutex_t mMutex; + + +void my_notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user) { + LOGD("\n\nnotifyCallback(%d)\n\n", msgType); +} + +void my_dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) { + LOGD("\n\ndataCallback(%d)\n\n", msgType); +} + +void my_dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user) { + LOGD("\n\ndataCallbackTimestamp(%d)\n\n", msgType); +} + +void surfaceInit() { + status_t retval; + + mComposerClient = new SurfaceComposerClient; + + retval = mComposerClient->initCheck(); + if (retval != NO_ERROR) { + LOGE("mCOmposerClient->initCheck failed"); + return; + } + + mSurfaceControl = mComposerClient->createSurface(String8("NativeSurface"), + 0, mWidth, mHeight, + PIXEL_FORMAT_OPAQUE, ISurfaceComposer::eFXSurfaceNormal); + + if (mSurfaceControl == NULL) { + LOGE("mComposerClient->createSurface failed"); + return; + } + + if (mSurfaceControl->isValid()) { + LOGE("mSurfaceControl is valid"); + } else { + LOGE("mSurfaceControl is not valid"); + return; + } + + SurfaceComposerClient::openGlobalTransaction(); + + retval = mSurfaceControl->setPosition(100,100); + if (retval != NO_ERROR) { + LOGE("mCOmposerClient->setPosition failed"); + return; + } + + retval = mSurfaceControl->setSize(400,400); + if (retval != NO_ERROR) { + LOGE("mCOmposerClient->setPosition failed"); + return; + } + + retval = mSurfaceControl->setLayer(999990); + if (retval != NO_ERROR) { + LOGE("mCOmposerClient->setLayer 999990 failed"); + return; + } + + retval = mSurfaceControl->show(); + if (retval != NO_ERROR) { + LOGE("mCOmposerClient->show failed"); + return; + } + SurfaceComposerClient::closeGlobalTransaction(); + + mSurface = mSurfaceControl->getSurface(); + if (mSurface == NULL) { + LOGE("mSurfaceControl->getSurface failed"); + return; + } + + surface_setup_complete = 1; +} + +int main (int argc, char* argv[]) { + status_t result; + char cmd[160],*cmdptr; + struct camera_info info; + char camera_device_name[10]; + + sp<ProcessState> proc(ProcessState::self()); + ProcessState::self()->startThreadPool(); + + if (argc >= 2) { + mPriority = atoi(argv[1]); + } + + if (argc == 3) { + startPreviewNow = atoi(argv[2]); + } + + if (hw_get_module(CAMERA_HARDWARE_MODULE_ID, + (const hw_module_t **)&mModule) < 0) { + LOGE("Could not load camera HAL module"); + return -1; + } + LOGD("\nLoaded the camera module\n"); + + + if (mModule->get_camera_info(cameraId, &info) != OK) { + LOGE("Invalid camera id %d", cameraId); + return -1; + } + LOGD("\nLoaded the camera properties\n"); + + if (startPreviewNow == 0) return -1; + + snprintf(camera_device_name, sizeof(camera_device_name), "%d", cameraId); + mHardware = new CameraHardwareInterface(camera_device_name); + if (mHardware->initialize(&mModule->common) != OK) { + mHardware.clear(); + LOGE("mHardware->initialize FAILED"); + return -1; + } + LOGD("\nInitialized the camera hardware\n"); + + mHardware->setCallbacks(my_notifyCallback, + my_dataCallback, + my_dataCallbackTimestamp, + (void *)cameraId); + + mHardware->enableMsgType(CAMERA_MSG_ERROR | CAMERA_MSG_PREVIEW_METADATA); + + String8 param_str(mHardware->getParameters().flatten()); + CameraParameters params(param_str); + params.setPreviewSize(mWidth, mHeight); + params.set("priority", mPriority); + //params.dump(); + mHardware->setParameters(params); + + surfaceInit(); + if (surface_setup_complete == 0) { + LOGE("\n\nsurfaceInit failed! \n\n"); + goto EXIT; + } + + mWindow = mSurface; + if (mWindow == 0) { + LOGE("\n\nWhy is mWindow == 0?? \n\n"); + goto EXIT; + } + + result = native_window_api_connect(mWindow.get(), NATIVE_WINDOW_API_CAMERA); + if (result != NO_ERROR) { + LOGE("native_window_api_connect failed: %s (%d)", strerror(-result), + result); + goto EXIT; + } + + native_window_set_scaling_mode(mWindow.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + native_window_set_buffers_transform(mWindow.get(), 0); + + result = mHardware->setPreviewWindow(mWindow); + if (result != NO_ERROR) { + LOGE("mHardware->setPreviewWindow"); + goto EXIT; + } + + mHardware->startPreview(); + +#if USE_TIMED_WAIT + pthread_mutex_init(&mMutex, NULL); + pthread_cond_init(&mCond, NULL); + + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += 30; + pthread_mutex_lock(&mMutex); + pthread_cond_timedwait(&mCond, &mMutex, &ts); + pthread_mutex_unlock(&mMutex); +#else + + while (1) { + cmdptr = fgets(cmd, sizeof(cmd), stdin); + if (!strncmp(cmdptr,"quit",4)) { + break; + } + } +#endif + LOGD("\n\n STOPPING PREVIEW \n\n"); + mHardware->stopPreview(); + + +EXIT: + if (mHardware != 0){ + mHardware->release(); + + if (mWindow != 0) { + result = native_window_api_disconnect(mWindow.get(), NATIVE_WINDOW_API_CAMERA); + if (result != NO_ERROR) { + LOGW("native_window_api_disconnect failed: %s (%d)", strerror(-result), result); + } + mWindow = 0; + } + mHardware.clear(); + } + + if ( NULL != mSurface.get() ) { + mSurface.clear(); + } + + if ( NULL != mSurfaceControl.get() ) { + mSurfaceControl->clear(); + mSurfaceControl.clear(); + } + + if ( NULL != mComposerClient.get() ) { + mComposerClient->dispose(); + mComposerClient.clear(); + } + +#if USE_TIMED_WAIT + pthread_mutex_destroy(&mMutex); + pthread_cond_destroy(&mCond); +#endif + + LOGD("\n\n SUCCESS \n\n"); + + return 0; +} + diff --git a/test/VTC/IOMXDecoder.cpp b/test/VTC/IOMXDecoder.cpp new file mode 100644 index 0000000..d8f34ab --- /dev/null +++ b/test/VTC/IOMXDecoder.cpp @@ -0,0 +1,1077 @@ +/* + * Copyright (C) Texas Instruments - http://www.ti.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "VTCLoopback.h" +#include "IOMXDecoder.h" +#define LOG_TAG "VTC_DEC" +#define LOG_NDEBUG 0 + +#define NO_PORT_RECONFIG 1 + +//Can have '7' in all cases except 1080p slice mode when VNF is enabled and +//1080p frame mode with Vstab as we will reach Tiler 2-D boundary and alloc fails +#define MAX_OUTPUT_BUF_NUM 5 + +#define MAX_FRAME_WIDTH 2048 //For 1080p +#define MAX_FRAME_HEIGHT 1184 //For 1080p + +#define MAX_FRAME_WIDTH_720P 1408 //For 720p +#define MAX_FRAME_HEIGHT_720P 832 //For 720p + +#define MY_LOGV(x, ...) LOGV(x, ##__VA_ARGS__) + +using namespace android; + + +static void PrintDecoderFPS() { + static int mFrameCount = 0; + static int mLastFrameCount = 0; + static nsecs_t mLastFpsTime = 0; + static float mFps = 0; + mFrameCount++; + if (!(mFrameCount & 0x1F)) { + nsecs_t now = systemTime(); + nsecs_t diff = now - mLastFpsTime; + mFps = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff; + mLastFpsTime = now; + mLastFrameCount = mFrameCount; + LOGD("Decoder: %d Frames, %f FPS", mFrameCount, mFps); + } + // XXX: mFPS has the value we want +} + +static void PrintVTCLatency(nsecs_t ts) { + static int mFrameCount = 0; + static int64_t sum_latency_ms = 0; + int64_t now, diff_ms, avg_latency; + + now = systemTime()/1000; + diff_ms = (now - ts) / 1000; + sum_latency_ms += diff_ms; + mFrameCount++; + if (!(mFrameCount & 0x1F)) { + avg_latency = sum_latency_ms / 32; + sum_latency_ms = 0; + LOGD("Avg Latency: %d Frames, %llu ms", mFrameCount, avg_latency); + } +} + +bool OMXDecoder::SourceHandler::Handler() { + MY_LOGV("\n SourceHandler::Handler \n"); + TIUTILS::Message msg; + volatile int forever = 1; + + while(forever) { + TIUTILS::MessageQueue::waitForMsg(&mCommandMsgQ, NULL, NULL, -1); + { + Mutex::Autolock lock(mLock); + mCommandMsgQ.get(&msg); + } + + switch ( msg.command ) { + case SourceHandler::COMMAND_PROCESS_MSG: { + InPortBufferInfo *info = (InPortBufferInfo *)(msg.arg1); + mOMXDecoder->drainInputBuffer(info); + break; + } + case SourceHandler::COMMAND_EXIT: { + LOGD("Exiting OMX callback handler"); + forever = 0; + break; + } + } + } + return false; +} + + +bool OMXDecoder::OMXCallbackHandler::Handler() { + MY_LOGV("\n OMXCallbackHandler::Handler \n"); + TIUTILS::Message msg; + volatile int forever = 1; + + while(forever) { + TIUTILS::MessageQueue::waitForMsg(&mCommandMsgQ, NULL, NULL, -1); + { + Mutex::Autolock lock(mLock); + mCommandMsgQ.get(&msg); + } + + switch ( msg.command ) { + case OMXCallbackHandler::COMMAND_PROCESS_MSG: { + omx_message *om = (omx_message*)(msg.arg1); + omx_message omsg = *om; + mOMXDecoder->on_message(omsg); + break; + } + case OMXCallbackHandler::COMMAND_EXIT: { + LOGD("Exiting OMX callback handler"); + forever = 0; + break; + } + } + } + return false; +} + + +OMXDecoder::OMXDecoder(int width, int height, int framerate): + mWidth(width), + mHeight(height), + mFrameRate(framerate), + mAcceptingBuffers(0), + mPortReconfigInProgress(false), + mSizeOfAllAllocatedOutputBuffers(0) { + +} + +OMXDecoder::~OMXDecoder() { + +} + +void OMXDecoder::on_message(const omx_message &msg) { + switch (msg.type) { + case omx_message::EVENT: + EventHandler(msg.u.event_data.event, msg.u.event_data.data1, msg.u.event_data.data2); + break; + case omx_message::EMPTY_BUFFER_DONE: + EmptyBufferDone((OMX_BUFFERHEADERTYPE*)msg.u.extended_buffer_data.buffer); + break; + case omx_message::FILL_BUFFER_DONE: + PrintVTCLatency(msg.u.extended_buffer_data.timestamp); + FillBufferDone((OMX_BUFFERHEADERTYPE*)msg.u.extended_buffer_data.buffer); + break; + default: + CHECK(!"############ Corrupted Message !!! #############"); + break; + } +} + +status_t OMXDecoder::configure(OMX_VIDEO_AVCPROFILETYPE profile, OMX_VIDEO_AVCLEVELTYPE level, OMX_U32 refFrames) { + status_t err; + LOG_FUNCTION_NAME_ENTRY + + createPlaybackSurface(); + + CHECK_EQ(mOMXClient.connect(), (status_t)OK); + mOMX = mOMXClient.interface(); + mNode = 0; + mObserver = new OMXDecoderObserver(); + err = mOMX->allocateNode("OMX.TI.DUCATI1.VIDEO.DECODER", mObserver, &mNode); + if (err != OK) { + LOGD("Failed to allocate OMX node!!"); + return -1; + } + mObserver->setCodec(this); + + // initialize omx callback handling thread + if(mOMXCallbackHandler.get() == NULL) + mOMXCallbackHandler = new OMXCallbackHandler(this); + + if ( NULL == mOMXCallbackHandler.get() ) { + LOGE("Couldn't create omx callback handler"); + return -1; + } + + err = mOMXCallbackHandler->run("OMXCallbackThread", PRIORITY_URGENT_DISPLAY); + if ( err != NO_ERROR ) { + if( err == INVALID_OPERATION) { + LOGE("omx callback handler thread already runnning!!"); + err = NO_ERROR; + } else { + LOGE("Couldn't run omx callback handler thread"); + return -1; + } + } + + // initialize source handling thread + if(mSourceHandler.get() == NULL) + mSourceHandler = new SourceHandler(this); + + if ( NULL == mSourceHandler.get() ) { + LOGE("Couldn't create source handler"); + return -1; + } + + err = mSourceHandler->run("SourceThread", PRIORITY_URGENT_DISPLAY); + if ( err != NO_ERROR ) { + if( err == INVALID_OPERATION) { + LOGE("source handler thread already runnning!!"); + err = NO_ERROR; + } else { + LOGE("Couldn't run source handler thread"); + return -1; + } + } + + mState = OMX_StateLoaded; + waitForStateChange = 0; + + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + INIT_OMX_STRUCT(&format, OMX_VIDEO_PARAM_PORTFORMATTYPE); + bool found = false; + OMX_U32 index = 0; + format.nPortIndex = OUTPUT_PORT; + format.nIndex = 0; + for (;;) { + format.nIndex = index; + err = mOMX->getParameter(mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); + if (err != OK) { + LOGD( "get OMX_IndexParamVideoPortFormat OutPort Error:%d", err); + return -1; + } + + if (format.eCompressionFormat == OMX_VIDEO_CodingUnused + && format.eColorFormat == (OMX_TI_COLOR_FormatYUV420PackedSemiPlanar)) { + found = true; + break; + } + ++index; + } + + if (!found) { + LOGE("Did not find a match."); + return -1; + } + + MY_LOGV("found a match."); + err = mOMX->setParameter(mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); + if (err != OK) { + LOGD( "set OMX_IndexParamVideoPortFormat OutPort Error:%d", err); + return -1; + } + + // + // Populate Video input Port + // + INIT_OMX_STRUCT(&tInPortDef, OMX_PARAM_PORTDEFINITIONTYPE); + + tInPortDef.nPortIndex = INPUT_PORT; + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef)); + if (err != OK) { + LOGD( "get OMX_IndexParamPortDefinition InPort Error:%d", err); + return -1; + } + + tInPortDef.format.video.nFrameWidth = mWidth; + tInPortDef.format.video.nFrameHeight = mHeight; + tInPortDef.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC; + tInPortDef.nBufferCountActual = 4; // better to match this with the number of encoder output buffers + tInPortDef.nBufferSize = (mWidth * mHeight); + err = mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef)); + if (err != OK) { + LOGD( "set OMX_IndexParamPortDefinition InPort Error:%d", err); + return -1; + } + + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef)); + if (err != OK) { + LOGD("get OMX_IndexParamPortDefinition InPort Error:%d", err); + return -1; + } + + // + // Populate Video output Port + // + INIT_OMX_STRUCT(&tOutPortDef, OMX_PARAM_PORTDEFINITIONTYPE); + tOutPortDef.nPortIndex = OUTPUT_PORT; + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition,&tOutPortDef, sizeof(tOutPortDef)); + if (err != OK) { + LOGD("get OMX_IndexParamPortDefinition OutPort Error:%d", err); + return -1; + } + + tOutPortDef.format.video.nFrameWidth = mWidth; + tOutPortDef.format.video.nFrameHeight = mHeight; + tOutPortDef.format.video.nStride = 4096; + tOutPortDef.format.video.xFramerate = (mFrameRate << 16); + tOutPortDef.format.video.eColorFormat = OMX_COLOR_FormatYUV420PackedSemiPlanar; +// tOutPortDef.nBufferCountActual += 2; // 2 for surface flinger. + //set buffer count such that port reconfig can be avoided.. + //is that possible? in any case add 2 for surface flinger. + + err = mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, &tOutPortDef, sizeof(tOutPortDef)); + if (err != OK) { + LOGD( "set OMX_IndexParamPortDefinition OutPort Error:%d", err); + return -1; + } + + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition,&tOutPortDef, sizeof(tOutPortDef)); + if (err != OK) { + LOGD("get OMX_IndexParamPortDefinition OutPort Error:%d", err); + return -1; + } + + // + // setup (code specific) AVC Decoder paramters + // + OMX_VIDEO_PARAM_AVCTYPE h264type; + INIT_OMX_STRUCT(&h264type,OMX_VIDEO_PARAM_AVCTYPE); + h264type.nPortIndex = INPUT_PORT; + + err = mOMX->getParameter(mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type)); + if (err != OK) { + LOGD("get OMX_IndexParamVideoAvc failed : %d", err); + return -1; + } + + h264type.eProfile = profile; + h264type.eLevel = level; + h264type.nRefFrames = refFrames; + + err = mOMX->setParameter(mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type)); + if (err != OK) { + LOGD("set OMX_IndexParamVideoAvc failed : %d", err); + return -1; + } + + // Native Window related calls + err = mOMX->enableGraphicBuffers(mNode, OUTPUT_PORT, OMX_TRUE); + if (err != 0) { + return err; + } + + android_native_rect_t crop; + crop.left = 0; + crop.top = 0; + crop.right = mWidth + 1; + crop.bottom = mHeight + 1; + + // We'll ignore any errors here, if the surface is + // already invalid, we'll know soon enough. + native_window_set_crop(mNativeWindow.get(), &crop); + + LOG_FUNCTION_NAME_EXIT + + return 0; + +} + +status_t OMXDecoder::prepare() { + status_t err; + + LOG_FUNCTION_NAME_ENTRY + + if (setCurrentState(OMX_StateIdle)) { + return -1; + } + + INIT_OMX_STRUCT(&tInPortDef, OMX_PARAM_PORTDEFINITIONTYPE); + tInPortDef.nPortIndex = INPUT_PORT; + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef)); + if (err != OK) { + LOGD("get OMX_IndexParamPortDefinition InPort Error:%d", err); + return -1; + } + //dump_video_port_values(tInPortDef); + + mDealer = new MemoryDealer((tInPortDef.nBufferCountActual*tInPortDef.nBufferSize), "PLAYBACK_INPUT"); + for (OMX_U32 i = 0; i < tInPortDef.nBufferCountActual; ++i) { + sp<IMemory> mMem = mDealer->allocate(tInPortDef.nBufferSize); + CHECK(mMem.get() != NULL); + IOMX::buffer_id buffer; + err = mOMX->allocateBufferWithBackup(mNode, INPUT_PORT, mMem, &buffer); + if (err != OK) { + LOGD("OMX_AllocateBuffer for input port index:%d failed:%d",(int)i,err); + } + InPortBufferInfo *info = new InPortBufferInfo; + info->mem = mMem; + info->b_id = buffer; + mInputBuffers.push(info); + mEmptyInputBuffers.push_back(info); + } + LOGD( "Allocated %d Input port Buffers. ", (int)tInPortDef.nBufferCountActual); + + + INIT_OMX_STRUCT(&tOutPortDef, OMX_PARAM_PORTDEFINITIONTYPE); + tOutPortDef.nPortIndex = OUTPUT_PORT; + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tOutPortDef, sizeof(tOutPortDef)); + if (err != OK) { + LOGD("get OMX_IndexParamPortDefinition OutPort Error:%d", err); + return -1; + } + //dump_video_port_values(tOutPortDef); + + err = allocateOutputBuffersFromNativeWindow(); + if (err != OK) { + LOGD("allocateOutputBuffersFromNativeWindow failed. err:%d", err); + return -1; + } + + // now wait until state becomes idle. + if (waitForStateSet(OMX_StateIdle)) { + LOGD("state change to IDLE failed"); + return -1; + } + + // now transition to exec + if (setCurrentState(OMX_StateExecuting)) { + return -1; + } + + if (waitForStateSet(OMX_StateExecuting)) { + LOGD("state change to EXECUTING failed"); + return -1; + } + + if (restart() != 0) return -1; + + return 0; +} + +status_t OMXDecoder::restart() { + status_t err; + LOG_FUNCTION_NAME_ENTRY + + // give output buffers to the OMX + for (int i=0; i<tOutPortDef.nBufferCountActual; i++) { + if (mOutputBuffers[i].mStatus == OWNED_BY_US) { + err = mOMX->fillBuffer(mNode, mOutputBuffers[i].b_id); + if (err != OK) { + LOGD("fillBuffer failed:%d", err); + } + else LOGD("called fillBuffer(%d)",i); + } + } + + return 0; +} + +status_t OMXDecoder::start(MetaData *params) { + LOG_FUNCTION_NAME_ENTRY + mAcceptingBuffers = 1; + return 0; +} + +status_t OMXDecoder::stop() { + status_t err; + + LOG_FUNCTION_NAME_ENTRY + + mAcceptingBuffers = 0; + // flush all the buffers + err = mOMX->sendCommand(mNode,OMX_CommandFlush, INPUT_PORT); + if (err != OK) { + LOGD("OMX_CommandFlush for input port (0) failed:%d", err); + } + + err = mOMX->sendCommand(mNode,OMX_CommandFlush, OUTPUT_PORT); + if (err != OK) { + LOGD("OMX_CommandFlush for output port (1) failed:%d", err); + } + + // change state to Idle if not already + if (mState != OMX_StateIdle && mState != OMX_StateLoaded) { + if (setCurrentState(OMX_StateIdle)) { + LOGD("OMX_StateIdle failed"); + } + + if (waitForStateSet(OMX_StateIdle)) { + LOGD("state change to IDLE failed"); + } + } + + // disable ports + err = mOMX->sendCommand(mNode, OMX_CommandPortDisable, -1); + if (err != OK) { + LOGD("Error in SendCommand()-OMX_CommandPortDisable:"); + } else { + LOGD("OMX_CommandPortDisable done"); + } + + // free input buffers + for (int i=0; i<(int)tInPortDef.nBufferCountActual; i++) { + err = mOMX->freeBuffer(mNode, INPUT_PORT, mInputBuffers[i]->b_id); + if( (err != OK)) { + LOGD("Free Buffer for Input Port buffer:%d failed:%d",i,err); + } + } + + freeOutputBuffers(); + + // change state to Loaded + if (mState != OMX_StateLoaded) { + if (setCurrentState(OMX_StateLoaded)) { + LOGD("OMX_StateLoaded failed"); + } + if (waitForStateSet(OMX_StateLoaded)) { + LOGD("state change to LOADED failed"); + } + } else { + LOGD("It was already OMX_StateLoaded???"); + } + + + usleep(5000); + + err = mOMX->freeNode(mNode); + CHECK_EQ(err, (status_t)OK); + + LOGD("OMX_FreeHandle completed"); + + //Exit and free ref to callback handling thread + if ( NULL != mOMXCallbackHandler.get() ) { + TIUTILS::Message msg; + msg.command = OMXCallbackHandler::COMMAND_EXIT; + //Clear all messages pending first + mOMXCallbackHandler->clearCommandQ(); + mOMXCallbackHandler->put(&msg); + mOMXCallbackHandler->requestExitAndWait(); + mOMXCallbackHandler.clear(); + } + + //Exit and free ref to source handling thread + if ( NULL != mSourceHandler.get() ) { + TIUTILS::Message msg; + msg.command = SourceHandler::COMMAND_EXIT; + //Clear all messages pending first + mSourceHandler->clearCommandQ(); + mSourceHandler->put(&msg); + mSourceHandler->requestExitAndWait(); + mSourceHandler.clear(); + } + + destroyPlaybackSurface(); + + //what else needs to be freed.. + + return 0; +} + +sp<MetaData> OMXDecoder::getFormat() { + return NULL; +} + +status_t OMXDecoder::read(MediaBuffer **buffer, const ReadOptions *options) { + return 0; +} + +void OMXDecoder::AcceptEncodedBuffer(void *pBuffer, OMX_U32 nFilledLen, OMX_TICKS nTimeStamp) { + //MY_LOGV("AcceptEncodedBuffer - Len = %d", nFilledLen); + if (mEmptyInputBuffers.empty()) { + LOGE("\n\n\n Ran out of input buffers. Dropping Frames.\n\n\n"); + return; + } + + if (mPortReconfigInProgress) { + MY_LOGV( "%s:\t mPortReconfigInProgress.", __FUNCTION__); + //return; + } + + while(mPortReconfigInProgress) sleep(1); + + List<InPortBufferInfo*>::iterator iter; + InPortBufferInfo *info; + iter = mEmptyInputBuffers.begin(); + info = (InPortBufferInfo *)*iter; + mEmptyInputBuffers.erase(iter); + + info->nFilledLen = nFilledLen; + info->nTimeStamp = nTimeStamp; + memcpy((void*)info->mem->pointer(), pBuffer, nFilledLen); + + TIUTILS::Message msg; + msg.command = SourceHandler::COMMAND_PROCESS_MSG; + msg.arg1 = (void*)info; + mSourceHandler->put(&msg); + +} + +OMX_ERRORTYPE OMXDecoder::EventHandler(OMX_EVENTTYPE eEvent, OMX_U32 nData1,OMX_U32 nData2) { + OMX_ERRORTYPE errorType; + MY_LOGV("########## EventHandler: eEvent:0x%x, nData1:0x%x, nData2:0x%x, pid=%d", (int)eEvent, (int)nData1, (int)nData2, getpid()); + + switch (eEvent) { + case OMX_EventCmdComplete: + if (nData1 == OMX_CommandPortDisable) { + if (nData2 == OMX_DirInput) { + LOGD( "Component OMX_EventCmdComplete OMX_CommandPortDisable OMX_DirInput"); + } + if (nData2 == OMX_DirOutput) { + LOGD( "Component OMX_EventCmdComplete OMX_CommandPortDisable OMX_DirOutput"); + if (mPortReconfigInProgress) { + status_t err = mOMX->sendCommand(mNode, OMX_CommandPortEnable, OUTPUT_PORT); + CHECK_EQ(err, (status_t)OK); + allocateOutputBuffersFromNativeWindow(); + } + } + } else if (nData1 == OMX_CommandPortEnable) { + if (nData2 == OUTPUT_PORT) { + LOGD( "Component OMX_EventCmdComplete OMX_CommandPortEnable OMX_DirOutput"); + restart(); + mPortReconfigInProgress = false; + LOGD("\nPort Reconfiguration completed.. \n"); + + } + } else if (nData1 == OMX_CommandStateSet) { + LOGD( "Component OMX_EventCmdComplete OMX_CommandStateSet new State:%s",OMXStateName((OMX_STATETYPE)nData2)); + { + Mutex::Autolock autoLock(mLock); + mState = (OMX_STATETYPE)nData2; + waitForStateChange = 0; + } + mAsyncCompletion.signal(); + + } else if (nData1 == OMX_CommandFlush) { + LOGD( "Component OMX_EventCmdComplete OMX_CommandFlush port:%d",(int)nData2); + } else { + LOGD( "Component OMX_EventCmdComplete command:%d",(int)nData1); + } + break; + + case OMX_EventPortSettingsChanged: + if (nData2 == 0 || nData2 == OMX_IndexParamPortDefinition) { + LOGD("\nPort Reconfiguration in progress.. \n"); + mPortReconfigInProgress = true; + status_t err = mOMX->sendCommand(mNode, OMX_CommandPortDisable, nData1); + CHECK_EQ(err, (status_t)OK); + + freeOutputBuffers(); + } else if ((nData1 == OUTPUT_PORT)&& (nData2 == OMX_IndexConfigCommonOutputCrop)) { + + OMX_CONFIG_RECTTYPE rect; + INIT_OMX_STRUCT(&rect, OMX_CONFIG_RECTTYPE); + rect.nPortIndex = OUTPUT_PORT; + status_t err = mOMX->getConfig(mNode, OMX_IndexConfigCommonOutputCrop, &rect, sizeof(rect)); + + if (err == OK) { + LOGI("Crop rect is %ld x %ld @ (%ld, %ld)", rect.nWidth, rect.nHeight, rect.nLeft, rect.nTop); + + android_native_rect_t crop; + crop.left = rect.nLeft; + crop.top = rect.nTop; + crop.right = rect.nLeft + rect.nWidth; + crop.bottom = rect.nTop + rect.nHeight; + + native_window_set_crop(mNativeWindow.get(), &crop); + } + } else { + LOGD("\n\nNOT PROCESSING THIS OMX_EventPortSettingsChanged EVENT: nData1 = 0x%x, nData2 = 0x%x\n\n", nData1, nData2); + } + break; + + case OMX_EventError: + errorType = (OMX_ERRORTYPE) nData1; + LOGD( "\n\n\nComponent OMX_EventError error:%x\n\n\n",errorType); + break; + + default: + break; + } + + LOG_FUNCTION_NAME_EXIT + return OMX_ErrorNone; +} + +status_t OMXDecoder::FillBufferDone(OMX_BUFFERHEADERTYPE* pBufferHdr) { + status_t err; + + OMX_U32 i=0; + + //LOG_FUNCTION_NAME_ENTRY + + // not mAcceptingBuffers, just return, eventually, decoder will stop + if (mAcceptingBuffers == 0) { + MY_LOGV( " in non mAcceptingBuffers mode"); + return OMX_ErrorNone; + } + + if (mPortReconfigInProgress) { + MY_LOGV( "%s:\t mPortReconfigInProgress", __FUNCTION__); + return OMX_ErrorNone; + } + + int sz = tOutPortDef.nBufferCountActual; + for (i = 0; i < sz; i++) { + if (pBufferHdr == mOutputBuffers[i].b_id) { + break; + } + } + + if (i == sz) { + LOGE("FillBufferDone returned unknown buffer header! i=%d",(int)i); + return -1; + } + //LOGD( "----- %d ----- ", (int)i); + PortBufferInfo *info = &mOutputBuffers.editItemAt(i); + info->mStatus = OWNED_BY_US; + + err = mNativeWindow->queueBuffer(mNativeWindow.get(), mOutputBuffers[i].gb.get()); + if (err != 0) { + LOGE("queueBuffer failed with error %s (%d)", strerror(-err), -err); + return -1; + } + info->mStatus = OWNED_BY_NATIVE_WINDOW; + + if (mDebugFlags & FPS_DECODER) PrintDecoderFPS(); + + ANativeWindowBuffer* buf; + err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf); + if (err != 0) { + LOGE("dequeueBuffer failed w/ error 0x%08x", err); + return -1; + } + + for (i = 0; i < sz; i++) { + if (mOutputBuffers[i].gb->handle == buf->handle) { + break; + } + } + + if (i == sz) { + LOGE("FillBufferDone returned unknown buffer header! i=%d",(int)i); + return -1; + } + + info = &mOutputBuffers.editItemAt(i); + info->mStatus = OWNED_BY_US; + + err = mOMX->fillBuffer(mNode, mOutputBuffers[i].b_id); + if (err != OK) { + LOGE("OMX_FillThisBuffer failed:%d", err); + } + info->mStatus = OWNED_BY_COMPONENT; + + //mBufferCount++; + //MY_LOGV("EXIT FillBufferDone: nFilledLen=%d, mBufferCount=%d", nFilledLen, mBufferCount); + + return OMX_ErrorNone; +} + +status_t OMXDecoder::EmptyBufferDone(OMX_BUFFERHEADERTYPE* pBufferHdr) { + status_t err; + OMX_U32 i=0; + //LOG_FUNCTION_NAME_ENTRY + + // not mAcceptingBuffers, just return, eventually, decoder will stop + if (mAcceptingBuffers == 0) { + MY_LOGV( " in non mAcceptingBuffers mode"); + return OMX_ErrorNone; + } + + int sz = mInputBuffers.size(); + for (i = 0; i < sz; i++) { + if (pBufferHdr == mInputBuffers[i]->b_id) + break; + } + + if (i == sz) { + LOGE("EmptyBufferDone returned unknown buffer header! i=%d",(int)i); + return -1; + } + + mEmptyInputBuffers.push_back(mInputBuffers[i]); + + //LOG_FUNCTION_NAME_EXIT + + return err; +} + +status_t OMXDecoder::setCurrentState(OMX_STATETYPE newState) { + LOGD("Attempting to set state to %s.", OMXStateName(newState)); + + status_t err = mOMX->sendCommand(mNode, OMX_CommandStateSet, newState); + if (err != OK) { + LOGD("setCurrentState: Error:%d", err); + return -1; + } + + LOG_FUNCTION_NAME_EXIT + return 0; +} + +status_t OMXDecoder::waitForStateSet(OMX_STATETYPE newState) { + LOGD("ENTER waitForStateSet: Waiting to move to state %s .....", OMXStateName(newState)); + + if (newState == mState) { + LOGD("New State [%s] already set!", OMXStateName(newState)); + return 0; + } + + status_t retval = mAsyncCompletion.waitRelative(mLock, TWO_SECOND); + if (retval) { + LOGD("mAsyncCompletion.waitRelative RETURNED %d", retval); + if (errno == ETIMEDOUT) { + LOGD("$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Waiting for State change timed out $$$$$$$$$$$$$$$$$$$$$$$$$$$$"); + waitForStateChange = 0; + } + } + + if (newState == mState) { + LOGD("State [%s] Set !!!!!!!!!!!!!!!!!", OMXStateName(newState)); + return 0; + } + + LOG_FUNCTION_NAME_EXIT + return -1; +} + + +status_t OMXDecoder::createPlaybackSurface() { + + mSurfaceComposerClient = new SurfaceComposerClient(); + CHECK_EQ(mSurfaceComposerClient->initCheck(), (status_t)OK); + + mSurfaceControl = mSurfaceComposerClient->createSurface(0, + 320, 320, HAL_PIXEL_FORMAT_RGB_565); + + mNativeWindow = mSurfaceControl->getSurface(); + + mSurfaceComposerClient->openGlobalTransaction(); + mSurfaceControl->setLayer(0x7fffffff); + mSurfaceControl->setPosition(10, 10); + mSurfaceControl->setSize(300, 300); + mSurfaceControl->show(); + mSurfaceComposerClient->closeGlobalTransaction(); + + return 0; +} + +status_t OMXDecoder::destroyPlaybackSurface() { + + if ( NULL != mNativeWindow.get() ) { + mNativeWindow.clear(); + } + + if ( NULL != mSurfaceControl.get() ) { + mSurfaceControl->clear(); + mSurfaceControl.clear(); + } + + if ( NULL != mSurfaceComposerClient.get() ) { + mSurfaceComposerClient->dispose(); + mSurfaceComposerClient.clear(); + } + + return 0; +} + +status_t OMXDecoder::allocateOutputBuffersFromNativeWindow() { + + status_t err; + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tOutPortDef, sizeof(tOutPortDef)); + if (err != OK) { + LOGD("get OMX_IndexParamPortDefinition OutPort Error:%d", err); + return -1; + } + + err = native_window_set_scaling_mode(mNativeWindow.get(), + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + + if (err != OK) { + return err; + } + + OMX_U32 nBufferCnt = tOutPortDef.nBufferCountActual; +#ifdef NO_PORT_RECONFIG + + LOGD("\nO/P Buffer Reqmt: %d buffers of size %d x %d\n", tOutPortDef.nBufferCountActual, tOutPortDef.format.video.nFrameWidth, tOutPortDef.format.video.nFrameHeight); + + int newBufferRqmt = tOutPortDef.nBufferCountActual * + tOutPortDef.format.video.nFrameWidth * + tOutPortDef.format.video.nFrameHeight * + 3 / 2; + bool bufferRqmtsChanged = (mSizeOfAllAllocatedOutputBuffers < newBufferRqmt) ? true : false; + nBufferCnt = (MAX_OUTPUT_BUF_NUM > tOutPortDef.nBufferCountActual) ? MAX_OUTPUT_BUF_NUM : tOutPortDef.nBufferCountActual; + + if ((mPortReconfigInProgress == false)|| bufferRqmtsChanged ) { + int framewidth = 0; + int frameheight = 0; + + if (tOutPortDef.format.video.nFrameWidth > MAX_FRAME_WIDTH_720P) { + framewidth = (tOutPortDef.format.video.nFrameWidth > MAX_FRAME_WIDTH)? tOutPortDef.format.video.nFrameWidth : MAX_FRAME_WIDTH; + } else { + framewidth = MAX_FRAME_WIDTH_720P; + } + + if (tOutPortDef.format.video.nFrameHeight > MAX_FRAME_HEIGHT_720P) { + frameheight = (tOutPortDef.format.video.nFrameHeight > MAX_FRAME_HEIGHT)? tOutPortDef.format.video.nFrameHeight : MAX_FRAME_HEIGHT; + } else { + frameheight = MAX_FRAME_HEIGHT_720P; + } + + err = native_window_set_buffers_geometry( + mNativeWindow.get(), + framewidth, + frameheight, + tOutPortDef.format.video.eColorFormat); + + if (err != 0) { + LOGE("native_window_set_buffers_geometry failed: %s (%d)", + strerror(-err), -err); + return err; + } + mSizeOfAllAllocatedOutputBuffers = nBufferCnt * framewidth * frameheight * 3 / 2; + LOGD("\nO/P Buffers actually allocated: %d buffers of size %d x %d\n\n", nBufferCnt, framewidth, frameheight); + } + else LOGI("\n---RECONFIGURING: skip native_window_set_buffers_geometry()\n"); + +#else + err = native_window_set_buffers_geometry( + mNativeWindow.get(), + tOutPortDef.format.video.nFrameWidth, + tOutPortDef.format.video.nFrameHeight, + tOutPortDef.format.video.eColorFormat); + + if (err != 0) { + LOGE("native_window_set_buffers_geometry failed: %s (%d)", + strerror(-err), -err); + return err; + } + +#endif + + // Set up the native window. + OMX_U32 usage = 0; + err = mOMX->getGraphicBufferUsage(mNode, OUTPUT_PORT, &usage); + if (err != 0) { + LOGW("querying usage flags from OMX IL component failed: %d", err); + // XXX: Currently this error is logged, but not fatal. + usage = 0; + } + + MY_LOGV("native_window_set_usage usage=0x%lx", usage); + err = native_window_set_usage( + mNativeWindow.get(), usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP); + if (err != 0) { + LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err); + return err; + } + +#ifdef NO_PORT_RECONFIG + + if ((mPortReconfigInProgress == false)|| bufferRqmtsChanged ) { + err = native_window_set_buffer_count(mNativeWindow.get(), nBufferCnt); + if (err != 0) { + LOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err), + -err); + return err; + } + LOGI("allocating %lu buffers from a native window", nBufferCnt); + } + else LOGI("---RECONFIGURING: skip native_window_set_buffer_count()\n\n"); +#else + + err = native_window_set_buffer_count(mNativeWindow.get(), nBufferCnt); + if (err != 0) { + LOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err), + -err); + return err; + } + + LOGI("allocating %lu buffers from a native window of size %lu on " + "output port", tOutPortDef.nBufferCountActual, tOutPortDef.nBufferSize); + +#endif + + OMX_U32 i = 0; + // Dequeue buffers and send them to OMX + for (i = 0; i < nBufferCnt; i++) { + ANativeWindowBuffer* buf; + err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf); + if (err != 0) { + LOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); + break; + } + + sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false)); + IOMX::buffer_id bufferId = 0; + + if (i < tOutPortDef.nBufferCountActual) { + err = mOMX->useGraphicBuffer(mNode, OUTPUT_PORT, graphicBuffer, + &bufferId); + if (err != 0) { + LOGE("registering GraphicBuffer with OMX IL component " + "failed: %d", err); + break; + } + } + + PortBufferInfo pbi; + pbi.gb = graphicBuffer; + pbi.b_id = bufferId; + pbi.mStatus = OWNED_BY_US; + mOutputBuffers.push(pbi); + + LOGI("registered graphic buffer with ID %p (pointer = %p)", + bufferId, graphicBuffer.get()); + } + + OMX_U32 cancelStart; + OMX_U32 cancelEnd; + if (err != 0) { + // If an error occurred while dequeuing we need to cancel any buffers + // that were dequeued. + cancelStart = 0; + cancelEnd = i; + } else { + // Return the last two buffers to the native window. + cancelStart = tOutPortDef.nBufferCountActual - 2; + cancelEnd = tOutPortDef.nBufferCountActual; + } + + for (OMX_U32 i = cancelStart; i < cancelEnd; i++) { + PortBufferInfo *info = &mOutputBuffers.editItemAt(i); + err = mNativeWindow->cancelBuffer( + mNativeWindow.get(), info->gb.get()); + if (err != 0) { + LOGE("cancelBuffer failed w/ error 0x%08x", err); + return err; + } + info->mStatus = OWNED_BY_NATIVE_WINDOW; + } + + return err; +} + +status_t OMXDecoder::drainInputBuffer(InPortBufferInfo *info) { + OMX_TICKS ts; + ts = info->nTimeStamp; + if (mDebugFlags & DECODER_LATENCY) ts = systemTime() / 1000; + status_t err = mOMX->emptyBuffer(mNode, info->b_id, 0, info->nFilledLen, OMX_BUFFERFLAG_ENDOFFRAME, ts); + if (err != OK) { + LOGD("OMX_EmptyThisBuffer failed:%d", err); + return err; + } + return 0; +} + +status_t OMXDecoder::freeOutputBuffers() { + status_t err = 0; + + for (size_t i = mOutputBuffers.size(); i-- > 0;) { + + if (i < tOutPortDef.nBufferCountActual) { + err = mOMX->freeBuffer(mNode, OUTPUT_PORT, mOutputBuffers[i].b_id); + if (err != OK) LOGE("\n\n\n Free Buffer for Output Port buffer:%d failed:%d\n\n\n",i,err); + } + + // Cancel the buffer if it belongs to an ANativeWindow. + if (mOutputBuffers[i].mStatus == OWNED_BY_US && mOutputBuffers[i].gb != 0) { + err = mNativeWindow->cancelBuffer(mNativeWindow.get(), mOutputBuffers[i].gb.get()); + if (err != 0)LOGE("\n\n\nCancel Buffer for Output Port buffer:%d failed:%d\n\n\n",i,err); + } + + mOutputBuffers.removeAt(i); + } + + return 0; +} + diff --git a/test/VTC/IOMXDecoder.h b/test/VTC/IOMXDecoder.h new file mode 100644 index 0000000..e73e336 --- /dev/null +++ b/test/VTC/IOMXDecoder.h @@ -0,0 +1,252 @@ +/* + * Copyright (C) Texas Instruments - http://www.ti.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef IOMX_DECODER_H +#define IOMX_DECODER_H + +#include <fcntl.h> +#include <getopt.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#include <semaphore.h> +#include <pthread.h> + +#include <binder/MemoryDealer.h> +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> +#include <system/audio.h> +#include <utils/List.h> +#include <cutils/log.h> +#include <OMX_Component.h> +#include <media/stagefright/OMXClient.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaDebug.h> + +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/ISurface.h> +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/ISurfaceComposerClient.h> +#include <surfaceflinger/SurfaceComposerClient.h> +#include <android/native_window.h> +#include <ui/GraphicBuffer.h> + +#include "MessageQueue.h" + + +using namespace android; + +struct OMXDecoderObserver; + +struct OMXDecoder : public MediaSource +{ + enum BufferStatus { + OWNED_BY_US, + OWNED_BY_COMPONENT, + OWNED_BY_NATIVE_WINDOW, + OWNED_BY_CLIENT, + }; + + struct BufferInfo { + void* pBuffer; + OMX_U32 nFilledLen; + OMX_TICKS nTimeStamp; + }; + + struct PortBufferInfo{ + sp<GraphicBuffer> gb; + IOMX::buffer_id b_id; + BufferStatus mStatus; + }; + + struct InPortBufferInfo{ + sp<IMemory> mem; + IOMX::buffer_id b_id; + OMX_U32 nFilledLen; + OMX_TICKS nTimeStamp; + }; + + friend class OMXDecoderObserver; + + status_t configure(OMX_VIDEO_AVCPROFILETYPE profile, OMX_VIDEO_AVCLEVELTYPE level, OMX_U32 refFrames); + status_t prepare(); + status_t start(MetaData *params = NULL); + status_t stop(); + sp<MetaData> getFormat(); + status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL); + void on_message(const omx_message &msg); + void AcceptEncodedBuffer(void *pBuffer, OMX_U32 nFilledLen, OMX_TICKS nTimeStamp); + OMXDecoder(int width, int height, int framerate); + OMXDecoder(const OMXDecoder &); + OMXDecoder &operator=(const OMXDecoder &); + ~OMXDecoder(); + uint32_t mDebugFlags; + +private: + + class SourceHandler : public Thread { + public: + SourceHandler(OMXDecoder* dec) + : Thread(false), mOMXDecoder(dec) { } + + virtual bool threadLoop() { + bool ret; + ret = Handler(); + return ret; + } + + status_t put(TIUTILS::Message* msg) { + Mutex::Autolock lock(mLock); + return mCommandMsgQ.put(msg); + } + + void clearCommandQ() { + Mutex::Autolock lock(mLock); + mCommandMsgQ.clear(); + } + + enum { + COMMAND_EXIT = -1, + COMMAND_PROCESS_MSG, + }; + + private: + bool Handler(); + TIUTILS::MessageQueue mCommandMsgQ; + OMXDecoder* mOMXDecoder; + Mutex mLock; + }; + + sp<SourceHandler> mSourceHandler; + + class OMXCallbackHandler : public Thread { + public: + OMXCallbackHandler(OMXDecoder* dec) + : Thread(false), mOMXDecoder(dec) { } + + virtual bool threadLoop() { + bool ret; + ret = Handler(); + return ret; + } + + status_t put(TIUTILS::Message* msg) { + Mutex::Autolock lock(mLock); + return mCommandMsgQ.put(msg); + } + + void clearCommandQ() { + Mutex::Autolock lock(mLock); + mCommandMsgQ.clear(); + } + + enum { + COMMAND_EXIT = -1, + COMMAND_PROCESS_MSG, + }; + + private: + bool Handler(); + TIUTILS::MessageQueue mCommandMsgQ; + OMXDecoder* mOMXDecoder; + Mutex mLock; + }; + + sp<OMXCallbackHandler> mOMXCallbackHandler; + + OMX_ERRORTYPE EventHandler(OMX_EVENTTYPE eEvent, OMX_U32 nData1,OMX_U32 nData2); + status_t FillBufferDone(OMX_BUFFERHEADERTYPE* pBufferHdr); + status_t EmptyBufferDone(OMX_BUFFERHEADERTYPE* pBufferHdr); + status_t setCurrentState(OMX_STATETYPE newState); + status_t waitForStateSet(OMX_STATETYPE newState); + status_t createPlaybackSurface(); + status_t destroyPlaybackSurface(); + status_t allocateOutputBuffersFromNativeWindow(); + status_t drainInputBuffer(InPortBufferInfo *info); + status_t freeOutputBuffers(); + status_t restart(); + + int mWidth; + int mHeight; + int mSizeOfAllAllocatedOutputBuffers; + uint32_t mFrameRate; + OMX_PARAM_PORTDEFINITIONTYPE tInPortDef; + OMX_PARAM_PORTDEFINITIONTYPE tOutPortDef; + sp<IOMX> mOMX; + IOMX::node_id mNode; + sp<OMXDecoderObserver> mObserver; + OMXClient mOMXClient; + Vector<PortBufferInfo> mOutputBuffers; + Vector<InPortBufferInfo*> mInputBuffers; + List<InPortBufferInfo*> mEmptyInputBuffers; + Condition mAsyncCompletion; + Mutex mLock; + int waitForStateChange; + OMX_STATETYPE mState; + int mAcceptingBuffers; + sp<MemoryDealer> mDealer; + sp<SurfaceComposerClient> mSurfaceComposerClient; + sp<SurfaceControl> mSurfaceControl; + sp<ANativeWindow> mNativeWindow; + bool mPortReconfigInProgress; +}; + +struct OMXDecoderObserver : public BnOMXObserver { + OMXDecoderObserver() { + } + + void setCodec(const sp<OMXDecoder> &target) { + mTarget = target; + } + + // from IOMXObserver + virtual void onMessage(const omx_message &omx_msg) { + TIUTILS::Message msg; + omx_message *ptemp_omx_msg; + // HACK HACK HACK LEAK LEAK LEAK FIXIT + ptemp_omx_msg = (omx_message *)malloc(sizeof(omx_message)); + memcpy(ptemp_omx_msg, &omx_msg, sizeof(omx_message)); + //LOGD("=================omx_msg.type = %x, temp_omx_msg.type = %x",omx_msg.type, ptemp_omx_msg->type); + sp<OMXDecoder> codec = mTarget.promote(); + if (codec.get() != NULL) { + msg.command = OMXDecoder::OMXCallbackHandler::COMMAND_PROCESS_MSG; + msg.arg1 = (void *)ptemp_omx_msg; + codec->mOMXCallbackHandler->put(&msg); + codec.clear(); + } + } + +protected: + virtual ~OMXDecoderObserver() {} + +private: + wp<OMXDecoder> mTarget; + OMXDecoderObserver(const OMXDecoderObserver &); + OMXDecoderObserver &operator=(const OMXDecoderObserver &); +}; + +#endif + diff --git a/test/VTC/IOMXEncoder.cpp b/test/VTC/IOMXEncoder.cpp new file mode 100644 index 0000000..88ce9af --- /dev/null +++ b/test/VTC/IOMXEncoder.cpp @@ -0,0 +1,1158 @@ +/* + * Copyright (C) Texas Instruments - http://www.ti.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "VTCLoopback.h" +#include "IOMXEncoder.h" +#define LOG_TAG "VTC_ENC" +#define LOG_NDEBUG 0 +//#define NO_MEMCOPY 1 +#define MY_LOGV(x, ...) LOGV(x, ##__VA_ARGS__) + +using namespace android; + +static void PrintEncoderFPS() { + static int mFrameCount = 0; + static int mLastFrameCount = 0; + static nsecs_t mLastFpsTime = 0; + static float mFps = 0; + mFrameCount++; + if (!(mFrameCount & 0x1F)) { + nsecs_t now = systemTime(); + nsecs_t diff = now - mLastFpsTime; + mFps = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff; + mLastFpsTime = now; + mLastFrameCount = mFrameCount; + LOGD("Encoder: %d Frames, %f FPS", mFrameCount, mFps); + } + // XXX: mFPS has the value we want +} + +static void PrintEncoderLatency(nsecs_t ts) { + static int mFrameCount = 0; + static int64_t sum_latency_ms = 0; + int64_t now, diff_ms, avg_latency; + + now = systemTime()/1000; + diff_ms = (now - ts) / 1000; + sum_latency_ms += diff_ms; + mFrameCount++; + if (!(mFrameCount & 0x1F)) { + avg_latency = sum_latency_ms / 32; + sum_latency_ms = 0; + LOGD("Avg Encoder Latency: %d Frames, %llu ms", mFrameCount, avg_latency); + } +} + +static uint64_t get_time_of_day_ms() { + struct timeval t0; + gettimeofday(&t0,0); + return t0.tv_sec*1000 + t0.tv_usec/1000; +} + +static void PrintEffectiveBitrate(OMX_U32 filledLen) { + static int framecount = 0; + static uint64_t bytecount = 0; + static uint64_t starttime = get_time_of_day_ms(); + const uint64_t wallclock = get_time_of_day_ms(); + framecount++; + bytecount+=filledLen; + int delta=wallclock-starttime; + if (delta>2000) { + int fps=framecount*10000/delta; + const uint64_t br=bytecount*8*1000/delta; + //LOGI("ENCODER FPS: %d.%d",fps/10,fps-(fps/10)*10); + LOGI("ENCODER EFFECTIVE BITRATE: %llu",br); + framecount=0; + bytecount=0; + starttime = wallclock; + } +} + + +bool OMXEncoder::OMXCallbackHandler::Handler() { + MY_LOGV("\n OMXCallbackHandler::Handler \n"); + TIUTILS::Message msg; + volatile int forever = 1; + + while(forever) { + TIUTILS::MessageQueue::waitForMsg(&mCommandMsgQ, NULL, NULL, -1); + { + Mutex::Autolock lock(mLock); + mCommandMsgQ.get(&msg); + } + + switch ( msg.command ) { + case OMXCallbackHandler::COMMAND_PROCESS_MSG: { + omx_message *om = (omx_message*)(msg.arg1); + omx_message omsg = *om; + mOMXEncoder->on_message(omsg); + break; + } + case OMXCallbackHandler::COMMAND_EXIT: { + LOGD("Exiting OMX callback handler"); + forever = 0; + break; + } + } + } + + return false; +} + +OMXEncoder::OMXEncoder(const sp<IOMX> &omx, IOMX::node_id node, sp<MyCameraClient> camera, int width, int height, int framerate, int bitrate, char *fname, int sliceHeight): + mOMX(omx), + mNode(node), + mCameraSource(camera) { + resetParameters(width, height, framerate, bitrate, fname, sliceHeight); +} + + +OMXEncoder::~OMXEncoder() { + status_t err = mOMX->freeNode(mNode); + CHECK_EQ(err, (status_t)OK); + LOGD("OMX_FreeHandle completed"); +} + +status_t OMXEncoder::resetParameters(int width, int height, int framerate, int bitrate, char *fname, int sliceHeight) { + mWidth = width; + mHeight = height; + mFrameRate= framerate; + mBitRate = bitrate; + mSliceHeight = sliceHeight; + mAcceptingBuffers = 0; + mOutputFD = NULL; + mBufferCount = 0; + mOutputBufferCount = 4; + mCallbackSet = false; + mState = OMX_StateLoaded; + return OK; +} + +void OMXEncoder::on_message(const omx_message &msg) { + switch (msg.type) { + case omx_message::EVENT: + EventHandler(msg.u.event_data.event, msg.u.event_data.data1, msg.u.event_data.data2); + break; + case omx_message::EMPTY_BUFFER_DONE: + EmptyBufferDone((OMX_BUFFERHEADERTYPE*)msg.u.extended_buffer_data.buffer); + break; + case omx_message::FILL_BUFFER_DONE: + FillBufferDone((OMX_BUFFERHEADERTYPE*)msg.u.extended_buffer_data.buffer, + msg.u.extended_buffer_data.range_offset, + msg.u.extended_buffer_data.range_length, + msg.u.extended_buffer_data.timestamp); + break; + default: + CHECK(!"############ Corrupted Message !!! #############"); + break; + } +} + +status_t OMXEncoder::configure(OMX_VIDEO_AVCPROFILETYPE profile, OMX_VIDEO_AVCLEVELTYPE level, OMX_U32 refFrames) { + status_t err; + LOG_FUNCTION_NAME_ENTRY + + MY_LOGV("\n\nPROFILE=%d\nLEVEL=%d\nRefFrames=%d\nWidth=%d\nHeight=%d\nFramerate=%d\nBitrate=%d\nSliceHeight=%d\n\n", + profile, level, refFrames, mWidth, mHeight, mFrameRate, mBitRate, mSliceHeight); + + if ((mCallbackSet == false) && ((mDebugFlags & ENCODER_NO_FILE_WRTIE) == 0)) { + mOutputFD = fopen("/mnt/sdcard/video_0.264","w"); + if (mOutputFD == NULL) { + LOGE("\n fopen failed\n"); + } + LOGD("\nCallback was NULL. Opened file for writing\n"); + } + + // initialize omx callback handling thread + if(mOMXCallbackHandler.get() == NULL) { + mOMXCallbackHandler = new OMXCallbackHandler(this); + } + + if ( NULL == mOMXCallbackHandler.get() ) { + LOGE("Couldn't create omx callback handler"); + return -1; + } + + err = mOMXCallbackHandler->run("OMXCallbackThread", PRIORITY_URGENT_DISPLAY); + if ( err != NO_ERROR ) { + if( err == INVALID_OPERATION) { + LOGE("omx callback handler thread already runnning!!"); + err = NO_ERROR; + } else { + LOGE("Couldn't run omx callback handler thread"); + return -1; + } + } + + mState = OMX_StateLoaded; + + OMX_VIDEO_PARAM_PORTFORMATTYPE format; + INIT_OMX_STRUCT(&format, OMX_VIDEO_PARAM_PORTFORMATTYPE); + format.nPortIndex = INPUT_PORT; + format.nIndex = 0; + bool found = false; + OMX_U32 index = 0; + for (;;) { + format.nIndex = index; + err = mOMX->getParameter(mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); + if (err != OK) { + LOGD( "get OMX_IndexParamVideoPortFormat InPort Error:0x%x. OMX_ErrorUnsupportedIndex=0x%x", err, OMX_ErrorUnsupportedIndex); + return -1; + } + + if (format.eCompressionFormat == OMX_VIDEO_CodingUnused + && format.eColorFormat == (OMX_COLOR_FORMATTYPE)OMX_TI_COLOR_FormatYUV420PackedSemiPlanar) { + found = true; + break; + } + ++index; + } + + if (!found) { + LOGE("Did not find a match."); + return -1; + } + + MY_LOGV("found a match."); + err = mOMX->setParameter(mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); + if (err != OK) { + LOGD( "set OMX_IndexParamVideoPortFormat InPort Error:%d", err); + return -1; + } + + format.nPortIndex = OUTPUT_PORT; + format.nIndex = 0; + found = false; + index = 0; + for (;;) { + format.nIndex = index; + err = mOMX->getParameter(mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); + if (err != OK) { + LOGD( "get OMX_IndexParamVideoPortFormat OutPort Error:%d", err); + return -1; + } + + if (format.eCompressionFormat == OMX_VIDEO_CodingAVC + && format.eColorFormat == (OMX_COLOR_FormatUnused)) { + found = true; + break; + } + ++index; + } + + if (!found) { + LOGE("Did not find a match."); + return -1; + } + + MY_LOGV("found a match."); + err = mOMX->setParameter(mNode, OMX_IndexParamVideoPortFormat, &format, sizeof(format)); + if (err != OK) { + LOGD( "set OMX_IndexParamVideoPortFormat OutPort Error:%d", err); + return -1; + } + + // + // Populate Video input Port + // + INIT_OMX_STRUCT(&tInPortDef, OMX_PARAM_PORTDEFINITIONTYPE); + + tInPortDef.nPortIndex = INPUT_PORT; + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef)); + if (err != OK) { + LOGD( "get OMX_IndexParamPortDefinition InPort Error:%d", err); + return -1; + } + + tInPortDef.format.video.nFrameWidth = mWidth; + tInPortDef.format.video.nFrameHeight = mHeight; + tInPortDef.format.video.nStride = 4096; + tInPortDef.format.video.nSliceHeight = mHeight; + tInPortDef.format.video.xFramerate = (mFrameRate << 16); + tInPortDef.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused; + tInPortDef.format.video.eColorFormat = (OMX_COLOR_FORMATTYPE)OMX_TI_COLOR_FormatYUV420PackedSemiPlanar; + tInPortDef.nBufferSize = (mWidth * mHeight *3)/2; + err = mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef)); + if (err != OK) { + LOGD( "set OMX_IndexParamPortDefinition InPort Error:%d", err); + return -1; + } + + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef)); + if (err != OK) { + LOGD("get OMX_IndexParamPortDefinition InPort Error:%d", err); + return -1; + } + + // + // Populate Video output Port + // + INIT_OMX_STRUCT(&tOutPortDef, OMX_PARAM_PORTDEFINITIONTYPE); + tOutPortDef.nPortIndex = OUTPUT_PORT; + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition,&tOutPortDef, sizeof(tOutPortDef)); + if (err != OK) { + LOGD("get OMX_IndexParamPortDefinition OutPort Error:%d", err); + return -1; + } + + tOutPortDef.format.video.nFrameWidth = mWidth; + tOutPortDef.format.video.nFrameHeight = mHeight; + tOutPortDef.format.video.xFramerate = 0; + tOutPortDef.format.video.nBitrate = mBitRate; + tOutPortDef.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC; + tOutPortDef.format.video.eColorFormat = OMX_COLOR_FormatUnused; + tOutPortDef.nBufferCountActual = mOutputBufferCount; + + err = mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, &tOutPortDef, sizeof(tOutPortDef)); + if (err != OK) { + LOGD( "set OMX_IndexParamPortDefinition OutPort Error:%d", err); + return -1; + } + + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition,&tOutPortDef, sizeof(tOutPortDef)); + if (err != OK) { + LOGD("get OMX_IndexParamPortDefinition OutPort Error:%d", err); + return -1; + } + + // + // setup (code specific) AVC Encoder paramters for OUTPUT port + // + OMX_VIDEO_PARAM_AVCTYPE h264type; + INIT_OMX_STRUCT(&h264type,OMX_VIDEO_PARAM_AVCTYPE); + + h264type.nPortIndex = OUTPUT_PORT; + + err = mOMX->getParameter(mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type)); + if (err != OK) { + LOGD("get OMX_IndexParamVideoAvc failed : %d", err); + return -1; + } + + h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; + h264type.eProfile = profile; + h264type.eLevel = level; + h264type.nSliceHeaderSpacing = 0; + h264type.bUseHadamard = OMX_TRUE; + h264type.nRefFrames = refFrames; + h264type.nBFrames = 0; + //h264type.nPFrames = 30; // assume iFrameInterval 1, frameRate + h264type.nPFrames = 0; // Let only the first frame be an I Frame. The rest will be P Frames. For VTC type of applications, you want to insert the IDR only when necessary. + h264type.nRefIdx10ActiveMinus1 = 0; + h264type.nRefIdx11ActiveMinus1 = 0; + h264type.bEntropyCodingCABAC = OMX_FALSE; + h264type.bWeightedPPrediction = OMX_FALSE; + h264type.bconstIpred = OMX_FALSE; + h264type.bDirect8x8Inference = OMX_FALSE; + h264type.bDirectSpatialTemporal = OMX_FALSE; + h264type.nCabacInitIdc = 0; + h264type.bEnableUEP = OMX_FALSE; + h264type.bEnableFMO = OMX_FALSE; + h264type.bEnableASO = OMX_FALSE; + h264type.bEnableRS = OMX_FALSE; + h264type.bFrameMBsOnly = OMX_TRUE; + h264type.bMBAFF = OMX_FALSE; + h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable; + + err = mOMX->setParameter(mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type)); + if (err != OK) { + LOGD("set OMX_IndexParamVideoAvc failed : %d", err); + return -1; + } + + // + // Set Profile and Level for OUTPUT + // + OMX_VIDEO_PARAM_PROFILELEVELTYPE profileLevel; + INIT_OMX_STRUCT(&profileLevel, OMX_VIDEO_PARAM_PROFILELEVELTYPE); + profileLevel.nPortIndex = OUTPUT_PORT; + err = mOMX->getParameter(mNode, OMX_IndexParamVideoProfileLevelCurrent, &profileLevel, sizeof(profileLevel)); + if (err != OK) { + LOGD("get OMX_IndexParamVideoProfileLevelCurrent failed : %d", err); + return -1; + } + + profileLevel.eProfile = profile; + profileLevel.eLevel = level; + err = mOMX->setParameter(mNode, OMX_IndexParamVideoProfileLevelCurrent, &profileLevel, sizeof(profileLevel)); + if (err != OK) { + LOGD("set OMX_IndexParamVideoProfileLevelCurrent failed : %d", err); + return -1; + } + + // + // Set data content type for input port + // + OMX_TI_VIDEO_PARAM_FRAMEDATACONTENTTYPE dataContent; + INIT_OMX_STRUCT(&dataContent, OMX_TI_VIDEO_PARAM_FRAMEDATACONTENTTYPE); + dataContent.nPortIndex = INPUT_PORT; + err = mOMX->getParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexParamVideoFrameDataContentSettings, &dataContent, sizeof(dataContent)); + if (err != OK) { + LOGD("get OMX_TI_IndexParamVideoFrameDataContentSettings failed : %d", err); + return -1; + } + + dataContent.eContentType = OMX_TI_Video_Progressive; //appears to be the default value + err = mOMX->setParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexParamVideoFrameDataContentSettings, &dataContent, sizeof(dataContent)); + if (err != OK) { + LOGD("set OMX_TI_IndexParamVideoFrameDataContentSettings failed : %d", err); + return -1; + } + + // + // setupBitRate for OUTPUT + // + OMX_VIDEO_PARAM_BITRATETYPE bitrateType; + INIT_OMX_STRUCT(&bitrateType,OMX_VIDEO_PARAM_BITRATETYPE); + bitrateType.nPortIndex = OUTPUT_PORT; + err = mOMX->getParameter(mNode, OMX_IndexParamVideoBitrate, &bitrateType, sizeof(bitrateType)); + if (err != OK) { + LOGD("get OMX_IndexParamVideoBitrate failed : %d", err); + return -1; + } + + bitrateType.eControlRate = OMX_Video_ControlRateVariable; + bitrateType.nTargetBitrate = mBitRate; + err = mOMX->setParameter(mNode, OMX_IndexParamVideoBitrate, &bitrateType, sizeof(bitrateType)); + if (err != OK) { + LOGD("set OMX_IndexParamVideoBitrate failed : %d", err); + return -1; + } + + err = mOMX->storeMetaDataInBuffers(mNode, INPUT_PORT, OMX_TRUE); + if (err != OK) { + LOGE("Storing meta data in video buffers is not supported"); + return -1; + } + + if (mSliceHeight == 0) { + LOG_FUNCTION_NAME_EXIT + return 0; + } + + /**************** Configuration specific to Slice based processing ******************/ + + // + // setup data sync mode for INPUT + // + OMX_VIDEO_PARAM_DATASYNCMODETYPE syncMode; + INIT_OMX_STRUCT(&syncMode, OMX_VIDEO_PARAM_DATASYNCMODETYPE); + syncMode.nPortIndex = INPUT_PORT; + err = mOMX->getParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexParamVideoDataSyncMode, &syncMode, sizeof(syncMode)); + if (err != OK) { + LOGD("get OMX_TI_IndexParamVideoDataSyncMode failed : %d", err); + return -1; + } + + syncMode.eDataMode = OMX_Video_NumMBRows; + err = mOMX->setParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexParamVideoDataSyncMode, &syncMode, sizeof(syncMode)); + if (err != OK) { + LOGD("set OMX_TI_IndexParamVideoDataSyncMode failed: %d", err); + return -1; + } + + // + // setup data sync mode for OUTPUT + // + INIT_OMX_STRUCT(&syncMode, OMX_VIDEO_PARAM_DATASYNCMODETYPE); + syncMode.nPortIndex = OUTPUT_PORT; + err = mOMX->getParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexParamVideoDataSyncMode, &syncMode, sizeof(syncMode)); + if (err != OK) { + LOGD("get OMX_TI_IndexParamVideoDataSyncMode failed : %d", err); + return -1; + } + + syncMode.eDataMode = OMX_Video_EntireFrame; + syncMode.nNumDataUnits = 1; + err = mOMX->setParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexParamVideoDataSyncMode, &syncMode, sizeof(syncMode)); + if (err != OK) { + LOGD("set OMX_TI_IndexParamVideoDataSyncMode failed: %d", err); + return -1; + } + + LOG_FUNCTION_NAME_EXIT + return 0; +} + +status_t OMXEncoder::prepare() { + status_t err; + + LOG_FUNCTION_NAME_ENTRY + + if (setCurrentState(OMX_StateIdle)) { + return -1; + } + + INIT_OMX_STRUCT(&tInPortDef, OMX_PARAM_PORTDEFINITIONTYPE); + tInPortDef.nPortIndex = INPUT_PORT; + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef)); + if (err != OK) { + LOGD("get OMX_IndexParamPortDefinition InPort Error:%d", err); + return -1; + } + + if (mSliceHeight == 0) { // non tunnel mode + + mDealer[INPUT_PORT] = new MemoryDealer((tInPortDef.nBufferCountActual*tInPortDef.nBufferSize), "RECORD_INPUT"); + for (OMX_U32 i = 0; i < tInPortDef.nBufferCountActual; ++i) { + mBufferInfo[INPUT_PORT][i].mEncMem= mDealer[INPUT_PORT]->allocate(tInPortDef.nBufferSize); + CHECK(mBufferInfo[INPUT_PORT][i].mEncMem.get() != NULL); + err = mOMX->allocateBufferWithBackup(mNode, INPUT_PORT, mBufferInfo[INPUT_PORT][i].mEncMem, (void**)(&(mBufferInfo[INPUT_PORT][i].mBufferHdr))); + if (err != OK) { + LOGE("OMX_AllocateBuffer for input port index:%d failed:%d",(int)i,err); + mBufferInfo[INPUT_PORT][i].mBufferHdr = NULL; + } + } + LOGD( "Allocated %d Input port Buffers. ", (int)tInPortDef.nBufferCountActual); + } + + INIT_OMX_STRUCT(&tOutPortDef, OMX_PARAM_PORTDEFINITIONTYPE); + tOutPortDef.nPortIndex = OUTPUT_PORT; + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tOutPortDef, sizeof(tOutPortDef)); + if (err != OK) { + LOGE("get OMX_IndexParamPortDefinition OutPort Error:%d", err); + return -1; + } + +#ifdef NO_MEMCOPY + err = allocateOutputBuffers(); + if (err != OK) return -1; +#else + + mDealer[OUTPUT_PORT] = new MemoryDealer((tOutPortDef.nBufferCountActual*tOutPortDef.nBufferSize), "RECORD_OUTPUT"); + for (OMX_U32 i = 0; i < tOutPortDef.nBufferCountActual; ++i) { + mBufferInfo[OUTPUT_PORT][i].mEncMem = mDealer[OUTPUT_PORT]->allocate(tOutPortDef.nBufferSize); + err = mOMX->allocateBufferWithBackup(mNode, OUTPUT_PORT, mBufferInfo[OUTPUT_PORT][i].mEncMem, (void**)(&(mBufferInfo[OUTPUT_PORT][i].mBufferHdr))); + if (err != OK) { + LOGD("OMX_UseBuffer for output port index:%d failed:%d",(int)i,err); + mBufferInfo[OUTPUT_PORT][i].mBufferHdr = NULL; + return -1; + } + } +#endif + + LOGD( "Allocated %d Output port Buffers. ", (int)tOutPortDef.nBufferCountActual); + + LOG_FUNCTION_NAME_EXIT + return 0; +} + +status_t OMXEncoder::start(MetaData *params) { + status_t err; + + LOG_FUNCTION_NAME_ENTRY + + // now wait until state becomes idle. + if (waitForStateSet(OMX_StateIdle)) { + LOGD("state change to IDLE failed"); + return -1; + } + + // If we are going to reuse the node, then port enable is a MUST + // since we are disabling the port during stop + + // now transition to exec + if (setCurrentState(OMX_StateExecuting)) { + return -1; + } + + if (waitForStateSet(OMX_StateExecuting)) { + LOGD("state change to EXECUTING failed"); + return -1; + } + + mAcceptingBuffers = 1; // let OMX callbacks to handle buffers + + INIT_OMX_STRUCT(&tInPortDef, OMX_PARAM_PORTDEFINITIONTYPE); + tInPortDef.nPortIndex = INPUT_PORT; + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tInPortDef, sizeof(tInPortDef)); + if (err != OK) { + LOGD("get OMX_IndexParamPortDefinition InPort Error:%d", err); + return -1; + } + + INIT_OMX_STRUCT(&tOutPortDef, OMX_PARAM_PORTDEFINITIONTYPE); + tOutPortDef.nPortIndex = OUTPUT_PORT; + err = mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, &tOutPortDef, sizeof(tOutPortDef)); + if (err != OK) { + LOGD("get OMX_IndexParamPortDefinition OutPort Error:%d", err); + return -1; + } + + // give output buffers to the OMX + for (int i=0; i<(int)tOutPortDef.nBufferCountActual; i++) { + err = mOMX->fillBuffer(mNode, mBufferInfo[OUTPUT_PORT][i].mBufferHdr); + if (err != OK) { + LOGD("OMX_FillThisBuffer failed:%d", err); + } else { + LOGD("called fillBuffer(%d)",i); + } + } + + // call camera encoder_is_ready + mCameraSource->encoderReady(); + + if (mSliceHeight) { + LOG_FUNCTION_NAME_EXIT + return 0; + } + + /************** non tunnel / frame mode ***************/ + + // now wait for payload to be available. when it does, call OMX_EmptyThisBuffer + int64_t time[3]; + sp<IMemory> payload[3]; + + // wait until camera returns a frame + for (int i=0; i<3; i++) { + payload[i] = mCameraSource->getCameraPayload(time[i]); + } + + for (int i=0; i<3; i++) { + mBufferInfo[INPUT_PORT][i].mCamMem = payload[i]; + memcpy((uint8_t *)mBufferInfo[INPUT_PORT][i].mEncMem->pointer(), payload[i]->pointer(), payload[i]->size()); + err = mOMX->emptyBuffer(mNode, mBufferInfo[INPUT_PORT][i].mBufferHdr, 0, payload[i]->size(), OMX_BUFFERFLAG_ENDOFFRAME, (OMX_TICKS)time); + if (err != OK) { + LOGD("OMX_EmptyThisBuffer failed:%d", err); + } else { + LOGD("Called EmptyThisBuffer[%d] ",i); + } + } + + LOG_FUNCTION_NAME_EXIT + return 0; +} + +status_t OMXEncoder::stop() { + status_t err; + + LOG_FUNCTION_NAME_ENTRY + + // stop mAcceptingBuffers any callbacks + mAcceptingBuffers = 0; + mCameraSource->encoderNotReady(); + + if (mOutputFD) { + fclose(mOutputFD); + mOutputFD = NULL; + } + + if (mSliceHeight == 0) { // non tunnel mode + // flush all the buffers since mAcceptingBuffers off they won't be returned. + err = mOMX->sendCommand(mNode,OMX_CommandFlush, INPUT_PORT); + if (err != OK) { + LOGD("OMX_CommandFlush for input port (0) failed:%d", err); + } + } + + err = mOMX->sendCommand(mNode,OMX_CommandFlush, OUTPUT_PORT); + if (err != OK) { + LOGD("OMX_CommandFlush for output port (1) failed:%d", err); + } + + // change state to Idle if not already + if (mState != OMX_StateIdle && mState != OMX_StateLoaded) { + if (setCurrentState(OMX_StateIdle)) { + LOGD("OMX_StateIdle failed"); + } + + if (waitForStateSet(OMX_StateIdle)) { + LOGD("state change to IDLE failed"); + } + } + + // disable ports + err = mOMX->sendCommand(mNode, OMX_CommandPortDisable, -1); + if (err != OK) { + LOGD("Error in SendCommand()-OMX_CommandPortDisable:"); + } else { + LOGD("OMX_CommandPortDisable done"); + } + + return 0; +} + +status_t OMXEncoder::deinit() { + status_t err; + + LOG_FUNCTION_NAME_ENTRY + + if (mSliceHeight == 0) { // non tunnel mode + // free input buffers + for (int i=0; i<(int)tInPortDef.nBufferCountActual; i++) { + if (mBufferInfo[INPUT_PORT][i].mBufferHdr) { + err = mOMX->freeBuffer(mNode, INPUT_PORT, mBufferInfo[INPUT_PORT][i].mBufferHdr); + if( (err != OK)) { + LOGD("Free Buffer for Input Port buffer:%d failed:%d",i,err); + } + } + } + } + + // free output buffers + for (int i=0; i <(int)tOutPortDef.nBufferCountActual; i++) { + if (mBufferInfo[OUTPUT_PORT][i].mBufferHdr) { + err = mOMX->freeBuffer(mNode,OUTPUT_PORT,mBufferInfo[OUTPUT_PORT][i].mBufferHdr); + if( (err != OK)) { + LOGD("Free Buffer for Output Port buffer:%d failed:%d",i,err); + } + } + } + + // change state to Loaded + if (mState != OMX_StateLoaded) { + if (setCurrentState(OMX_StateLoaded)) { + LOGD("OMX_StateLoaded failed"); + } + if (waitForStateSet(OMX_StateLoaded)) { + LOGD("state change to LOADED failed"); + } + } else { + LOGD("It was already OMX_StateLoaded???"); + } + + usleep(5000); + + + //Exit and free ref to callback handling thread + if ( NULL != mOMXCallbackHandler.get() ) { + TIUTILS::Message msg; + msg.command = OMXCallbackHandler::COMMAND_EXIT; + //Clear all messages pending first + mOMXCallbackHandler->clearCommandQ(); + mOMXCallbackHandler->put(&msg); + mOMXCallbackHandler->requestExitAndWait(); + mOMXCallbackHandler.clear(); + } + + LOG_FUNCTION_NAME_EXIT + return 0; +} + +sp<MetaData> OMXEncoder::getFormat() { + return NULL; +} + +status_t OMXEncoder::read(MediaBuffer **buffer, const ReadOptions *options) { + return 0; +} + + +OMX_ERRORTYPE OMXEncoder::EventHandler(OMX_EVENTTYPE eEvent, OMX_U32 nData1,OMX_U32 nData2) { + OMX_ERRORTYPE errorType; + MY_LOGV("########## EventHandler: eEvent:0x%x, nData1:%d, nData2:%d, pid=%d", (int)eEvent, (int)nData1, (int)nData2, getpid()); + + switch (eEvent) { + case OMX_EventCmdComplete: + if (nData1 == OMX_CommandPortDisable) { + if (nData2 == OMX_DirInput) { + LOGD( "Component OMX_EventCmdComplete OMX_CommandPortDisable OMX_DirInput"); + } + if (nData2 == OMX_DirOutput) { + LOGD( "Component OMX_EventCmdComplete OMX_CommandPortDisable OMX_DirOutput"); + } + } else if (nData1 == OMX_CommandStateSet) { + LOGD( "Component OMX_EventCmdComplete OMX_CommandStateSet new State:%s",OMXStateName((OMX_STATETYPE)nData2)); + { + Mutex::Autolock autoLock(mLock); + mState = (OMX_STATETYPE)nData2; + } + mAsyncCompletion.signal(); + + } else if (nData1 == OMX_CommandFlush) { + LOGD( "Component OMX_EventCmdComplete OMX_CommandFlush port:%d",(int)nData2); + } else { + LOGD( "Component OMX_EventCmdComplete command:%d",(int)nData1); + } + break; + + case OMX_EventError: + errorType = (OMX_ERRORTYPE) nData1; + LOGD( "\n\n\nComponent OMX_EventError error:%x\n\n\n",errorType); + break; + default: + break; + } + MY_LOGV("EXIT EventHandler"); + return errorType; +} + +status_t OMXEncoder::FillBufferDone(OMX_BUFFERHEADERTYPE* pBufferHdr, OMX_U32 nOffset, OMX_U32 nFilledLen, OMX_TICKS nTimeStamp) { + status_t err; + OMX_U32 i=0; + + // not mAcceptingBuffers, just return, eventually, decoder will stop + if (mAcceptingBuffers == 0) { + MY_LOGV( " in non mAcceptingBuffers mode"); + return OMX_ErrorNone; + } + + for (i = 0; i < tOutPortDef.nBufferCountActual; i++) { + if (pBufferHdr == mBufferInfo[OUTPUT_PORT][i].mBufferHdr) { + break; + } + } + if (i == tOutPortDef.nBufferCountActual) { + LOGE("FillBufferDone returned unknown buffer header! i=%d",(int)i); + return -1; + } + //LOGD( "----- %d ----- ", (int)i); + + if (mDebugFlags & DEBUG_DUMP_ENCODER_TIMESTAMP) LOGD("FBD TS: %lld", nTimeStamp); + + if (mDebugFlags & FPS_ENCODER) PrintEncoderFPS(); + + if (mDebugFlags & ENCODER_LATENCY) PrintEncoderLatency(nTimeStamp); + + if (mDebugFlags & ENCODER_EFFECTIVE_BITRATE) PrintEffectiveBitrate(nFilledLen); + + if (mCallbackSet) { + mEncodedBufferCallback((mBufferInfo[OUTPUT_PORT][i].mEncMem->pointer() + nOffset), nFilledLen, nTimeStamp); + } else { + if (mOutputFD != NULL) { + i = fwrite((unsigned char *)(mBufferInfo[OUTPUT_PORT][i].mEncMem->pointer() + nOffset), 1, nFilledLen, mOutputFD); + if (i != nFilledLen) { + LOGD("fwrite failed:%d should have been:%d\n", i, nFilledLen); + return -1; + } + fflush(mOutputFD); + } + } + + err = mOMX->fillBuffer(mNode, pBufferHdr); + if (err != OK) { + LOGE("OMX_FillThisBuffer failed:%d", err); + } + + mBufferCount++; + MY_LOGV("EXIT FillBufferDone: nOffset: %d, nFilledLen=%d, mBufferCount=%d", nOffset, nFilledLen, mBufferCount); + return OMX_ErrorNone; +} + +status_t OMXEncoder::EmptyBufferDone(OMX_BUFFERHEADERTYPE* pBufferHdr) +{ + status_t err; + OMX_U32 i=0; + //MY_LOGV("ENTER EmptyBufferDone"); + + // not mAcceptingBuffers, just return, eventually, decoder will stop + if (mAcceptingBuffers == 0) { + MY_LOGV( " in non mAcceptingBuffers mode"); + return OMX_ErrorNone; + } + + for (i = 0; i < tInPortDef.nBufferCountActual; i++) { + if (pBufferHdr == mBufferInfo[INPUT_PORT][i].mBufferHdr) break; + } + + if (i == tInPortDef.nBufferCountActual) { + LOGE("EmptyBufferDone returned unknown buffer header! i=%d",(int)i); + return -1; + } + + mCameraSource->releaseBuffer(mBufferInfo[INPUT_PORT][i].mCamMem); + + if (mSliceHeight == 0) { // non tunnel mode + + // now get the next buffer and feed the encoder + sp<IMemory> payload; + int64_t time; + + // wait til buffer in the camera + payload = mCameraSource->getCameraPayload(time); + if (payload != NULL) { + mBufferInfo[INPUT_PORT][i].mCamMem = payload; + memcpy((uint8_t *)mBufferInfo[INPUT_PORT][i].mEncMem->pointer(), payload->pointer(), payload->size()); + err = mOMX->emptyBuffer(mNode, mBufferInfo[INPUT_PORT][i].mBufferHdr, 0, payload->size(), OMX_BUFFERFLAG_ENDOFFRAME, time); + if (err != OK) { + LOGE("OMX_EmptyThisBuffer failed:%d", err); + } + } + } + + //MY_LOGV("EXIT EmptyBufferDone"); + return err; +} + +status_t OMXEncoder::setCurrentState(OMX_STATETYPE newState) { + MY_LOGV("Attempting to set state to %s.", OMXStateName(newState)); + + status_t err = mOMX->sendCommand(mNode, OMX_CommandStateSet, newState); + if (err != OK) { + LOGD("setCurrentState: Error:%d", err); + return -1; + } + + LOG_FUNCTION_NAME_EXIT + return 0; +} + +status_t OMXEncoder::waitForStateSet(OMX_STATETYPE newState) { + MY_LOGV("waitForStateSet: Waiting to move to state %s .....", OMXStateName(newState)); + + if (newState == mState) { + LOGD("New State [%s] already set!", OMXStateName(newState)); + return 0; + } + + status_t retval = mAsyncCompletion.waitRelative(mLock, TWO_SECOND); + if (retval) { + if (errno == ETIMEDOUT) { + LOGD("$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Waiting for State change timed out $$$$$$$$$$$$$$$$$$$$$$$$$$$$"); + } else { + LOGD("$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Waiting for State errno :%d, retval:%d \n", errno, retval); + } + } + + if (newState == mState) { + LOGD("State [%s] Set !!!!!!!!!!!!!!!!!", OMXStateName(newState)); + return 0; + } + + LOG_FUNCTION_NAME_EXIT + return -1; +} + +//Function to set the slice mode for video encoder output port +status_t OMXEncoder::setEncoderOutputSlice(OMX_U32 nHeight, OMX_U32 nWidth, OMX_U32 sizeBytes, OMX_U32 sizeMB) { + status_t err = 0; + + MY_LOGV("Setting Video Output Slice Mode Size in Bytes:%d, Size in MB:%d\n",sizeBytes, sizeMB); + + if ((!sizeBytes) && (!sizeMB)) { + //Enc o/p slice not set + return err; + } + + if (sizeBytes) { + + if (nWidth <= 320) { + LOGD ("Setting the Video Encoder output slice mode NOT supported for given Resolution(width should be > 320)\n"); + return err; + } + + if (sizeBytes < 256) { + LOGD ("Slice size provided for Video Encoder output port too small, should be atleast 256 bytes\n"); + return err; + } + + OMX_VIDEO_CONFIG_SLICECODINGTYPE slicetype; + INIT_OMX_STRUCT(&slicetype, OMX_VIDEO_CONFIG_SLICECODINGTYPE); + slicetype.nPortIndex = OUTPUT_PORT; + + err = mOMX->getConfig( + mNode, (OMX_INDEXTYPE)OMX_TI_IndexConfigSliceSettings, &slicetype, sizeof(slicetype)); + if (err != OK) { + LOGD("get OMX_TI_IndexConfigSliceSettings failed : 0x%x", err); + return -1; + } + + slicetype.eSliceMode = OMX_VIDEO_SLICEMODE_AVCByteSlice; + slicetype.nSlicesize = sizeBytes; + + err = mOMX->setConfig( + mNode, (OMX_INDEXTYPE)OMX_TI_IndexConfigSliceSettings, &slicetype, sizeof(slicetype)); + if (err != OK) { + LOGD("set OMX_TI_IndexConfigSliceSettings OMX_VIDEO_SLICEMODE_AVCByteSlice failed : 0x%x", err); + return -1; + } + + } else if (sizeMB) { + + if (sizeMB <= 6) { + LOGD ("Macro Block set for the Video Encoder output slice mode NOT supported (very low should be > 6) \n"); + return err; + } + + /* Max # of MB + 1080p=8160 + 720p=3600 + VGA=1200 + */ + if (sizeMB > (((nWidth+15)>> 4) * ((nHeight+15)>> 4))) { + LOGD ("Macro Block set for the Video Encoder output slice mode is too large, should be less then \ + (((PreviewWidth+15)>> 4) * ((PreviewHeight+15)>> 4)) \n"); + return err; + } + + OMX_VIDEO_CONFIG_SLICECODINGTYPE slicetype; + INIT_OMX_STRUCT(&slicetype, OMX_VIDEO_CONFIG_SLICECODINGTYPE); + slicetype.nPortIndex = OUTPUT_PORT; + + err = mOMX->getConfig( + mNode, (OMX_INDEXTYPE)OMX_TI_IndexConfigSliceSettings, &slicetype, sizeof(slicetype)); + if (err != OK) { + LOGD("get OMX_TI_IndexConfigSliceSettings failed : 0x%x", err); + return -1; + } + + slicetype.eSliceMode = OMX_VIDEO_SLICEMODE_AVCMBSlice; + slicetype.nSlicesize = sizeMB; + + err = mOMX->setConfig( + mNode, (OMX_INDEXTYPE)OMX_TI_IndexConfigSliceSettings, &slicetype, sizeof(slicetype)); + if (err != OK) { + LOGD("set OMX_TI_IndexConfigSliceSettings OMX_VIDEO_SLICEMODE_AVCMBSlice failed : 0x%x", err); + return -1; + } + + } + + /* Other limitations: + - Input content type should be progressive (currently nPFrames = 0 by default) + - Changing parameters at run time will not have effect until next I-frame (hence setting IDR frame forcefully below) + - Incase of doing the initial setting of nPFrames = 0 (only initial frame is I-frame and all others P-frames), + you must request an I-frame to the codec after you have set nSlicesize to see your changes take place. + */ + + MY_LOGV ("Insert IDR frame for the Encoder o/p slice mode setting to get effective \n"); + //Insert IDR frame for the setting to get effective + OMX_CONFIG_INTRAREFRESHVOPTYPE voptype; + INIT_OMX_STRUCT(&voptype, OMX_CONFIG_INTRAREFRESHVOPTYPE); + voptype.nPortIndex = OUTPUT_PORT; + + err = mOMX->getConfig( + mNode, OMX_IndexConfigVideoIntraVOPRefresh, &voptype, sizeof(voptype)); + if (err != OK) { + LOGD("get OMX_IndexConfigVideoIntraVOPRefresh failed : %d", err); + return -1; + } + + voptype.IntraRefreshVOP = OMX_TRUE; + err = mOMX->setConfig( + mNode, OMX_IndexConfigVideoIntraVOPRefresh, &voptype, sizeof(voptype)); + if (err != OK) { + LOGD("set OMX_IndexConfigVideoIntraVOPRefresh failed : %d", err); + return -1; + } + + MY_LOGV("Insert IDR Frame DONE!!!, Setting Video Output Slice Mode\n"); + return err; +} + + + +void OMXEncoder::setCallback(EncodedBufferCallback fp) { + mEncodedBufferCallback = fp; + mCallbackSet = true; +} + +status_t OMXEncoder::changeFrameRate(int framerate) { + LOGV("setConfigVideoFrameRate: %d", frameRate); + OMX_CONFIG_FRAMERATETYPE framerateType; + INIT_OMX_STRUCT(&framerateType, OMX_CONFIG_FRAMERATETYPE); + framerateType.nPortIndex = INPUT_PORT; + + status_t err = mOMX->getConfig( + mNode, OMX_IndexConfigVideoFramerate, + &framerateType, sizeof(framerateType)); + if (err != OK) { + return BAD_VALUE; + } + + framerateType.xEncodeFramerate = framerate << 16; + err = mOMX->setConfig(mNode, OMX_IndexConfigVideoFramerate, &framerateType, sizeof(framerateType)); + if (err != OK) { + return BAD_VALUE; + } + return OK; +} + +status_t OMXEncoder::changeBitRate(int bitrate) { + OMX_VIDEO_CONFIG_BITRATETYPE bitrateType; + INIT_OMX_STRUCT(&bitrateType,OMX_VIDEO_CONFIG_BITRATETYPE); + bitrateType.nPortIndex = OUTPUT_PORT; + status_t err = mOMX->getConfig(mNode, OMX_IndexConfigVideoBitrate, &bitrateType, sizeof(bitrateType)); + if (err != OMX_ErrorNone) { + LOGE("get OMX_IndexConfigVideoBitrate failed err:%X", err); + return err; + } + + LOGD("\nSet encoder bitrate to %d.\n\n", bitrate); + bitrateType.nEncodeBitrate = bitrate; + err = mOMX->setConfig(mNode, OMX_IndexConfigVideoBitrate, &bitrateType, sizeof(bitrateType)); + if (err != OMX_ErrorNone) { + LOGE("set OMX_IndexConfigVideoBitrate failed error:%x", err); + return err; + } + + return OK; +} + +#ifdef NO_MEMCOPY +status_t OMXEncoder::allocateOutputBuffer() { + //USE_ION_BUFFERS_ALLOCATED_BY_DOMX + + int ion_fd = ion_open(); + if(ion_fd == 0) { + LOGE("ion_open failed!!!"); + return -1; + } + + struct ion_handle *importedHandle = NULL; + + for (OMX_U32 i = 0; i < tOutPortDef.nBufferCountActual; ++i) { + + IOMX::buffer_id buffer; + void *pBuffer; + err = mOMX->allocateBuffer(mNode, OUTPUT_PORT, tOutPortDef.nBufferSize, &buffer, &pBuffer); + if (err != OK) { + LOGD("OMX_UseBuffer for output port index:%d failed:%d",(int)i,err); + mBufferInfo[OUTPUT_PORT][i].mBufferHdr = NULL; + } + + OMX_TI_ION_SHARE_FD shareFDParam; + INIT_OMX_STRUCT(&shareFDParam, OMX_TI_ION_SHARE_FD); + shareFDParam.nPortIndex = OUTPUT_PORT; + shareFDParam.nBufferIndex = i; + err = mOMX->getParameter(mNode, (OMX_INDEXTYPE)OMX_TI_IndexIONBufferShareHandle, (void*)&shareFDParam, sizeof(shareFDParam)); + if (err != OK) { + LOGD("get OMX_TI_IndexIONBufferShareHandle Error:%d", err); + return -1; + } + LOGD("ENC SHARE FD = %d", shareFDParam.nShareFD); + + OMX_U32 nSize = (tOutPortDef.nBufferSize + LINUX_PAGE_SIZE - 1) & ~(LINUX_PAGE_SIZE - 1); + + err = ion_import(ion_fd, shareFDParam.nShareFD, &importedHandle); + if (err != OK) { + LOGD("ion_import failed. ret = %d", err); + return -1; + } + LOGD("IMPORT SUCCEEDED"); + + int mmap_fd; + void *pIONBuffer; + err = ion_map(ion_fd, importedHandle, nSize, PROT_READ | PROT_WRITE, MAP_SHARED, 0, (unsigned char**)&pIONBuffer, &mmap_fd); + if (err) { + LOGE("\n\n$$$$$$$$$$$$$$$$ Userspace mapping of ION buffers returned error %d\n\n", err); + err = ion_free(ion_fd, h); + if (err) LOGE("\n ion_free failed err=%d.\n\n%s\n\n", err, strerror(errno)); + return -1; + } + // TODO: More work needs to be done here.. This is just a skeleton for the moment. + } + + return OK; +} + +#endif + + diff --git a/test/VTC/IOMXEncoder.h b/test/VTC/IOMXEncoder.h new file mode 100644 index 0000000..367e56b --- /dev/null +++ b/test/VTC/IOMXEncoder.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) Texas Instruments - http://www.ti.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef IOMX_ENCODER_H +#define IOMX_ENCODER_H + +#include <fcntl.h> +#include <getopt.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#include <semaphore.h> +#include <pthread.h> + +#include <binder/MemoryDealer.h> +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> +#include <system/audio.h> +#include <utils/List.h> +#include <cutils/log.h> +#include <OMX_Component.h> +#include <camera/Camera.h> +#include <camera/ICamera.h> +#include <camera/ICameraClient.h> +#include <camera/ICameraService.h> +#include <media/mediaplayer.h> +#include <media/mediarecorder.h> +#include <media/stagefright/OMXClient.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MPEG4Writer.h> +#include <media/stagefright/CameraSource.h> +#include <media/stagefright/MetaData.h> + + +using namespace android; + +typedef void (*EncodedBufferCallback)(void* pBuffer, OMX_U32 nFilledLen, OMX_TICKS nTimeStamp); + +struct OMXEncoderObserver; + +struct OMXEncoder : public MediaSource { + struct BufferInfo { + OMX_BUFFERHEADERTYPE* mBufferHdr; + sp<IMemory> mEncMem; + sp<IMemory> mCamMem; + }; + + friend class OMXEncoderObserver; + + status_t resetParameters(int width, int height, int framerate, int bitrate, char *fname, int sliceHeight); + status_t configure(OMX_VIDEO_AVCPROFILETYPE profile, OMX_VIDEO_AVCLEVELTYPE level, OMX_U32 refFrames); + status_t prepare(); + status_t start(MetaData *params = NULL); + status_t stop(); + status_t deinit(); + status_t changeFrameRate(int framerate); + status_t changeBitRate(int bitrate); + status_t setEncoderOutputSlice(OMX_U32 nHeight, OMX_U32 nWidth, OMX_U32 sizeBytes, OMX_U32 sizeMB); + sp<MetaData> getFormat(); + status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL); + void on_message(const omx_message &msg); + void setCallback(EncodedBufferCallback fp); + OMXEncoder(const sp<IOMX> &omx, IOMX::node_id node, sp<MyCameraClient> camera, int width, int height, int framerate, int bitrate, char *fname, int sliceHeight); + OMXEncoder(const OMXEncoder &); + OMXEncoder &operator=(const OMXEncoder &); + ~OMXEncoder(); + uint32_t mDebugFlags; + uint32_t mOutputBufferCount; + +private: + + class OMXCallbackHandler : public Thread { + public: + OMXCallbackHandler(OMXEncoder* enc) + : Thread(false), mOMXEncoder(enc) { } + + virtual bool threadLoop() { + bool ret; + ret = Handler(); + return ret; + } + + status_t put(TIUTILS::Message* msg) { + Mutex::Autolock lock(mLock); + return mCommandMsgQ.put(msg); + } + + void clearCommandQ() { + Mutex::Autolock lock(mLock); + mCommandMsgQ.clear(); + } + + enum { + COMMAND_EXIT = -1, + COMMAND_PROCESS_MSG, + }; + + private: + bool Handler(); + TIUTILS::MessageQueue mCommandMsgQ; + OMXEncoder* mOMXEncoder; + Mutex mLock; + }; + + sp<OMXCallbackHandler> mOMXCallbackHandler; + + OMX_ERRORTYPE EventHandler(OMX_EVENTTYPE eEvent, OMX_U32 nData1,OMX_U32 nData2); + status_t FillBufferDone(OMX_BUFFERHEADERTYPE* pBufferHdr, OMX_U32 nOffset, OMX_U32 nFilledLen, OMX_TICKS nTimeStamp); + status_t EmptyBufferDone(OMX_BUFFERHEADERTYPE* pBufferHdr); + status_t setCurrentState(OMX_STATETYPE newState); + status_t waitForStateSet(OMX_STATETYPE newState); + + int mWidth; + int mHeight; + uint32_t mBitRate; + uint32_t mFrameRate; + sp<MyCameraClient> mCameraSource; + OMX_PARAM_PORTDEFINITIONTYPE tInPortDef; + OMX_PARAM_PORTDEFINITIONTYPE tOutPortDef; + sp<IOMX> mOMX; + IOMX::node_id mNode; + BufferInfo mBufferInfo[NUM_PORTS][ENCODER_MAX_BUFFER_COUNT]; + Condition mAsyncCompletion; + Mutex mLock; + OMX_STATETYPE mState; + int mAcceptingBuffers; + sp<MemoryDealer> mDealer[NUM_PORTS]; + FILE* mOutputFD; + int mSliceHeight; + int mBufferCount; + EncodedBufferCallback mEncodedBufferCallback; + bool mCallbackSet; +}; + +struct OMXEncoderObserver : public BnOMXObserver { + OMXEncoderObserver() { + } + + void setCodec(const sp<OMXEncoder> &target) { + mTarget = target; + } + + // from IOMXObserver + virtual void onMessage(const omx_message &omx_msg) { + TIUTILS::Message msg; + omx_message *ptemp_omx_msg; + // TODO: Check on the memory scope of below allocation + ptemp_omx_msg = (omx_message *)malloc(sizeof(omx_message)); + memcpy(ptemp_omx_msg, &omx_msg, sizeof(omx_message)); + //LOGD("=================omx_msg.type = %x, temp_omx_msg.type = %x",omx_msg.type, ptemp_omx_msg->type); + sp<OMXEncoder> codec = mTarget.promote(); + if (codec.get() != NULL) { + msg.command = OMXEncoder::OMXCallbackHandler::COMMAND_PROCESS_MSG; + msg.arg1 = (void *)ptemp_omx_msg; + codec->mOMXCallbackHandler->put(&msg); + codec.clear(); + } + } + +protected: + virtual ~OMXEncoderObserver() {} + +private: + wp<OMXEncoder> mTarget; + OMXEncoderObserver(const OMXEncoderObserver &); + OMXEncoderObserver &operator=(const OMXEncoderObserver &); +}; + +#endif diff --git a/test/VTC/VTCLoopback.cpp b/test/VTC/VTCLoopback.cpp new file mode 100644 index 0000000..9c7c06b --- /dev/null +++ b/test/VTC/VTCLoopback.cpp @@ -0,0 +1,883 @@ +/* + * Copyright (C) Texas Instruments - http://www.ti.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "VTCLoopback.h" +#include "IOMXEncoder.h" +#include "IOMXDecoder.h" + +#define LOG_NDEBUG 0 +#define LOG_TAG "VTC" + +#define MY_LOGV(x, ...) LOGV(x, ##__VA_ARGS__) + +using namespace android; + + +int gFilename = 0; +int gDuration = 10; +int gTestcaseID = 1; +int gPreviewWidth = WIDTH; +int gPreviewHeight = HEIGHT; +int gCameraIndex = 0; +int gCameraFrameRate = 30; +int gNewCameraFrameRate = 0; +int gEnableAlgo = 0; +uint32_t gSliceHeight = 0; +uint32_t gCameraWinX = 50; +uint32_t gCameraWinY = 50; +uint32_t gCameraSurfaceWidth = 400; +uint32_t gCameraSurfaceHeight = 400; +uint32_t gEncoderBitRate = BITRATE; +uint32_t gMinEncoderBitRate = BITRATE; +uint32_t gMaxEncoderBitRate = BITRATE; +uint32_t gDebugFlags = FPS_DECODER; // | ENCODER_ONLY ; +uint32_t gEncoderOutputBufferCount = 4; +uint32_t gEncoderOutputSliceSizeBytes = 0; +uint32_t gEncoderOutputSliceSizeMB = 0; +bool gEnableLoopback = false; +bool gVaryFrameRate = false; +bool gVaryOrientation = false; +char mParamValue[100]; +char gRecordFileName[256]; +sp<SurfaceComposerClient> gSurfaceComposerClient; +sp<SurfaceControl> gSurfaceControl; +sp<Surface> gPreviewSurface; +sp<ICameraService> gCameraService; +sp<ICamera> gICamera; +sp<MyCameraClient> gCameraClient; +sp<OMXDecoder> mOMXDecoder; +OMX_VIDEO_AVCPROFILETYPE gProfile = OMX_VIDEO_AVCProfileBaseline; +OMX_VIDEO_AVCLEVELTYPE gLevel = OMX_VIDEO_AVCLevel4; +OMX_U32 gRefFrames = 1; + +// Add more parameters as needed. +struct Configuration { + size_t width, height; +}; + +int test_DEFAULT_Slice(); +int test_DEFAULT_Frame(); +int test_Robustness(); +int test_Frame_Robustness(); +int test_Slice_Robustness(); + +typedef int (*pt2TestFunction)(); +pt2TestFunction TestFunctions[10] = {0, test_DEFAULT_Frame, test_DEFAULT_Slice, test_Frame_Robustness, test_Slice_Robustness, 0, 0, 0, 0, 0}; + + +static void PrintCameraFPS() { + static int mFrameCount = 0; + static int mLastFrameCount = 0; + static nsecs_t mLastFpsTime = 0; + static float mFps = 0; + mFrameCount++; + if (!(mFrameCount & 0x1F)) { + nsecs_t now = systemTime(); + nsecs_t diff = now - mLastFpsTime; + mFps = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff; + mLastFpsTime = now; + mLastFrameCount = mFrameCount; + LOGD("Camera: %d Frames, %f FPS", mFrameCount, mFps); + } + // XXX: mFPS has the value we want +} + +void dump_video_port_values(OMX_PARAM_PORTDEFINITIONTYPE& def) { + OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video; + + LOGD("--------------------------------------------------------------------------------"); + LOGD("nPortIndex:%d eDir:%s\n",(int)def.nPortIndex,(def.eDir == OMX_DirInput)?"OMX_DirInput":"OMX_DirOutput"); + LOGD("BufferCountActual:%d BufferCountMin:%d nBufferSize:%d\n", (int)def.nBufferCountActual, (int)def.nBufferCountMin, (int)def.nBufferSize); + LOGD("bEnabled:%s bPopulated:%s eDomain:%s BuffersContiguous:%s BufferAlignment:%d", + (def.bEnabled)?"TRUE":"FALSE",(def.bPopulated)?"TRUE":"FALSE", + (def.eDomain == OMX_PortDomainVideo)?"Video":"Not Video", + (def.bBuffersContiguous)?"TRUE":"FALSE",(int)def.nBufferAlignment); + + LOGD("cMIMEType:%s pNativeRender:%p\n",(def.format.video.cMIMEType)?(def.format.video.cMIMEType):"NULL",def.format.video.pNativeRender); + LOGD("nFrameWidth:%d nFrameHeight:%d nStride:%d nSliceHeight:%d", + (int)def.format.video.nFrameWidth, (int)def.format.video.nFrameHeight, (int)def.format.video.nStride, (int)def.format.video.nSliceHeight); + LOGD("nBitrate:%d xFramerate:%d bFlagErrorConcealment:%s", + (int)def.format.video.nBitrate, (int)def.format.video.xFramerate, (def.format.video.bFlagErrorConcealment)?"TRUE":"FALSE"); + LOGD("eCompressionFormat:0x%x eColorFormat:0x%x pNativeWindow:%p", + (def.format.video.eCompressionFormat), + (def.format.video.eColorFormat), def.format.video.pNativeWindow); + LOGD("--------------------------------------------------------------------------------"); +} + +#define NAME(n) case n: return #n +const char *OMXStateName(OMX_STATETYPE state) { + switch (state) { + NAME(OMX_StateInvalid); + NAME(OMX_StateLoaded); + NAME(OMX_StateIdle); + NAME(OMX_StateExecuting); + NAME(OMX_StatePause); + NAME(OMX_StateWaitForResources); + NAME(OMX_StateKhronosExtensions); + NAME(OMX_StateVendorStartUnused); + default: return "???"; + } +} + + +MyCameraClient::MyCameraClient() { + encoder_is_ready = 0; + cameraPayloadWaitFlag = 0; +} + +void MyCameraClient::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& data) { + //MY_LOGV("=============================================dataCallbackTimestamp"); + CHECK(data != NULL && data->size() > 0); + if (msgType == CAMERA_MSG_VIDEO_FRAME) { + if ((gSliceHeight == 0) && (encoder_is_ready)) { // non tunnel mode + putCameraPayload(data,(int64_t)timestamp/1000); + } else { + if (mReleaser != NULL) { + mReleaser->releaseRecordingFrame(data); + //MY_LOGV("CAMERA_MSG_VIDEO_FRAME %p released",data->pointer()); + } + } + } +} + +void MyCameraClient::putCameraPayload(sp<IMemory> payload, int64_t frameTime) { + if (gDebugFlags & FPS_CAMERA) PrintCameraFPS(); + if (gDebugFlags & DEBUG_DUMP_CAMERA_TIMESTAMP) LOGD("CAM TS: %lld", frameTime); + + Mutex::Autolock autoLock(cameraPayloadQueueMutex); + cameraPayloadQueue.push_back(payload); + frameTimeQueue.push_back(frameTime); + if (cameraPayloadQueue.size() >= 1) { + if ( cameraPayloadWaitFlag ) cameraPayloadWait.signal(); + } + return; +} + +sp<IMemory> MyCameraClient::getCameraPayload(int64_t& frameTime) { + if (!encoder_is_ready) { + frameTime = 0; + LOGE("getCameraPayload --- returning null"); + return NULL; + } + + { + status_t retval; + Mutex::Autolock autoLock(cameraPayloadQueueMutex); + if (cameraPayloadQueue.empty()) { + cameraPayloadWaitFlag = 1; + retval = cameraPayloadWait.waitRelative(cameraPayloadQueueMutex, TWO_SECOND); + if (retval || !encoder_is_ready) { + LOGD("$$$$$$$$$$$$$$$$$$$$$$$$$$$$ getCameraPayload timed out or we are stopping $$$$$$$$$$$$$$$$$$$$$$$$$$$$"); + frameTime = 0; + return NULL; + } + cameraPayloadWaitFlag = 0; + } + } + + List< sp<IMemory> >::iterator iterPayload; + sp<IMemory> payload; + + List<int64_t>::iterator iterTime; + int64_t time; + + { + Mutex::Autolock autoLock(cameraPayloadQueueMutex); + + iterPayload = cameraPayloadQueue.begin(); + payload = (sp<IMemory>)*iterPayload; + cameraPayloadQueue.erase(iterPayload); + + iterTime = frameTimeQueue.begin(); + time = (int64_t)*iterTime; + frameTimeQueue.erase(iterTime); + } + + frameTime = time; + //MY_LOGV("%s: pointer[%p], size[%d]", __FUNCTION__, payload->pointer(), payload->size()); + return payload; +} + + + +int createPreviewSurface() { + + gSurfaceComposerClient = new SurfaceComposerClient(); + CHECK_EQ(gSurfaceComposerClient->initCheck(), (status_t)OK); + + gCameraSurfaceWidth = gSurfaceComposerClient->getDisplayWidth(0); + gCameraSurfaceHeight = gSurfaceComposerClient->getDisplayHeight(0); + + gSurfaceControl = gSurfaceComposerClient->createSurface(0, + gCameraSurfaceWidth, + gCameraSurfaceHeight, + HAL_PIXEL_FORMAT_RGB_565); + + gPreviewSurface = gSurfaceControl->getSurface(); + + gSurfaceComposerClient->openGlobalTransaction(); + gSurfaceControl->setLayer(0x7ffffff0); + gSurfaceControl->setPosition(gCameraWinX, gCameraWinY); + gSurfaceControl->setSize(gCameraSurfaceWidth, gCameraSurfaceHeight); + gSurfaceControl->show(); + gSurfaceComposerClient->closeGlobalTransaction(); + + return 0; +} + +int destroyPreviewSurface() { + + if ( NULL != gPreviewSurface.get() ) { + gPreviewSurface.clear(); + } + + if ( NULL != gSurfaceControl.get() ) { + gSurfaceControl->clear(); + gSurfaceControl.clear(); + } + + if ( NULL != gSurfaceComposerClient.get() ) { + gSurfaceComposerClient->dispose(); + gSurfaceComposerClient.clear(); + } + + return 0; +} + + +int configureCamera() { + + createPreviewSurface(); + + sp<IServiceManager> sm = defaultServiceManager(); + CHECK(sm.get() != NULL); + + sp<IBinder> binder = sm->getService(String16("media.camera")); + CHECK(binder.get() != NULL); + + gCameraService = interface_cast<ICameraService>(binder); + CHECK(gCameraService.get() != NULL); + + gCameraClient = new MyCameraClient(); + int i = 0; + do { + gICamera = gCameraService->connect(gCameraClient, gCameraIndex); + if (gICamera.get() != NULL) break; + LOGD("\n\n\n========= Camera Busy. So relax and retry. ===========\n\n\n"); + sleep(1); + i++; + } while (i < 10); + + if (gICamera == NULL) return -1; + + LOGD("Acquired Camera"); + gICamera->setPreviewDisplay(gPreviewSurface); + gCameraClient->setReleaser(gICamera.get()); + + String8 param_str = gICamera->getParameters(); + CameraParameters params(param_str); + params.setPreviewSize(gPreviewWidth, gPreviewHeight); + params.setPreviewFrameRate(gCameraFrameRate); + params.set(CameraParameters::KEY_RECORDING_HINT, CameraParameters::TRUE);// Set recording hint, otherwise it defaults to high-quality and there is not enough memory available to do playback and camera preview simultaneously! + params.set("internal-vtc-hint", CameraParameters::TRUE); + + if (gEnableAlgo & 1) { //VNF Enabled based on cmd line + LOGD("VNF is enabled!!! \n"); + params.set("vnf", CameraParameters::TRUE); + } else { + LOGD("VNF is disabled!!! \n"); + params.set("vnf", CameraParameters::FALSE); + } + + sprintf(mParamValue,"%u,%u", gCameraFrameRate*1000, gCameraFrameRate*1000); + params.set("preview-fps-range", mParamValue); + params.set("mode","video-mode"); + + if ((!gSliceHeight) && (gEnableAlgo & 2)) { // VSTAB not supported for Slice Mode + LOGD("VSTAB is enabled!!! \n"); + params.set("vstab",CameraParameters::TRUE); + params.set("video-stabilization",CameraParameters::TRUE); + } else { + LOGD("VSTAB is disabled!!! \n"); + params.set("vstab",CameraParameters::FALSE); + params.set("video-stabilization",CameraParameters::FALSE); + } + + //params.dump(); + gICamera->setParameters(params.flatten()); + return 0; +} + +void stopPreview() { + gICamera->stopPreview(); + gCameraClient->setReleaser(NULL); + gICamera->disconnect(); + gICamera.clear(); + gCameraClient.clear(); + gCameraService.clear(); + destroyPreviewSurface(); +} + +void encodedBufferCallback(void* pBuffer, OMX_U32 nFilledLen, OMX_TICKS nTimeStamp) { + if (mOMXDecoder.get()) { + mOMXDecoder->AcceptEncodedBuffer(pBuffer, nFilledLen, nTimeStamp); + } else { + LOGE("\n\nDECODER IS NULL !!!!!!! \n\n"); + } +} + +void setFrameRate(sp<OMXEncoder> pOMXEncoder) { + sleep(8); + pOMXEncoder->mDebugFlags |= FPS_ENCODER; + + // Changing the framerate in camera. + String8 param_str = gICamera->getParameters(); + CameraParameters params(param_str); + LOGD("Setting new framerate: %d", gNewCameraFrameRate); + params.setPreviewFrameRate(gNewCameraFrameRate); + sprintf(mParamValue,"%u,%u", gNewCameraFrameRate*1000, gNewCameraFrameRate*1000); + params.set("preview-fps-range", mParamValue); + gICamera->setParameters(params.flatten()); + + // Changing the framerate in encoder. + pOMXEncoder->changeFrameRate(gNewCameraFrameRate); +} + +void varyBitRate1(sp<OMXEncoder> pOMXEncoder) { + uint32_t currentBitRate = gMinEncoderBitRate; + uint32_t step = (gMaxEncoderBitRate - gMinEncoderBitRate) / 10; + while (currentBitRate <= gMaxEncoderBitRate) { + sleep(10); + pOMXEncoder->changeBitRate(currentBitRate); + currentBitRate += step; + } +} + + +void varyBitRate2(sp<OMXEncoder> pOMXEncoder) { + int32_t step = gMaxEncoderBitRate - gMinEncoderBitRate; + uint32_t currentBitRate = gMinEncoderBitRate; + + int i = 1; + while (i <= 10) { + sleep(10); + pOMXEncoder->changeBitRate(currentBitRate); + currentBitRate += step; + step *= -1; + i++; + } +} + + +void varyBitRate(sp<OMXEncoder> pOMXEncoder) { + if ((gMinEncoderBitRate == 0) || (gMaxEncoderBitRate == 0)) return; + + pOMXEncoder->mDebugFlags |= ENCODER_EFFECTIVE_BITRATE; + + varyBitRate1(pOMXEncoder); + varyBitRate2(pOMXEncoder); +} + +void varyFrameRate(sp<OMXEncoder> pOMXEncoder) { + for (int i = 7; i <= 30; i+=2) { + gNewCameraFrameRate = i; + setFrameRate(pOMXEncoder); + } + for (int i = 30; i >= 7; i-=2) { + gNewCameraFrameRate = i; + setFrameRate(pOMXEncoder); + } +} + +void varyOrientation(sp<OMXEncoder> pOMXEncoder) { + //TODO : Check on the requirement and enhance to support this functionality +} + + +int test_DEFAULT_Frame() { + status_t err = 0; + + if (gEnableLoopback) { + mOMXDecoder = new OMXDecoder(gPreviewWidth, gPreviewHeight, gCameraFrameRate); + mOMXDecoder->mDebugFlags = gDebugFlags; + err = mOMXDecoder->configure(gProfile, gLevel, gRefFrames); + if (err != 0) return -1; + err = mOMXDecoder->prepare(); + if (err != 0) return -1; + } + + configureCamera(); + + OMXClient omxclient; + CHECK_EQ(omxclient.connect(), (status_t)OK); + sp<IOMX> omx = omxclient.interface(); + IOMX::node_id node = 0; + sp<OMXEncoderObserver> observer = new OMXEncoderObserver(); + err = omx->allocateNode("OMX.TI.DUCATI1.VIDEO.H264E", observer, &node); + if (err != OK) { + LOGD("Failed to allocate OMX node!!"); + return -1; + } + + sp<OMXEncoder> pOMXEncoder = new OMXEncoder(omx, node, gCameraClient, + gPreviewWidth, gPreviewHeight, gCameraFrameRate, gEncoderBitRate, gRecordFileName, 0 /*SliceHeight*/); + observer->setCodec(pOMXEncoder); + pOMXEncoder->mDebugFlags = gDebugFlags; + pOMXEncoder->mOutputBufferCount = gEncoderOutputBufferCount; + + if (gEnableLoopback) pOMXEncoder->setCallback(&encodedBufferCallback); + + err = pOMXEncoder->configure(gProfile, gLevel, gRefFrames); + if (err != 0) return -1; + err = pOMXEncoder->prepare(); + if (err != 0) return -1; + + gICamera->startPreview(); + sleep(SLEEP_AFTER_STARTING_PREVIEW); + + gICamera->startRecording(); + err = pOMXEncoder->start(); + if (err != 0) return -1; + + if (gEnableLoopback) { + err = mOMXDecoder->start(); + if (err != 0) return -1; + } + + + if (gNewCameraFrameRate) setFrameRate(pOMXEncoder); + if (gMinEncoderBitRate != gMaxEncoderBitRate) varyBitRate(pOMXEncoder); + if (gVaryFrameRate) varyFrameRate(pOMXEncoder); + if (gVaryOrientation) varyOrientation(pOMXEncoder); + + sleep(gDuration); + + gICamera->stopRecording(); + pOMXEncoder->stop(); + pOMXEncoder->deinit(); + stopPreview(); + + if (gEnableLoopback) { + mOMXDecoder->stop(); + mOMXDecoder.clear(); + } + pOMXEncoder.clear(); + observer.clear(); + + return 0; +} + +int test_DEFAULT_Slice() { + status_t err = 0; + if (gSliceHeight == 0) gSliceHeight = gPreviewHeight / 2; + if (gSliceHeight < 128) gSliceHeight = gPreviewHeight; + + if (gEnableLoopback) { + mOMXDecoder = new OMXDecoder(gPreviewWidth, gPreviewHeight, gCameraFrameRate); + if (gEncoderOutputSliceSizeBytes || gEncoderOutputSliceSizeMB) { + mOMXDecoder->mDebugFlags = gDebugFlags | INPUT_OUTPUT_SLICE_MODE; + } else { + mOMXDecoder->mDebugFlags = gDebugFlags | INPUT_SLICE_MODE; + } + err = mOMXDecoder->configure(gProfile, gLevel, gRefFrames); + if (err != 0) return -1; + err = mOMXDecoder->prepare(); + if (err != 0) return -1; + } + + configureCamera(); + + OMXClient omxclient; + CHECK_EQ(omxclient.connect(), (status_t)OK); + sp<IOMX> omx = omxclient.interface(); + IOMX::node_id node = 0; + sp<OMXEncoderObserver> observer = new OMXEncoderObserver(); + err = omx->allocateNode("OMX.TI.DUCATI1.VIDEO.H264E", observer, &node); + if (err != OK) { + LOGD("Failed to allocate OMX node!!"); + return -1; + } + + sp<OMXEncoder> pOMXEncoder = new OMXEncoder(omx, node, gCameraClient, gPreviewWidth, gPreviewHeight, gCameraFrameRate, gEncoderBitRate, gRecordFileName, gSliceHeight); + observer->setCodec(pOMXEncoder); + pOMXEncoder->mDebugFlags = gDebugFlags; + if (gEnableLoopback) pOMXEncoder->setCallback(&encodedBufferCallback); + err = pOMXEncoder->configure(gProfile, gLevel, gRefFrames); + if (err != 0) return -1; + + OMX_TI_COMPONENT_HANDLE compHandle; + INIT_OMX_STRUCT(&compHandle, OMX_TI_COMPONENT_HANDLE); + err = omx->getParameter(node, (OMX_INDEXTYPE)OMX_TI_IndexComponentHandle, &compHandle, sizeof(compHandle)); + if (err != OK) { + LOGD("get OMX_TI_IndexComponentHandle failed : %d", err); + return -1; + } + + // Set up the Tunnel + String8 param_str = gICamera->getParameters(); + CameraParameters params(param_str); + sprintf(mParamValue,"%u", gSliceHeight); + params.set("encoder_slice_height", mParamValue); + sprintf(mParamValue,"%u", (int)compHandle.pHandle); + params.set("encoder_handle", mParamValue); + gICamera->setParameters(params.flatten()); + + err = pOMXEncoder->prepare(); + if (err != 0) return -1; + + gICamera->sendCommand(CAMERA_CMD_PREVIEW_INITIALIZATION, 0, 0); + + if (gEncoderOutputSliceSizeBytes || gEncoderOutputSliceSizeMB) { + sleep(SLEEP_AFTER_STARTING_PREVIEW); + //Setting the Encoder output slice mode + err = pOMXEncoder->setEncoderOutputSlice(gPreviewHeight, gPreviewWidth, gEncoderOutputSliceSizeBytes, gEncoderOutputSliceSizeMB); + if (err != 0) { + LOGD("pOMXEncoder->setEncoderOutputSlice error \n"); + return -1; + } + } + + //Moving to Executing State + err = pOMXEncoder->start(); + if (err != 0) return -1; + + err = gICamera->startPreview(); + if (err != 0) return -1; + + if (gEnableLoopback) err = mOMXDecoder->start(); + if (err != 0) return -1; + + + if (gNewCameraFrameRate) setFrameRate(pOMXEncoder); + if (gMinEncoderBitRate != gMaxEncoderBitRate) varyBitRate(pOMXEncoder); + if (gVaryFrameRate) varyFrameRate(pOMXEncoder); + + + sleep(gDuration); + //camera goes to idle + gICamera->sendCommand(CAMERA_CMD_PREVIEW_DEINITIALIZATION, 0, 0); + //encoder goes to idle + pOMXEncoder->stop(); + //delay for state changes + usleep (100000); + //camera goes to loaded + stopPreview(); + //encoder goes to loaded + pOMXEncoder->deinit(); + + if (gEnableLoopback) { + mOMXDecoder->stop(); + mOMXDecoder.clear(); + } + pOMXEncoder.clear(); + //observer.clear(); + + return 0; +} + +// TODO: One should not be required to release the camera and destroy the node +// everytime we start and stop or change resolution or some other parameter. +// Presently, this code does not work as is... +int test_Robustness() { + status_t err = 0; + int loopCnt = 0; + gDuration = 5; + + configureCamera(); + gICamera->startPreview(); + sleep(SLEEP_AFTER_STARTING_PREVIEW); + + OMXClient omxclient; + CHECK_EQ(omxclient.connect(), (status_t)OK); + sp<IOMX> omx = omxclient.interface(); + IOMX::node_id node = 0; + sp<OMXEncoderObserver> observer = new OMXEncoderObserver(); + err = omx->allocateNode("OMX.TI.DUCATI1.VIDEO.H264E", observer, &node); + if (err != OK) { + LOGD("Failed to allocate OMX node!!"); + return -1; + } + + sp<OMXEncoder> pOMXEncoder = new OMXEncoder(omx, node, gCameraClient, gPreviewWidth, gPreviewHeight, gCameraFrameRate, gEncoderBitRate, gRecordFileName, 0 /*SliceHeight*/); + observer->setCodec(pOMXEncoder); + + + while(loopCnt < 3) { + sleep(SLEEP_AFTER_STARTING_PREVIEW); + + pOMXEncoder->resetParameters(gPreviewWidth, gPreviewHeight, gCameraFrameRate, gEncoderBitRate, gRecordFileName, 0 /*SliceHeight*/); + pOMXEncoder->mDebugFlags = gDebugFlags; + pOMXEncoder->mOutputBufferCount = gEncoderOutputBufferCount; + + if (gEnableLoopback) pOMXEncoder->setCallback(&encodedBufferCallback); + + err = pOMXEncoder->configure(gProfile, gLevel, gRefFrames); + if (err != 0) return -1; + err = pOMXEncoder->prepare(); + if (err != 0) return -1; + + + gICamera->startRecording(); + err = pOMXEncoder->start(); + if (err != 0) return -1; + + + sleep(gDuration); + + gICamera->stopRecording(); + pOMXEncoder->stop(); + pOMXEncoder->deinit(); + + loopCnt++; + LOGD("#######################################################################"); + LOGD("#######################################################################"); + } + + stopPreview(); + + return 0; +} + +// TODO: Combine these 2 tests into one. Reuse. +int test_Frame_Robustness() { + const Configuration configdata[] = { + { 160, 120 }, + { 320, 240 }, + { 640, 480 }, + { 1280, 720 }, + }; + + for (int i = 0, j = 0; i < 4000; i++) { + gPreviewWidth = configdata[j].width; + gPreviewHeight = configdata[j].height; + LOGD("##################################################################"); + LOGD("##################### ITERATION %d : %d x %d ###################", i, gPreviewWidth, gPreviewHeight); + LOGD("##################################################################"); + sleep(1); + + test_DEFAULT_Frame(); + j++; + j = j % (sizeof(configdata)/sizeof(Configuration)); + } + return 0; + +} + +int test_Slice_Robustness() { + const Configuration configdata[] = { +// { 160, 120 }, // If this resolution is included, it results in random errors being thrown. Recall that the min slice height is 128 which is more than 120. + { 320, 240 }, + { 640, 480 }, + { 1280, 720 }, + }; + + for (int i = 0, j = 0; i < 4000; i++) { + gPreviewWidth = configdata[j].width; + gPreviewHeight = configdata[j].height; + LOGD("##################################################################"); + LOGD("##################### ITERATION %d : %d x %d ###################", i, gPreviewWidth, gPreviewHeight); + LOGD("##################################################################"); + sleep(1); + + test_DEFAULT_Slice(); + j++; + j = j % (sizeof(configdata)/sizeof(Configuration)); + } + return 0; + +} + + +void printUsage() { + printf("\n\nApplication for testing VTC using IOMX"); + printf("\n\nIn this application:"); + printf("\n\t- Camera Preview is started."); + printf("\n\t- Preview Frames are sent to encoder for encoding."); + printf("\n\t- The encoded frames are then sent to the decoder for decoding."); + printf("\n\t- The decoded frames are then rendered along side the preview window."); + printf("\n\nThe application can be run in frame mode or slice mode."); + + printf("\n\n\nUsage: /system/bin/VTCLoopbackTest <Testcase_ID> <options>\n"); + + printf("\n\n\nTest Case ID can be any of the following"); + printf("\n1 - Default test case. Frame Mode. No Loopback. Does not require any parameters to be set."); + printf("\n2 - Slice Mode. No Loopback. Does not require any parameters to be set."); + printf("\n3 - Test Robustness. Frame Mode."); + printf("\n4 - Test Robustness. Slice Mode."); + + printf("\n\n\nAvailable Options:"); + printf("\n-t: Test case ID. Default = %d", gTestcaseID); + printf("\n-n: Record Filename(/mnt/sdcard/video_0.264) is appended with this number. Default = %d", gFilename); + printf("\n-w: Preview/Record Width. Default = %d", gPreviewWidth); + printf("\n-e: Preview/Record Height. Default = %d", gPreviewHeight); + printf("\n-d: Recording time in secs. Default = %d seconds", gDuration); + printf("\n-f: Framerate. Default = %d", gCameraFrameRate); + printf("\n-b: Bitrate. Default = %d", gEncoderBitRate); + printf("\n-s: Slice Height in # of lines. Default = %d", gSliceHeight); + printf("\n-g: Debug Options. Refer to source for usage."); + printf("\n-c: Camera Index. Default = %d", gCameraIndex); + printf("\n-p: Print FPS. 4 = dont write to file. Print Encoder FPS"); + printf("\n-o: Encoder Output Buffer Count. Default = %d", gEncoderOutputBufferCount); + printf("\n-l: Enable loopback. Default = %d", gEnableLoopback); + printf("\n-j: Change to this framerate at runtime after 8 seconds. Default = %d", gNewCameraFrameRate); + printf("\n-v: VNF/VSTAB Option. 1-Enable VNF, 2-Enable VSTAB, 3-Enable Both, Default = %d", gEnableAlgo); + printf("\n-i: Min bitrate. Vary the bitrate at run time between min and max values"); + printf("\n-a: Max bitrate. Vary the bitrate at run time between min and max values"); + printf("\n-x: Vary framerate between 7 and 30 at run time"); + printf("\n-r: Test Rotation. Not supported yet."); + printf("\n-h: Print help menu"); + + printf("\n\n\nSample Commands:"); + printf("\n\nTesting Frame mode. Loopback. 720p"); + printf("\nVTCLoopbackTest -t 1 -d 10 -w 1280 -e 720 -b 2000000 -l 1"); + printf("\n\nTesting Slice mode. Loopback. 720p"); + printf("\nVTCLoopbackTest -t 2 -d 10 -w 1280 -e 720 -b 2000000 -l 1"); + printf("\n\nTesting Robustness. Frame mode."); + printf("\nVTCLoopbackTest -t 3 -l 1"); + printf("\n\nTesting Robustness. Slice mode."); + printf("\nVTCLoopbackTest -t 4 -l 1"); + printf("\n\nVary Framerate at runtime between 7 and 30"); + printf("\nVTCLoopbackTest -t 1 -x 1 -g 2"); + printf("\n\nTesting Bitrate"); + printf("\nVTCLoopbackTest -t 1 -w 1280 -e 720 -b 2000000 -i 1000000 -a 3000000 -g 64"); + printf("\n\n\n"); + +} + +int main (int argc, char* argv[]) { + sp<ProcessState> proc(ProcessState::self()); + ProcessState::self()->startThreadPool(); + + int opt; + const char* const short_options = "a:g:n:w:e:d:b:f:s:c:p:t:o:y:m:l:j:i:v:x:r:h"; + const struct option long_options[] = { + {"debug_flags", 1, NULL, 'g'}, + {"record_filename", 1, NULL, 'n'}, + {"width", 1, NULL, 'w'}, + {"height", 1, NULL, 'e'}, + {"gDuration", 1, NULL, 'd'}, + {"bitrate", 1, NULL, 'b'}, + {"min_bitrate", 1, NULL, 'i'}, + {"max_bitrate", 1, NULL, 'a'}, + {"framerate", 1, NULL, 'f'}, + {"sliceheight", 1, NULL, 's'}, + {"camera_index", 1, NULL, 'c'}, + {"print_fps", 1, NULL, 'p'}, + {"testcase_id", 1, NULL, 't'}, + {"out_buff_cnt", 1, NULL, 'o'}, + {"encoder_slicesize_bytes", 1, NULL, 'y'}, + {"encoder_slicesize_mb", 1, NULL, 'm'}, + {"enable_loopback", 1, NULL, 'l'}, + {"new_framerate", 1, NULL, 'j'}, + {"vary_framerate", 1, NULL, 'x'}, + {"vary_rotation", 1, NULL, 'r'}, + {"algo", 1, NULL, 'v'}, + {"help", 1, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + while((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { + switch(opt) { + case 'h': + printUsage(); + return 0; + case 'g': + gDebugFlags = atoi(optarg); + break; + case 'l': + gEnableLoopback = atoi(optarg); + break; + case 'x': + gVaryFrameRate = atoi(optarg); + break; + case 'r': + gVaryOrientation = atoi(optarg); + break; + case 'o': + gEncoderOutputBufferCount = atoi(optarg); + if (gEncoderOutputBufferCount > ENCODER_MAX_BUFFER_COUNT) + gEncoderOutputBufferCount = ENCODER_MAX_BUFFER_COUNT; + break; + case 'y': + gEncoderOutputSliceSizeBytes = atoi(optarg); + break; + case 'm': + gEncoderOutputSliceSizeMB = atoi(optarg); + break; + case 't': + gTestcaseID = atoi(optarg); + break; + case 'n': + gFilename = atoi(optarg); + break; + case 'w': + gPreviewWidth = atoi(optarg); + break; + case 'e': + gPreviewHeight = atoi(optarg); + break; + case 'd': + gDuration = atoi(optarg); + break; + case 'b': + gEncoderBitRate = atoi(optarg); + break; + case 'i': + gMinEncoderBitRate = atoi(optarg); + break; + case 'a': + gMaxEncoderBitRate = atoi(optarg); + break; + case 'f': + gCameraFrameRate = atoi(optarg); + break; + case 'j': + gNewCameraFrameRate = atoi(optarg); + break; + case 's': + gSliceHeight = atoi(optarg); + break; + case 'c': + gCameraIndex = atoi(optarg); + break; + case 'v': + gEnableAlgo = atoi(optarg); + break; + case ':': + LOGE("\nError - Option `%c' needs a value\n\n", optopt); + return -1; + case '?': + LOGE("\nError - No such option: `%c'\n\n", optopt); + return -1; + } + } + + if (argc == 1) printUsage(); + if (gVaryFrameRate) gCameraFrameRate = 30; // Framerate must be initialized to 30. + + sprintf(gRecordFileName, "/mnt/sdcard/video_%d.264", gFilename); + LOGI("\n\nRecorded Output is stored in %s\n\n", gRecordFileName); + + system("setprop debug.vfr.enable 0"); + + TestFunctions[gTestcaseID](); + return 0; +} + diff --git a/test/VTC/VTCLoopback.h b/test/VTC/VTCLoopback.h new file mode 100644 index 0000000..ff3fe23 --- /dev/null +++ b/test/VTC/VTCLoopback.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) Texas Instruments - http://www.ti.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef VTC_LOOPBACK_H +#define VTC_LOOPBACK_H + +#include <fcntl.h> +#include <getopt.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#include <semaphore.h> +#include <pthread.h> + +#include <binder/MemoryDealer.h> +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> +#include <system/audio.h> +#include <utils/List.h> +#include <cutils/log.h> +#include <OMX_Component.h> +#include <camera/Camera.h> +#include <camera/ICamera.h> +#include <camera/ICameraClient.h> +#include <camera/ICameraService.h> +#include <media/mediaplayer.h> +#include <media/mediarecorder.h> +#include <media/stagefright/OMXClient.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MPEG4Writer.h> +#include <media/stagefright/CameraSource.h> +#include <media/stagefright/MetaData.h> + +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/ISurface.h> +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/ISurfaceComposerClient.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +#include "../../../domx/omx_core/inc/OMX_TI_Index.h" // for OMX_TI_VIDEO_PARAM_FRAMEDATACONTENTTYPE +#include "../../../domx/omx_core/inc/OMX_TI_Video.h" // for OMX_VIDEO_PARAM_DATASYNCMODETYPE +#include "../../../domx/omx_core/inc/OMX_TI_Common.h" // for OMX_TI_COMPONENT_HANDLE +#include "../../../domx/omx_core/inc/OMX_TI_IVCommon.h"// for OMX_TI_COLOR_FormatYUV420PackedSemiPlanar +#include "MessageQueue.h" + + +#define SLEEP_AFTER_STARTING_PREVIEW 2 +#define WIDTH 640 +#define HEIGHT 480 +#define BITRATE 5000000 // 5 mbps +#define INPUT_PORT 0 +#define OUTPUT_PORT 1 +#define TWO_SECOND 1000000000ll // 1000,000,000 nsec = 1sec + +#define FPS_CAMERA 0x1 +#define FPS_ENCODER 0X2 +#define FPS_DECODER 0x4 +#define DEBUG_DUMP_CAMERA_TIMESTAMP 0x8 +#define DEBUG_DUMP_ENCODER_TIMESTAMP 0x10 +#define ENCODER_EFFECTIVE_BITRATE 0x20 +#define ENCODER_NO_FILE_WRTIE 0x40 +#define INPUT_SLICE_MODE 0x80 +#define INPUT_OUTPUT_SLICE_MODE 0x100 +#define ENCODER_LATENCY 0x200 +#define DECODER_LATENCY 0x400 + +#define ENCODER_MAX_BUFFER_COUNT 10 +#define NUM_PORTS 2 + +#define LOG_FUNCTION_NAME_ENTRY LOGV("\n ENTER %s \n", __FUNCTION__); +#define LOG_FUNCTION_NAME_EXIT LOGV("\n EXIT %s \n", __FUNCTION__); + +#define INIT_OMX_STRUCT(_s_, _name_) \ + memset((_s_), 0x0, sizeof(_name_)); \ + (_s_)->nSize = sizeof(_name_); \ + (_s_)->nVersion.s.nVersionMajor = 0x1; \ + (_s_)->nVersion.s.nVersionMinor = 0x1; \ + (_s_)->nVersion.s.nRevision = 0x0; \ + (_s_)->nVersion.s.nStep = 0x0 + +using namespace android; + + + +class MyCameraClient : public BnCameraClient { +public: + virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {} + virtual void dataCallback(int32_t msgType, const sp<IMemory>& data, + camera_frame_metadata_t *metadata){} + virtual void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& data); + + MyCameraClient(); + ~MyCameraClient() {} + sp<IMemory> getCameraPayload(int64_t& frameTime); + void encoderReady() { encoder_is_ready = 1; } + void encoderNotReady() { encoder_is_ready = 0; } + void releaseBuffer(sp<IMemory> data) + { if (mReleaser != NULL) { mReleaser->releaseRecordingFrame(data); } } + void setReleaser(ICamera *releaser) { + mReleaser = releaser; + } + +private: + + void putCameraPayload(sp<IMemory> payload, int64_t frameTime); + int encoder_is_ready; + ICamera *mReleaser; + List<sp<IMemory> > cameraPayloadQueue; + List<int64_t> frameTimeQueue; + Mutex cameraPayloadQueueMutex; + Condition cameraPayloadWait; + int cameraPayloadWaitFlag; + +}; + +void dump_video_port_values(OMX_PARAM_PORTDEFINITIONTYPE& def); +const char *OMXStateName(OMX_STATETYPE state); + + +#endif + diff --git a/test/VTC/VTCTestApp.cpp b/test/VTC/VTCTestApp.cpp index b64db55..b7cb7ff 100644 --- a/test/VTC/VTCTestApp.cpp +++ b/test/VTC/VTCTestApp.cpp @@ -235,8 +235,7 @@ int getMediaserverInfo(int *PID, int *VSIZE){ return 0; } -int my_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t * mutex, int waitTimeInMilliSecs) -{ +int my_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t * mutex, int waitTimeInMilliSecs) { if (waitTimeInMilliSecs == 0) { return pthread_cond_wait(cond, mutex); @@ -245,9 +244,11 @@ int my_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t * mutex, int struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); - if (waitTimeInMilliSecs >= 1000) // > 1 sec + if (waitTimeInMilliSecs >= 1000) { // > 1 sec ts.tv_sec += (waitTimeInMilliSecs/1000); - else ts.tv_nsec += waitTimeInMilliSecs * 1000000; + } else { + ts.tv_nsec += waitTimeInMilliSecs * 1000000; + } return pthread_cond_timedwait(cond, mutex, &ts); } @@ -335,9 +336,9 @@ int createPreviewSurface() { } surfaceControl = client->createSurface(0, - cameraSurfaceWidth, - cameraSurfaceHeight, - HAL_PIXEL_FORMAT_RGB_565); + cameraSurfaceWidth, + cameraSurfaceHeight, + HAL_PIXEL_FORMAT_RGB_565); previewSurface = surfaceControl->getSurface(); @@ -552,8 +553,7 @@ void stopPreview() { destroyPreviewSurface(); } -int test_DEFAULT() -{ +int test_DEFAULT() { startPreview(); startRecording(); sleep(mDuration); @@ -562,9 +562,7 @@ int test_DEFAULT() return 0; } - -int test_InsertIDRFrames() -{ +int test_InsertIDRFrames() { status_t err = 0; mIFramesIntervalSec = 0; startPreview(); @@ -589,16 +587,14 @@ int test_InsertIDRFrames() } -int test_MaxNALSize() -{ +int test_MaxNALSize() { status_t err = 0; - if (mIsSizeInBytes){ + if (mIsSizeInBytes) { //Testing size base on bytes CHECK(mPreviewWidth > 320); CHECK(mSliceSizeBytes >= 256); - } - else{ + } else { //Testing size base on MB CHECK(mSliceSizeMB > 6); CHECK(mSliceSizeMB < (((mPreviewWidth+15)>> 4) * ((mPreviewHeight+15)>> 4))); @@ -620,14 +616,13 @@ int test_MaxNALSize() startPreview(); startRecording(); - if (mIsSizeInBytes){ + if (mIsSizeInBytes) { sprintf(mParamValue,"video-param-nalsize-bytes=%u", mSliceSizeBytes); String8 param(mParamValue); err = recorder->setParameters(param); if (err != OK) return -1; LOGI("\n Set the Slice Size in bytes.\n"); - } - else{ + } else { sprintf(mParamValue,"video-param-nalsize-macroblocks=%u", mSliceSizeMB); String8 param(mParamValue); err = recorder->setParameters(param); @@ -651,8 +646,7 @@ int test_MaxNALSize() } -int test_ChangeBitRate() -{ +int test_ChangeBitRate() { startPreview(); startRecording(); sleep(mDuration/2); @@ -670,8 +664,7 @@ int test_ChangeBitRate() } -int test_ChangeFrameRate() -{ +int test_ChangeFrameRate() { startPreview(); startRecording(); sleep(mDuration/2); @@ -697,16 +690,14 @@ int test_ChangeFrameRate() return 0; } -int test_PlaybackAndRecord_sidebyside() -{ +int test_PlaybackAndRecord_sidebyside() { playbackComposerClient = new SurfaceComposerClient(); CHECK_EQ(playbackComposerClient->initCheck(), (status_t)OK); int panelwidth = playbackComposerClient->getDisplayWidth(0); int panelheight = playbackComposerClient->getDisplayHeight(0); LOGD("Panel WxH = %d x %d", panelwidth, panelheight); - if (panelwidth < panelheight) //Portrait Phone - { + if (panelwidth < panelheight) {//Portrait Phone LOGD("\nPortrait Device\n"); playbackSurfaceWidth = panelwidth; playbackSurfaceHeight = panelheight/2; @@ -717,9 +708,7 @@ int test_PlaybackAndRecord_sidebyside() cameraWinY = playbackSurfaceHeight; cameraSurfaceWidth = panelwidth; cameraSurfaceHeight = panelheight/2; - } - else // Landscape - { + } else {// Landscape LOGD("\n Landscape Device\n"); playbackSurfaceWidth = panelwidth/2; playbackSurfaceHeight = panelheight; @@ -755,16 +744,14 @@ int test_PlaybackAndRecord_sidebyside() } -int test_PlaybackAndRecord_PIP() -{ +int test_PlaybackAndRecord_PIP() { playbackComposerClient = new SurfaceComposerClient(); CHECK_EQ(playbackComposerClient->initCheck(), (status_t)OK); uint32_t panelwidth = playbackComposerClient->getDisplayWidth(0); uint32_t panelheight = playbackComposerClient->getDisplayHeight(0); LOGD("Panel WxH = %d x %d", panelwidth, panelheight); - if (panelwidth < panelheight) //Portrait Phone - { + if (panelwidth < panelheight) {//Portrait Phone LOGD("\nPortrait Device\n"); playbackSurfaceWidth = panelwidth; playbackSurfaceHeight = panelheight; @@ -775,9 +762,7 @@ int test_PlaybackAndRecord_PIP() cameraSurfaceHeight = panelheight/4; cameraWinX = (panelwidth - cameraSurfaceWidth) / 2; cameraWinY = 0; - } - else // Landscape - { + } else { // Landscape LOGD("\n Landscape Device\n"); playbackSurfaceWidth = panelwidth; playbackSurfaceHeight = panelheight; @@ -816,8 +801,7 @@ int test_PlaybackAndRecord_PIP() client->openGlobalTransaction(); surfaceControl->hide(); client->closeGlobalTransaction(); - } - else{ + } else { client->openGlobalTransaction(); surfaceControl->show(); client->closeGlobalTransaction(); @@ -843,8 +827,7 @@ int test_PlaybackOnly() int panelwidth = playbackComposerClient->getDisplayWidth(0); int panelheight = playbackComposerClient->getDisplayHeight(0); LOGD("Panel WxH = %d x %d", panelwidth, panelheight); - if (panelwidth < panelheight) //Portrait Phone - { + if (panelwidth < panelheight) {//Portrait Phone LOGD("\nPortrait Device\n"); playbackSurfaceWidth = panelwidth; playbackSurfaceHeight = panelheight/2; @@ -855,9 +838,7 @@ int test_PlaybackOnly() cameraWinY = playbackSurfaceHeight; cameraSurfaceWidth = panelwidth; cameraSurfaceHeight = panelheight/2; - } - else // Landscape - { + } else {// Landscape LOGD("\n Landscape Device\n"); playbackSurfaceWidth = panelwidth; playbackSurfaceHeight = panelheight; @@ -924,8 +905,7 @@ void updatePassRate(int test_status, bool verifyRecordedClip) { mStartMemory = endMemory; //I shouldn't be doing this right?? } -int test_Robust() -{ +int test_Robust() { int status = 0; uint32_t cyclesCompleted = 0; getMediaserverInfo(&mMediaServerPID, &mStartMemory); @@ -1375,8 +1355,7 @@ int test_ALL() } -void printUsage() -{ +void printUsage() { printf("\n\nApplication for testing VTC requirements"); printf("\nUsage: /system/bin/VTCTestApp test_case_id"); printf("\n\n\nTest Case ID can be any of the following"); @@ -1412,8 +1391,7 @@ void printUsage() } -int main (int argc, char* argv[]) -{ +int main (int argc, char* argv[]) { sp<ProcessState> proc(ProcessState::self()); ProcessState::self()->startThreadPool(); pthread_mutex_init(&mMutex, NULL); |