From 374bd92aa9f7216967aec8233b2cf7e490debd6b Mon Sep 17 00:00:00 2001 From: Stricted Date: Tue, 1 May 2018 03:55:08 +0200 Subject: [PATCH] add mediatek color format patch Change-Id: Ia47347ff3b265c2625290760cdcd28ece50d246c --- ...01-Add-MediaTek-color-format-support.patch | 5504 +++++++++++++++++ patches/patch.sh | 13 + 2 files changed, 5517 insertions(+) create mode 100644 patches/frameworks/av/0001-Add-MediaTek-color-format-support.patch create mode 100644 patches/patch.sh diff --git a/patches/frameworks/av/0001-Add-MediaTek-color-format-support.patch b/patches/frameworks/av/0001-Add-MediaTek-color-format-support.patch new file mode 100644 index 0000000..98ec88c --- /dev/null +++ b/patches/frameworks/av/0001-Add-MediaTek-color-format-support.patch @@ -0,0 +1,5504 @@ +From 28ce6b9bf98afd40d242520c43d8c48e149c5d25 Mon Sep 17 00:00:00 2001 +From: adi766 +Date: Sat, 23 Sep 2017 16:10:43 +0000 +Subject: [PATCH] Add MediaTek color format support + +Signed-off-by: teja98 +--- + include/media/stagefright/ACodec.h | 3 + + include/media/stagefright/ColorConverter.h | 4 + + .../media/stagefright/dpframework/DpBlitStream.h | 243 ++ + include/media/stagefright/dpframework/DpDataType.h | 450 ++ + media/libstagefright/Android.mk | 11 + + media/libstagefright/CameraSource.cpp | 8 + + media/libstagefright/OMXCodec.cpp | 4549 ++++++++++++++++++++ + media/libstagefright/colorconversion/Android.mk | 7 + + .../colorconversion/ColorConverter.cpp | 101 + + 9 files changed, 5376 insertions(+) + create mode 100644 include/media/stagefright/dpframework/DpBlitStream.h + create mode 100644 include/media/stagefright/dpframework/DpDataType.h + create mode 100644 media/libstagefright/OMXCodec.cpp + +diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h +index d68263272..10a3905ef 100644 +--- a/include/media/stagefright/ACodec.h ++++ b/include/media/stagefright/ACodec.h +@@ -351,6 +351,9 @@ protected: + #ifdef USE_SAMSUNG_COLORFORMAT + void setNativeWindowColorFormat(OMX_COLOR_FORMATTYPE &eNativeColorFormat); + #endif ++#ifdef MTK_HARDWARE ++ void setHalWindowColorFormat(OMX_COLOR_FORMATTYPE &eHalColorFormat); ++#endif + status_t cancelBufferToNativeWindow(BufferInfo *info); + status_t freeOutputBuffersNotOwnedByComponent(); + BufferInfo *dequeueBufferFromNativeWindow(); +diff --git a/include/media/stagefright/ColorConverter.h b/include/media/stagefright/ColorConverter.h +index 270c80966..cf0cda5af 100644 +--- a/include/media/stagefright/ColorConverter.h ++++ b/include/media/stagefright/ColorConverter.h +@@ -82,6 +82,10 @@ private: + status_t convertTIYUV420PackedSemiPlanar( + const BitmapParams &src, const BitmapParams &dst); + ++#ifdef MTK_HARDWARE ++ status_t convertYUVToRGBHW(const BitmapParams &src, const BitmapParams &dst); ++#endif ++ + ColorConverter(const ColorConverter &); + ColorConverter &operator=(const ColorConverter &); + }; +diff --git a/include/media/stagefright/dpframework/DpBlitStream.h b/include/media/stagefright/dpframework/DpBlitStream.h +new file mode 100644 +index 000000000..57fd482d6 +--- /dev/null ++++ b/include/media/stagefright/dpframework/DpBlitStream.h +@@ -0,0 +1,243 @@ ++#ifndef __DP_BLIT_STREAM_H__ ++#define __DP_BLIT_STREAM_H__ ++ ++#include "DpDataType.h" ++ ++enum ++{ ++ DP_BLIT_GENERAL_USER = 0, ++ DP_BLIT_HWC0 = 0, ++ DP_BLIT_GPU = 1, ++ DP_BLIT_HWC1 = 1, ++ DP_BLIT_HWC2 = 2, ++ DP_BLIT_HWC3 = 3 ++}; ++ ++ ++class DpBlitStream ++{ ++public: ++ static bool queryHWSupport(uint32_t srcWidth, ++ uint32_t srcHeight, ++ uint32_t dstWidth, ++ uint32_t dstHeight, ++ int32_t Orientation = 0); ++ ++ DpBlitStream(); ++ ++ ~DpBlitStream(); ++ ++ enum DpOrientation ++ { ++ ROT_0 = 0x00000000, ++ FLIP_H = 0x00000001, ++ FLIP_V = 0x00000002, ++ ROT_90 = 0x00000004, ++ ROT_180 = FLIP_H|FLIP_V, ++ ROT_270 = ROT_180|ROT_90, ++ ROT_INVALID = 0x80 ++ }; ++ ++ DP_STATUS_ENUM setSrcBuffer(void *pVABase, ++ uint32_t size); ++ ++ DP_STATUS_ENUM setSrcBuffer(void **pVAList, ++ uint32_t *pSizeList, ++ uint32_t planeNumber); ++ ++ // VA + MVA address interface ++ DP_STATUS_ENUM setSrcBuffer(void** pVAddrList, ++ void** pMVAddrList, ++ uint32_t *pSizeList, ++ uint32_t planeNumber); ++ ++ // for ION file descriptor ++ DP_STATUS_ENUM setSrcBuffer(int32_t fileDesc, ++ uint32_t *sizeList, ++ uint32_t planeNumber); ++ ++ DP_STATUS_ENUM setSrcConfig(int32_t width, ++ int32_t height, ++ DpColorFormat format, ++ DpInterlaceFormat field = eInterlace_None, ++ DpRect *pROI = 0); ++ ++ DP_STATUS_ENUM setSrcConfig(int32_t width, ++ int32_t height, ++ int32_t yPitch, ++ int32_t uvPitch, ++ DpColorFormat format, ++ DP_PROFILE_ENUM profile = DP_PROFILE_BT601, ++ DpInterlaceFormat field = eInterlace_None, ++ DpRect *pROI = 0, ++ DpSecure secure = DP_SECURE_NONE, ++ bool doFlush = true); ++ ++ DP_STATUS_ENUM setDstBuffer(void *pVABase, ++ uint32_t size); ++ ++ DP_STATUS_ENUM setDstBuffer(void **pVABaseList, ++ uint32_t *pSizeList, ++ uint32_t planeNumber); ++ ++ // VA + MVA address interface ++ DP_STATUS_ENUM setDstBuffer(void** pVABaseList, ++ void** pMVABaseList, ++ uint32_t *pSizeList, ++ uint32_t planeNumber); ++ ++ // for ION file descriptor ++ DP_STATUS_ENUM setDstBuffer(int32_t fileDesc, ++ uint32_t *pSizeList, ++ uint32_t planeNumber); ++ ++ DP_STATUS_ENUM setDstConfig(int32_t width, ++ int32_t height, ++ DpColorFormat format, ++ DpInterlaceFormat field = eInterlace_None, ++ DpRect *pROI = 0); ++ ++ DP_STATUS_ENUM setDstConfig(int32_t width, ++ int32_t height, ++ int32_t yPitch, ++ int32_t uvPitch, ++ DpColorFormat format, ++ DP_PROFILE_ENUM profile = DP_PROFILE_BT601, ++ DpInterlaceFormat field = eInterlace_None, ++ DpRect *pROI = 0, ++ DpSecure secure = DP_SECURE_NONE, ++ bool doFlush = true); ++ ++ DP_STATUS_ENUM setRotate(int32_t rotation) ++ { ++ if ((m_rotation != rotation) || ++ (mRotate != rotation)) ++ { ++ m_rotation = rotation; ++ mRotate = rotation; ++ m_frameChange = true; ++ } ++ ++ return DP_STATUS_RETURN_SUCCESS; ++ } ++ ++ //Compatible to 89 ++ DP_STATUS_ENUM setFlip(int flip) ++ { ++ if (mFlip != flip) ++ { ++ mFlip = flip; ++ m_flipStatus = ((flip!= 0)? true: false); ++ m_frameChange = true; ++ } ++ ++ return DP_STATUS_RETURN_SUCCESS; ++ } ++ ++ DP_STATUS_ENUM setOrientation(uint32_t transform); ++ ++ DP_STATUS_ENUM setTdshp(int gain) ++ { ++ if (mTdshp != gain) ++ { ++ mTdshp = gain; ++ m_frameChange = true; ++ } ++ ++ return DP_STATUS_RETURN_SUCCESS; ++ } ++ ++ uint32_t getPqID(); ++ ++ DP_STATUS_ENUM setPQParameter(const DpPqParam &pParam); ++ ++ DP_STATUS_ENUM setDither(bool enDither) ++ { ++ if (m_ditherStatus != enDither) ++ { ++ m_ditherStatus = enDither; ++ m_frameChange = true; ++ } ++ ++ return DP_STATUS_RETURN_SUCCESS; ++ } ++ ++ DP_STATUS_ENUM setAdaptiveLuma(bool enADL) ++ { ++ m_adaptiveLuma = enADL; ++ ++ return DP_STATUS_RETURN_SUCCESS; ++ } ++ ++ DP_STATUS_ENUM setUser(uint32_t eID = 0); ++ ++ ++ DP_STATUS_ENUM invalidate(); ++ ++ DP_STATUS_ENUM pq_process(); ++ ++ // for dump register ++ void enableDumpReg(unsigned int flags){mDumpRegFlags = flags;} ++ ++private: ++ DpStream *m_pStream; ++ DpChannel *m_pChannel; ++ int32_t m_channelID; ++ DpBasicBufferPool *m_pSrcPool; ++ DpBasicBufferPool *m_pDstPool; ++ int32_t m_srcBuffer; ++ int32_t m_srcWidth; ++ int32_t m_srcHeight; ++ int32_t m_srcYPitch; ++ int32_t m_srcUVPitch; ++ DpColorFormat m_srcFormat; ++ DP_PROFILE_ENUM m_srcProfile; ++ DpSecure m_srcSecure; ++ bool m_srcFlush; ++ int32_t m_dstBuffer; ++ int32_t m_dstWidth; ++ int32_t m_dstHeight; ++ int32_t m_dstYPitch; ++ int32_t m_dstUVPitch; ++ DpColorFormat m_dstFormat; ++ DP_PROFILE_ENUM m_dstProfile; ++ DpSecure m_dstSecure; ++ bool m_dstFlush; ++ DpStream *m_pPqStream; ++ DpChannel *m_pPqChannel; ++ DpAutoBufferPool *m_pPqPool; ++ int32_t m_pqBuffer; ++ int32_t m_cropXStart; ++ int32_t m_cropYStart; ++ int32_t m_cropWidth; ++ int32_t m_cropHeight; ++ int32_t m_cropSubPixelX; ++ int32_t m_cropSubPixelY; ++ int32_t m_targetXStart; ++ int32_t m_targetYStart; ++ int32_t m_rotation; ++ bool m_frameChange; ++ bool m_flipStatus; ++ bool m_ditherStatus; ++ bool m_adaptiveLuma; ++ uint32_t m_userID; ++ DpPqConfig m_PqConfig; ++ uint32_t m_PqID; ++ uint32_t m_engFlag; ++ //Compatible to 89 ++ int mRotate; ++ int mFlip; ++ int mTdshp; ++ ++ DpStream *mStream; ++ DpChannel *mChannel; ++ DpBufferPool *mSrcPool; ++ DpBufferPool *mDstPool; ++ DpPortOption *mSrcPort; ++ DpPortOption *mDstPort; ++ int mSrcBufferId; ++ int mDstBufferId; ++ unsigned int mDumpRegFlags; ++}; ++ ++#endif // __DP_BLIT_STREAM_H__ +diff --git a/include/media/stagefright/dpframework/DpDataType.h b/include/media/stagefright/dpframework/DpDataType.h +new file mode 100644 +index 000000000..d858e9727 +--- /dev/null ++++ b/include/media/stagefright/dpframework/DpDataType.h +@@ -0,0 +1,450 @@ ++#ifndef __DP_DATA_TYPE_H__ ++#define __DP_DATA_TYPE_H__ ++ ++#ifndef __KERNEL__ ++#include ++#include ++#include ++#include ++#include ++#endif ++ ++#ifndef MAX ++ #define MAX(x, y) ((x) >= (y))? (x): (y) ++#endif // MAX ++ ++#ifndef MIN ++ #define MIN(x, y) ((x) <= (y))? (x): (y) ++#endif // MIN ++ ++#ifndef __KERNEL__ ++class DpStream; ++class DpChannel; ++ ++class DpBasicBufferPool; ++class DpAutoBufferPool; ++class DpCommand; ++class DpBufferPool; ++#endif ++ ++typedef unsigned long long DpJobID; ++typedef int DpEngineType; ++ ++typedef enum DP_STATUS_ENUM ++{ ++ DP_STATUS_ABORTED_BY_USER = 4, ++ DP_STATUS_ALL_TEST_DONE = 3, ++ DP_STATUS_ALL_TPIPE_DONE = 2, ++ DP_STATUS_BUFFER_DONE = 1, ++ DP_STATUS_RETURN_SUCCESS = 0, ++ DP_STATUS_INVALID_PARAX = -1, ++ DP_STATUS_INVALID_PORT = -2, ++ DP_STATUS_INVALID_PATH = -3, ++ DP_STATUS_INVALID_FILE = -4, ++ DP_STATUS_INVALID_CHANNEL = -5, ++ DP_STATUS_INVALID_BUFFER = -6, ++ DP_STATUS_INVALID_STATE = -7, ++ DP_STATUS_INVALID_ENGINE = -8, ++ DP_STATUS_INVALID_FORMAT = -9, ++ DP_STATUS_INVALID_X_INPUT = -10, ++ DP_STATUS_INVALID_Y_INPUT = -11, ++ DP_STATUS_INVALID_X_OUTPUT = -12, ++ DP_STATUS_INVALID_Y_OUTPUT = -13, ++ DP_STATUS_INVALID_X_ALIGN = -14, ++ DP_STATUS_INVALID_Y_ALIGN = -15, ++ DP_STATUS_INVALID_WIDTH = -16, ++ DP_STATUS_INVALID_HEIGHT = -17, ++ DP_STATUS_INVALID_CROP = -18, ++ DP_STATUS_INVALID_ANGLE = -19, ++ DP_STATUS_INVALID_EVENT = -20, ++ DP_STATUS_INVALID_OPCODE = -21, ++ DP_STATUS_CAN_NOT_MERGE = -22, ++ DP_STATUS_OUT_OF_MEMORY = -23, ++ DP_STATUS_BUFFER_FULL = -24, ++ DP_STATUS_BUFFER_EMPTY = -25, ++ DP_STATUS_OPERATION_FAILED = -26, ++ DP_STATUS_OVER_MAX_BRANCH = -27, ++ DP_STATUS_OVER_MAX_ENGINE = -28, ++ DP_STATUS_OVER_MAX_BACKUP = -29, ++ DP_STATUS_SCHEDULE_ERROR = -30, ++ DP_STATUS_OVER_MAX_WIDTH = -31, ++ DP_STATUS_OVER_MAX_HEIGHT = -32, ++ DP_STATUS_LEFT_EDGE_ERROR = -33, ++ DP_STATUS_RIGHT_EDGE_ERROR = -34, ++ DP_STATUS_TOP_EDGE_ERROR = -35, ++ DP_STATUS_BOTTOM_EDGE_ERROR = -36, ++ DP_STATUS_X_LESS_THAN_LAST = -37, ++ DP_STATUS_Y_LESS_THAN_LAST = -38, ++ DP_STATUS_UNWANTED_X_CAL = -39, ++ DP_STATUS_LOSS_OVER_WIDTH = -40, ++ DP_STATUS_LOSS_OVER_HEIGHT = -41, ++ DP_STATUS_X_ALIGN_ERROR = -42, ++ DP_STATUS_Y_ALIGN_ERROR = -43, ++ DP_STATUS_X_OUT_OVERLAP = -44, ++ DP_STATUS_Y_OUT_OVERLAP = -45, ++ DP_STATUS_BACK_LE_FORWARD = -46, ++ DP_STATUS_UNKNOWN_ERROR = -47, ++} DP_STATUS_ENUM; ++ ++ ++typedef enum DP_MEMORY_ENUM ++{ ++ DP_MEMORY_VA, ++ DP_MEMORY_ION, ++ DP_MEMORY_PHY, ++ DP_MEMORY_MVA ++} DP_MEMORY_ENUM; ++ ++typedef struct DpJPEGEnc_Config_st // for JPEG port only ++{ ++ int32_t fileDesc; ++ uint32_t size; ++ uint32_t fQuality; ++ uint32_t soi_en; ++ void *memSWAddr[3]; ++} DpJPEGEnc_Config; ++ ++typedef struct DpVEnc_Config // for VENC port only ++{ ++ /* Venc Modify + */ ++ unsigned long rVencDrvHandle; ++ /* Venc Modify - */ ++ uint32_t memYUVMVAAddr[3]; ++ uint32_t memYUVMVASize[3]; ++ void *memYUVSWAddr[3]; ++ void *memOutputSWAddr[3]; ++ ++ uint32_t* pNumPABuffer; ++ uint32_t* pPABuffer; ++ uint64_t* pConfigFrameCount; ++ uint64_t* pDequeueFrameCount; ++ DpCommand* pVEncCommander; ++} DpVEnc_Config; ++ ++ ++#ifndef __KERNEL__ ++class DpRect ++{ ++public: ++ ++ enum ++ { ++ eINVALID_VALUE = -1, ++ eINITIAL_VALUE = 0 //TBD, why to set as "0"? ++ }; ++ ++ inline DpRect(void) ++ : x(eINITIAL_VALUE), sub_x(eINITIAL_VALUE), ++ y(eINITIAL_VALUE), sub_y(eINITIAL_VALUE), ++ w(eINITIAL_VALUE), h(eINITIAL_VALUE) ++ {} ++ ++ inline DpRect(int32_t in_x, int32_t in_y, int32_t in_w, int32_t in_h, ++ int32_t in_sub_x = 0, int32_t in_sub_y = 0) ++ : x(in_x), ++ sub_x(in_sub_x), ++ y(in_y), ++ sub_y(in_sub_y), ++ w(in_w), ++ h(in_h) ++ {} ++ ++ inline DpRect(const DpRect& rt) ++ : x(rt.x), ++ sub_x(rt.sub_x), ++ y(rt.y), ++ sub_y(rt.sub_y), ++ w(rt.w), ++ h(rt.h) ++ {} ++ ++ ~DpRect(void) {} ++ ++ inline DpRect& operator= (const DpRect rval) ++ { ++ x = rval.x; ++ sub_x = rval.sub_x; ++ y = rval.y; ++ sub_y = rval.sub_y; ++ w = rval.w; ++ h = rval.h; ++ return *this; ++ } ++ ++ int32_t x; ++ int32_t sub_x; ++ int32_t y; ++ int32_t sub_y; ++ int32_t w; ++ int32_t h; ++}; ++#endif ++ ++typedef enum DP_PROFILE_ENUM ++{ ++ DP_PROFILE_BT601, //Limited range ++ DP_PROFILE_BT709, ++ DP_PROFILE_JPEG, ++ DP_PROFILE_FULL_BT601 = DP_PROFILE_JPEG ++} DP_PROFILE_ENUM; ++ ++ ++typedef enum DP_STREAM_ID_ENUM ++{ ++ DP_BLITSTREAM = 0x10000000, ++ DP_FRAGSTREAM = 0x20000000, ++ DP_ISPSTREAM = 0x30000000, ++ DP_ASYNCBLITSTREAM = 0x40000000, ++ DP_UNKNOWN_STREAM = 0xF0000000, ++} DP_STREAM_ID_ENUM; ++ ++typedef enum DP_MEDIA_TYPE_ENUM ++{ ++ MEDIA_UNKNOWN, ++ MEDIA_VIDEO, ++ MEDIA_PICTURE, ++ MEDIA_ISP_PREVIEW ++} DP_MEDIA_TYPE_ENUM; ++ ++typedef struct ++{ ++ uint32_t id; ++ uint32_t timeStamp; ++ uint32_t reserved[28]; // padding and reserved ++} DpVideoParam; ++ ++typedef struct ++{ ++ bool withHist; ++ uint32_t info[20]; ++ uint32_t reserved[9]; // padding and reserved ++} DpImageParam; ++ ++struct DpPqParam { ++ bool enable; ++ DP_MEDIA_TYPE_ENUM scenario; ++ ++ union { ++ DpVideoParam video; ++ DpImageParam image; ++ } u; ++}; ++ ++struct DpPqConfig { ++ uint32_t enSharp; ++ uint32_t enDC; ++ uint32_t enColor; ++}; ++ ++ ++//FMT GROUP , 0-RGB , 1-YUV , 2-Bayer raw , 3-compressed format ++#define DP_COLORFMT_PACK(VIDEO, PLANE, COPLANE, HFACTOR, VFACTOR, BITS, GROUP ,SWAP_ENABLE, UNIQUEID) \ ++ ((VIDEO << 27) | \ ++ (PLANE << 24) | \ ++ (COPLANE << 22) | \ ++ (HFACTOR << 20) | \ ++ (VFACTOR << 18) | \ ++ (BITS << 8) | \ ++ (GROUP << 6) | \ ++ (SWAP_ENABLE << 5) | \ ++ (UNIQUEID << 0)) ++ ++#define DP_COLOR_GET_UFP_ENABLE(color) ((0x20000000 & color) >> 29) ++#define DP_COLOR_GET_INTERLACED_MODE(color) ((0x10000000 & color) >> 28) ++#define DP_COLOR_GET_BLOCK_MODE(color) ((0x08000000 & color) >> 27) ++#define DP_COLOR_GET_PLANE_COUNT(color) ((0x07000000 & color) >> 24) ++#define DP_COLOR_IS_UV_COPLANE(color) ((0x00C00000 & color) >> 22) ++#define DP_COLOR_GET_H_SUBSAMPLE(color) ((0x00300000 & color) >> 20) ++#define DP_COLOR_GET_V_SUBSAMPLE(color) ((0x000C0000 & color) >> 18) ++#define DP_COLOR_BITS_PER_PIXEL(color) ((0x0003FF00 & color) >> 8) ++#define DP_COLOR_GET_COLOR_GROUP(color) ((0x000000C0 & color) >> 6) ++#define DP_COLOR_GET_SWAP_ENABLE(color) ((0x00000020 & color) >> 5) ++#define DP_COLOR_GET_UNIQUE_ID(color) ((0x0000001F & color) >> 0) ++#define DP_COLOR_GET_HW_FORMAT(color) ((0x0000001F & color) >> 0) ++ ++typedef enum DP_COLOR_ENUM ++{ ++ DP_COLOR_UNKNOWN = 0, ++ DP_COLOR_FULLG8 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 8, 3, 0, 20), ++ DP_COLOR_FULLG10 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 10, 3, 0, 21), ++ DP_COLOR_FULLG12 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 12, 3, 0, 22), ++ DP_COLOR_FULLG14 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 14, 3, 0, 26), ++ DP_COLOR_UFO10 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 10, 3, 0, 27), ++ ++ DP_COLOR_BAYER8 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 8, 2, 0, 20), ++ DP_COLOR_BAYER10 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 10, 2, 0, 21), ++ DP_COLOR_BAYER12 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 12, 2, 0, 22), ++ ++ // Unified format ++ DP_COLOR_GREY = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 8, 1, 0, 7), ++ ++ DP_COLOR_RGB565 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 16, 0, 0, 0), ++ DP_COLOR_BGR565 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 16, 0, 1, 0), ++ DP_COLOR_RGB888 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 24, 0, 1, 1), ++ DP_COLOR_BGR888 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 24, 0, 0, 1), ++ DP_COLOR_RGBA8888 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 32, 0, 1, 2), ++ DP_COLOR_BGRA8888 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 32, 0, 0, 2), ++ DP_COLOR_ARGB8888 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 32, 0, 1, 3), ++ DP_COLOR_ABGR8888 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 32, 0, 0, 3), ++ ++ DP_COLOR_UYVY = DP_COLORFMT_PACK(0, 1, 0, 1, 0, 16, 1, 0, 4), ++ DP_COLOR_VYUY = DP_COLORFMT_PACK(0, 1, 0, 1, 0, 16, 1, 1, 4), ++ DP_COLOR_YUYV = DP_COLORFMT_PACK(0, 1, 0, 1, 0, 16, 1, 0, 5), ++ DP_COLOR_YVYU = DP_COLORFMT_PACK(0, 1, 0, 1, 0, 16, 1, 1, 5), ++ ++ DP_COLOR_I420 = DP_COLORFMT_PACK(0, 3, 0, 1, 1, 8, 1, 0, 8), ++ DP_COLOR_YV12 = DP_COLORFMT_PACK(0, 3, 0, 1, 1, 8, 1, 1, 8), ++ DP_COLOR_I422 = DP_COLORFMT_PACK(0, 3, 0, 1, 0, 8, 1, 0, 9), ++ DP_COLOR_YV16 = DP_COLORFMT_PACK(0, 3, 0, 1, 0, 8, 1, 1, 9), ++ DP_COLOR_I444 = DP_COLORFMT_PACK(0, 3, 0, 0, 0, 8, 1, 0, 10), ++ DP_COLOR_YV24 = DP_COLORFMT_PACK(0, 3, 0, 0, 0, 8, 1, 1, 10), ++ ++ DP_COLOR_NV12 = DP_COLORFMT_PACK(0, 2, 1, 1, 1, 8, 1, 0, 12), ++ DP_COLOR_NV21 = DP_COLORFMT_PACK(0, 2, 1, 1, 1, 8, 1, 1, 12), ++ DP_COLOR_NV16 = DP_COLORFMT_PACK(0, 2, 1, 1, 0, 8, 1, 0, 13), ++ DP_COLOR_NV61 = DP_COLORFMT_PACK(0, 2, 1, 1, 0, 8, 1, 1, 13), ++ DP_COLOR_NV24 = DP_COLORFMT_PACK(0, 2, 1, 0, 0, 8, 1, 0, 14), ++ DP_COLOR_NV42 = DP_COLORFMT_PACK(0, 2, 1, 0, 0, 8, 1, 1, 14), ++ ++ // Mediatek proprietary format ++ DP_COLOR_420_BLKP_UFO = DP_COLORFMT_PACK(5, 2, 1, 1, 1, 256, 1, 0, 12),//Frame mode + Block mode ++ DP_COLOR_420_BLKP = DP_COLORFMT_PACK(1, 2, 1, 1, 1, 256, 1, 0, 12),//Frame mode + Block mode ++ DP_COLOR_420_BLKI = DP_COLORFMT_PACK(3, 2, 1, 1, 1, 256, 1, 0, 12),//Field mode + Block mode ++ DP_COLOR_422_BLKP = DP_COLORFMT_PACK(1, 1, 0, 1, 0, 512, 1, 0, 4), //Frame mode ++ ++ DP_COLOR_PARGB8888 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 32, 0, 0, 26), ++ DP_COLOR_XARGB8888 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 32, 0, 0, 27), ++ DP_COLOR_PABGR8888 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 32, 0, 0, 28), ++ DP_COLOR_XABGR8888 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 32, 0, 0, 29), ++ ++ DP_COLOR_IYU2 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 24, 1, 0, 25), ++ DP_COLOR_YUV444 = DP_COLORFMT_PACK(0, 1, 0, 0, 0, 24, 1, 0, 30), ++// DP_COLOR_YUV422I = DP_COLORFMT_PACK(1, 0, 1, 0, 16, 1, 41),//Dup to DP_COLOR_YUYV ++// DP_COLOR_Y800 = DP_COLORFMT_PACK(1, 0, 1, 0, 8, 1, 42),//Dup to DP_COLOR_GREY ++// DP_COLOR_COMPACT_RAW1 = DP_COLORFMT_PACK(1, 0, 1, 0, 10, 2, 43),//Dup to Bayer10 ++// DP_COLOR_420_3P_YVU = DP_COLORFMT_PACK(3, 0, 1, 1, 8, 1, 44),//Dup to DP_COLOR_YV12 ++} DP_COLOR_ENUM; ++ ++// Legacy for 6589 compatible ++typedef DP_COLOR_ENUM DpColorFormat; ++ ++#define eYUV_420_3P DP_COLOR_I420 ++#define eYUV_420_2P_YUYV DP_COLOR_YUYV ++#define eYUV_420_2P_UYVY DP_COLOR_UYVY ++#define eYUV_420_2P_YVYU DP_COLOR_YVYU ++#define eYUV_420_2P_VYUY DP_COLOR_VYUY ++#define eYUV_420_2P_ISP_BLK DP_COLOR_420_BLKP ++#define eYUV_420_2P_VDO_BLK DP_COLOR_420_BLKI ++#define eYUV_422_3P DP_COLOR_I422 ++#define eYUV_422_2P DP_COLOR_NV16 ++#define eYUV_422_I DP_COLOR_YUYV ++#define eYUV_422_I_BLK DP_COLOR_422_BLKP ++#define eYUV_444_3P DP_COLOR_I444 ++#define eYUV_444_2P DP_COLOR_NV24 ++#define eYUV_444_1P DP_COLOR_YUV444 ++#define eBAYER8 DP_COLOR_BAYER8 ++#define eBAYER10 DP_COLOR_BAYER10 ++#define eBAYER12 DP_COLOR_BAYER12 ++#define eRGB565 DP_COLOR_RGB565 ++#define eBGR565 DP_COLOR_BGR565 ++#define eRGB888 DP_COLOR_RGB888 ++#define eBGR888 DP_COLOR_BGR888 ++#define eARGB8888 DP_COLOR_ARGB8888 ++#define eABGR8888 DP_COLOR_ABGR8888 ++#define DP_COLOR_XRGB8888 DP_COLOR_ARGB8888 ++#define DP_COLOR_XBGR8888 DP_COLOR_ABGR8888 ++#define eRGBA8888 DP_COLOR_RGBA8888 ++#define eBGRA8888 DP_COLOR_BGRA8888 ++#define eXRGB8888 DP_COLOR_XRGB8888 ++#define eXBGR8888 DP_COLOR_XBGR8888 ++#define DP_COLOR_RGBX8888 DP_COLOR_RGBA8888 ++#define DP_COLOR_BGRX8888 DP_COLOR_BGRA8888 ++#define eRGBX8888 DP_COLOR_RGBX8888 ++#define eBGRX8888 DP_COLOR_BGRX8888 ++#define ePARGB8888 DP_COLOR_PARGB8888 ++#define eXARGB8888 DP_COLOR_XARGB8888 ++#define ePABGR8888 DP_COLOR_PABGR8888 ++#define eXABGR8888 DP_COLOR_XABGR8888 ++#define eGREY DP_COLOR_GREY ++#define eI420 DP_COLOR_I420 ++#define eYV12 DP_COLOR_YV12 ++#define eIYU2 DP_COLOR_IYU2 ++ ++ ++#define eYV21 DP_COLOR_I420 ++#define eNV12_BLK DP_COLOR_420_BLKP ++#define eNV12_BLK_FCM DP_COLOR_420_BLKI ++#define eYUV_420_3P_YVU DP_COLOR_YV12 ++ ++#define eNV12_BP DP_COLOR_420_BLKP ++#define eNV12_BI DP_COLOR_420_BLKI ++#define eNV12 DP_COLOR_NV12 ++#define eNV21 DP_COLOR_NV21 ++#define eI422 DP_COLOR_I422 ++#define eYV16 DP_COLOR_YV16 ++#define eNV16 DP_COLOR_NV16 ++#define eNV61 DP_COLOR_NV61 ++#define eUYVY DP_COLOR_UYVY ++#define eVYUY DP_COLOR_VYUY ++#define eYUYV DP_COLOR_YUYV ++#define eYVYU DP_COLOR_YVYU ++#define eUYVY_BP DP_COLOR_422_BLKP ++#define eI444 DP_COLOR_I444 ++#define eNV24 DP_COLOR_NV24 ++#define eNV42 DP_COLOR_NV42 ++#define DP_COLOR_YUY2 DP_COLOR_YUYV ++#define eYUY2 DP_COLOR_YUY2 ++#define eY800 DP_COLOR_GREY ++//#define eIYU2 ++#define eMTKYUV DP_COLOR_422_BLKP ++ ++#define eCompactRaw1 DP_COLOR_BAYER10 ++ ++ ++enum DpInterlaceFormat ++{ ++ eInterlace_None, ++ eTop_Field, ++ eBottom_Field ++}; ++ ++enum DpSecure ++{ ++ DP_SECURE_NONE = 0, ++ DP_SECURE = 1, ++ DP_SECURE_SHIFT = 8 ++}; ++ ++#define MAX_NUM_READBACK_REGS (20) ++ ++#define VENC_ENABLE_FLAG (0x08967) ++ ++#ifndef __KERNEL__ ++struct DpPortOption ++{ ++ int width; ++ int height; ++ DpRect ROI; ++ DpColorFormat format; ++ DpInterlaceFormat interlace; ++ ++ enum DpPort ++ { ++ eLCD0_PORT, ++ eLCD1_PORT, ++ eHDMI_PORT, ++ eTVOUT_PORT, ++ eOVERLAY_PORT, ++ eVIRTUAL_PORT, ++ eMEMORY_PORT ++ }; ++ ++ DpPort port; ++ ++ int overlayID; // setting if choose port = eOVERLAY ++ int virtualID; // setting if choose port = eVIRTUAL_PORT ++ DpBufferPool *buffer; // setting if choose port = eMEMORY ++}; ++#endif // __KERNEL__ ++ ++#endif // __DP_DATA_TYPE_H__ +diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk +index e708f68ae..322253c0d 100644 +--- a/media/libstagefright/Android.mk ++++ b/media/libstagefright/Android.mk +@@ -202,6 +202,17 @@ LOCAL_C_INCLUDES += \ + $(TOP)/hardware/samsung/exynos4/include + endif + ++# Mediatek ++ifeq ($(strip $(BOARD_HAS_MTK_HARDWARE)),true) ++LOCAL_CFLAGS += -DMTK_HARDWARE ++ ++LOCAL_C_INCLUDES += \ ++ $(TOP)/hardware/mediatek/dpframework/inc ++ ++LOCAL_SHARED_LIBRARIES += \ ++ libdpframework ++endif ++ + LOCAL_MODULE:= libstagefright + + LOCAL_MODULE_TAGS := optional +diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp +index aa582d2d2..8f542bf60 100644 +--- a/media/libstagefright/CameraSource.cpp ++++ b/media/libstagefright/CameraSource.cpp +@@ -44,6 +44,10 @@ + #define UNUSED_UNLESS_VERBOSE(x) + #endif + ++#ifdef MTK_HARDWARE ++#define OMX_MTK_COLOR_FormatYV12 0x7F000200 ++#endif ++ + namespace android { + + static const int64_t CAMERA_SOURCE_TIMEOUT_NS = 3000000000LL; +@@ -119,7 +123,11 @@ static int32_t getColorFormat(const char* colorFormat) { + } + + if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420P)) { ++#ifdef MTK_HARDWARE ++ return OMX_MTK_COLOR_FormatYV12; ++#else + return OMX_COLOR_FormatYUV420Planar; ++#endif + } + + if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422SP)) { +diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp +new file mode 100644 +index 000000000..20c59750e +--- /dev/null ++++ b/media/libstagefright/OMXCodec.cpp +@@ -0,0 +1,4549 @@ ++/* ++ * Copyright (C) 2009 The Android Open Source Project ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++#include ++ ++//#define LOG_NDEBUG 0 ++#define LOG_TAG "OMXCodec" ++ ++#ifdef __LP64__ ++#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS ++#endif ++ ++#include ++ ++#include "include/AACEncoder.h" ++ ++#include "include/ESDS.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "include/avc_utils.h" ++ ++#ifdef USE_S3D_SUPPORT ++#include "Exynos_OMX_Def.h" ++#include "ExynosHWCService.h" ++#endif ++ ++namespace android { ++ ++// Treat time out as an error if we have not received any output ++// buffers after 3 seconds. ++const static int64_t kBufferFilledEventTimeOutNs = 3000000000LL; ++ ++// OMX Spec defines less than 50 color formats. If the query for ++// color format is executed for more than kMaxColorFormatSupported, ++// the query will fail to avoid looping forever. ++// 1000 is more than enough for us to tell whether the omx ++// component in question is buggy or not. ++const static uint32_t kMaxColorFormatSupported = 1000; ++ ++#define FACTORY_CREATE_ENCODER(name) \ ++static sp Make##name(const sp &source, const sp &meta) { \ ++ return new name(source, meta); \ ++} ++ ++#define FACTORY_REF(name) { #name, Make##name }, ++ ++FACTORY_CREATE_ENCODER(AACEncoder) ++ ++static sp InstantiateSoftwareEncoder( ++ const char *name, const sp &source, ++ const sp &meta) { ++ struct FactoryInfo { ++ const char *name; ++ sp (*CreateFunc)(const sp &, const sp &); ++ }; ++ ++ static const FactoryInfo kFactoryInfo[] = { ++ FACTORY_REF(AACEncoder) ++ }; ++ for (size_t i = 0; ++ i < sizeof(kFactoryInfo) / sizeof(kFactoryInfo[0]); i) { ++ if (!strcmp(name, kFactoryInfo[i].name)) { ++ return (*kFactoryInfo[i].CreateFunc)(source, meta); ++ } ++ } ++ ++ return NULL; ++} ++ ++#undef FACTORY_CREATE_ENCODER ++#undef FACTORY_REF ++ ++#define CODEC_LOGI(x, ...) ALOGI("[%s] " x, mComponentName, ##__VA_ARGS__) ++#define CODEC_LOGV(x, ...) ALOGV("[%s] " x, mComponentName, ##__VA_ARGS__) ++#define CODEC_LOGW(x, ...) ALOGW("[%s] " x, mComponentName, ##__VA_ARGS__) ++#define CODEC_LOGE(x, ...) ALOGE("[%s] " x, mComponentName, ##__VA_ARGS__) ++ ++struct OMXCodecObserver : public BnOMXObserver { ++ OMXCodecObserver() { ++ } ++ ++ void setCodec(const sp &target) { ++ mTarget = target; ++ } ++ ++ // from IOMXObserver ++ virtual void onMessages(const std::list &messages) { ++ sp codec = mTarget.promote(); ++ ++ if (codec.get() != NULL) { ++ Mutex::Autolock autoLock(codec->mLock); ++ for (std::list::const_iterator it = messages.cbegin(); ++ it != messages.cend(); it) { ++ codec->on_message(*it); ++ } ++ codec.clear(); ++ } ++ } ++ ++protected: ++ virtual ~OMXCodecObserver() {} ++ ++private: ++ wp mTarget; ++ ++ OMXCodecObserver(const OMXCodecObserver &); ++ OMXCodecObserver &operator=(const OMXCodecObserver &); ++}; ++ ++template ++static void InitOMXParams(T *params) { ++ COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(OMX_PTR) == 4); // check OMX_PTR is 4 bytes. ++ params->nSize = sizeof(T); ++ params->nVersion.s.nVersionMajor = 1; ++ params->nVersion.s.nVersionMinor = 0; ++ params->nVersion.s.nRevision = 0; ++ params->nVersion.s.nStep = 0; ++} ++ ++static bool IsSoftwareCodec(const char *componentName) { ++ if (!strncmp("OMX.google.", componentName, 11) ++ || !strncmp("OMX.ffmpeg.", componentName, 11)) { ++ return true; ++ } ++ ++ if (!strncmp("OMX.", componentName, 4)) { ++ return false; ++ } ++ ++ return true; ++} ++ ++// A sort order in which OMX software codecs are first, followed ++// by other (non-OMX) software codecs, followed by everything else. ++static int CompareSoftwareCodecsFirst( ++ const OMXCodec::CodecNameAndQuirks *elem1, ++ const OMXCodec::CodecNameAndQuirks *elem2) { ++ bool isOMX1 = !strncmp(elem1->mName.string(), "OMX.", 4); ++ bool isOMX2 = !strncmp(elem2->mName.string(), "OMX.", 4); ++ ++ bool isSoftwareCodec1 = IsSoftwareCodec(elem1->mName.string()); ++ bool isSoftwareCodec2 = IsSoftwareCodec(elem2->mName.string()); ++ ++ if (isSoftwareCodec1) { ++ if (!isSoftwareCodec2) { return -1; } ++ ++ if (isOMX1) { ++ if (isOMX2) { return 0; } ++ ++ return -1; ++ } else { ++ if (isOMX2) { return 0; } ++ ++ return 1; ++ } ++ ++ return -1; ++ } ++ ++ if (isSoftwareCodec2) { ++ return 1; ++ } ++ ++ return 0; ++} ++ ++// static ++void OMXCodec::findMatchingCodecs( ++ const char *mime, ++ bool createEncoder, const char *matchComponentName, ++ uint32_t flags, ++ Vector *matchingCodecs) { ++ matchingCodecs->clear(); ++ ++ const sp list = MediaCodecList::getInstance(); ++ if (list == NULL) { ++ return; ++ } ++ ++ size_t index = 0; ++ for (;;) { ++ ssize_t matchIndex = ++ list->findCodecByType(mime, createEncoder, index); ++ ++ if (matchIndex < 0) { ++ break; ++ } ++ ++ index = matchIndex 1; ++ ++ const sp info = list->getCodecInfo(matchIndex); ++ CHECK(info != NULL); ++ const char *componentName = info->getCodecName(); ++ ++ // If a specific codec is requested, skip the non-matching ones. ++ if (matchComponentName && strcmp(componentName, matchComponentName)) { ++ continue; ++ } ++ ++ // When requesting software-only codecs, only push software codecs ++ // When requesting hardware-only codecs, only push hardware codecs ++ // When there is request neither for software-only nor for ++ // hardware-only codecs, push all codecs ++ if (((flags & kSoftwareCodecsOnly) && IsSoftwareCodec(componentName)) || ++ ((flags & kHardwareCodecsOnly) && !IsSoftwareCodec(componentName)) || ++ (!(flags & (kSoftwareCodecsOnly | kHardwareCodecsOnly)))) { ++ ++ ssize_t index = matchingCodecs->add(); ++ CodecNameAndQuirks *entry = &matchingCodecs->editItemAt(index); ++ entry->mName = String8(componentName); ++ entry->mQuirks = getComponentQuirks(info); ++ ++ ALOGV("matching '%s' quirks 0x%08x", ++ entry->mName.string(), entry->mQuirks); ++ } ++ } ++ ++ if (flags & kPreferSoftwareCodecs) { ++ matchingCodecs->sort(CompareSoftwareCodecsFirst); ++ } ++} ++ ++// static ++uint32_t OMXCodec::getComponentQuirks( ++ const sp &info) { ++ uint32_t quirks = 0; ++ if (info->hasQuirk("requires-allocate-on-input-ports")) { ++ quirks |= kRequiresAllocateBufferOnInputPorts; ++ } ++ if (info->hasQuirk("requires-allocate-on-output-ports")) { ++ quirks |= kRequiresAllocateBufferOnOutputPorts; ++ } ++ if (info->hasQuirk("output-buffers-are-unreadable")) { ++ quirks |= kOutputBuffersAreUnreadable; ++ } ++ ++ return quirks; ++} ++ ++// static ++bool OMXCodec::findCodecQuirks(const char *componentName, uint32_t *quirks) { ++ const sp list = MediaCodecList::getInstance(); ++ if (list == NULL) { ++ return false; ++ } ++ ++ ssize_t index = list->findCodecByName(componentName); ++ ++ if (index < 0) { ++ return false; ++ } ++ ++ const sp info = list->getCodecInfo(index); ++ CHECK(info != NULL); ++ *quirks = getComponentQuirks(info); ++ ++ return true; ++} ++ ++// static ++sp OMXCodec::Create( ++ const sp &omx, ++ const sp &meta, bool createEncoder, ++ const sp &source, ++ const char *matchComponentName, ++ uint32_t flags, ++ const sp &nativeWindow) { ++ int32_t requiresSecureBuffers; ++ if (source->getFormat()->findInt32( ++ kKeyRequiresSecureBuffers, ++ &requiresSecureBuffers) ++ && requiresSecureBuffers) { ++ flags |= kIgnoreCodecSpecificData; ++ flags |= kUseSecureInputBuffers; ++ } ++ ++ const char *mime; ++ bool success = meta->findCString(kKeyMIMEType, &mime); ++ CHECK(success); ++ ++ Vector matchingCodecs; ++ findMatchingCodecs( ++ mime, createEncoder, matchComponentName, flags, &matchingCodecs); ++ ++ if (matchingCodecs.isEmpty()) { ++ ALOGV("No matching codecs! (mime: %s, createEncoder: %s, " ++ "matchComponentName: %s, flags: 0x%x)", ++ mime, createEncoder ? "true" : "false", matchComponentName, flags); ++ return NULL; ++ } ++ ++ sp observer = new OMXCodecObserver; ++ IOMX::node_id node = 0; ++ ++ for (size_t i = 0; i < matchingCodecs.size(); i) { ++ const char *componentNameBase = matchingCodecs[i].mName.string(); ++ uint32_t quirks = matchingCodecs[i].mQuirks; ++ const char *componentName = componentNameBase; ++ ++ AString tmp; ++ if (flags & kUseSecureInputBuffers) { ++ tmp = componentNameBase; ++ tmp.append(".secure"); ++ ++ componentName = tmp.c_str(); ++ } ++ ++ if (createEncoder) { ++ sp softwareCodec = ++ InstantiateSoftwareEncoder(componentName, source, meta); ++ ++ if (softwareCodec != NULL) { ++ ALOGV("Successfully allocated software codec '%s'", componentName); ++ ++ return softwareCodec; ++ } ++ } ++ ++ ALOGV("Attempting to allocate OMX node '%s'", componentName); ++ ++ status_t err = omx->allocateNode(componentName, observer, &node); ++ if (err == OK) { ++ ALOGV("Successfully allocated OMX node '%s'", componentName); ++ ++ sp codec = new OMXCodec( ++ omx, node, quirks, flags, ++ createEncoder, mime, componentName, ++ source, nativeWindow); ++ ++ observer->setCodec(codec); ++ ++ err = codec->configureCodec(meta); ++ if (err == OK) { ++ return codec; ++ } ++ ++ ALOGV("Failed to configure codec '%s'", componentName); ++ } ++ } ++ ++ return NULL; ++} ++ ++status_t OMXCodec::parseHEVCCodecSpecificData( ++ const void *data, size_t size, ++ unsigned *profile, unsigned *level) { ++ const uint8_t *ptr = (const uint8_t *)data; ++ ++ // verify minimum size and configurationVersion == 1. ++ if (size < 23 || ptr[0] != 1) { ++ return ERROR_MALFORMED; ++ } ++ ++ *profile = (ptr[1] & 31); ++ *level = ptr[12]; ++ ++ ptr = 22; ++ size -= 22; ++ ++ size_t numofArrays = (char)ptr[0]; ++ ptr = 1; ++ size -= 1; ++ size_t j = 0, i = 0; ++ for (i = 0; i < numofArrays; i) { ++ if (size < 3) { ++ return ERROR_MALFORMED; ++ } ++ ptr = 1; ++ size -= 1; ++ ++ // Num of nals ++ size_t numofNals = U16_AT(ptr); ++ ptr = 2; ++ size -= 2; ++ ++ for (j = 0;j < numofNals;j) { ++ if (size < 2) { ++ return ERROR_MALFORMED; ++ } ++ ++ size_t length = U16_AT(ptr); ++ ++ ptr = 2; ++ size -= 2; ++ ++ if (size < length) { ++ return ERROR_MALFORMED; ++ } ++ addCodecSpecificData(ptr, length); ++ ++ ptr = length; ++ size -= length; ++ } ++ } ++ return OK; ++} ++ ++status_t OMXCodec::parseAVCCodecSpecificData( ++ const void *data, size_t size, ++ unsigned *profile, unsigned *level) { ++ const uint8_t *ptr = (const uint8_t *)data; ++ ++ // verify minimum size and configurationVersion == 1. ++ if (size < 7 || ptr[0] != 1) { ++ return ERROR_MALFORMED; ++ } ++ ++ *profile = ptr[1]; ++ *level = ptr[3]; ++ ++ // There is decodable content out there that fails the following ++ // assertion, let's be lenient for now... ++ // CHECK((ptr[4] >> 2) == 0x3f); // reserved ++ ++ size_t lengthSize __unused = 1 (ptr[4] & 3); ++ ++ // commented out check below as H264_QVGA_500_NO_AUDIO.3gp ++ // violates it... ++ // CHECK((ptr[5] >> 5) == 7); // reserved ++ ++ size_t numSeqParameterSets = ptr[5] & 31; ++ ++ ptr = 6; ++ size -= 6; ++ ++ for (size_t i = 0; i < numSeqParameterSets; i) { ++ if (size < 2) { ++ return ERROR_MALFORMED; ++ } ++ ++ size_t length = U16_AT(ptr); ++ ++ ptr = 2; ++ size -= 2; ++ ++ if (size < length) { ++ return ERROR_MALFORMED; ++ } ++ ++ addCodecSpecificData(ptr, length); ++ ++ ptr = length; ++ size -= length; ++ } ++ ++ if (size < 1) { ++ return ERROR_MALFORMED; ++ } ++ ++ size_t numPictureParameterSets = *ptr; ++ ptr; ++ --size; ++ ++ for (size_t i = 0; i < numPictureParameterSets; i) { ++ if (size < 2) { ++ return ERROR_MALFORMED; ++ } ++ ++ size_t length = U16_AT(ptr); ++ ++ ptr = 2; ++ size -= 2; ++ ++ if (size < length) { ++ return ERROR_MALFORMED; ++ } ++ ++ addCodecSpecificData(ptr, length); ++ ++ ptr = length; ++ size -= length; ++ } ++ ++ return OK; ++} ++ ++status_t OMXCodec::configureCodec(const sp &meta) { ++ ALOGV("configureCodec protected=%d", ++ (mFlags & kEnableGrallocUsageProtected) ? 1 : 0); ++ ++ if (!(mFlags & kIgnoreCodecSpecificData)) { ++ uint32_t type; ++ const void *data; ++ size_t size; ++ if (meta->findData(kKeyESDS, &type, &data, &size)) { ++ ESDS esds((const char *)data, size); ++ CHECK_EQ(esds.InitCheck(), (status_t)OK); ++ ++ const void *codec_specific_data; ++ size_t codec_specific_data_size; ++ esds.getCodecSpecificInfo( ++ &codec_specific_data, &codec_specific_data_size); ++ ++ addCodecSpecificData( ++ codec_specific_data, codec_specific_data_size); ++ } else if (meta->findData(kKeyAVCC, &type, &data, &size)) { ++ // Parse the AVCDecoderConfigurationRecord ++ ++ unsigned profile, level; ++ status_t err; ++ if ((err = parseAVCCodecSpecificData( ++ data, size, &profile, &level)) != OK) { ++ ALOGE("Malformed AVC codec specific data."); ++ return err; ++ } ++ ++ CODEC_LOGI( ++ "AVC profile = %u (%s), level = %u", ++ profile, AVCProfileToString(profile), level); ++ } else if (meta->findData(kKeyHVCC, &type, &data, &size)) { ++ // Parse the HEVCDecoderConfigurationRecord ++ ++ unsigned profile, level; ++ status_t err; ++ if ((err = parseHEVCCodecSpecificData( ++ data, size, &profile, &level)) != OK) { ++ ALOGE("Malformed HEVC codec specific data."); ++ return err; ++ } ++ ++ CODEC_LOGI( ++ "HEVC profile = %u , level = %u", ++ profile, level); ++ } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) { ++ addCodecSpecificData(data, size); ++ ++ CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size)); ++ addCodecSpecificData(data, size); ++ } else if (meta->findData(kKeyOpusHeader, &type, &data, &size)) { ++ addCodecSpecificData(data, size); ++ ++ CHECK(meta->findData(kKeyOpusCodecDelay, &type, &data, &size)); ++ addCodecSpecificData(data, size); ++ CHECK(meta->findData(kKeyOpusSeekPreRoll, &type, &data, &size)); ++ addCodecSpecificData(data, size); ++ } ++ } ++ ++ int32_t bitRate = 0; ++ if (mIsEncoder) { ++ CHECK(meta->findInt32(kKeyBitRate, &bitRate)); ++ } ++ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mMIME)) { ++ setAMRFormat(false /* isWAMR */, bitRate); ++ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mMIME)) { ++ setAMRFormat(true /* isWAMR */, bitRate); ++ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mMIME)) { ++ int32_t numChannels, sampleRate, aacProfile; ++ CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); ++ CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); ++ ++ if (!meta->findInt32(kKeyAACProfile, &aacProfile)) { ++ aacProfile = OMX_AUDIO_AACObjectNull; ++ } ++ ++ int32_t isADTS; ++ if (!meta->findInt32(kKeyIsADTS, &isADTS)) { ++ isADTS = false; ++ } ++ ++ status_t err = setAACFormat(numChannels, sampleRate, bitRate, aacProfile, isADTS); ++ if (err != OK) { ++ CODEC_LOGE("setAACFormat() failed (err = %d)", err); ++ return err; ++ } ++ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_MPEG, mMIME)) { ++ int32_t numChannels, sampleRate; ++ if (meta->findInt32(kKeyChannelCount, &numChannels) ++ && meta->findInt32(kKeySampleRate, &sampleRate)) { ++ // Since we did not always check for these, leave them optional ++ // and have the decoder figure it all out. ++ setRawAudioFormat( ++ mIsEncoder ? kPortIndexInput : kPortIndexOutput, ++ sampleRate, ++ numChannels); ++ } ++ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AC3, mMIME)) { ++ int32_t numChannels; ++ int32_t sampleRate; ++ CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); ++ CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); ++ ++ status_t err = setAC3Format(numChannels, sampleRate); ++ if (err != OK) { ++ CODEC_LOGE("setAC3Format() failed (err = %d)", err); ++ return err; ++ } ++ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_ALAW, mMIME) ++ || !strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_MLAW, mMIME)) { ++ // These are PCM-like formats with a fixed sample rate but ++ // a variable number of channels. ++ ++ int32_t sampleRate; ++ int32_t numChannels; ++ CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); ++ if (!meta->findInt32(kKeySampleRate, &sampleRate)) { ++ sampleRate = 8000; ++ } ++ ++ setG711Format(sampleRate, numChannels); ++ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mMIME)) { ++ CHECK(!mIsEncoder); ++ ++ int32_t numChannels, sampleRate; ++ CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); ++ CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); ++ ++ setRawAudioFormat(kPortIndexInput, sampleRate, numChannels); ++ } ++ ++ if (!strncasecmp(mMIME, "video/", 6)) { ++ ++ if (mIsEncoder) { ++ setVideoInputFormat(mMIME, meta); ++ } else { ++ status_t err = setVideoOutputFormat( ++ mMIME, meta); ++ ++ if (err != OK) { ++ return err; ++ } ++ } ++ } ++ ++ int32_t maxInputSize; ++ if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) { ++ setMinBufferSize(kPortIndexInput, (OMX_U32)maxInputSize); ++ } ++ ++ initOutputFormat(meta); ++ ++ if (mNativeWindow != NULL ++ && !mIsEncoder ++ && !strncasecmp(mMIME, "video/", 6) ++ && !strncmp(mComponentName, "OMX.", 4)) { ++ status_t err = initNativeWindow(); ++ if (err != OK) { ++ return err; ++ } ++ } ++ ++ return OK; ++} ++ ++void OMXCodec::setMinBufferSize(OMX_U32 portIndex, OMX_U32 size) { ++ OMX_PARAM_PORTDEFINITIONTYPE def; ++ InitOMXParams(&def); ++ def.nPortIndex = portIndex; ++ ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ if ((portIndex == kPortIndexInput && (mQuirks & kInputBufferSizesAreBogus)) ++ || (def.nBufferSize < size)) { ++ def.nBufferSize = size; ++ } ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ err = mOMX->getParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ // Make sure the setting actually stuck. ++ if (portIndex == kPortIndexInput ++ && (mQuirks & kInputBufferSizesAreBogus)) { ++ CHECK_EQ(def.nBufferSize, size); ++ } else { ++ CHECK(def.nBufferSize >= size); ++ } ++} ++ ++status_t OMXCodec::setVideoPortFormatType( ++ OMX_U32 portIndex, ++ OMX_VIDEO_CODINGTYPE compressionFormat, ++ OMX_COLOR_FORMATTYPE colorFormat) { ++ OMX_VIDEO_PARAM_PORTFORMATTYPE format; ++ InitOMXParams(&format); ++ format.nPortIndex = portIndex; ++ format.nIndex = 0; ++ bool found = false; ++ ++ OMX_U32 index = 0; ++ for (;;) { ++ format.nIndex = index; ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamVideoPortFormat, ++ &format, sizeof(format)); ++ ++ if (err != OK) { ++ return err; ++ } ++ ++ // The following assertion is violated by TI's video decoder. ++ // CHECK_EQ(format.nIndex, index); ++ ++#if 1 ++ CODEC_LOGV("portIndex: %u, index: %u, eCompressionFormat=%d eColorFormat=%d", ++ portIndex, ++ index, format.eCompressionFormat, format.eColorFormat); ++#endif ++ ++ if (format.eCompressionFormat == compressionFormat ++ && format.eColorFormat == colorFormat) { ++ found = true; ++ break; ++ } ++ ++ index; ++ if (index >= kMaxColorFormatSupported) { ++ CODEC_LOGE("color format %d or compression format %d is not supported", ++ colorFormat, compressionFormat); ++ return UNKNOWN_ERROR; ++ } ++ } ++ ++ if (!found) { ++ return UNKNOWN_ERROR; ++ } ++ ++ CODEC_LOGV("found a match."); ++ status_t err = mOMX->setParameter( ++ mNode, OMX_IndexParamVideoPortFormat, ++ &format, sizeof(format)); ++ ++ return err; ++} ++ ++static size_t getFrameSize( ++ OMX_COLOR_FORMATTYPE colorFormat, int32_t width, int32_t height) { ++ switch (colorFormat) { ++ case OMX_COLOR_FormatYCbYCr: ++ case OMX_COLOR_FormatCbYCrY: ++ return width * height * 2; ++ ++ case OMX_COLOR_FormatYUV420Planar: ++ case OMX_COLOR_FormatYUV420SemiPlanar: ++ case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: ++ /* ++ * FIXME: For the Opaque color format, the frame size does not ++ * need to be (w*h*3)/2. It just needs to ++ * be larger than certain minimum buffer size. However, ++ * currently, this opaque foramt has been tested only on ++ * YUV420 formats. If that is changed, then we need to revisit ++ * this part in the future ++ */ ++ case OMX_COLOR_FormatAndroidOpaque: ++ return (width * height * 3) / 2; ++ ++ default: ++ CHECK(!"Should not be here. Unsupported color format."); ++ break; ++ } ++ return 0; ++} ++ ++status_t OMXCodec::findTargetColorFormat( ++ const sp& meta, OMX_COLOR_FORMATTYPE *colorFormat) { ++ ALOGV("findTargetColorFormat"); ++ CHECK(mIsEncoder); ++ ++ *colorFormat = OMX_COLOR_FormatYUV420SemiPlanar; ++ int32_t targetColorFormat; ++ if (meta->findInt32(kKeyColorFormat, &targetColorFormat)) { ++ *colorFormat = (OMX_COLOR_FORMATTYPE) targetColorFormat; ++ } ++ ++ // Check whether the target color format is supported. ++ return isColorFormatSupported(*colorFormat, kPortIndexInput); ++} ++ ++status_t OMXCodec::isColorFormatSupported( ++ OMX_COLOR_FORMATTYPE colorFormat, int portIndex) { ++ ALOGV("isColorFormatSupported: %d", static_cast(colorFormat)); ++ ++ // Enumerate all the color formats supported by ++ // the omx component to see whether the given ++ // color format is supported. ++ OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat; ++ InitOMXParams(&portFormat); ++ portFormat.nPortIndex = portIndex; ++ OMX_U32 index = 0; ++ portFormat.nIndex = index; ++ while (true) { ++ if (OMX_ErrorNone != mOMX->getParameter( ++ mNode, OMX_IndexParamVideoPortFormat, ++ &portFormat, sizeof(portFormat))) { ++ break; ++ } ++ // Make sure that omx component does not overwrite ++ // the incremented index (bug 2897413). ++ CHECK_EQ(index, portFormat.nIndex); ++ if (portFormat.eColorFormat == colorFormat) { ++ CODEC_LOGV("Found supported color format: %d", portFormat.eColorFormat); ++ return OK; // colorFormat is supported! ++ } ++ index; ++ portFormat.nIndex = index; ++ ++ if (index >= kMaxColorFormatSupported) { ++ CODEC_LOGE("More than %u color formats are supported???", index); ++ break; ++ } ++ } ++ ++ CODEC_LOGE("color format %d is not supported", colorFormat); ++ return UNKNOWN_ERROR; ++} ++ ++void OMXCodec::setVideoInputFormat( ++ const char *mime, const sp& meta) { ++ ++ int32_t width, height, frameRate, bitRate, stride, sliceHeight; ++ bool success = meta->findInt32(kKeyWidth, &width); ++ success = success && meta->findInt32(kKeyHeight, &height); ++ success = success && meta->findInt32(kKeyFrameRate, &frameRate); ++ success = success && meta->findInt32(kKeyBitRate, &bitRate); ++ success = success && meta->findInt32(kKeyStride, &stride); ++ success = success && meta->findInt32(kKeySliceHeight, &sliceHeight); ++ CHECK(success); ++ CHECK(stride != 0); ++ ++ OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused; ++ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { ++ compressionFormat = OMX_VIDEO_CodingAVC; ++ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) { ++ compressionFormat = OMX_VIDEO_CodingHEVC; ++ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) || ++ !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4_DP, mime)) { ++ compressionFormat = OMX_VIDEO_CodingMPEG4; ++ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { ++ compressionFormat = OMX_VIDEO_CodingH263; ++ } else { ++ ALOGE("Not a supported video mime type: %s", mime); ++ CHECK(!"Should not be here. Not a supported video mime type."); ++ } ++ ++ OMX_COLOR_FORMATTYPE colorFormat; ++ CHECK_EQ((status_t)OK, findTargetColorFormat(meta, &colorFormat)); ++ ++ status_t err; ++ OMX_PARAM_PORTDEFINITIONTYPE def; ++ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video; ++ ++ //////////////////////// Input port ///////////////////////// ++ CHECK_EQ(setVideoPortFormatType( ++ kPortIndexInput, OMX_VIDEO_CodingUnused, ++ colorFormat), (status_t)OK); ++ ++ InitOMXParams(&def); ++ def.nPortIndex = kPortIndexInput; ++ ++ err = mOMX->getParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ def.nBufferSize = getFrameSize(colorFormat, ++ stride > 0? stride: -stride, sliceHeight); ++ ++ CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo); ++ ++ video_def->nFrameWidth = width; ++ video_def->nFrameHeight = height; ++ video_def->nStride = stride; ++ video_def->nSliceHeight = sliceHeight; ++ video_def->xFramerate = (frameRate << 16); // Q16 format ++ video_def->eCompressionFormat = OMX_VIDEO_CodingUnused; ++ video_def->eColorFormat = colorFormat; ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ //////////////////////// Output port ///////////////////////// ++ CHECK_EQ(setVideoPortFormatType( ++ kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused), ++ (status_t)OK); ++ InitOMXParams(&def); ++ def.nPortIndex = kPortIndexOutput; ++ ++ err = mOMX->getParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ ++ CHECK_EQ(err, (status_t)OK); ++ CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo); ++ ++ video_def->nFrameWidth = width; ++ video_def->nFrameHeight = height; ++ video_def->xFramerate = 0; // No need for output port ++ video_def->nBitrate = bitRate; // Q16 format ++ video_def->eCompressionFormat = compressionFormat; ++ video_def->eColorFormat = OMX_COLOR_FormatUnused; ++ if (mQuirks & kRequiresLargerEncoderOutputBuffer) { ++ // Increases the output buffer size ++ def.nBufferSize = ((def.nBufferSize * 3) >> 1); ++ } ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ /////////////////// Codec-specific //////////////////////// ++ switch (compressionFormat) { ++ case OMX_VIDEO_CodingMPEG4: ++ { ++ CHECK_EQ(setupMPEG4EncoderParameters(meta), (status_t)OK); ++ break; ++ } ++ ++ case OMX_VIDEO_CodingH263: ++ CHECK_EQ(setupH263EncoderParameters(meta), (status_t)OK); ++ break; ++ ++ case OMX_VIDEO_CodingAVC: ++ { ++ CHECK_EQ(setupAVCEncoderParameters(meta), (status_t)OK); ++ break; ++ } ++ ++ default: ++ CHECK(!"Support for this compressionFormat to be implemented."); ++ break; ++ } ++} ++ ++static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) { ++ if (iFramesInterval < 0) { ++ return 0xFFFFFFFF; ++ } else if (iFramesInterval == 0) { ++ return 0; ++ } ++ OMX_U32 ret = frameRate * iFramesInterval - 1; ++ return ret; ++} ++ ++status_t OMXCodec::setupErrorCorrectionParameters() { ++ OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType; ++ InitOMXParams(&errorCorrectionType); ++ errorCorrectionType.nPortIndex = kPortIndexOutput; ++ ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamVideoErrorCorrection, ++ &errorCorrectionType, sizeof(errorCorrectionType)); ++ if (err != OK) { ++ ALOGW("Error correction param query is not supported"); ++ return OK; // Optional feature. Ignore this failure ++ } ++ ++ errorCorrectionType.bEnableHEC = OMX_FALSE; ++ errorCorrectionType.bEnableResync = OMX_TRUE; ++ errorCorrectionType.nResynchMarkerSpacing = 256; ++ errorCorrectionType.bEnableDataPartitioning = OMX_FALSE; ++ errorCorrectionType.bEnableRVLC = OMX_FALSE; ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamVideoErrorCorrection, ++ &errorCorrectionType, sizeof(errorCorrectionType)); ++ if (err != OK) { ++ ALOGW("Error correction param configuration is not supported"); ++ } ++ ++ // Optional feature. Ignore the failure. ++ return OK; ++} ++ ++status_t OMXCodec::setupBitRate(int32_t bitRate) { ++ OMX_VIDEO_PARAM_BITRATETYPE bitrateType; ++ InitOMXParams(&bitrateType); ++ bitrateType.nPortIndex = kPortIndexOutput; ++ ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamVideoBitrate, ++ &bitrateType, sizeof(bitrateType)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ bitrateType.eControlRate = OMX_Video_ControlRateVariable; ++ bitrateType.nTargetBitrate = bitRate; ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamVideoBitrate, ++ &bitrateType, sizeof(bitrateType)); ++ CHECK_EQ(err, (status_t)OK); ++ return OK; ++} ++ ++status_t OMXCodec::getVideoProfileLevel( ++ const sp& meta, ++ const CodecProfileLevel& defaultProfileLevel, ++ CodecProfileLevel &profileLevel) { ++ CODEC_LOGV("Default profile: %u, level #x%x", ++ defaultProfileLevel.mProfile, defaultProfileLevel.mLevel); ++ ++ // Are the default profile and level overwriten? ++ int32_t profile, level; ++ if (!meta->findInt32(kKeyVideoProfile, &profile)) { ++ profile = defaultProfileLevel.mProfile; ++ } ++ if (!meta->findInt32(kKeyVideoLevel, &level)) { ++ level = defaultProfileLevel.mLevel; ++ } ++ CODEC_LOGV("Target profile: %d, level: %d", profile, level); ++ ++ // Are the target profile and level supported by the encoder? ++ OMX_VIDEO_PARAM_PROFILELEVELTYPE param; ++ InitOMXParams(¶m); ++ param.nPortIndex = kPortIndexOutput; ++ for (param.nProfileIndex = 0;; param.nProfileIndex) { ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamVideoProfileLevelQuerySupported, ++ ¶m, sizeof(param)); ++ ++ if (err != OK) break; ++ ++ int32_t supportedProfile = static_cast(param.eProfile); ++ int32_t supportedLevel = static_cast(param.eLevel); ++ CODEC_LOGV("Supported profile: %d, level %d", ++ supportedProfile, supportedLevel); ++ ++ if (profile == supportedProfile && ++ level <= supportedLevel) { ++ // We can further check whether the level is a valid ++ // value; but we will leave that to the omx encoder component ++ // via OMX_SetParameter call. ++ profileLevel.mProfile = profile; ++ profileLevel.mLevel = level; ++ return OK; ++ } ++ } ++ ++ CODEC_LOGE("Target profile (%d) and level (%d) is not supported", ++ profile, level); ++ return BAD_VALUE; ++} ++ ++status_t OMXCodec::setupH263EncoderParameters(const sp& meta) { ++ int32_t iFramesInterval, frameRate, bitRate; ++ bool success = meta->findInt32(kKeyBitRate, &bitRate); ++ success = success && meta->findInt32(kKeyFrameRate, &frameRate); ++ success = success && meta->findInt32(kKeyIFramesInterval, &iFramesInterval); ++ CHECK(success); ++ OMX_VIDEO_PARAM_H263TYPE h263type; ++ InitOMXParams(&h263type); ++ h263type.nPortIndex = kPortIndexOutput; ++ ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ h263type.nAllowedPictureTypes = ++ OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; ++ ++ h263type.nPFrames = setPFramesSpacing(iFramesInterval, frameRate); ++ if (h263type.nPFrames == 0) { ++ h263type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; ++ } ++ h263type.nBFrames = 0; ++ ++ // Check profile and level parameters ++ CodecProfileLevel defaultProfileLevel, profileLevel; ++ defaultProfileLevel.mProfile = h263type.eProfile; ++ defaultProfileLevel.mLevel = h263type.eLevel; ++ err = getVideoProfileLevel(meta, defaultProfileLevel, profileLevel); ++ if (err != OK) return err; ++ h263type.eProfile = static_cast(profileLevel.mProfile); ++ h263type.eLevel = static_cast(profileLevel.mLevel); ++ ++ h263type.bPLUSPTYPEAllowed = OMX_FALSE; ++ h263type.bForceRoundingTypeToZero = OMX_FALSE; ++ h263type.nPictureHeaderRepetition = 0; ++ h263type.nGOBHeaderInterval = 0; ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ CHECK_EQ(setupBitRate(bitRate), (status_t)OK); ++ CHECK_EQ(setupErrorCorrectionParameters(), (status_t)OK); ++ ++ return OK; ++} ++ ++status_t OMXCodec::setupMPEG4EncoderParameters(const sp& meta) { ++ int32_t iFramesInterval, frameRate, bitRate; ++ bool success = meta->findInt32(kKeyBitRate, &bitRate); ++ success = success && meta->findInt32(kKeyFrameRate, &frameRate); ++ success = success && meta->findInt32(kKeyIFramesInterval, &iFramesInterval); ++ CHECK(success); ++ OMX_VIDEO_PARAM_MPEG4TYPE mpeg4type; ++ InitOMXParams(&mpeg4type); ++ mpeg4type.nPortIndex = kPortIndexOutput; ++ ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ mpeg4type.nSliceHeaderSpacing = 0; ++ mpeg4type.bSVH = OMX_FALSE; ++ mpeg4type.bGov = OMX_FALSE; ++ ++ mpeg4type.nAllowedPictureTypes = ++ OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; ++ ++ mpeg4type.nPFrames = setPFramesSpacing(iFramesInterval, frameRate); ++ if (mpeg4type.nPFrames == 0) { ++ mpeg4type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; ++ } ++ mpeg4type.nBFrames = 0; ++ mpeg4type.nIDCVLCThreshold = 0; ++ mpeg4type.bACPred = OMX_TRUE; ++ mpeg4type.nMaxPacketSize = 256; ++ mpeg4type.nTimeIncRes = 1000; ++ mpeg4type.nHeaderExtension = 0; ++ mpeg4type.bReversibleVLC = OMX_FALSE; ++ ++ // Check profile and level parameters ++ CodecProfileLevel defaultProfileLevel, profileLevel; ++ defaultProfileLevel.mProfile = mpeg4type.eProfile; ++ defaultProfileLevel.mLevel = mpeg4type.eLevel; ++ err = getVideoProfileLevel(meta, defaultProfileLevel, profileLevel); ++ if (err != OK) return err; ++ mpeg4type.eProfile = static_cast(profileLevel.mProfile); ++ mpeg4type.eLevel = static_cast(profileLevel.mLevel); ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ CHECK_EQ(setupBitRate(bitRate), (status_t)OK); ++ CHECK_EQ(setupErrorCorrectionParameters(), (status_t)OK); ++ ++ return OK; ++} ++ ++status_t OMXCodec::setupAVCEncoderParameters(const sp& meta) { ++ int32_t iFramesInterval, frameRate, bitRate; ++ bool success = meta->findInt32(kKeyBitRate, &bitRate); ++ success = success && meta->findInt32(kKeyFrameRate, &frameRate); ++ success = success && meta->findInt32(kKeyIFramesInterval, &iFramesInterval); ++ CHECK(success); ++ ++ OMX_VIDEO_PARAM_AVCTYPE h264type; ++ InitOMXParams(&h264type); ++ h264type.nPortIndex = kPortIndexOutput; ++ ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ h264type.nAllowedPictureTypes = ++ OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; ++ ++ // Check profile and level parameters ++ CodecProfileLevel defaultProfileLevel, profileLevel; ++ defaultProfileLevel.mProfile = h264type.eProfile; ++ defaultProfileLevel.mLevel = h264type.eLevel; ++ err = getVideoProfileLevel(meta, defaultProfileLevel, profileLevel); ++ if (err != OK) return err; ++ h264type.eProfile = static_cast(profileLevel.mProfile); ++ h264type.eLevel = static_cast(profileLevel.mLevel); ++ ++ // XXX ++ if (h264type.eProfile != OMX_VIDEO_AVCProfileBaseline) { ++ ALOGW("Use baseline profile instead of %d for AVC recording", ++ h264type.eProfile); ++ h264type.eProfile = OMX_VIDEO_AVCProfileBaseline; ++ } ++ ++ if (h264type.eProfile == OMX_VIDEO_AVCProfileBaseline) { ++ h264type.nSliceHeaderSpacing = 0; ++ h264type.bUseHadamard = OMX_TRUE; ++ h264type.nRefFrames = 1; ++ h264type.nBFrames = 0; ++ h264type.nPFrames = setPFramesSpacing(iFramesInterval, frameRate); ++ if (h264type.nPFrames == 0) { ++ h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; ++ } ++ 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; ++ } ++ ++ if (h264type.nBFrames != 0) { ++ h264type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB; ++ } ++ ++ 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)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ CHECK_EQ(setupBitRate(bitRate), (status_t)OK); ++ ++ return OK; ++} ++ ++status_t OMXCodec::setVideoOutputFormat( ++ const char *mime, const sp& meta) { ++ ++ int32_t width, height; ++ bool success = meta->findInt32(kKeyWidth, &width); ++ success = success && meta->findInt32(kKeyHeight, &height); ++ CHECK(success); ++ ++ CODEC_LOGV("setVideoOutputFormat width=%d, height=%d", width, height); ++ ++ OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused; ++ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { ++ compressionFormat = OMX_VIDEO_CodingAVC; ++ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) || ++ !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4_DP, mime)) { ++ compressionFormat = OMX_VIDEO_CodingMPEG4; ++ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) { ++ compressionFormat = OMX_VIDEO_CodingHEVC; ++ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { ++ compressionFormat = OMX_VIDEO_CodingH263; ++ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VP8, mime)) { ++ compressionFormat = OMX_VIDEO_CodingVP8; ++ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VP9, mime)) { ++ compressionFormat = OMX_VIDEO_CodingVP9; ++ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) { ++ compressionFormat = OMX_VIDEO_CodingMPEG2; ++ } else { ++ ALOGE("Not a supported video mime type: %s", mime); ++ CHECK(!"Should not be here. Not a supported video mime type."); ++ } ++ ++ status_t err = setVideoPortFormatType( ++ kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused); ++ ++ if (err != OK) { ++ return err; ++ } ++ ++#if 1 ++ { ++ OMX_VIDEO_PARAM_PORTFORMATTYPE format; ++ InitOMXParams(&format); ++ format.nPortIndex = kPortIndexOutput; ++ format.nIndex = 0; ++ ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamVideoPortFormat, ++ &format, sizeof(format)); ++ CHECK_EQ(err, (status_t)OK); ++ CHECK_EQ((int)format.eCompressionFormat, (int)OMX_VIDEO_CodingUnused); ++ ++ int32_t colorFormat; ++ if (meta->findInt32(kKeyColorFormat, &colorFormat) ++ && colorFormat != OMX_COLOR_FormatUnused ++ && colorFormat != format.eColorFormat) { ++ ++ while (OMX_ErrorNoMore != err) { ++ format.nIndex; ++ err = mOMX->getParameter( ++ mNode, OMX_IndexParamVideoPortFormat, ++ &format, sizeof(format)); ++ if (format.eColorFormat == colorFormat) { ++ break; ++ } ++ } ++ if (format.eColorFormat != colorFormat) { ++ CODEC_LOGE("Color format %d is not supported", colorFormat); ++ return ERROR_UNSUPPORTED; ++ } ++ } ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamVideoPortFormat, ++ &format, sizeof(format)); ++ ++ if (err != OK) { ++ return err; ++ } ++ } ++#endif ++ ++ OMX_PARAM_PORTDEFINITIONTYPE def; ++ InitOMXParams(&def); ++ def.nPortIndex = kPortIndexInput; ++ ++ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video; ++ ++ err = mOMX->getParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ ++ CHECK_EQ(err, (status_t)OK); ++ ++#if 1 ++ // XXX Need a (much) better heuristic to compute input buffer sizes. ++ const size_t X = 64 * 1024; ++ if (def.nBufferSize < X) { ++ def.nBufferSize = X; ++ } ++#endif ++ ++ CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo); ++ ++ video_def->nFrameWidth = width; ++ video_def->nFrameHeight = height; ++ ++ video_def->eCompressionFormat = compressionFormat; ++ video_def->eColorFormat = OMX_COLOR_FormatUnused; ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ ++ if (err != OK) { ++ return err; ++ } ++ ++ //////////////////////////////////////////////////////////////////////////// ++ ++ InitOMXParams(&def); ++ def.nPortIndex = kPortIndexOutput; ++ ++ err = mOMX->getParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++ CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainVideo); ++ ++#if 0 ++ def.nBufferSize = ++ (((width 15) & -16) * ((height 15) & -16) * 3) / 2; // YUV420 ++#endif ++ ++ video_def->nFrameWidth = width; ++ video_def->nFrameHeight = height; ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ ++ return err; ++} ++ ++OMXCodec::OMXCodec( ++ const sp &omx, IOMX::node_id node, ++ uint32_t quirks, uint32_t flags, ++ bool isEncoder, ++ const char *mime, ++ const char *componentName, ++ const sp &source, ++ const sp &nativeWindow) ++ : mOMX(omx), ++ mOMXLivesLocally(omx->livesLocally(node, getpid())), ++ mNode(node), ++ mQuirks(quirks), ++ mFlags(flags), ++ mIsEncoder(isEncoder), ++ mIsVideo(!strncasecmp("video/", mime, 6)), ++ mMIME(strdup(mime)), ++ mComponentName(strdup(componentName)), ++ mSource(source), ++ mCodecSpecificDataIndex(0), ++ mState(LOADED), ++ mInitialBufferSubmit(true), ++ mSignalledEOS(false), ++ mNoMoreOutputData(false), ++ mOutputPortSettingsHaveChanged(false), ++ mSeekTimeUs(-1), ++ mSeekMode(ReadOptions::SEEK_CLOSEST_SYNC), ++ mTargetTimeUs(-1), ++ mOutputPortSettingsChangedPending(false), ++ mSkipCutBuffer(NULL), ++ mLeftOverBuffer(NULL), ++ mPaused(false), ++ mNativeWindow( ++ (!strncmp(componentName, "OMX.google.", 11)) ++ ? NULL : nativeWindow) { ++ mPortStatus[kPortIndexInput] = ENABLED; ++ mPortStatus[kPortIndexOutput] = ENABLED; ++ ++ setComponentRole(); ++} ++ ++// static ++void OMXCodec::setComponentRole( ++ const sp &omx, IOMX::node_id node, bool isEncoder, ++ const char *mime) { ++ struct MimeToRole { ++ const char *mime; ++ const char *decoderRole; ++ const char *encoderRole; ++ }; ++ ++ static const MimeToRole kMimeToRole[] = { ++ { MEDIA_MIMETYPE_AUDIO_MPEG, ++ "audio_decoder.mp3", "audio_encoder.mp3" }, ++ { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I, ++ "audio_decoder.mp1", "audio_encoder.mp1" }, ++ { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II, ++ "audio_decoder.mp2", "audio_encoder.mp2" }, ++ { MEDIA_MIMETYPE_AUDIO_AMR_NB, ++ "audio_decoder.amrnb", "audio_encoder.amrnb" }, ++ { MEDIA_MIMETYPE_AUDIO_AMR_WB, ++ "audio_decoder.amrwb", "audio_encoder.amrwb" }, ++ { MEDIA_MIMETYPE_AUDIO_AAC, ++ "audio_decoder.aac", "audio_encoder.aac" }, ++ { MEDIA_MIMETYPE_AUDIO_VORBIS, ++ "audio_decoder.vorbis", "audio_encoder.vorbis" }, ++ { MEDIA_MIMETYPE_AUDIO_OPUS, ++ "audio_decoder.opus", "audio_encoder.opus" }, ++ { MEDIA_MIMETYPE_AUDIO_G711_MLAW, ++ "audio_decoder.g711mlaw", "audio_encoder.g711mlaw" }, ++ { MEDIA_MIMETYPE_AUDIO_G711_ALAW, ++ "audio_decoder.g711alaw", "audio_encoder.g711alaw" }, ++ { MEDIA_MIMETYPE_VIDEO_AVC, ++ "video_decoder.avc", "video_encoder.avc" }, ++ { MEDIA_MIMETYPE_VIDEO_HEVC, ++ "video_decoder.hevc", "video_encoder.hevc" }, ++ { MEDIA_MIMETYPE_VIDEO_MPEG4, ++ "video_decoder.mpeg4", "video_encoder.mpeg4" }, ++ { MEDIA_MIMETYPE_VIDEO_MPEG4_DP, ++ "video_decoder.mpeg4", NULL }, ++ { MEDIA_MIMETYPE_VIDEO_H263, ++ "video_decoder.h263", "video_encoder.h263" }, ++ { MEDIA_MIMETYPE_VIDEO_VP8, ++ "video_decoder.vp8", "video_encoder.vp8" }, ++ { MEDIA_MIMETYPE_VIDEO_VP9, ++ "video_decoder.vp9", "video_encoder.vp9" }, ++ { MEDIA_MIMETYPE_AUDIO_RAW, ++ "audio_decoder.raw", "audio_encoder.raw" }, ++ { MEDIA_MIMETYPE_AUDIO_FLAC, ++ "audio_decoder.flac", "audio_encoder.flac" }, ++ { MEDIA_MIMETYPE_AUDIO_MSGSM, ++ "audio_decoder.gsm", "audio_encoder.gsm" }, ++ { MEDIA_MIMETYPE_VIDEO_MPEG2, ++ "video_decoder.mpeg2", "video_encoder.mpeg2" }, ++ { MEDIA_MIMETYPE_AUDIO_AC3, ++ "audio_decoder.ac3", "audio_encoder.ac3" }, ++ }; ++ ++ static const size_t kNumMimeToRole = ++ sizeof(kMimeToRole) / sizeof(kMimeToRole[0]); ++ ++ size_t i; ++ for (i = 0; i < kNumMimeToRole; i) { ++ if (!strcasecmp(mime, kMimeToRole[i].mime)) { ++ break; ++ } ++ } ++ ++ if (i == kNumMimeToRole) { ++ return; ++ } ++ ++ const char *role = ++ isEncoder ? kMimeToRole[i].encoderRole ++ : kMimeToRole[i].decoderRole; ++ ++ if (role != NULL) { ++ OMX_PARAM_COMPONENTROLETYPE roleParams; ++ InitOMXParams(&roleParams); ++ ++ strncpy((char *)roleParams.cRole, ++ role, OMX_MAX_STRINGNAME_SIZE - 1); ++ ++ roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0'; ++ ++ status_t err = omx->setParameter( ++ node, OMX_IndexParamStandardComponentRole, ++ &roleParams, sizeof(roleParams)); ++ ++ if (err != OK) { ++ ALOGW("Failed to set standard component role '%s'.", role); ++ } ++ } ++} ++ ++void OMXCodec::setComponentRole() { ++ setComponentRole(mOMX, mNode, mIsEncoder, mMIME); ++} ++ ++OMXCodec::~OMXCodec() { ++ mSource.clear(); ++ ++ CHECK(mState == LOADED || mState == ERROR || mState == LOADED_TO_IDLE); ++ ++ status_t err = mOMX->freeNode(mNode); ++ CHECK_EQ(err, (status_t)OK); ++ ++ mNode = 0; ++ setState(DEAD); ++ ++ clearCodecSpecificData(); ++ ++ free(mComponentName); ++ mComponentName = NULL; ++ ++ free(mMIME); ++ mMIME = NULL; ++} ++ ++status_t OMXCodec::init() { ++ // mLock is held. ++ ++ CHECK_EQ((int)mState, (int)LOADED); ++ ++ status_t err; ++ if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) { ++ err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle); ++ CHECK_EQ(err, (status_t)OK); ++ setState(LOADED_TO_IDLE); ++ } ++ ++ err = allocateBuffers(); ++ if (err != (status_t)OK) { ++ return err; ++ } ++ ++ if (mQuirks & kRequiresLoadedToIdleAfterAllocation) { ++ err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle); ++ CHECK_EQ(err, (status_t)OK); ++ ++ setState(LOADED_TO_IDLE); ++ } ++ ++ while (mState != EXECUTING && mState != ERROR) { ++ mAsyncCompletion.wait(mLock); ++ } ++ ++ return mState == ERROR ? UNKNOWN_ERROR : OK; ++} ++ ++// static ++bool OMXCodec::isIntermediateState(State state) { ++ return state == LOADED_TO_IDLE ++ || state == IDLE_TO_EXECUTING ++ || state == EXECUTING_TO_IDLE ++ || state == PAUSING ++ || state == FLUSHING ++ || state == IDLE_TO_LOADED ++ || state == RECONFIGURING; ++} ++ ++status_t OMXCodec::allocateBuffers() { ++ status_t err = allocateBuffersOnPort(kPortIndexInput); ++ ++ if (err != OK) { ++ return err; ++ } ++ ++ return allocateBuffersOnPort(kPortIndexOutput); ++} ++ ++status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) { ++ if (mNativeWindow != NULL && portIndex == kPortIndexOutput) { ++ return allocateOutputBuffersFromNativeWindow(); ++ } ++ ++ if ((mFlags & kEnableGrallocUsageProtected) && portIndex == kPortIndexOutput) { ++ ALOGE("protected output buffers must be stent to an ANativeWindow"); ++ return PERMISSION_DENIED; ++ } ++ ++ status_t err = OK; ++ if ((mFlags & kStoreMetaDataInVideoBuffers) ++ && portIndex == kPortIndexInput) { ++ err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexInput, OMX_TRUE); ++ if (err != OK) { ++ ALOGE("Storing meta data in video buffers is not supported"); ++ return err; ++ } ++ } ++ ++ OMX_PARAM_PORTDEFINITIONTYPE def; ++ InitOMXParams(&def); ++ def.nPortIndex = portIndex; ++ ++ err = mOMX->getParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ ++ if (err != OK) { ++ return err; ++ } ++ ++ CODEC_LOGV("allocating %u buffers of size %u on %s port", ++ def.nBufferCountActual, def.nBufferSize, ++ portIndex == kPortIndexInput ? "input" : "output"); ++ ++ if (def.nBufferSize != 0 && def.nBufferCountActual > SIZE_MAX / def.nBufferSize) { ++ return BAD_VALUE; ++ } ++ size_t totalSize = def.nBufferCountActual * def.nBufferSize; ++ mDealer[portIndex] = new MemoryDealer(totalSize, "OMXCodec"); ++ ++ for (OMX_U32 i = 0; i < def.nBufferCountActual; i) { ++ sp mem = mDealer[portIndex]->allocate(def.nBufferSize); ++ if (mem == NULL || mem->pointer() == NULL) { ++ return NO_MEMORY; ++ } ++ ++ BufferInfo info; ++ info.mData = NULL; ++ info.mSize = def.nBufferSize; ++ ++ IOMX::buffer_id buffer; ++ if (portIndex == kPortIndexInput ++ && ((mQuirks & kRequiresAllocateBufferOnInputPorts) ++ || (mFlags & kUseSecureInputBuffers))) { ++ if (mOMXLivesLocally) { ++ mem.clear(); ++ ++ err = mOMX->allocateBuffer( ++ mNode, portIndex, def.nBufferSize, &buffer, ++ &info.mData); ++ } else { ++ err = mOMX->allocateBufferWithBackup( ++ mNode, portIndex, mem, &buffer, mem->size()); ++ } ++ } else if (portIndex == kPortIndexOutput ++ && (mQuirks & kRequiresAllocateBufferOnOutputPorts)) { ++ if (mOMXLivesLocally) { ++ mem.clear(); ++ ++ err = mOMX->allocateBuffer( ++ mNode, portIndex, def.nBufferSize, &buffer, ++ &info.mData); ++ } else { ++ err = mOMX->allocateBufferWithBackup( ++ mNode, portIndex, mem, &buffer, mem->size()); ++ } ++ } else { ++ err = mOMX->useBuffer(mNode, portIndex, mem, &buffer, mem->size()); ++ } ++ ++ if (err != OK) { ++ ALOGE("allocate_buffer_with_backup failed"); ++ return err; ++ } ++ ++ if (mem != NULL) { ++ info.mData = mem->pointer(); ++ } ++ ++ info.mBuffer = buffer; ++ info.mStatus = OWNED_BY_US; ++ info.mMem = mem; ++ info.mMediaBuffer = NULL; ++ ++ if (portIndex == kPortIndexOutput) { ++ // Fail deferred MediaBuffer creation until FILL_BUFFER_DONE; ++ // this legacy mode is no longer supported. ++ LOG_ALWAYS_FATAL_IF((mOMXLivesLocally ++ && (mQuirks & kRequiresAllocateBufferOnOutputPorts) ++ && (mQuirks & kDefersOutputBufferAllocation)), ++ "allocateBuffersOnPort cannot defer buffer allocation"); ++ ++ info.mMediaBuffer = new MediaBuffer(info.mData, info.mSize); ++ info.mMediaBuffer->setObserver(this); ++ } ++ ++ mPortBuffers[portIndex].push(info); ++ ++ CODEC_LOGV("allocated buffer %u on %s port", buffer, ++ portIndex == kPortIndexInput ? "input" : "output"); ++ } ++ ++ if (portIndex == kPortIndexOutput) { ++ ++ sp meta = mSource->getFormat(); ++ int32_t delay = 0; ++ if (!meta->findInt32(kKeyEncoderDelay, &delay)) { ++ delay = 0; ++ } ++ int32_t padding = 0; ++ if (!meta->findInt32(kKeyEncoderPadding, &padding)) { ++ padding = 0; ++ } ++ int32_t numchannels = 0; ++ if (delay padding) { ++ if (mOutputFormat->findInt32(kKeyChannelCount, &numchannels)) { ++ if (mSkipCutBuffer != NULL) { ++ size_t prevbuffersize = mSkipCutBuffer->size(); ++ if (prevbuffersize != 0) { ++ ALOGW("Replacing SkipCutBuffer holding %zu bytes", prevbuffersize); ++ } ++ } ++ mSkipCutBuffer = new SkipCutBuffer(delay, padding, numchannels); ++ } ++ } ++ } ++ ++ // dumpPortStatus(portIndex); ++ ++ if (portIndex == kPortIndexInput && (mFlags & kUseSecureInputBuffers)) { ++ Vector buffers; ++ for (size_t i = 0; i < def.nBufferCountActual; i) { ++ const BufferInfo &info = mPortBuffers[kPortIndexInput].itemAt(i); ++ ++ MediaBuffer *mbuf = new MediaBuffer(info.mData, info.mSize); ++ buffers.push(mbuf); ++ } ++ ++ status_t err = mSource->setBuffers(buffers); ++ ++ if (err != OK) { ++ for (size_t i = 0; i < def.nBufferCountActual; i) { ++ buffers.editItemAt(i)->release(); ++ } ++ buffers.clear(); ++ ++ CODEC_LOGE( ++ "Codec requested to use secure input buffers but " ++ "upstream source didn't support that."); ++ ++ return err; ++ } ++ } ++ ++ return OK; ++} ++ ++status_t OMXCodec::allocateOutputBuffersFromNativeWindow() { ++ // Get the number of buffers needed. ++ OMX_PARAM_PORTDEFINITIONTYPE def; ++ InitOMXParams(&def); ++ def.nPortIndex = kPortIndexOutput; ++ ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ if (err != OK) { ++ CODEC_LOGE("getParameter failed: %d", err); ++ return err; ++ } ++ ++ sp meta = mSource->getFormat(); ++ ++ int32_t rotationDegrees; ++ if (!meta->findInt32(kKeyRotation, &rotationDegrees)) { ++ rotationDegrees = 0; ++ } ++ ++ // Set up the native window. ++ OMX_U32 usage = 0; ++ err = mOMX->getGraphicBufferUsage(mNode, kPortIndexOutput, &usage); ++ if (err != 0) { ++ ALOGW("querying usage flags from OMX IL component failed: %d", err); ++ // XXX: Currently this error is logged, but not fatal. ++ usage = 0; ++ } ++ ++ if (mFlags & kEnableGrallocUsageProtected) { ++ usage |= GRALLOC_USAGE_PROTECTED; ++#ifdef GRALLOC_USAGE_PRIVATE_NONSECURE ++ if (!(mFlags & kUseSecureInputBuffers)) ++ usage |= GRALLOC_USAGE_PRIVATE_NONSECURE; ++#endif ++ } ++ ++ err = setNativeWindowSizeFormatAndUsage( ++ mNativeWindow.get(), ++ def.format.video.nFrameWidth, ++ def.format.video.nFrameHeight, ++ def.format.video.eColorFormat, ++ rotationDegrees, ++ usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP); ++ if (err != 0) { ++ return err; ++ } ++ ++ int minUndequeuedBufs = 0; ++ err = mNativeWindow->query(mNativeWindow.get(), ++ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs); ++ if (err != 0) { ++ ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)", ++ strerror(-err), -err); ++ return err; ++ } ++ // FIXME: assume that surface is controlled by app (native window ++ // returns the number for the case when surface is not controlled by app) ++ // FIXME2: This means that minUndeqeueudBufs can be 1 larger than reported ++ // For now, try to allocate 1 more buffer, but don't fail if unsuccessful ++ ++ // Use conservative allocation while also trying to reduce starvation ++ // ++ // 1. allocate at least nBufferCountMin minUndequeuedBuffers - that is the ++ // minimum needed for the consumer to be able to work ++ // 2. try to allocate two (2) additional buffers to reduce starvation from ++ // the consumer ++ // plus an extra buffer to account for incorrect minUndequeuedBufs ++ CODEC_LOGI("OMX-buffers: min=%u actual=%u undeq=%d1", ++ def.nBufferCountMin, def.nBufferCountActual, minUndequeuedBufs); ++#ifdef BOARD_CANT_REALLOCATE_OMX_BUFFERS ++ // Some devices don't like to set OMX_IndexParamPortDefinition at this ++ // point (even with an unmodified def), so skip it if possible. ++ // This check was present in KitKat. ++ if (def.nBufferCountActual < def.nBufferCountMin minUndequeuedBufs) { ++#endif ++ for (OMX_U32 extraBuffers = 2 1; /* condition inside loop */; extraBuffers--) { ++ OMX_U32 newBufferCount = ++ def.nBufferCountMin minUndequeuedBufs extraBuffers; ++ def.nBufferCountActual = newBufferCount; ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ ++ if (err == OK) { ++ minUndequeuedBufs = extraBuffers; ++ break; ++ } ++ ++ CODEC_LOGW("setting nBufferCountActual to %u failed: %d", ++ newBufferCount, err); ++ /* exit condition */ ++ if (extraBuffers == 0) { ++ return err; ++ } ++ } ++ CODEC_LOGI("OMX-buffers: min=%u actual=%u undeq=%d1", ++ def.nBufferCountMin, def.nBufferCountActual, minUndequeuedBufs); ++#ifdef BOARD_CANT_REALLOCATE_OMX_BUFFERS ++ } ++#endif ++ ++ err = native_window_set_buffer_count( ++ mNativeWindow.get(), def.nBufferCountActual); ++ if (err != 0) { ++ ALOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err), ++ -err); ++ return err; ++ } ++ ++ CODEC_LOGV("allocating %u buffers from a native window of size %u on " ++ "output port", def.nBufferCountActual, def.nBufferSize); ++ ++ // Dequeue buffers and send them to OMX ++ for (OMX_U32 i = 0; i < def.nBufferCountActual; i) { ++ ANativeWindowBuffer* buf; ++ err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); ++ if (err != 0) { ++ ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); ++ break; ++ } ++ ++ sp graphicBuffer(new GraphicBuffer(buf, false)); ++ BufferInfo info; ++ info.mData = NULL; ++ info.mSize = def.nBufferSize; ++ info.mStatus = OWNED_BY_US; ++ info.mMem = NULL; ++ info.mMediaBuffer = new MediaBuffer(graphicBuffer); ++ info.mMediaBuffer->setObserver(this); ++ mPortBuffers[kPortIndexOutput].push(info); ++ ++ IOMX::buffer_id bufferId; ++ err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer, ++ &bufferId); ++ if (err != 0) { ++ CODEC_LOGE("registering GraphicBuffer with OMX IL component " ++ "failed: %d", err); ++ break; ++ } ++ ++ mPortBuffers[kPortIndexOutput].editItemAt(i).mBuffer = bufferId; ++ ++ CODEC_LOGV("registered graphic buffer with ID %u (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 = mPortBuffers[kPortIndexOutput].size(); ++ } else { ++ // Return the last two buffers to the native window. ++ cancelStart = def.nBufferCountActual - minUndequeuedBufs; ++ cancelEnd = def.nBufferCountActual; ++ } ++ ++ for (OMX_U32 i = cancelStart; i < cancelEnd; i) { ++ BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(i); ++ cancelBufferToNativeWindow(info); ++ } ++ ++ return err; ++} ++ ++status_t OMXCodec::cancelBufferToNativeWindow(BufferInfo *info) { ++ CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US); ++ CODEC_LOGV("Calling cancelBuffer on buffer %u", info->mBuffer); ++ int err = mNativeWindow->cancelBuffer( ++ mNativeWindow.get(), info->mMediaBuffer->graphicBuffer().get(), -1); ++ if (err != 0) { ++ CODEC_LOGE("cancelBuffer failed w/ error 0x%08x", err); ++ ++ setState(ERROR); ++ return err; ++ } ++ info->mStatus = OWNED_BY_NATIVE_WINDOW; ++ return OK; ++} ++ ++OMXCodec::BufferInfo* OMXCodec::dequeueBufferFromNativeWindow() { ++ // Dequeue the next buffer from the native window. ++ ANativeWindowBuffer* buf; ++ int err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); ++ if (err != 0) { ++ CODEC_LOGE("dequeueBuffer failed w/ error 0x%08x", err); ++ ++ setState(ERROR); ++ return 0; ++ } ++ ++ // Determine which buffer we just dequeued. ++ Vector *buffers = &mPortBuffers[kPortIndexOutput]; ++ BufferInfo *bufInfo = 0; ++ for (size_t i = 0; i < buffers->size(); i) { ++ sp graphicBuffer = buffers->itemAt(i). ++ mMediaBuffer->graphicBuffer(); ++ if (graphicBuffer->handle == buf->handle) { ++ bufInfo = &buffers->editItemAt(i); ++ break; ++ } ++ } ++ ++ if (bufInfo == 0) { ++ CODEC_LOGE("dequeued unrecognized buffer: %p", buf); ++ ++ setState(ERROR); ++ return 0; ++ } ++ ++ // The native window no longer owns the buffer. ++ CHECK_EQ((int)bufInfo->mStatus, (int)OWNED_BY_NATIVE_WINDOW); ++ bufInfo->mStatus = OWNED_BY_US; ++ ++ return bufInfo; ++} ++ ++int64_t OMXCodec::getDecodingTimeUs() { ++ CHECK(mIsEncoder && mIsVideo); ++ ++ if (mDecodingTimeList.empty()) { ++ CHECK(mSignalledEOS || mNoMoreOutputData); ++ // No corresponding input frame available. ++ // This could happen when EOS is reached. ++ return 0; ++ } ++ ++ List::iterator it = mDecodingTimeList.begin(); ++ int64_t timeUs = *it; ++ mDecodingTimeList.erase(it); ++ return timeUs; ++} ++ ++void OMXCodec::on_message(const omx_message &msg) { ++ if (mState == ERROR) { ++ /* ++ * only drop EVENT messages, EBD and FBD are still ++ * processed for bookkeeping purposes ++ */ ++ if (msg.type == omx_message::EVENT) { ++ ALOGW("Dropping OMX EVENT message - we're in ERROR state."); ++ return; ++ } ++ } ++ ++ switch (msg.type) { ++ case omx_message::EVENT: ++ { ++ onEvent( ++ msg.u.event_data.event, msg.u.event_data.data1, ++ msg.u.event_data.data2); ++ ++ break; ++ } ++ ++ case omx_message::EMPTY_BUFFER_DONE: ++ { ++ IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer; ++ ++ CODEC_LOGV("EMPTY_BUFFER_DONE(buffer: %u)", buffer); ++ ++ Vector *buffers = &mPortBuffers[kPortIndexInput]; ++ size_t i = 0; ++ while (i < buffers->size() && (*buffers)[i].mBuffer != buffer) { ++ i; ++ } ++ ++ CHECK(i < buffers->size()); ++ if ((*buffers)[i].mStatus != OWNED_BY_COMPONENT) { ++ ALOGW("We already own input buffer %u, yet received " ++ "an EMPTY_BUFFER_DONE.", buffer); ++ } ++ ++ BufferInfo* info = &buffers->editItemAt(i); ++ info->mStatus = OWNED_BY_US; ++ ++ // Buffer could not be released until empty buffer done is called. ++ if (info->mMediaBuffer != NULL) { ++ info->mMediaBuffer->release(); ++ info->mMediaBuffer = NULL; ++ } ++ ++ if (mPortStatus[kPortIndexInput] == DISABLING) { ++ CODEC_LOGV("Port is disabled, freeing buffer %u", buffer); ++ ++ status_t err = freeBuffer(kPortIndexInput, i); ++ CHECK_EQ(err, (status_t)OK); ++ } else if (mState != ERROR ++ && mPortStatus[kPortIndexInput] != SHUTTING_DOWN) { ++ CHECK_EQ((int)mPortStatus[kPortIndexInput], (int)ENABLED); ++ ++ if (mFlags & kUseSecureInputBuffers) { ++ drainAnyInputBuffer(); ++ } else { ++ drainInputBuffer(&buffers->editItemAt(i)); ++ } ++ } ++ break; ++ } ++ ++ case omx_message::FILL_BUFFER_DONE: ++ { ++ IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer; ++ OMX_U32 flags = msg.u.extended_buffer_data.flags; ++ ++ CODEC_LOGV("FILL_BUFFER_DONE(buffer: %u, size: %u, flags: 0x%08x, timestamp: %lld us (%.2f secs))", ++ buffer, ++ msg.u.extended_buffer_data.range_length, ++ flags, ++ msg.u.extended_buffer_data.timestamp, ++ msg.u.extended_buffer_data.timestamp / 1E6); ++ ++ Vector *buffers = &mPortBuffers[kPortIndexOutput]; ++ size_t i = 0; ++ while (i < buffers->size() && (*buffers)[i].mBuffer != buffer) { ++ i; ++ } ++ ++ CHECK(i < buffers->size()); ++ BufferInfo *info = &buffers->editItemAt(i); ++ ++ if (info->mStatus != OWNED_BY_COMPONENT) { ++ ALOGW("We already own output buffer %u, yet received " ++ "a FILL_BUFFER_DONE.", buffer); ++ } ++ ++ info->mStatus = OWNED_BY_US; ++ ++ if (mPortStatus[kPortIndexOutput] == DISABLING) { ++ CODEC_LOGV("Port is disabled, freeing buffer %u", buffer); ++ ++ status_t err = freeBuffer(kPortIndexOutput, i); ++ CHECK_EQ(err, (status_t)OK); ++ ++#if 0 ++ } else if (mPortStatus[kPortIndexOutput] == ENABLED ++ && (flags & OMX_BUFFERFLAG_EOS)) { ++ CODEC_LOGV("No more output data."); ++ mNoMoreOutputData = true; ++ mBufferFilled.signal(); ++#endif ++ } else if (mPortStatus[kPortIndexOutput] != SHUTTING_DOWN) { ++ CHECK_EQ((int)mPortStatus[kPortIndexOutput], (int)ENABLED); ++ ++ MediaBuffer *buffer = info->mMediaBuffer; ++ bool isGraphicBuffer = buffer->graphicBuffer() != NULL; ++ ++ if (!isGraphicBuffer ++ && msg.u.extended_buffer_data.range_offset ++ msg.u.extended_buffer_data.range_length ++ > buffer->size()) { ++ CODEC_LOGE( ++ "Codec lied about its buffer size requirements, " ++ "sending a buffer larger than the originally " ++ "advertised size in FILL_BUFFER_DONE!"); ++ } ++ buffer->set_range( ++ msg.u.extended_buffer_data.range_offset, ++ msg.u.extended_buffer_data.range_length); ++ ++ buffer->meta_data()->clear(); ++ ++ buffer->meta_data()->setInt64( ++ kKeyTime, msg.u.extended_buffer_data.timestamp); ++ ++ if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_SYNCFRAME) { ++ buffer->meta_data()->setInt32(kKeyIsSyncFrame, true); ++ } ++ bool isCodecSpecific = false; ++ if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_CODECCONFIG) { ++ buffer->meta_data()->setInt32(kKeyIsCodecConfig, true); ++ isCodecSpecific = true; ++ } ++ ++ if (isGraphicBuffer || mQuirks & kOutputBuffersAreUnreadable) { ++ buffer->meta_data()->setInt32(kKeyIsUnreadable, true); ++ } ++ ++ buffer->meta_data()->setInt32( ++ kKeyBufferID, ++ msg.u.extended_buffer_data.buffer); ++ ++ if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_EOS) { ++ CODEC_LOGV("No more output data."); ++ mNoMoreOutputData = true; ++ } ++ ++ if (mIsEncoder && mIsVideo) { ++ int64_t decodingTimeUs = isCodecSpecific? 0: getDecodingTimeUs(); ++ buffer->meta_data()->setInt64(kKeyDecodingTime, decodingTimeUs); ++ } ++ ++ if (mTargetTimeUs >= 0) { ++ CHECK(msg.u.extended_buffer_data.timestamp <= mTargetTimeUs); ++ ++ if (msg.u.extended_buffer_data.timestamp < mTargetTimeUs) { ++ CODEC_LOGV( ++ "skipping output buffer at timestamp %lld us", ++ msg.u.extended_buffer_data.timestamp); ++ ++ fillOutputBuffer(info); ++ break; ++ } ++ ++ CODEC_LOGV( ++ "returning output buffer at target timestamp " ++ "%lld us", ++ msg.u.extended_buffer_data.timestamp); ++ ++ mTargetTimeUs = -1; ++ } ++ ++ mFilledBuffers.push_back(i); ++ mBufferFilled.signal(); ++ if (mIsEncoder) { ++ sched_yield(); ++ } ++ } ++ ++ break; ++ } ++ ++ default: ++ { ++ CHECK(!"should not be here."); ++ break; ++ } ++ } ++} ++ ++// Has the format changed in any way that the client would have to be aware of? ++static bool formatHasNotablyChanged( ++ const sp &from, const sp &to) { ++ if (from.get() == NULL && to.get() == NULL) { ++ return false; ++ } ++ ++ if ((from.get() == NULL && to.get() != NULL) ++ || (from.get() != NULL && to.get() == NULL)) { ++ return true; ++ } ++ ++ const char *mime_from, *mime_to; ++ CHECK(from->findCString(kKeyMIMEType, &mime_from)); ++ CHECK(to->findCString(kKeyMIMEType, &mime_to)); ++ ++ if (strcasecmp(mime_from, mime_to)) { ++ return true; ++ } ++ ++ if (!strcasecmp(mime_from, MEDIA_MIMETYPE_VIDEO_RAW)) { ++ int32_t colorFormat_from, colorFormat_to; ++ CHECK(from->findInt32(kKeyColorFormat, &colorFormat_from)); ++ CHECK(to->findInt32(kKeyColorFormat, &colorFormat_to)); ++ ++ if (colorFormat_from != colorFormat_to) { ++ return true; ++ } ++ ++ int32_t width_from, width_to; ++ CHECK(from->findInt32(kKeyWidth, &width_from)); ++ CHECK(to->findInt32(kKeyWidth, &width_to)); ++ ++ if (width_from != width_to) { ++ return true; ++ } ++ ++ int32_t height_from, height_to; ++ CHECK(from->findInt32(kKeyHeight, &height_from)); ++ CHECK(to->findInt32(kKeyHeight, &height_to)); ++ ++ if (height_from != height_to) { ++ return true; ++ } ++ ++ int32_t left_from, top_from, right_from, bottom_from; ++ CHECK(from->findRect( ++ kKeyCropRect, ++ &left_from, &top_from, &right_from, &bottom_from)); ++ ++ int32_t left_to, top_to, right_to, bottom_to; ++ CHECK(to->findRect( ++ kKeyCropRect, ++ &left_to, &top_to, &right_to, &bottom_to)); ++ ++ if (left_to != left_from || top_to != top_from ++ || right_to != right_from || bottom_to != bottom_from) { ++ return true; ++ } ++ } else if (!strcasecmp(mime_from, MEDIA_MIMETYPE_AUDIO_RAW)) { ++ int32_t numChannels_from, numChannels_to; ++ CHECK(from->findInt32(kKeyChannelCount, &numChannels_from)); ++ CHECK(to->findInt32(kKeyChannelCount, &numChannels_to)); ++ ++ if (numChannels_from != numChannels_to) { ++ return true; ++ } ++ ++ int32_t sampleRate_from, sampleRate_to; ++ CHECK(from->findInt32(kKeySampleRate, &sampleRate_from)); ++ CHECK(to->findInt32(kKeySampleRate, &sampleRate_to)); ++ ++ if (sampleRate_from != sampleRate_to) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { ++ switch (event) { ++ case OMX_EventCmdComplete: ++ { ++ onCmdComplete((OMX_COMMANDTYPE)data1, data2); ++ break; ++ } ++ ++ case OMX_EventError: ++ { ++ CODEC_LOGE("OMX_EventError(0x%08x, %u)", data1, data2); ++ ++ setState(ERROR); ++ break; ++ } ++ ++ case OMX_EventPortSettingsChanged: ++ { ++ CODEC_LOGV("OMX_EventPortSettingsChanged(port=%u, data2=0x%08x)", ++ data1, data2); ++ ++ if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) { ++ onPortSettingsChanged(data1); ++ } else if (data1 == kPortIndexOutput && ++ (data2 == OMX_IndexConfigCommonOutputCrop || ++ data2 == OMX_IndexConfigCommonScale)) { ++ ++ sp oldOutputFormat = mOutputFormat; ++ initOutputFormat(mSource->getFormat()); ++ ++ if (data2 == OMX_IndexConfigCommonOutputCrop && ++ formatHasNotablyChanged(oldOutputFormat, mOutputFormat)) { ++ mOutputPortSettingsHaveChanged = true; ++ ++ } else if (data2 == OMX_IndexConfigCommonScale) { ++ OMX_CONFIG_SCALEFACTORTYPE scale; ++ InitOMXParams(&scale); ++ scale.nPortIndex = kPortIndexOutput; ++ ++ // Change display dimension only when necessary. ++ if (OK == mOMX->getConfig( ++ mNode, ++ OMX_IndexConfigCommonScale, ++ &scale, sizeof(scale))) { ++ int32_t left, top, right, bottom; ++ CHECK(mOutputFormat->findRect(kKeyCropRect, ++ &left, &top, ++ &right, &bottom)); ++ ++ // The scale is in 16.16 format. ++ // scale 1.0 = 0x010000. When there is no ++ // need to change the display, skip it. ++ ALOGV("Get OMX_IndexConfigScale: 0x%x/0x%x", ++ scale.xWidth, scale.xHeight); ++ ++ if (scale.xWidth != 0x010000) { ++ mOutputFormat->setInt32(kKeyDisplayWidth, ++ ((right - left 1) * scale.xWidth) >> 16); ++ mOutputPortSettingsHaveChanged = true; ++ } ++ ++ if (scale.xHeight != 0x010000) { ++ mOutputFormat->setInt32(kKeyDisplayHeight, ++ ((bottom - top 1) * scale.xHeight) >> 16); ++ mOutputPortSettingsHaveChanged = true; ++ } ++ } ++ } ++ } ++ break; ++ } ++ ++#if 0 ++ case OMX_EventBufferFlag: ++ { ++ CODEC_LOGV("EVENT_BUFFER_FLAG(%ld)", data1); ++ ++ if (data1 == kPortIndexOutput) { ++ mNoMoreOutputData = true; ++ } ++ break; ++ } ++#endif ++#ifdef USE_S3D_SUPPORT ++ case (OMX_EVENTTYPE)OMX_EventS3DInformation: ++ { ++ sp sm = defaultServiceManager(); ++ sp hwc = interface_cast( ++ sm->getService(String16("Exynos.HWCService"))); ++ if (hwc != NULL) { ++ if (data1 == OMX_TRUE) { ++ int eS3DMode; ++ switch (data2) { ++ case OMX_SEC_FPARGMT_SIDE_BY_SIDE: ++ eS3DMode = S3D_SBS; ++ break; ++ case OMX_SEC_FPARGMT_TOP_BOTTOM: ++ eS3DMode = S3D_TB; ++ break; ++ case OMX_SEC_FPARGMT_CHECKERBRD_INTERL: // unsupport format at HDMI ++ case OMX_SEC_FPARGMT_COLUMN_INTERL: ++ case OMX_SEC_FPARGMT_ROW_INTERL: ++ case OMX_SEC_FPARGMT_TEMPORAL_INTERL: ++ default: ++ eS3DMode = S3D_NONE; ++ } ++ ++ hwc->setHdmiResolution(0, eS3DMode); ++ } ++ } else { ++ ALOGE("Exynos.HWCService is unavailable"); ++ } ++ break; ++ } ++#endif ++ default: ++ { ++ CODEC_LOGV("EVENT(%d, %u, %u)", event, data1, data2); ++ break; ++ } ++ } ++} ++ ++void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) { ++ switch (cmd) { ++ case OMX_CommandStateSet: ++ { ++ onStateChange((OMX_STATETYPE)data); ++ break; ++ } ++ ++ case OMX_CommandPortDisable: ++ { ++ OMX_U32 portIndex = data; ++ CODEC_LOGV("PORT_DISABLED(%u)", portIndex); ++ ++ CHECK(mState == EXECUTING || mState == RECONFIGURING); ++ CHECK_EQ((int)mPortStatus[portIndex], (int)DISABLING); ++ CHECK_EQ(mPortBuffers[portIndex].size(), 0u); ++ ++ mPortStatus[portIndex] = DISABLED; ++ ++ if (mState == RECONFIGURING) { ++ CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput); ++ ++ sp oldOutputFormat = mOutputFormat; ++ initOutputFormat(mSource->getFormat()); ++ ++ // Don't notify clients if the output port settings change ++ // wasn't of importance to them, i.e. it may be that just the ++ // number of buffers has changed and nothing else. ++ bool formatChanged = formatHasNotablyChanged(oldOutputFormat, mOutputFormat); ++ if (!mOutputPortSettingsHaveChanged) { ++ mOutputPortSettingsHaveChanged = formatChanged; ++ } ++ ++ status_t err = enablePortAsync(portIndex); ++ if (err != OK) { ++ CODEC_LOGE("enablePortAsync(%u) failed (err = %d)", portIndex, err); ++ setState(ERROR); ++ } else { ++ err = allocateBuffersOnPort(portIndex); ++ if (err != OK) { ++ CODEC_LOGE("allocateBuffersOnPort (%s) failed " ++ "(err = %d)", ++ portIndex == kPortIndexInput ++ ? "input" : "output", ++ err); ++ ++ setState(ERROR); ++ } ++ } ++ } ++ break; ++ } ++ ++ case OMX_CommandPortEnable: ++ { ++ OMX_U32 portIndex = data; ++ CODEC_LOGV("PORT_ENABLED(%u)", portIndex); ++ ++ CHECK(mState == EXECUTING || mState == RECONFIGURING); ++ CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLING); ++ ++ mPortStatus[portIndex] = ENABLED; ++ ++ if (mState == RECONFIGURING) { ++ CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput); ++ ++ setState(EXECUTING); ++ ++ fillOutputBuffers(); ++ } ++ break; ++ } ++ ++ case OMX_CommandFlush: ++ { ++ OMX_U32 portIndex = data; ++ ++ CODEC_LOGV("FLUSH_DONE(%u)", portIndex); ++ ++ CHECK_EQ((int)mPortStatus[portIndex], (int)SHUTTING_DOWN); ++ mPortStatus[portIndex] = ENABLED; ++ ++ CHECK_EQ(countBuffersWeOwn(mPortBuffers[portIndex]), ++ mPortBuffers[portIndex].size()); ++ ++ if (mSkipCutBuffer != NULL && mPortStatus[kPortIndexOutput] == ENABLED) { ++ mSkipCutBuffer->clear(); ++ } ++ ++ if (mState == RECONFIGURING) { ++ CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput); ++ ++ disablePortAsync(portIndex); ++ } else if (mState == EXECUTING_TO_IDLE) { ++ if (mPortStatus[kPortIndexInput] == ENABLED ++ && mPortStatus[kPortIndexOutput] == ENABLED) { ++ CODEC_LOGV("Finished flushing both ports, now completing " ++ "transition from EXECUTING to IDLE."); ++ ++ mPortStatus[kPortIndexInput] = SHUTTING_DOWN; ++ mPortStatus[kPortIndexOutput] = SHUTTING_DOWN; ++ ++ status_t err = ++ mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle); ++ CHECK_EQ(err, (status_t)OK); ++ } ++ } else { ++ // We're flushing both ports in preparation for seeking. ++ ++ if (mPortStatus[kPortIndexInput] == ENABLED ++ && mPortStatus[kPortIndexOutput] == ENABLED) { ++ CODEC_LOGV("Finished flushing both ports, now continuing from" ++ " seek-time."); ++ ++ // We implicitly resume pulling on our upstream source. ++ mPaused = false; ++ mNoMoreOutputData = false; ++ ++ drainInputBuffers(); ++ fillOutputBuffers(); ++ } ++ ++ if (mOutputPortSettingsChangedPending) { ++ CODEC_LOGV( ++ "Honoring deferred output port settings change."); ++ ++ mOutputPortSettingsChangedPending = false; ++ onPortSettingsChanged(kPortIndexOutput); ++ } ++ } ++ ++ break; ++ } ++ ++ default: ++ { ++ CODEC_LOGV("CMD_COMPLETE(%d, %u)", cmd, data); ++ break; ++ } ++ } ++} ++ ++void OMXCodec::onStateChange(OMX_STATETYPE newState) { ++ CODEC_LOGV("onStateChange %d", newState); ++ ++ switch (newState) { ++ case OMX_StateIdle: ++ { ++ CODEC_LOGV("Now Idle."); ++ if (mState == LOADED_TO_IDLE) { ++ status_t err = mOMX->sendCommand( ++ mNode, OMX_CommandStateSet, OMX_StateExecuting); ++ ++ CHECK_EQ(err, (status_t)OK); ++ ++ setState(IDLE_TO_EXECUTING); ++ } else { ++ CHECK_EQ((int)mState, (int)EXECUTING_TO_IDLE); ++ ++ if (countBuffersWeOwn(mPortBuffers[kPortIndexInput]) != ++ mPortBuffers[kPortIndexInput].size()) { ++ ALOGE("Codec did not return all input buffers " ++ "(received %zu / %zu)", ++ countBuffersWeOwn(mPortBuffers[kPortIndexInput]), ++ mPortBuffers[kPortIndexInput].size()); ++ TRESPASS(); ++ } ++ ++ if (countBuffersWeOwn(mPortBuffers[kPortIndexOutput]) != ++ mPortBuffers[kPortIndexOutput].size()) { ++ ALOGE("Codec did not return all output buffers " ++ "(received %zu / %zu)", ++ countBuffersWeOwn(mPortBuffers[kPortIndexOutput]), ++ mPortBuffers[kPortIndexOutput].size()); ++ TRESPASS(); ++ } ++ ++ status_t err = mOMX->sendCommand( ++ mNode, OMX_CommandStateSet, OMX_StateLoaded); ++ ++ CHECK_EQ(err, (status_t)OK); ++ ++ err = freeBuffersOnPort(kPortIndexInput); ++ CHECK_EQ(err, (status_t)OK); ++ ++ err = freeBuffersOnPort(kPortIndexOutput); ++ CHECK_EQ(err, (status_t)OK); ++ ++ mPortStatus[kPortIndexInput] = ENABLED; ++ mPortStatus[kPortIndexOutput] = ENABLED; ++ ++ if ((mFlags & kEnableGrallocUsageProtected) && ++ mNativeWindow != NULL) { ++ // We push enough 1x1 blank buffers to ensure that one of ++ // them has made it to the display. This allows the OMX ++ // component teardown to zero out any protected buffers ++ // without the risk of scanning out one of those buffers. ++ pushBlankBuffersToNativeWindow(mNativeWindow.get()); ++ } ++ ++ setState(IDLE_TO_LOADED); ++ } ++ break; ++ } ++ ++ case OMX_StateExecuting: ++ { ++ CHECK_EQ((int)mState, (int)IDLE_TO_EXECUTING); ++ ++ CODEC_LOGV("Now Executing."); ++ ++ mOutputPortSettingsChangedPending = false; ++ ++ setState(EXECUTING); ++ ++ // Buffers will be submitted to the component in the first ++ // call to OMXCodec::read as mInitialBufferSubmit is true at ++ // this point. This ensures that this on_message call returns, ++ // releases the lock and ::init can notice the state change and ++ // itself return. ++ break; ++ } ++ ++ case OMX_StateLoaded: ++ { ++ CHECK_EQ((int)mState, (int)IDLE_TO_LOADED); ++ ++ CODEC_LOGV("Now Loaded."); ++ ++ setState(LOADED); ++ break; ++ } ++ ++ case OMX_StatePause: ++ { ++ CODEC_LOGV("Now paused."); ++ CHECK_EQ((int)mState, (int)PAUSING); ++ setState(PAUSED); ++ break; ++ } ++ ++ case OMX_StateInvalid: ++ { ++ setState(ERROR); ++ break; ++ } ++ ++ default: ++ { ++ CHECK(!"should not be here."); ++ break; ++ } ++ } ++} ++ ++// static ++size_t OMXCodec::countBuffersWeOwn(const Vector &buffers) { ++ size_t n = 0; ++ for (size_t i = 0; i < buffers.size(); i) { ++ if (buffers[i].mStatus != OWNED_BY_COMPONENT) { ++ n; ++ } ++ } ++ ++ return n; ++} ++ ++status_t OMXCodec::freeBuffersOnPort( ++ OMX_U32 portIndex, bool onlyThoseWeOwn) { ++ Vector *buffers = &mPortBuffers[portIndex]; ++ ++ status_t stickyErr = OK; ++ ++ for (size_t i = buffers->size(); i > 0;) { ++ i--; ++ BufferInfo *info = &buffers->editItemAt(i); ++ ++ if (onlyThoseWeOwn && info->mStatus == OWNED_BY_COMPONENT) { ++ continue; ++ } ++ ++ CHECK(info->mStatus == OWNED_BY_US ++ || info->mStatus == OWNED_BY_NATIVE_WINDOW); ++ ++ CODEC_LOGV("freeing buffer %u on port %u", info->mBuffer, portIndex); ++ ++ status_t err = freeBuffer(portIndex, i); ++ ++ if (err != OK) { ++ stickyErr = err; ++ } ++ ++ } ++ ++ CHECK(onlyThoseWeOwn || buffers->isEmpty()); ++ ++ return stickyErr; ++} ++ ++status_t OMXCodec::freeBuffer(OMX_U32 portIndex, size_t bufIndex) { ++ Vector *buffers = &mPortBuffers[portIndex]; ++ ++ BufferInfo *info = &buffers->editItemAt(bufIndex); ++ ++ status_t err = mOMX->freeBuffer(mNode, portIndex, info->mBuffer); ++ ++ if (err == OK && info->mMediaBuffer != NULL) { ++ CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput); ++ info->mMediaBuffer->setObserver(NULL); ++ ++ // Make sure nobody but us owns this buffer at this point. ++ CHECK_EQ(info->mMediaBuffer->refcount(), 0); ++ ++ // Cancel the buffer if it belongs to an ANativeWindow. ++ sp graphicBuffer = info->mMediaBuffer->graphicBuffer(); ++ if (info->mStatus == OWNED_BY_US && graphicBuffer != 0) { ++ err = cancelBufferToNativeWindow(info); ++ } ++ ++ info->mMediaBuffer->release(); ++ info->mMediaBuffer = NULL; ++ } ++ ++ if (err == OK) { ++ buffers->removeAt(bufIndex); ++ } ++ ++ return err; ++} ++ ++void OMXCodec::onPortSettingsChanged(OMX_U32 portIndex) { ++ CODEC_LOGV("PORT_SETTINGS_CHANGED(%u)", portIndex); ++ ++ CHECK(mState == EXECUTING || mState == EXECUTING_TO_IDLE); ++ CHECK_EQ(portIndex, (OMX_U32)kPortIndexOutput); ++ CHECK(!mOutputPortSettingsChangedPending); ++ ++ if (mPortStatus[kPortIndexOutput] != ENABLED) { ++ CODEC_LOGV("Deferring output port settings change."); ++ mOutputPortSettingsChangedPending = true; ++ return; ++ } ++ ++ setState(RECONFIGURING); ++ ++ if (mQuirks & kNeedsFlushBeforeDisable) { ++ if (!flushPortAsync(portIndex)) { ++ onCmdComplete(OMX_CommandFlush, portIndex); ++ } ++ } else { ++ disablePortAsync(portIndex); ++ } ++} ++ ++bool OMXCodec::flushPortAsync(OMX_U32 portIndex) { ++ CHECK(mState == EXECUTING || mState == RECONFIGURING ++ || mState == EXECUTING_TO_IDLE || mState == FLUSHING); ++ ++ CODEC_LOGV("flushPortAsync(%u): we own %zu out of %zu buffers already.", ++ portIndex, countBuffersWeOwn(mPortBuffers[portIndex]), ++ mPortBuffers[portIndex].size()); ++ ++ CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLED); ++ mPortStatus[portIndex] = SHUTTING_DOWN; ++ ++ if ((mQuirks & kRequiresFlushCompleteEmulation) ++ && countBuffersWeOwn(mPortBuffers[portIndex]) ++ == mPortBuffers[portIndex].size()) { ++ // No flush is necessary and this component fails to send a ++ // flush-complete event in this case. ++ ++ return false; ++ } ++ ++ status_t err = ++ mOMX->sendCommand(mNode, OMX_CommandFlush, portIndex); ++ CHECK_EQ(err, (status_t)OK); ++ ++ return true; ++} ++ ++void OMXCodec::disablePortAsync(OMX_U32 portIndex) { ++ CHECK(mState == EXECUTING || mState == RECONFIGURING); ++ ++ CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLED); ++ mPortStatus[portIndex] = DISABLING; ++ ++ CODEC_LOGV("sending OMX_CommandPortDisable(%u)", portIndex); ++ status_t err = ++ mOMX->sendCommand(mNode, OMX_CommandPortDisable, portIndex); ++ CHECK_EQ(err, (status_t)OK); ++ ++ freeBuffersOnPort(portIndex, true); ++} ++ ++status_t OMXCodec::enablePortAsync(OMX_U32 portIndex) { ++ CHECK(mState == EXECUTING || mState == RECONFIGURING); ++ ++ CHECK_EQ((int)mPortStatus[portIndex], (int)DISABLED); ++ mPortStatus[portIndex] = ENABLING; ++ ++ CODEC_LOGV("sending OMX_CommandPortEnable(%u)", portIndex); ++ return mOMX->sendCommand(mNode, OMX_CommandPortEnable, portIndex); ++} ++ ++void OMXCodec::fillOutputBuffers() { ++ CHECK(mState == EXECUTING || mState == FLUSHING); ++ ++ // This is a workaround for some decoders not properly reporting ++ // end-of-output-stream. If we own all input buffers and also own ++ // all output buffers and we already signalled end-of-input-stream, ++ // the end-of-output-stream is implied. ++ if (mSignalledEOS ++ && countBuffersWeOwn(mPortBuffers[kPortIndexInput]) ++ == mPortBuffers[kPortIndexInput].size() ++ && countBuffersWeOwn(mPortBuffers[kPortIndexOutput]) ++ == mPortBuffers[kPortIndexOutput].size()) { ++ mNoMoreOutputData = true; ++ mBufferFilled.signal(); ++ ++ return; ++ } ++ ++ Vector *buffers = &mPortBuffers[kPortIndexOutput]; ++ for (size_t i = 0; i < buffers->size(); i) { ++ BufferInfo *info = &buffers->editItemAt(i); ++ if (info->mStatus == OWNED_BY_US) { ++ fillOutputBuffer(&buffers->editItemAt(i)); ++ } ++ } ++} ++ ++void OMXCodec::drainInputBuffers() { ++ CHECK(mState == EXECUTING || mState == RECONFIGURING || mState == FLUSHING); ++ ++ if (mFlags & kUseSecureInputBuffers) { ++ Vector *buffers = &mPortBuffers[kPortIndexInput]; ++ for (size_t i = 0; i < buffers->size(); i) { ++ if (!drainAnyInputBuffer() ++ || (mFlags & kOnlySubmitOneInputBufferAtOneTime)) { ++ break; ++ } ++ } ++ } else { ++ Vector *buffers = &mPortBuffers[kPortIndexInput]; ++ for (size_t i = 0; i < buffers->size(); i) { ++ BufferInfo *info = &buffers->editItemAt(i); ++ ++ if (info->mStatus != OWNED_BY_US) { ++ continue; ++ } ++ ++ if (!drainInputBuffer(info)) { ++ break; ++ } ++ ++ if (mFlags & kOnlySubmitOneInputBufferAtOneTime) { ++ break; ++ } ++ } ++ } ++} ++ ++bool OMXCodec::drainAnyInputBuffer() { ++ return drainInputBuffer((BufferInfo *)NULL); ++} ++ ++OMXCodec::BufferInfo *OMXCodec::findInputBufferByDataPointer(void *ptr) { ++ Vector *infos = &mPortBuffers[kPortIndexInput]; ++ for (size_t i = 0; i < infos->size(); i) { ++ BufferInfo *info = &infos->editItemAt(i); ++ ++ if (info->mData == ptr) { ++ CODEC_LOGV( ++ "input buffer data ptr = %p, buffer_id = %u", ++ ptr, ++ info->mBuffer); ++ ++ return info; ++ } ++ } ++ ++ TRESPASS(); ++} ++ ++OMXCodec::BufferInfo *OMXCodec::findEmptyInputBuffer() { ++ Vector *infos = &mPortBuffers[kPortIndexInput]; ++ for (size_t i = 0; i < infos->size(); i) { ++ BufferInfo *info = &infos->editItemAt(i); ++ ++ if (info->mStatus == OWNED_BY_US) { ++ return info; ++ } ++ } ++ ++ TRESPASS(); ++} ++ ++bool OMXCodec::drainInputBuffer(BufferInfo *info) { ++ if (info != NULL) { ++ CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US); ++ } ++ ++ if (mSignalledEOS) { ++ return false; ++ } ++ ++ if (mCodecSpecificDataIndex < mCodecSpecificData.size()) { ++ CHECK(!(mFlags & kUseSecureInputBuffers)); ++ ++ const CodecSpecificData *specific = ++ mCodecSpecificData[mCodecSpecificDataIndex]; ++ ++ size_t size = specific->mSize; ++ ++ if ((!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mMIME) || ++ !strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mMIME)) ++ && !(mQuirks & kWantsNALFragments)) { ++ static const uint8_t kNALStartCode[4] = ++ { 0x00, 0x00, 0x00, 0x01 }; ++ ++ CHECK(info->mSize >= specific->mSize 4); ++ ++ size = 4; ++ ++ memcpy(info->mData, kNALStartCode, 4); ++ memcpy((uint8_t *)info->mData 4, ++ specific->mData, specific->mSize); ++ } else { ++ CHECK(info->mSize >= specific->mSize); ++ memcpy(info->mData, specific->mData, specific->mSize); ++ } ++ ++ mNoMoreOutputData = false; ++ ++ CODEC_LOGV("calling emptyBuffer with codec specific data"); ++ ++ status_t err = mOMX->emptyBuffer( ++ mNode, info->mBuffer, 0, size, ++ OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG, ++ 0); ++ CHECK_EQ(err, (status_t)OK); ++ ++ info->mStatus = OWNED_BY_COMPONENT; ++ ++ mCodecSpecificDataIndex; ++ return true; ++ } ++ ++ if (mPaused) { ++ return false; ++ } ++ ++ status_t err; ++ ++ bool signalEOS = false; ++ int64_t timestampUs = 0; ++ ++ size_t offset = 0; ++ int32_t n = 0; ++ ++ ++ for (;;) { ++ MediaBuffer *srcBuffer; ++ if (mSeekTimeUs >= 0) { ++ if (mLeftOverBuffer) { ++ mLeftOverBuffer->release(); ++ mLeftOverBuffer = NULL; ++ } ++ ++ MediaSource::ReadOptions options; ++ options.setSeekTo(mSeekTimeUs, mSeekMode); ++ ++ mSeekTimeUs = -1; ++ mSeekMode = ReadOptions::SEEK_CLOSEST_SYNC; ++ mBufferFilled.signal(); ++ ++ err = mSource->read(&srcBuffer, &options); ++ ++ if (err == OK) { ++ int64_t targetTimeUs; ++ if (srcBuffer->meta_data()->findInt64( ++ kKeyTargetTime, &targetTimeUs) ++ && targetTimeUs >= 0) { ++ CODEC_LOGV("targetTimeUs = %lld us", (long long)targetTimeUs); ++ mTargetTimeUs = targetTimeUs; ++ } else { ++ mTargetTimeUs = -1; ++ } ++ } ++ } else if (mLeftOverBuffer) { ++ srcBuffer = mLeftOverBuffer; ++ mLeftOverBuffer = NULL; ++ ++ err = OK; ++ } else { ++ err = mSource->read(&srcBuffer); ++ } ++ ++ if (err != OK) { ++ signalEOS = true; ++ mFinalStatus = err; ++ mSignalledEOS = true; ++ mBufferFilled.signal(); ++ break; ++ } ++ ++ if (mFlags & kUseSecureInputBuffers) { ++ info = findInputBufferByDataPointer(srcBuffer->data()); ++ CHECK(info != NULL); ++ } ++ ++ size_t remainingBytes = info->mSize - offset; ++ ++ if (srcBuffer->range_length() > remainingBytes) { ++ if (offset == 0) { ++ CODEC_LOGE( ++ "Codec's input buffers are too small to accomodate " ++ "buffer read from source (info->mSize = %zu, srcLength = %zu)", ++ info->mSize, srcBuffer->range_length()); ++ ++ srcBuffer->release(); ++ srcBuffer = NULL; ++ ++ setState(ERROR); ++ return false; ++ } ++ ++ mLeftOverBuffer = srcBuffer; ++ break; ++ } ++ ++ bool releaseBuffer = true; ++ if (mFlags & kStoreMetaDataInVideoBuffers) { ++ releaseBuffer = false; ++ info->mMediaBuffer = srcBuffer; ++ } ++ ++ if (mFlags & kUseSecureInputBuffers) { ++ // Data in "info" is already provided at this time. ++ ++ releaseBuffer = false; ++ ++ CHECK(info->mMediaBuffer == NULL); ++ info->mMediaBuffer = srcBuffer; ++ } else { ++ CHECK(srcBuffer->data() != NULL) ; ++ memcpy((uint8_t *)info->mData offset, ++ (const uint8_t *)srcBuffer->data() ++ srcBuffer->range_offset(), ++ srcBuffer->range_length()); ++ } ++ ++ int64_t lastBufferTimeUs; ++ CHECK(srcBuffer->meta_data()->findInt64(kKeyTime, &lastBufferTimeUs)); ++ CHECK(lastBufferTimeUs >= 0); ++ if (mIsEncoder && mIsVideo) { ++ mDecodingTimeList.push_back(lastBufferTimeUs); ++ } ++ ++ if (offset == 0) { ++ timestampUs = lastBufferTimeUs; ++ } ++ ++ offset = srcBuffer->range_length(); ++ ++ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_VORBIS, mMIME)) { ++ CHECK(!(mQuirks & kSupportsMultipleFramesPerInputBuffer)); ++ CHECK_GE(info->mSize, offset sizeof(int32_t)); ++ ++ int32_t numPageSamples; ++ if (!srcBuffer->meta_data()->findInt32( ++ kKeyValidSamples, &numPageSamples)) { ++ numPageSamples = -1; ++ } ++ ++ memcpy((uint8_t *)info->mData offset, ++ &numPageSamples, ++ sizeof(numPageSamples)); ++ ++ offset = sizeof(numPageSamples); ++ } ++ ++ if (releaseBuffer) { ++ srcBuffer->release(); ++ srcBuffer = NULL; ++ } ++ ++ n; ++ ++ if (!(mQuirks & kSupportsMultipleFramesPerInputBuffer)) { ++ break; ++ } ++ ++ int64_t coalescedDurationUs = lastBufferTimeUs - timestampUs; ++ ++ if (coalescedDurationUs > 250000ll) { ++ // Don't coalesce more than 250ms worth of encoded data at once. ++ break; ++ } ++ } ++ ++ if (n > 1) { ++ ALOGV("coalesced %d frames into one input buffer", n); ++ } ++ ++ OMX_U32 flags = OMX_BUFFERFLAG_ENDOFFRAME; ++ ++ if (signalEOS) { ++ flags |= OMX_BUFFERFLAG_EOS; ++ } else { ++ mNoMoreOutputData = false; ++ } ++ ++ if (info == NULL) { ++ CHECK(mFlags & kUseSecureInputBuffers); ++ CHECK(signalEOS); ++ ++ // This is fishy, there's still a MediaBuffer corresponding to this ++ // info available to the source at this point even though we're going ++ // to use it to signal EOS to the codec. ++ info = findEmptyInputBuffer(); ++ } ++ ++ CODEC_LOGV("Calling emptyBuffer on buffer %u (length %zu), " ++ "timestamp %lld us (%.2f secs)", ++ info->mBuffer, offset, ++ (long long)timestampUs, timestampUs / 1E6); ++ ++ err = mOMX->emptyBuffer( ++ mNode, info->mBuffer, 0, offset, ++ flags, timestampUs); ++ ++ if (err != OK) { ++ setState(ERROR); ++ return false; ++ } ++ ++ info->mStatus = OWNED_BY_COMPONENT; ++ ++ return true; ++} ++ ++void OMXCodec::fillOutputBuffer(BufferInfo *info) { ++ CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US); ++ ++ if (mNoMoreOutputData) { ++ CODEC_LOGV("There is no more output data available, not " ++ "calling fillOutputBuffer"); ++ return; ++ } ++ ++ CODEC_LOGV("Calling fillBuffer on buffer %u", info->mBuffer); ++ status_t err = mOMX->fillBuffer(mNode, info->mBuffer); ++ ++ if (err != OK) { ++ CODEC_LOGE("fillBuffer failed w/ error 0x%08x", err); ++ ++ setState(ERROR); ++ return; ++ } ++ ++ info->mStatus = OWNED_BY_COMPONENT; ++} ++ ++bool OMXCodec::drainInputBuffer(IOMX::buffer_id buffer) { ++ Vector *buffers = &mPortBuffers[kPortIndexInput]; ++ for (size_t i = 0; i < buffers->size(); i) { ++ if ((*buffers)[i].mBuffer == buffer) { ++ return drainInputBuffer(&buffers->editItemAt(i)); ++ } ++ } ++ ++ CHECK(!"should not be here."); ++ ++ return false; ++} ++ ++void OMXCodec::fillOutputBuffer(IOMX::buffer_id buffer) { ++ Vector *buffers = &mPortBuffers[kPortIndexOutput]; ++ for (size_t i = 0; i < buffers->size(); i) { ++ if ((*buffers)[i].mBuffer == buffer) { ++ fillOutputBuffer(&buffers->editItemAt(i)); ++ return; ++ } ++ } ++ ++ CHECK(!"should not be here."); ++} ++ ++void OMXCodec::setState(State newState) { ++ mState = newState; ++ mAsyncCompletion.signal(); ++ ++ // This may cause some spurious wakeups but is necessary to ++ // unblock the reader if we enter ERROR state. ++ mBufferFilled.signal(); ++} ++ ++status_t OMXCodec::waitForBufferFilled_l() { ++ ++ if (mIsEncoder) { ++ // For timelapse video recording, the timelapse video recording may ++ // not send an input frame for a _long_ time. Do not use timeout ++ // for video encoding. ++ return mBufferFilled.wait(mLock); ++ } ++ status_t err = mBufferFilled.waitRelative(mLock, kBufferFilledEventTimeOutNs); ++ if (err != OK) { ++ CODEC_LOGE("Timed out waiting for output buffers: %zu/%zu", ++ countBuffersWeOwn(mPortBuffers[kPortIndexInput]), ++ countBuffersWeOwn(mPortBuffers[kPortIndexOutput])); ++ } ++ return err; ++} ++ ++void OMXCodec::setRawAudioFormat( ++ OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels) { ++ ++ // port definition ++ OMX_PARAM_PORTDEFINITIONTYPE def; ++ InitOMXParams(&def); ++ def.nPortIndex = portIndex; ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++ def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; ++ CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, ++ &def, sizeof(def)), (status_t)OK); ++ ++ // pcm param ++ OMX_AUDIO_PARAM_PCMMODETYPE pcmParams; ++ InitOMXParams(&pcmParams); ++ pcmParams.nPortIndex = portIndex; ++ ++ err = mOMX->getParameter( ++ mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams)); ++ ++ CHECK_EQ(err, (status_t)OK); ++ ++ pcmParams.nChannels = numChannels; ++ pcmParams.eNumData = OMX_NumericalDataSigned; ++ pcmParams.bInterleaved = OMX_TRUE; ++ pcmParams.nBitPerSample = 16; ++ pcmParams.nSamplingRate = sampleRate; ++ pcmParams.ePCMMode = OMX_AUDIO_PCMModeLinear; ++ ++ CHECK_EQ(getOMXChannelMapping( ++ numChannels, pcmParams.eChannelMapping), (status_t)OK); ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams)); ++ ++ CHECK_EQ(err, (status_t)OK); ++} ++ ++static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate(bool isAMRWB, int32_t bps) { ++ if (isAMRWB) { ++ if (bps <= 6600) { ++ return OMX_AUDIO_AMRBandModeWB0; ++ } else if (bps <= 8850) { ++ return OMX_AUDIO_AMRBandModeWB1; ++ } else if (bps <= 12650) { ++ return OMX_AUDIO_AMRBandModeWB2; ++ } else if (bps <= 14250) { ++ return OMX_AUDIO_AMRBandModeWB3; ++ } else if (bps <= 15850) { ++ return OMX_AUDIO_AMRBandModeWB4; ++ } else if (bps <= 18250) { ++ return OMX_AUDIO_AMRBandModeWB5; ++ } else if (bps <= 19850) { ++ return OMX_AUDIO_AMRBandModeWB6; ++ } else if (bps <= 23050) { ++ return OMX_AUDIO_AMRBandModeWB7; ++ } ++ ++ // 23850 bps ++ return OMX_AUDIO_AMRBandModeWB8; ++ } else { // AMRNB ++ if (bps <= 4750) { ++ return OMX_AUDIO_AMRBandModeNB0; ++ } else if (bps <= 5150) { ++ return OMX_AUDIO_AMRBandModeNB1; ++ } else if (bps <= 5900) { ++ return OMX_AUDIO_AMRBandModeNB2; ++ } else if (bps <= 6700) { ++ return OMX_AUDIO_AMRBandModeNB3; ++ } else if (bps <= 7400) { ++ return OMX_AUDIO_AMRBandModeNB4; ++ } else if (bps <= 7950) { ++ return OMX_AUDIO_AMRBandModeNB5; ++ } else if (bps <= 10200) { ++ return OMX_AUDIO_AMRBandModeNB6; ++ } ++ ++ // 12200 bps ++ return OMX_AUDIO_AMRBandModeNB7; ++ } ++} ++ ++void OMXCodec::setAMRFormat(bool isWAMR, int32_t bitRate) { ++ OMX_U32 portIndex = mIsEncoder ? kPortIndexOutput : kPortIndexInput; ++ ++ OMX_AUDIO_PARAM_AMRTYPE def; ++ InitOMXParams(&def); ++ def.nPortIndex = portIndex; ++ ++ status_t err = ++ mOMX->getParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); ++ ++ CHECK_EQ(err, (status_t)OK); ++ ++ def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; ++ ++ def.eAMRBandMode = pickModeFromBitRate(isWAMR, bitRate); ++ err = mOMX->setParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ //////////////////////// ++ ++ if (mIsEncoder) { ++ sp format = mSource->getFormat(); ++ int32_t sampleRate; ++ int32_t numChannels; ++ CHECK(format->findInt32(kKeySampleRate, &sampleRate)); ++ CHECK(format->findInt32(kKeyChannelCount, &numChannels)); ++ ++ setRawAudioFormat(kPortIndexInput, sampleRate, numChannels); ++ } ++} ++ ++status_t OMXCodec::setAACFormat( ++ int32_t numChannels, int32_t sampleRate, int32_t bitRate, int32_t aacProfile, bool isADTS) { ++ if (numChannels > 2) { ++ ALOGW("Number of channels: (%d) \n", numChannels); ++ } ++ ++ if (mIsEncoder) { ++ if (isADTS) { ++ return -EINVAL; ++ } ++ ++ //////////////// input port //////////////////// ++ setRawAudioFormat(kPortIndexInput, sampleRate, numChannels); ++ ++ //////////////// output port //////////////////// ++ // format ++ OMX_AUDIO_PARAM_PORTFORMATTYPE format; ++ InitOMXParams(&format); ++ format.nPortIndex = kPortIndexOutput; ++ format.nIndex = 0; ++ status_t err = OMX_ErrorNone; ++ while (OMX_ErrorNone == err) { ++ CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioPortFormat, ++ &format, sizeof(format)), (status_t)OK); ++ if (format.eEncoding == OMX_AUDIO_CodingAAC) { ++ break; ++ } ++ format.nIndex; ++ } ++ CHECK_EQ((status_t)OK, err); ++ CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamAudioPortFormat, ++ &format, sizeof(format)), (status_t)OK); ++ ++ // port definition ++ OMX_PARAM_PORTDEFINITIONTYPE def; ++ InitOMXParams(&def); ++ def.nPortIndex = kPortIndexOutput; ++ CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamPortDefinition, ++ &def, sizeof(def)), (status_t)OK); ++ def.format.audio.bFlagErrorConcealment = OMX_TRUE; ++ def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; ++ CHECK_EQ(mOMX->setParameter(mNode, OMX_IndexParamPortDefinition, ++ &def, sizeof(def)), (status_t)OK); ++ ++ // profile ++ OMX_AUDIO_PARAM_AACPROFILETYPE profile; ++ InitOMXParams(&profile); ++ profile.nPortIndex = kPortIndexOutput; ++ CHECK_EQ(mOMX->getParameter(mNode, OMX_IndexParamAudioAac, ++ &profile, sizeof(profile)), (status_t)OK); ++ profile.nChannels = numChannels; ++ profile.eChannelMode = (numChannels == 1? ++ OMX_AUDIO_ChannelModeMono: OMX_AUDIO_ChannelModeStereo); ++ profile.nSampleRate = sampleRate; ++ profile.nBitRate = bitRate; ++ profile.nAudioBandWidth = 0; ++ profile.nFrameLength = 0; ++ profile.nAACtools = OMX_AUDIO_AACToolAll; ++ profile.nAACERtools = OMX_AUDIO_AACERNone; ++ profile.eAACProfile = (OMX_AUDIO_AACPROFILETYPE) aacProfile; ++ profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF; ++ err = mOMX->setParameter(mNode, OMX_IndexParamAudioAac, ++ &profile, sizeof(profile)); ++ ++ if (err != OK) { ++ CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed " ++ "(err = %d)", ++ err); ++ return err; ++ } ++ } else { ++ OMX_AUDIO_PARAM_AACPROFILETYPE profile; ++ InitOMXParams(&profile); ++ profile.nPortIndex = kPortIndexInput; ++ ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ profile.nChannels = numChannels; ++ profile.nSampleRate = sampleRate; ++ ++ profile.eAACStreamFormat = ++ isADTS ++ ? OMX_AUDIO_AACStreamFormatMP4ADTS ++ : OMX_AUDIO_AACStreamFormatMP4FF; ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); ++ ++ if (err != OK) { ++ CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed " ++ "(err = %d)", ++ err); ++ return err; ++ } ++ } ++ ++ return OK; ++} ++ ++status_t OMXCodec::setAC3Format(int32_t numChannels, int32_t sampleRate) { ++ OMX_AUDIO_PARAM_ANDROID_AC3TYPE def; ++ InitOMXParams(&def); ++ def.nPortIndex = kPortIndexInput; ++ ++ status_t err = mOMX->getParameter( ++ mNode, ++ (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3, ++ &def, ++ sizeof(def)); ++ ++ if (err != OK) { ++ return err; ++ } ++ ++ def.nChannels = numChannels; ++ def.nSampleRate = sampleRate; ++ ++ return mOMX->setParameter( ++ mNode, ++ (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3, ++ &def, ++ sizeof(def)); ++} ++ ++void OMXCodec::setG711Format(int32_t sampleRate, int32_t numChannels) { ++ CHECK(!mIsEncoder); ++ setRawAudioFormat(kPortIndexInput, sampleRate, numChannels); ++} ++ ++void OMXCodec::setImageOutputFormat( ++ OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height) { ++ CODEC_LOGV("setImageOutputFormat(%u, %u)", width, height); ++ ++#if 0 ++ OMX_INDEXTYPE index; ++ status_t err = mOMX->get_extension_index( ++ mNode, "OMX.TI.JPEG.decode.Config.OutputColorFormat", &index); ++ CHECK_EQ(err, (status_t)OK); ++ ++ err = mOMX->set_config(mNode, index, &format, sizeof(format)); ++ CHECK_EQ(err, (status_t)OK); ++#endif ++ ++ OMX_PARAM_PORTDEFINITIONTYPE def; ++ InitOMXParams(&def); ++ def.nPortIndex = kPortIndexOutput; ++ ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainImage); ++ ++ OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image; ++ ++ CHECK_EQ((int)imageDef->eCompressionFormat, (int)OMX_IMAGE_CodingUnused); ++ imageDef->eColorFormat = format; ++ imageDef->nFrameWidth = width; ++ imageDef->nFrameHeight = height; ++ ++ switch (format) { ++ case OMX_COLOR_FormatYUV420PackedPlanar: ++ case OMX_COLOR_FormatYUV411Planar: ++ { ++ def.nBufferSize = (width * height * 3) / 2; ++ break; ++ } ++ ++ case OMX_COLOR_FormatCbYCrY: ++ { ++ def.nBufferSize = width * height * 2; ++ break; ++ } ++ ++ case OMX_COLOR_Format32bitARGB8888: ++ { ++ def.nBufferSize = width * height * 4; ++ break; ++ } ++ ++ case OMX_COLOR_Format16bitARGB4444: ++ case OMX_COLOR_Format16bitARGB1555: ++ case OMX_COLOR_Format16bitRGB565: ++ case OMX_COLOR_Format16bitBGR565: ++ { ++ def.nBufferSize = width * height * 2; ++ break; ++ } ++ ++ default: ++ CHECK(!"Should not be here. Unknown color format."); ++ break; ++ } ++ ++ def.nBufferCountActual = def.nBufferCountMin; ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++} ++ ++void OMXCodec::setJPEGInputFormat( ++ OMX_U32 width, OMX_U32 height, OMX_U32 compressedSize) { ++ OMX_PARAM_PORTDEFINITIONTYPE def; ++ InitOMXParams(&def); ++ def.nPortIndex = kPortIndexInput; ++ ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ CHECK_EQ((int)def.eDomain, (int)OMX_PortDomainImage); ++ OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image; ++ ++ CHECK_EQ((int)imageDef->eCompressionFormat, (int)OMX_IMAGE_CodingJPEG); ++ imageDef->nFrameWidth = width; ++ imageDef->nFrameHeight = height; ++ ++ def.nBufferSize = compressedSize; ++ def.nBufferCountActual = def.nBufferCountMin; ++ ++ err = mOMX->setParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++} ++ ++void OMXCodec::addCodecSpecificData(const void *data, size_t size) { ++ CodecSpecificData *specific = ++ (CodecSpecificData *)malloc(sizeof(CodecSpecificData) size - 1); ++ ++ specific->mSize = size; ++ memcpy(specific->mData, data, size); ++ ++ mCodecSpecificData.push(specific); ++} ++ ++void OMXCodec::clearCodecSpecificData() { ++ for (size_t i = 0; i < mCodecSpecificData.size(); i) { ++ free(mCodecSpecificData.editItemAt(i)); ++ } ++ mCodecSpecificData.clear(); ++ mCodecSpecificDataIndex = 0; ++} ++ ++status_t OMXCodec::start(MetaData *meta) { ++ Mutex::Autolock autoLock(mLock); ++ ++ if (mPaused) { ++ status_t err = resumeLocked(true); ++ return err; ++ } ++ ++ if (mState != LOADED) { ++ CODEC_LOGE("called start in the unexpected state: %d", mState); ++ return UNKNOWN_ERROR; ++ } ++ ++ sp params = new MetaData; ++ if (mQuirks & kWantsNALFragments) { ++ params->setInt32(kKeyWantsNALFragments, true); ++ } ++ if (meta) { ++ int64_t startTimeUs = 0; ++ int64_t timeUs; ++ if (meta->findInt64(kKeyTime, &timeUs)) { ++ startTimeUs = timeUs; ++ } ++ params->setInt64(kKeyTime, startTimeUs); ++ } ++ ++ mCodecSpecificDataIndex = 0; ++ mInitialBufferSubmit = true; ++ mSignalledEOS = false; ++ mNoMoreOutputData = false; ++ mOutputPortSettingsHaveChanged = false; ++ mSeekTimeUs = -1; ++ mSeekMode = ReadOptions::SEEK_CLOSEST_SYNC; ++ mTargetTimeUs = -1; ++ mFilledBuffers.clear(); ++ mPaused = false; ++ ++ status_t err; ++ if (mIsEncoder) { ++ // Calling init() before starting its source so that we can configure, ++ // if supported, the source to use exactly the same number of input ++ // buffers as requested by the encoder. ++ if ((err = init()) != OK) { ++ CODEC_LOGE("init failed: %d", err); ++ return err; ++ } ++ ++ params->setInt32(kKeyNumBuffers, mPortBuffers[kPortIndexInput].size()); ++ err = mSource->start(params.get()); ++ if (err != OK) { ++ CODEC_LOGE("source failed to start: %d", err); ++ stopOmxComponent_l(); ++ } ++ return err; ++ } ++ ++ // Decoder case ++ if ((err = mSource->start(params.get())) != OK) { ++ CODEC_LOGE("source failed to start: %d", err); ++ return err; ++ } ++ return init(); ++} ++ ++status_t OMXCodec::stop() { ++ CODEC_LOGV("stop mState=%d", mState); ++ Mutex::Autolock autoLock(mLock); ++ status_t err = stopOmxComponent_l(); ++ mSource->stop(); ++ ++ CODEC_LOGV("stopped in state %d", mState); ++ return err; ++} ++ ++status_t OMXCodec::stopOmxComponent_l() { ++ CODEC_LOGV("stopOmxComponent_l mState=%d", mState); ++ ++ while (isIntermediateState(mState)) { ++ mAsyncCompletion.wait(mLock); ++ } ++ ++ bool isError = false; ++ switch (mState) { ++ case LOADED: ++ break; ++ ++ case ERROR: ++ { ++ if (mPortStatus[kPortIndexOutput] == ENABLING) { ++ // Codec is in a wedged state (technical term) ++ // We've seen an output port settings change from the codec, ++ // We've disabled the output port, then freed the output ++ // buffers, initiated re-enabling the output port but ++ // failed to reallocate the output buffers. ++ // There doesn't seem to be a way to orderly transition ++ // from executing->idle and idle->loaded now that the ++ // output port hasn't been reenabled yet... ++ // Simply free as many resources as we can and pretend ++ // that we're in LOADED state so that the destructor ++ // will free the component instance without asserting. ++ freeBuffersOnPort(kPortIndexInput, true /* onlyThoseWeOwn */); ++ freeBuffersOnPort(kPortIndexOutput, true /* onlyThoseWeOwn */); ++ setState(LOADED); ++ break; ++ } else { ++ OMX_STATETYPE state = OMX_StateInvalid; ++ status_t err = mOMX->getState(mNode, &state); ++ CHECK_EQ(err, (status_t)OK); ++ ++ if (state != OMX_StateExecuting) { ++ break; ++ } ++ // else fall through to the idling code ++ } ++ ++ isError = true; ++ } ++ ++ case PAUSED: ++ case EXECUTING: ++ { ++ setState(EXECUTING_TO_IDLE); ++ ++ if (mQuirks & kRequiresFlushBeforeShutdown) { ++ CODEC_LOGV("This component requires a flush before transitioning " ++ "from EXECUTING to IDLE..."); ++ ++ bool emulateInputFlushCompletion = ++ !flushPortAsync(kPortIndexInput); ++ ++ bool emulateOutputFlushCompletion = ++ !flushPortAsync(kPortIndexOutput); ++ ++ if (emulateInputFlushCompletion) { ++ onCmdComplete(OMX_CommandFlush, kPortIndexInput); ++ } ++ ++ if (emulateOutputFlushCompletion) { ++ onCmdComplete(OMX_CommandFlush, kPortIndexOutput); ++ } ++ } else { ++ mPortStatus[kPortIndexInput] = SHUTTING_DOWN; ++ mPortStatus[kPortIndexOutput] = SHUTTING_DOWN; ++ ++ status_t err = ++ mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle); ++ CHECK_EQ(err, (status_t)OK); ++ } ++ ++ while (mState != LOADED && mState != ERROR) { ++ mAsyncCompletion.wait(mLock); ++ } ++ ++ if (isError) { ++ // We were in the ERROR state coming in, so restore that now ++ // that we've idled the OMX component. ++ setState(ERROR); ++ } ++ ++ break; ++ } ++ ++ default: ++ { ++ CHECK(!"should not be here."); ++ break; ++ } ++ } ++ ++ if (mLeftOverBuffer) { ++ mLeftOverBuffer->release(); ++ mLeftOverBuffer = NULL; ++ } ++ ++ return OK; ++} ++ ++sp OMXCodec::getFormat() { ++ Mutex::Autolock autoLock(mLock); ++ ++ return mOutputFormat; ++} ++ ++status_t OMXCodec::read( ++ MediaBuffer **buffer, const ReadOptions *options) { ++ status_t err = OK; ++ *buffer = NULL; ++ ++ Mutex::Autolock autoLock(mLock); ++ ++ if (mPaused) { ++ err = resumeLocked(false); ++ if(err != OK) { ++ CODEC_LOGE("Failed to restart codec err= %d", err); ++ return err; ++ } ++ } ++ ++ if (mState != EXECUTING && mState != RECONFIGURING) { ++ return UNKNOWN_ERROR; ++ } ++ ++ bool seeking = false; ++ int64_t seekTimeUs; ++ ReadOptions::SeekMode seekMode; ++ if (options && options->getSeekTo(&seekTimeUs, &seekMode)) { ++ seeking = true; ++ } ++ ++ if (mInitialBufferSubmit) { ++ mInitialBufferSubmit = false; ++ ++ if (seeking) { ++ CHECK(seekTimeUs >= 0); ++ mSeekTimeUs = seekTimeUs; ++ mSeekMode = seekMode; ++ ++ // There's no reason to trigger the code below, there's ++ // nothing to flush yet. ++ seeking = false; ++ mPaused = false; ++ } ++ ++ drainInputBuffers(); ++ ++ if (mState == EXECUTING) { ++ // Otherwise mState == RECONFIGURING and this code will trigger ++ // after the output port is reenabled. ++ fillOutputBuffers(); ++ } ++ } ++ ++ if (seeking) { ++ while (mState == RECONFIGURING) { ++ if ((err = waitForBufferFilled_l()) != OK) { ++ return err; ++ } ++ } ++ ++ if (mState != EXECUTING) { ++ return UNKNOWN_ERROR; ++ } ++ ++ CODEC_LOGV("seeking to %" PRId64 " us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6); ++ ++ mSignalledEOS = false; ++ ++ CHECK(seekTimeUs >= 0); ++ mSeekTimeUs = seekTimeUs; ++ mSeekMode = seekMode; ++ ++ mFilledBuffers.clear(); ++ ++ CHECK_EQ((int)mState, (int)EXECUTING); ++ //DSP supports flushing of ports simultaneously. Flushing individual port is not supported. ++ setState(FLUSHING); ++ ++ bool emulateInputFlushCompletion = !flushPortAsync(kPortIndexInput); ++ bool emulateOutputFlushCompletion = !flushPortAsync(kPortIndexOutput); ++ ++ if (emulateInputFlushCompletion) { ++ onCmdComplete(OMX_CommandFlush, kPortIndexInput); ++ } ++ ++ if (emulateOutputFlushCompletion) { ++ onCmdComplete(OMX_CommandFlush, kPortIndexOutput); ++ } ++ ++ while (mSeekTimeUs >= 0) { ++ if ((err = waitForBufferFilled_l()) != OK) { ++ return err; ++ } ++ } ++ } ++ ++ while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) { ++ if ((err = waitForBufferFilled_l()) != OK) { ++ return err; ++ } ++ } ++ ++ if (mState == ERROR) { ++ return UNKNOWN_ERROR; ++ } ++ ++ if (seeking) { ++ CHECK_EQ((int)mState, (int)FLUSHING); ++ setState(EXECUTING); ++ } ++ ++ if (mFilledBuffers.empty()) { ++ return mSignalledEOS ? mFinalStatus : ERROR_END_OF_STREAM; ++ } ++ ++ if (mOutputPortSettingsHaveChanged) { ++ mOutputPortSettingsHaveChanged = false; ++ ++ return INFO_FORMAT_CHANGED; ++ } ++ ++ size_t index = *mFilledBuffers.begin(); ++ mFilledBuffers.erase(mFilledBuffers.begin()); ++ ++ BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index); ++ CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US); ++ info->mStatus = OWNED_BY_CLIENT; ++ ++ info->mMediaBuffer->add_ref(); ++ if (mSkipCutBuffer != NULL) { ++ mSkipCutBuffer->submit(info->mMediaBuffer); ++ } ++ *buffer = info->mMediaBuffer; ++ ++ return OK; ++} ++ ++void OMXCodec::signalBufferReturned(MediaBuffer *buffer) { ++ Mutex::Autolock autoLock(mLock); ++ ++ Vector *buffers = &mPortBuffers[kPortIndexOutput]; ++ for (size_t i = 0; i < buffers->size(); i) { ++ BufferInfo *info = &buffers->editItemAt(i); ++ ++ if (info->mMediaBuffer == buffer) { ++ CHECK_EQ((int)mPortStatus[kPortIndexOutput], (int)ENABLED); ++ CHECK_EQ((int)info->mStatus, (int)OWNED_BY_CLIENT); ++ ++ info->mStatus = OWNED_BY_US; ++ ++ if (buffer->graphicBuffer() == 0) { ++ fillOutputBuffer(info); ++ } else { ++ sp metaData = info->mMediaBuffer->meta_data(); ++ int32_t rendered = 0; ++ if (!metaData->findInt32(kKeyRendered, &rendered)) { ++ rendered = 0; ++ } ++ if (!rendered) { ++ status_t err = cancelBufferToNativeWindow(info); ++ if (err < 0) { ++ return; ++ } ++ } ++ ++ info->mStatus = OWNED_BY_NATIVE_WINDOW; ++ ++ // Dequeue the next buffer from the native window. ++ BufferInfo *nextBufInfo = dequeueBufferFromNativeWindow(); ++ if (nextBufInfo == 0) { ++ return; ++ } ++ ++ // Give the buffer to the OMX node to fill. ++ fillOutputBuffer(nextBufInfo); ++ } ++ return; ++ } ++ } ++ ++ CHECK(!"should not be here."); ++} ++ ++void OMXCodec::dumpPortStatus(OMX_U32 portIndex) { ++ OMX_PARAM_PORTDEFINITIONTYPE def; ++ InitOMXParams(&def); ++ def.nPortIndex = portIndex; ++ ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ printf("%s Port = {\n", portIndex == kPortIndexInput ? "Input" : "Output"); ++ ++ CHECK((portIndex == kPortIndexInput && def.eDir == OMX_DirInput) ++ || (portIndex == kPortIndexOutput && def.eDir == OMX_DirOutput)); ++ ++ printf(" nBufferCountActual = %" PRIu32 "\n", def.nBufferCountActual); ++ printf(" nBufferCountMin = %" PRIu32 "\n", def.nBufferCountMin); ++ printf(" nBufferSize = %" PRIu32 "\n", def.nBufferSize); ++ ++ switch (def.eDomain) { ++ case OMX_PortDomainImage: ++ { ++ const OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image; ++ ++ printf("\n"); ++ printf(" // Image\n"); ++ printf(" nFrameWidth = %" PRIu32 "\n", imageDef->nFrameWidth); ++ printf(" nFrameHeight = %" PRIu32 "\n", imageDef->nFrameHeight); ++ printf(" nStride = %" PRIu32 "\n", imageDef->nStride); ++ ++ printf(" eCompressionFormat = %s\n", ++ asString(imageDef->eCompressionFormat)); ++ ++ printf(" eColorFormat = %s\n", ++ asString(imageDef->eColorFormat)); ++ ++ break; ++ } ++ ++ case OMX_PortDomainVideo: ++ { ++ OMX_VIDEO_PORTDEFINITIONTYPE *videoDef = &def.format.video; ++ ++ printf("\n"); ++ printf(" // Video\n"); ++ printf(" nFrameWidth = %" PRIu32 "\n", videoDef->nFrameWidth); ++ printf(" nFrameHeight = %" PRIu32 "\n", videoDef->nFrameHeight); ++ printf(" nStride = %" PRIu32 "\n", videoDef->nStride); ++ ++ printf(" eCompressionFormat = %s\n", ++ asString(videoDef->eCompressionFormat)); ++ ++ printf(" eColorFormat = %s\n", ++ asString(videoDef->eColorFormat)); ++ ++ break; ++ } ++ ++ case OMX_PortDomainAudio: ++ { ++ OMX_AUDIO_PORTDEFINITIONTYPE *audioDef = &def.format.audio; ++ ++ printf("\n"); ++ printf(" // Audio\n"); ++ printf(" eEncoding = %s\n", ++ asString(audioDef->eEncoding)); ++ ++ if (audioDef->eEncoding == OMX_AUDIO_CodingPCM) { ++ OMX_AUDIO_PARAM_PCMMODETYPE params; ++ InitOMXParams(¶ms); ++ params.nPortIndex = portIndex; ++ ++ err = mOMX->getParameter( ++ mNode, OMX_IndexParamAudioPcm, ¶ms, sizeof(params)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ printf(" nSamplingRate = %" PRIu32 "\n", params.nSamplingRate); ++ printf(" nChannels = %" PRIu32 "\n", params.nChannels); ++ printf(" bInterleaved = %d\n", params.bInterleaved); ++ printf(" nBitPerSample = %" PRIu32 "\n", params.nBitPerSample); ++ ++ printf(" eNumData = %s\n", ++ params.eNumData == OMX_NumericalDataSigned ++ ? "signed" : "unsigned"); ++ ++ printf(" ePCMMode = %s\n", asString(params.ePCMMode)); ++ } else if (audioDef->eEncoding == OMX_AUDIO_CodingAMR) { ++ OMX_AUDIO_PARAM_AMRTYPE amr; ++ InitOMXParams(&amr); ++ amr.nPortIndex = portIndex; ++ ++ err = mOMX->getParameter( ++ mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ printf(" nChannels = %" PRIu32 "\n", amr.nChannels); ++ printf(" eAMRBandMode = %s\n", ++ asString(amr.eAMRBandMode)); ++ printf(" eAMRFrameFormat = %s\n", ++ asString(amr.eAMRFrameFormat)); ++ } ++ ++ break; ++ } ++ ++ default: ++ { ++ printf(" // Unknown\n"); ++ break; ++ } ++ } ++ ++ printf("}\n"); ++} ++ ++status_t OMXCodec::initNativeWindow() { ++ // Enable use of a GraphicBuffer as the output for this node. This must ++ // happen before getting the IndexParamPortDefinition parameter because it ++ // will affect the pixel format that the node reports. ++ status_t err = mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_TRUE); ++ if (err != 0) { ++ return err; ++ } ++ ++ return OK; ++} ++ ++void OMXCodec::initNativeWindowCrop() { ++ int32_t left, top, right, bottom; ++ ++ CHECK(mOutputFormat->findRect( ++ kKeyCropRect, ++ &left, &top, &right, &bottom)); ++ ++ android_native_rect_t crop; ++ crop.left = left; ++ crop.top = top; ++ crop.right = right 1; ++ crop.bottom = bottom 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); ++} ++ ++void OMXCodec::initOutputFormat(const sp &inputFormat) { ++ mOutputFormat = new MetaData; ++ mOutputFormat->setCString(kKeyDecoderComponent, mComponentName); ++ if (mIsEncoder) { ++ int32_t timeScale; ++ if (inputFormat->findInt32(kKeyTimeScale, &timeScale)) { ++ mOutputFormat->setInt32(kKeyTimeScale, timeScale); ++ } ++ } ++ ++ OMX_PARAM_PORTDEFINITIONTYPE def; ++ InitOMXParams(&def); ++ def.nPortIndex = kPortIndexOutput; ++ ++ status_t err = mOMX->getParameter( ++ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ switch (def.eDomain) { ++ case OMX_PortDomainImage: ++ { ++ OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image; ++ CHECK_EQ((int)imageDef->eCompressionFormat, ++ (int)OMX_IMAGE_CodingUnused); ++ ++ mOutputFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); ++ mOutputFormat->setInt32(kKeyColorFormat, imageDef->eColorFormat); ++ mOutputFormat->setInt32(kKeyWidth, imageDef->nFrameWidth); ++ mOutputFormat->setInt32(kKeyHeight, imageDef->nFrameHeight); ++ break; ++ } ++ ++ case OMX_PortDomainAudio: ++ { ++ OMX_AUDIO_PORTDEFINITIONTYPE *audio_def = &def.format.audio; ++ ++ if (audio_def->eEncoding == OMX_AUDIO_CodingPCM) { ++ OMX_AUDIO_PARAM_PCMMODETYPE params; ++ InitOMXParams(¶ms); ++ params.nPortIndex = kPortIndexOutput; ++ ++ err = mOMX->getParameter( ++ mNode, OMX_IndexParamAudioPcm, ¶ms, sizeof(params)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ CHECK_EQ((int)params.eNumData, (int)OMX_NumericalDataSigned); ++ CHECK_EQ(params.nBitPerSample, 16u); ++ CHECK_EQ((int)params.ePCMMode, (int)OMX_AUDIO_PCMModeLinear); ++ ++ int32_t numChannels, sampleRate; ++ inputFormat->findInt32(kKeyChannelCount, &numChannels); ++ inputFormat->findInt32(kKeySampleRate, &sampleRate); ++ ++ if ((OMX_U32)numChannels != params.nChannels) { ++ ALOGV("Codec outputs a different number of channels than " ++ "the input stream contains (contains %d channels, " ++ "codec outputs %u channels).", ++ numChannels, params.nChannels); ++ } ++ ++ if (sampleRate != (int32_t)params.nSamplingRate) { ++ ALOGV("Codec outputs at different sampling rate than " ++ "what the input stream contains (contains data at " ++ "%d Hz, codec outputs %u Hz)", ++ sampleRate, params.nSamplingRate); ++ } ++ ++ mOutputFormat->setCString( ++ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); ++ ++ // Use the codec-advertised number of channels, as some ++ // codecs appear to output stereo even if the input data is ++ // mono. If we know the codec lies about this information, ++ // use the actual number of channels instead. ++ mOutputFormat->setInt32( ++ kKeyChannelCount, ++ (mQuirks & kDecoderLiesAboutNumberOfChannels) ++ ? numChannels : params.nChannels); ++ ++ mOutputFormat->setInt32(kKeySampleRate, params.nSamplingRate); ++ } else if (audio_def->eEncoding == OMX_AUDIO_CodingAMR) { ++ OMX_AUDIO_PARAM_AMRTYPE amr; ++ InitOMXParams(&amr); ++ amr.nPortIndex = kPortIndexOutput; ++ ++ err = mOMX->getParameter( ++ mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr)); ++ CHECK_EQ(err, (status_t)OK); ++ ++ CHECK_EQ(amr.nChannels, 1u); ++ mOutputFormat->setInt32(kKeyChannelCount, 1); ++ ++ if (amr.eAMRBandMode >= OMX_AUDIO_AMRBandModeNB0 ++ && amr.eAMRBandMode <= OMX_AUDIO_AMRBandModeNB7) { ++ mOutputFormat->setCString( ++ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_NB); ++ mOutputFormat->setInt32(kKeySampleRate, 8000); ++ } else if (amr.eAMRBandMode >= OMX_AUDIO_AMRBandModeWB0 ++ && amr.eAMRBandMode <= OMX_AUDIO_AMRBandModeWB8) { ++ mOutputFormat->setCString( ++ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_WB); ++ mOutputFormat->setInt32(kKeySampleRate, 16000); ++ } else { ++ CHECK(!"Unknown AMR band mode."); ++ } ++ } else if (audio_def->eEncoding == OMX_AUDIO_CodingAAC) { ++ mOutputFormat->setCString( ++ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); ++ int32_t numChannels, sampleRate, bitRate; ++ inputFormat->findInt32(kKeyChannelCount, &numChannels); ++ inputFormat->findInt32(kKeySampleRate, &sampleRate); ++ inputFormat->findInt32(kKeyBitRate, &bitRate); ++ mOutputFormat->setInt32(kKeyChannelCount, numChannels); ++ mOutputFormat->setInt32(kKeySampleRate, sampleRate); ++ mOutputFormat->setInt32(kKeyBitRate, bitRate); ++ } else if (audio_def->eEncoding == ++ (OMX_AUDIO_CODINGTYPE)OMX_AUDIO_CodingAndroidAC3) { ++ mOutputFormat->setCString( ++ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3); ++ int32_t numChannels, sampleRate, bitRate; ++ inputFormat->findInt32(kKeyChannelCount, &numChannels); ++ inputFormat->findInt32(kKeySampleRate, &sampleRate); ++ inputFormat->findInt32(kKeyBitRate, &bitRate); ++ mOutputFormat->setInt32(kKeyChannelCount, numChannels); ++ mOutputFormat->setInt32(kKeySampleRate, sampleRate); ++ mOutputFormat->setInt32(kKeyBitRate, bitRate); ++ } else { ++ CHECK(!"Should not be here. Unknown audio encoding."); ++ } ++ break; ++ } ++ ++ case OMX_PortDomainVideo: ++ { ++ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video; ++ ++ if (video_def->eCompressionFormat == OMX_VIDEO_CodingUnused) { ++ mOutputFormat->setCString( ++ kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); ++ } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingMPEG4) { ++ mOutputFormat->setCString( ++ kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); ++ } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingH263) { ++ mOutputFormat->setCString( ++ kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263); ++ } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingAVC) { ++ mOutputFormat->setCString( ++ kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); ++ } else { ++ CHECK(!"Unknown compression format."); ++ } ++ ++ mOutputFormat->setInt32(kKeyWidth, video_def->nFrameWidth); ++ mOutputFormat->setInt32(kKeyHeight, video_def->nFrameHeight); ++ mOutputFormat->setInt32(kKeyColorFormat, video_def->eColorFormat); ++ ++ if (!mIsEncoder) { ++ OMX_CONFIG_RECTTYPE rect; ++ InitOMXParams(&rect); ++ rect.nPortIndex = kPortIndexOutput; ++ status_t err = ++ mOMX->getConfig( ++ mNode, OMX_IndexConfigCommonOutputCrop, ++ &rect, sizeof(rect)); ++ ++ CODEC_LOGI("video dimensions are %u x %u", ++ video_def->nFrameWidth, video_def->nFrameHeight); ++ ++ if (err == OK) { ++ CHECK_GE(rect.nLeft, 0); ++ CHECK_GE(rect.nTop, 0); ++ CHECK_GE(rect.nWidth, 0u); ++ CHECK_GE(rect.nHeight, 0u); ++ CHECK_LE(rect.nLeft rect.nWidth - 1, video_def->nFrameWidth); ++ CHECK_LE(rect.nTop rect.nHeight - 1, video_def->nFrameHeight); ++ ++ mOutputFormat->setRect( ++ kKeyCropRect, ++ rect.nLeft, ++ rect.nTop, ++ rect.nLeft rect.nWidth - 1, ++ rect.nTop rect.nHeight - 1); ++ ++ CODEC_LOGI("Crop rect is %u x %u @ (%d, %d)", ++ rect.nWidth, rect.nHeight, rect.nLeft, rect.nTop); ++ } else { ++ mOutputFormat->setRect( ++ kKeyCropRect, ++ 0, 0, ++ video_def->nFrameWidth - 1, ++ video_def->nFrameHeight - 1); ++ } ++ ++ if (mNativeWindow != NULL) { ++ initNativeWindowCrop(); ++ } ++ } ++ break; ++ } ++ ++ default: ++ { ++ CHECK(!"should not be here, neither audio nor video."); ++ break; ++ } ++ } ++ ++ // If the input format contains rotation information, flag the output ++ // format accordingly. ++ ++ int32_t rotationDegrees; ++ if (mSource->getFormat()->findInt32(kKeyRotation, &rotationDegrees)) { ++ mOutputFormat->setInt32(kKeyRotation, rotationDegrees); ++ } ++} ++ ++status_t OMXCodec::pause() { ++ CODEC_LOGV("pause mState=%d", mState); ++ ++ Mutex::Autolock autoLock(mLock); ++ ++ if (mState != EXECUTING) { ++ return UNKNOWN_ERROR; ++ } ++ ++ while (isIntermediateState(mState)) { ++ mAsyncCompletion.wait(mLock); ++ } ++ if (!strncmp(mComponentName, "OMX.qcom.", 9)) { ++ status_t err = mOMX->sendCommand(mNode, ++ OMX_CommandStateSet, OMX_StatePause); ++ CHECK_EQ(err, (status_t)OK); ++ setState(PAUSING); ++ ++ mPaused = true; ++ while (mState != PAUSED && mState != ERROR) { ++ mAsyncCompletion.wait(mLock); ++ } ++ return mState == ERROR ? UNKNOWN_ERROR : OK; ++ } else { ++ mPaused = true; ++ return OK; ++ } ++ ++} ++ ++status_t OMXCodec::resumeLocked(bool drainInputBuf) { ++ CODEC_LOGV("resume mState=%d", mState); ++ ++ if (!strncmp(mComponentName, "OMX.qcom.", 9)) { ++ while (isIntermediateState(mState)) { ++ mAsyncCompletion.wait(mLock); ++ } ++ CHECK_EQ(mState, (status_t)PAUSED); ++ status_t err = mOMX->sendCommand(mNode, ++ OMX_CommandStateSet, OMX_StateExecuting); ++ CHECK_EQ(err, (status_t)OK); ++ setState(IDLE_TO_EXECUTING); ++ mPaused = false; ++ while (mState != EXECUTING && mState != ERROR) { ++ mAsyncCompletion.wait(mLock); ++ } ++ if(drainInputBuf) ++ drainInputBuffers(); ++ return mState == ERROR ? UNKNOWN_ERROR : OK; ++ } else { // SW Codec ++ mPaused = false; ++ if(drainInputBuf) ++ drainInputBuffers(); ++ return OK; ++ } ++} ++ ++//////////////////////////////////////////////////////////////////////////////// ++ ++status_t QueryCodecs( ++ const sp &omx, ++ const char *mime, bool queryDecoders, bool hwCodecOnly, ++ Vector *results) { ++ Vector matchingCodecs; ++ results->clear(); ++ ++ OMXCodec::findMatchingCodecs(mime, ++ !queryDecoders /*createEncoder*/, ++ NULL /*matchComponentName*/, ++ hwCodecOnly ? OMXCodec::kHardwareCodecsOnly : 0 /*flags*/, ++ &matchingCodecs); ++ ++ for (size_t c = 0; c < matchingCodecs.size(); c) { ++ const char *componentName = matchingCodecs.itemAt(c).mName.string(); ++ ++ results->push(); ++ CodecCapabilities *caps = &results->editItemAt(results->size() - 1); ++ ++ status_t err = ++ QueryCodec(omx, componentName, mime, !queryDecoders, caps); ++ ++ if (err != OK) { ++ results->removeAt(results->size() - 1); ++ } ++ } ++ ++ return OK; ++} ++ ++status_t QueryCodec( ++ const sp &omx, ++ const char *componentName, const char *mime, ++ bool isEncoder, ++ CodecCapabilities *caps) { ++ bool isVideo = !strncasecmp(mime, "video/", 6); ++ ++ sp observer = new OMXCodecObserver; ++ IOMX::node_id node; ++ status_t err = omx->allocateNode(componentName, observer, &node); ++ ++ if (err != OK) { ++ return err; ++ } ++ ++ OMXCodec::setComponentRole(omx, node, isEncoder, mime); ++ ++ caps->mFlags = 0; ++ caps->mComponentName = componentName; ++ ++ // NOTE: OMX does not provide a way to query AAC profile support ++ if (isVideo) { ++ OMX_VIDEO_PARAM_PROFILELEVELTYPE param; ++ InitOMXParams(¶m); ++ ++ param.nPortIndex = !isEncoder ? 0 : 1; ++ ++ for (param.nProfileIndex = 0;; param.nProfileIndex) { ++ err = omx->getParameter( ++ node, OMX_IndexParamVideoProfileLevelQuerySupported, ++ ¶m, sizeof(param)); ++ ++ if (err != OK) { ++ break; ++ } ++ ++ CodecProfileLevel profileLevel; ++ profileLevel.mProfile = param.eProfile; ++ profileLevel.mLevel = param.eLevel; ++ ++ caps->mProfileLevels.push(profileLevel); ++ } ++ ++ // Color format query ++ // return colors in the order reported by the OMX component ++ // prefix "flexible" standard ones with the flexible equivalent ++ OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat; ++ InitOMXParams(&portFormat); ++ portFormat.nPortIndex = !isEncoder ? 1 : 0; ++ for (portFormat.nIndex = 0;; portFormat.nIndex) { ++ err = omx->getParameter( ++ node, OMX_IndexParamVideoPortFormat, ++ &portFormat, sizeof(portFormat)); ++ if (err != OK) { ++ break; ++ } ++ ++ OMX_U32 flexibleEquivalent; ++ if (ACodec::isFlexibleColorFormat( ++ omx, node, portFormat.eColorFormat, false /* usingNativeWindow */, ++ &flexibleEquivalent)) { ++ bool marked = false; ++ for (size_t i = 0; i < caps->mColorFormats.size(); i) { ++ if (caps->mColorFormats.itemAt(i) == flexibleEquivalent) { ++ marked = true; ++ break; ++ } ++ } ++ if (!marked) { ++ caps->mColorFormats.push(flexibleEquivalent); ++ } ++ } ++ caps->mColorFormats.push(portFormat.eColorFormat); ++ } ++ } ++ ++ if (isVideo && !isEncoder) { ++ if (omx->storeMetaDataInBuffers( ++ node, 1 /* port index */, OMX_TRUE) == OK || ++ omx->prepareForAdaptivePlayback( ++ node, 1 /* port index */, OMX_TRUE, ++ 1280 /* width */, 720 /* height */) == OK) { ++ caps->mFlags |= CodecCapabilities::kFlagSupportsAdaptivePlayback; ++ } ++ } ++ ++ CHECK_EQ(omx->freeNode(node), (status_t)OK); ++ ++ return OK; ++} ++ ++status_t QueryCodecs( ++ const sp &omx, ++ const char *mimeType, bool queryDecoders, ++ Vector *results) { ++ return QueryCodecs(omx, mimeType, queryDecoders, false /*hwCodecOnly*/, results); ++} ++ ++// These are supposed be equivalent to the logic in ++// "audio_channel_out_mask_from_count". ++status_t getOMXChannelMapping(size_t numChannels, OMX_AUDIO_CHANNELTYPE map[]) { ++ switch (numChannels) { ++ case 1: ++ map[0] = OMX_AUDIO_ChannelCF; ++ break; ++ case 2: ++ map[0] = OMX_AUDIO_ChannelLF; ++ map[1] = OMX_AUDIO_ChannelRF; ++ break; ++ case 3: ++ map[0] = OMX_AUDIO_ChannelLF; ++ map[1] = OMX_AUDIO_ChannelRF; ++ map[2] = OMX_AUDIO_ChannelCF; ++ break; ++ case 4: ++ map[0] = OMX_AUDIO_ChannelLF; ++ map[1] = OMX_AUDIO_ChannelRF; ++ map[2] = OMX_AUDIO_ChannelLR; ++ map[3] = OMX_AUDIO_ChannelRR; ++ break; ++ case 5: ++ map[0] = OMX_AUDIO_ChannelLF; ++ map[1] = OMX_AUDIO_ChannelRF; ++ map[2] = OMX_AUDIO_ChannelCF; ++ map[3] = OMX_AUDIO_ChannelLR; ++ map[4] = OMX_AUDIO_ChannelRR; ++ break; ++ case 6: ++ map[0] = OMX_AUDIO_ChannelLF; ++ map[1] = OMX_AUDIO_ChannelRF; ++ map[2] = OMX_AUDIO_ChannelCF; ++ map[3] = OMX_AUDIO_ChannelLFE; ++ map[4] = OMX_AUDIO_ChannelLR; ++ map[5] = OMX_AUDIO_ChannelRR; ++ break; ++ case 7: ++ map[0] = OMX_AUDIO_ChannelLF; ++ map[1] = OMX_AUDIO_ChannelRF; ++ map[2] = OMX_AUDIO_ChannelCF; ++ map[3] = OMX_AUDIO_ChannelLFE; ++ map[4] = OMX_AUDIO_ChannelLR; ++ map[5] = OMX_AUDIO_ChannelRR; ++ map[6] = OMX_AUDIO_ChannelCS; ++ break; ++ case 8: ++ map[0] = OMX_AUDIO_ChannelLF; ++ map[1] = OMX_AUDIO_ChannelRF; ++ map[2] = OMX_AUDIO_ChannelCF; ++ map[3] = OMX_AUDIO_ChannelLFE; ++ map[4] = OMX_AUDIO_ChannelLR; ++ map[5] = OMX_AUDIO_ChannelRR; ++ map[6] = OMX_AUDIO_ChannelLS; ++ map[7] = OMX_AUDIO_ChannelRS; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return OK; ++} ++ ++} // namespace android +diff --git a/media/libstagefright/colorconversion/Android.mk b/media/libstagefright/colorconversion/Android.mk +index 0bf9701c8..334aa7e5e 100644 +--- a/media/libstagefright/colorconversion/Android.mk ++++ b/media/libstagefright/colorconversion/Android.mk +@@ -13,6 +13,13 @@ LOCAL_C_INCLUDES := \ + LOCAL_STATIC_LIBRARIES := \ + libyuv_static \ + ++ifeq ($(BOARD_HAS_MTK_HARDWARE),true) ++LOCAL_CFLAGS += -DMTK_HARDWARE ++ ++LOCAL_C_INCLUDES += \ ++ $(TOP)/frameworks/av/include/media/stagefright/dpframework ++endif ++ + LOCAL_CFLAGS += -Werror + LOCAL_CLANG := true + LOCAL_SANITIZE := signed-integer-overflow +diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp +index 3ca7cc05c..b250342e7 100644 +--- a/media/libstagefright/colorconversion/ColorConverter.cpp ++++ b/media/libstagefright/colorconversion/ColorConverter.cpp +@@ -26,6 +26,14 @@ + + #define USE_LIBYUV + ++#ifdef MTK_HARDWARE ++#include ++ ++const OMX_COLOR_FORMATTYPE OMX_MTK_COLOR_FormatYV12 = (OMX_COLOR_FORMATTYPE)0x7F000200; ++const OMX_COLOR_FORMATTYPE OMX_COLOR_FormatVendorMTKYUV = (OMX_COLOR_FORMATTYPE)0x7F000001; ++const OMX_COLOR_FORMATTYPE OMX_COLOR_FormatVendorMTKYUV_FCM = (OMX_COLOR_FORMATTYPE)0x7F000002; ++#endif ++ + namespace android { + + ColorConverter::ColorConverter( +@@ -51,6 +59,11 @@ bool ColorConverter::isValid() const { + case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: + case OMX_COLOR_FormatYUV420SemiPlanar: + case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: ++#ifdef MTK_HARDWARE ++ case OMX_MTK_COLOR_FormatYV12: ++ case OMX_COLOR_FormatVendorMTKYUV: ++ case OMX_COLOR_FormatVendorMTKYUV_FCM: ++#endif + return true; + + default: +@@ -557,4 +570,92 @@ uint8_t *ColorConverter::initClip() { + return &mClip[-kClipMin]; + } + ++#ifdef MTK_HARDWARE ++status_t ColorConverter::convertYUVToRGBHW(const BitmapParams &src, const BitmapParams &dst) { ++ DpBlitStream blitStream; ++ unsigned int srcWStride = src.mWidth; ++ unsigned int srcHStride = src.mHeight; ++ ++ DpRect srcRoi; ++ srcRoi.x = src.mCropLeft; ++ srcRoi.y = src.mCropTop; ++ srcRoi.w = src.mCropRight - src.mCropLeft; ++ srcRoi.h = src.mCropBottom - src.mCropTop; ++ ++ unsigned int dstWStride = dst.mWidth ; ++ unsigned int dstHStride = dst.mHeight ; ++ char name_yuv[100]; ++ char retriever_yuv_propty[100]; ++ char name_rgb[100]; ++ char retriever_propty_rgb[100]; ++ ++ if (mSrcFormat == OMX_COLOR_FormatYUV420Planar) { ++ char* planar[3]; ++ unsigned int length[3]; ++ planar[0] = (char*)src.mBits; ++ length[0] = srcWStride*srcHStride; ++ planar[1] = planar[0] + length[0]; ++ length[1] = srcWStride*srcHStride/4; ++ planar[2] = planar[1] + length[1]; ++ length[2] = length[1]; ++ ++ blitStream.setSrcBuffer((void**)planar, (unsigned int*)length, 3); ++ blitStream.setSrcConfig(srcWStride, srcHStride, eYUV_420_3P, eInterlace_None, &srcRoi); ++ } ++ else if (mSrcFormat == OMX_MTK_COLOR_FormatYV12) { ++ char* planar[3]; ++ unsigned int length[3]; ++ planar[0] = (char*)src.mBits; ++ length[0] = srcWStride*srcHStride; ++ planar[1] = planar[0] + length[0]; ++ length[1] = srcWStride*srcHStride/4; ++ planar[2] = planar[1] + length[1]; ++ length[2] = length[1]; ++ ++ blitStream.setSrcBuffer((void**)planar, (unsigned int*)length, 3); ++ blitStream.setSrcConfig(srcWStride, srcHStride, eYV12, eInterlace_None, &srcRoi); ++ } ++ else if (mSrcFormat == OMX_COLOR_FormatVendorMTKYUV) { ++ char* planar[2]; ++ unsigned int length[2]; ++ planar[0] = (char*)src.mBits; ++ length[0] = srcWStride*srcHStride; ++ planar[1] = planar[0] + length[0]; ++ length[1] = srcWStride*srcHStride/2; ++ ++ blitStream.setSrcBuffer((void**)planar, (unsigned int*)length, 2); ++ blitStream.setSrcConfig(srcWStride, srcHStride, srcWStride * 32, srcWStride * 16, eNV12_BLK, DP_PROFILE_BT601, eInterlace_None, &srcRoi); ++ } ++ else if (mSrcFormat == OMX_COLOR_FormatVendorMTKYUV_FCM) { ++ char* planar[2]; ++ unsigned int length[2]; ++ planar[0] = (char*)src.mBits; ++ length[0] = srcWStride*srcHStride; ++ planar[1] = planar[0] + length[0]; ++ length[1] = srcWStride*srcHStride/2; ++ ++ blitStream.setSrcBuffer((void**)planar, (unsigned int*)length, 2); ++ blitStream.setSrcConfig(srcWStride, srcHStride, srcWStride * 32, srcWStride * 16, eNV12_BLK_FCM, DP_PROFILE_BT601, eInterlace_None, &srcRoi); ++ } ++ ++ if (mDstFormat == OMX_COLOR_Format16bitRGB565) { ++ blitStream.setDstBuffer(dst.mBits, dst.mWidth * dst.mHeight * 2); ++ blitStream.setDstConfig(dst.mWidth, dst.mHeight, eRGB565); ++ } ++ else if (mDstFormat == OMX_COLOR_Format32bitARGB8888) { ++ blitStream.setDstBuffer(dst.mBits, dst.mWidth * dst.mHeight * 4); ++ blitStream.setDstConfig(dst.mWidth, dst.mHeight, eRGBA8888); ++ } ++ ++ // Add Sharpness in Video Thumbnail ++ blitStream.setTdshp(1); ++ bool bRet = blitStream.invalidate(); ++ ++ if (!bRet) ++ return OK; ++ else ++ return UNKNOWN_ERROR; ++} ++#endif ++ + } // namespace android +-- +2.11.0 + diff --git a/patches/patch.sh b/patches/patch.sh new file mode 100644 index 0000000..fd76e31 --- /dev/null +++ b/patches/patch.sh @@ -0,0 +1,13 @@ +ROOT="${PWD}" +DEVICE_DIR="device/alcatel/ttab" +REPOSITORIES=( + 'frameworks/av' +) + +for repository in "${REPOSITORIES[@]}"; do + cd "${ROOT}/${repository}" + + git am --whitespace=nowarn --keep-cr "${ROOT}/${DEVICE_DIR}/patches/${repository}"/* + + cd "${ROOT}" +done -- 2.20.1