diff options
author | Dharmaray Kundargi <dharmaray@google.com> | 2011-01-16 15:59:43 -0800 |
---|---|---|
committer | Dharmaray Kundargi <dharmaray@google.com> | 2011-01-17 09:59:01 -0800 |
commit | 7c9d8018755adf1857571125ba1b3598c96ea506 (patch) | |
tree | 6f97c14846692c0a7580c3a6019f7c91c669ffcf /libvideoeditor/vss/stagefrightshells/src | |
parent | 5358e878396e1c451e9f9ef07237c2e6ab662d49 (diff) | |
download | frameworks_av-7c9d8018755adf1857571125ba1b3598c96ea506.zip frameworks_av-7c9d8018755adf1857571125ba1b3598c96ea506.tar.gz frameworks_av-7c9d8018755adf1857571125ba1b3598c96ea506.tar.bz2 |
Removed unwanted line in M4READER_Amr.h
vss core files upload on honeycomb
Change-Id: I61206ae2398ce8ac544c6fb01a76fe8917bce75b
Diffstat (limited to 'libvideoeditor/vss/stagefrightshells/src')
9 files changed, 7895 insertions, 0 deletions
diff --git a/libvideoeditor/vss/stagefrightshells/src/Android.mk b/libvideoeditor/vss/stagefrightshells/src/Android.mk new file mode 100755 index 0000000..297bfa7 --- /dev/null +++ b/libvideoeditor/vss/stagefrightshells/src/Android.mk @@ -0,0 +1,79 @@ +# +# Copyright (C) 2011 NXP Software +# Copyright (C) 2011 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. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + VideoEditorVideoDecoder.cpp \ + VideoEditorAudioDecoder.cpp \ + VideoEditorMp3Reader.cpp \ + VideoEditor3gpReader.cpp \ + VideoEditorUtils.cpp \ + VideoEditorBuffer.c \ + VideoEditorVideoEncoder.cpp \ + VideoEditorAudioEncoder.cpp + +LOCAL_C_INCLUDES += \ + $(TOP)/frameworks/base/core/jni \ + $(TOP)/frameworks/base/include \ + $(TOP)/frameworks/base/include/media \ + $(TOP)/frameworks/base/media/libmediaplayerservice \ + $(TOP)/frameworks/base/media/libstagefright \ + $(TOP)/frameworks/base/media/libstagefright/include \ + $(TOP)/frameworks/base/media/libstagefright/rtsp \ + $(JNI_H_INCLUDE) \ + $(call include-path-for, corecg graphics) \ + $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \ + $(TOP)/external/opencore/android \ + $(TOP)/vendor/qcom/proprietary/qdsp6/mm-core/omxcore/inc \ + $(TOP)/frameworks/base/core/jni/mediaeditor \ + $(TOP)/frameworks/media/libvideoeditor/vss/inc \ + $(TOP)/frameworks/media/libvideoeditor/vss/common/inc \ + $(TOP)/frameworks/media/libvideoeditor/vss/mcs/inc \ + $(TOP)/frameworks/media/libvideoeditor/lvpp \ + $(TOP)/frameworks/media/libvideoeditor/osal/inc \ + $(TOP)/frameworks/media/libvideoeditor/vss/stagefrightshells/inc + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libandroid_runtime \ + libnativehelper \ + libmedia \ + libbinder \ + libstagefright \ + libstagefright_omx \ + libsurfaceflinger_client \ + libvideoeditorplayer + +LOCAL_CFLAGS += \ + + + +LOCAL_LDFLAGS += -fuse-ld=bfd + +LOCAL_STATIC_LIBRARIES := \ + libvideoeditor_osal \ + libstagefright_color_conversion + + +LOCAL_MODULE:= libvideoeditor_stagefrightshells + +LOCAL_MODULE_TAGS := eng development + +include $(BUILD_STATIC_LIBRARY) diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp new file mode 100755 index 0000000..70a5a81 --- /dev/null +++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp @@ -0,0 +1,1967 @@ +/* + * Copyright (C) 2011 NXP Software + * Copyright (C) 2011 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. + */ +/** +************************************************************************* +* @file VideoEditor3gpReader.cpp +* @brief StageFright shell 3GP Reader +************************************************************************* +*/ + +#define LOG_NDEBUG 1 +#define LOG_TAG "VIDEOEDITOR_3GPREADER" + +/** + * HEADERS + * + */ +#define VIDEOEDITOR_BITSTREAM_PARSER + +#include "M4OSA_Debug.h" +#include "VideoEditor3gpReader.h" +#include "M4SYS_AccessUnit.h" +#include "VideoEditorUtils.h" +#include "M4READER_3gpCom.h" +#include "M4_Common.h" +#include "M4OSA_FileWriter.h" + +#ifdef VIDEOEDITOR_BITSTREAM_PARSER +#include "M4OSA_CoreID.h" +#include "M4OSA_Error.h" +#include "M4OSA_Memory.h" +#include "M4_Utils.h" +#endif + +#include "ESDS.h" +#include "utils/Log.h" +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/FileSource.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> + +/** + * SOURCE CLASS + */ +namespace android { +/** + * ENGINE INTERFACE + */ + +/** + ************************************************************************ + * @brief Array of AMR NB/WB bitrates + * @note Array to match the mode and the bit rate + ************************************************************************ +*/ +const M4OSA_UInt32 VideoEditor3gpReader_AmrBitRate [2 /* 8kHz / 16kHz */] + [9 /* the bitrate mode */] = +{ + {4750, 5150, 5900, 6700, 7400, 7950, 10200, 12200, 0}, + {6600, 8850, 12650, 14250, 15850, 18250, 19850, 23050, 23850} +}; + +/** + ******************************************************************************* + * structure VideoEditor3gpReader_Context + * @brief:This structure defines the context of the StageFright 3GP shell Reader + ******************************************************************************* +*/ +typedef struct { + sp<DataSource> mDataSource; + sp<MediaExtractor> mExtractor; + sp<MediaSource> mAudioSource; + sp<MediaSource> mVideoSource; + M4_StreamHandler* mAudioStreamHandler; + M4_StreamHandler* mVideoStreamHandler; + M4SYS_AccessUnit mAudioAu; + M4SYS_AccessUnit mVideoAu; + M4OSA_Time mMaxDuration; + int32_t mFileSize; + M4_StreamType mStreamType; + M4OSA_UInt32 mStreamId; + int32_t mTracks; + int32_t mCurrTrack; + M4OSA_Bool mAudioSeeking; + M4OSA_Time mAudioSeekTime; + M4OSA_Bool mVideoSeeking; + M4OSA_Time mVideoSeekTime; + +} VideoEditor3gpReader_Context; + +#ifdef VIDEOEDITOR_BITSTREAM_PARSER +/** + ************************************************************************ + * structure VideoEditor3gpReader_BitStreamParserContext + * @brief Internal BitStreamParser context + ************************************************************************ +*/ +typedef struct { + M4OSA_UInt32* mPbitStream; /**< bitstream pointer (32bits aligned) */ + M4OSA_Int32 mSize; /**< bitstream size in bytes */ + M4OSA_Int32 mIndex; /**< byte index */ + M4OSA_Int32 mBitIndex; /**< bit index */ + M4OSA_Int32 mStructSize; /**< size of structure */ +} VideoEditor3gpReader_BitStreamParserContext; + +/** + ******************************************************************************* + * @brief Allocates the context and initializes internal data. + * @param pContext (OUT) Pointer to the BitStreamParser context to create. + * @param bitStream A pointer to the bitstream + * @param size The size of the bitstream in bytes + ******************************************************************************* +*/ +static void VideoEditor3gpReader_BitStreamParserInit(void** pContext, + void* pBitStream, M4OSA_Int32 size) { + VideoEditor3gpReader_BitStreamParserContext* pStreamContext; + + *pContext=M4OSA_NULL; + pStreamContext = (VideoEditor3gpReader_BitStreamParserContext*)M4OSA_malloc( + sizeof(VideoEditor3gpReader_BitStreamParserContext), M4READER_3GP, + (M4OSA_Char*)"3GP BitStreamParser Context"); + if (M4OSA_NULL == pStreamContext) { + return; + } + pStreamContext->mPbitStream=(M4OSA_UInt32*)pBitStream; + pStreamContext->mSize=size; + pStreamContext->mIndex=0; + pStreamContext->mBitIndex=0; + pStreamContext->mStructSize = + sizeof(VideoEditor3gpReader_BitStreamParserContext); + + *pContext=pStreamContext; +} +/** + ********************************************************************** + * @brief Clean up context + * @param pContext (IN/OUT) BitStreamParser context. + ********************************************************************** +*/ +static void VideoEditor3gpReader_BitStreamParserCleanUp(void* pContext) { + M4OSA_free((M4OSA_Int32*)pContext); +} +/** + ***************************************************************************** + * @brief Read the next <length> bits in the bitstream. + * @note The function does not update the bitstream pointer. + * @param pContext (IN/OUT) BitStreamParser context. + * @param length (IN) The number of bits to extract from the bitstream + * @return the read bits + ***************************************************************************** +*/ +static M4OSA_UInt32 VideoEditor3gpReader_BitStreamParserShowBits(void* pContext, + M4OSA_Int32 length) { + VideoEditor3gpReader_BitStreamParserContext* pStreamContext = + (VideoEditor3gpReader_BitStreamParserContext*)pContext; + + M4OSA_UInt32 u_mask; + M4OSA_UInt32 retval; + M4OSA_Int32 i_ovf; + + M4OSA_DEBUG_IF1((M4OSA_NULL==pStreamContext), 0, + "VideoEditor3gpReader_BitStreamParserShowBits:invalid context pointer"); + + retval=(M4OSA_UInt32)GET_MEMORY32(pStreamContext->\ + mPbitStream[ pStreamContext->mIndex ]); + i_ovf = pStreamContext->mBitIndex + length - 32; + u_mask = (length >= 32) ? 0xffffffff: (1 << length) - 1; + + /* do we have enough bits availble in the current word(32bits)*/ + if (i_ovf <= 0) { + retval=(retval >> (- i_ovf)) & u_mask; + } else { + M4OSA_UInt32 u_nextword = (M4OSA_UInt32)GET_MEMORY32( + pStreamContext->mPbitStream[ pStreamContext->mIndex + 1 ]); + M4OSA_UInt32 u_msb_mask, u_msb_value, u_lsb_mask, u_lsb_value; + + u_msb_mask = ((1 << (32 - pStreamContext->mBitIndex)) - 1) << i_ovf; + u_msb_value = retval << i_ovf; + u_lsb_mask = (1 << i_ovf) - 1; + u_lsb_value = u_nextword >> (32 - i_ovf); + retval= (u_msb_value & u_msb_mask ) | (u_lsb_value & u_lsb_mask); + } + /* return the bits...*/ + return retval; +} +/** + ************************************************************************ + * @brief Increment the bitstream pointer of <length> bits. + * @param pContext (IN/OUT) BitStreamParser context. + * @param length (IN) The number of bit to shift the bitstream + ************************************************************************ +*/ +static void VideoEditor3gpReader_BitStreamParserFlushBits(void* pContext, + M4OSA_Int32 length) { + VideoEditor3gpReader_BitStreamParserContext* pStreamContext=( + VideoEditor3gpReader_BitStreamParserContext*)pContext; + M4OSA_Int32 val; + + if (M4OSA_NULL == pStreamContext) { + return; + } + val=pStreamContext->mBitIndex + length; + /* update the bits...*/ + pStreamContext->mBitIndex += length; + + if (val - 32 >= 0) { + /* update the bits...*/ + pStreamContext->mBitIndex -= 32; + /* update the words*/ + pStreamContext->mIndex++; + } +} + +static M4OSA_UInt32 VideoEditor3gpReader_BitStreamParserGetBits( + void* pContext,M4OSA_Int32 bitPos, M4OSA_Int32 bitLength) { + VideoEditor3gpReader_BitStreamParserContext* pStreamContext = + (VideoEditor3gpReader_BitStreamParserContext*)pContext; + + M4OSA_Int32 bitLocation, bitIndex; + M4OSA_UInt32 retval=0; + + M4OSA_DEBUG_IF1((M4OSA_NULL==pStreamContext), 0, + "VideoEditor3gpReader_BitStreamParserGetBits: invalid context pointer"); + + /* computes the word location*/ + bitLocation=bitPos/32; + bitIndex=(bitPos) % 32; + + if (bitLocation < pStreamContext->mSize) { + M4OSA_UInt32 u_mask; + M4OSA_Int32 i_ovf = bitIndex + bitLength - 32; + retval=(M4OSA_UInt32)GET_MEMORY32( + pStreamContext->mPbitStream[ bitLocation ]); + + u_mask = (bitLength >= 32) ? 0xffffffff: (1 << bitLength) - 1; + + if (i_ovf <= 0) { + retval=(retval >> (- i_ovf)) & u_mask; + } else { + M4OSA_UInt32 u_nextword = (M4OSA_UInt32)GET_MEMORY32( + pStreamContext->mPbitStream[ bitLocation + 1 ]); + M4OSA_UInt32 u_msb_mask, u_msb_value, u_lsb_mask, u_lsb_value; + + u_msb_mask = ((1 << (32 - bitIndex)) - 1) << i_ovf; + u_msb_value = retval << i_ovf; + u_lsb_mask = (1 << i_ovf) - 1; + u_lsb_value = u_nextword >> (32 - i_ovf); + retval= (u_msb_value & u_msb_mask ) | (u_lsb_value & u_lsb_mask); + } + } + return retval; +} + +static void VideoEditor3gpReader_BitStreamParserRestart(void* pContext) { + VideoEditor3gpReader_BitStreamParserContext* pStreamContext = + (VideoEditor3gpReader_BitStreamParserContext*)pContext; + + if (M4OSA_NULL == pStreamContext) { + return; + } + /* resets the bitstream pointers*/ + pStreamContext->mIndex=0; + pStreamContext->mBitIndex=0; +} +/** + ******************************************************************************* + * @brief Get a pointer to the current byte pointed by the bitstream pointer. + * @note It should be used carefully as the pointer is in the bitstream itself + * and no copy is made. + * @param pContext (IN/OUT) BitStreamParser context. + * @return Pointer to the current location in the bitstream + ******************************************************************************* +*/ +static M4OSA_UInt8* VideoEditor3gpReader_GetCurrentbitStreamPointer( + void* pContext) { + VideoEditor3gpReader_BitStreamParserContext* pStreamContext = + (VideoEditor3gpReader_BitStreamParserContext*)pContext; + M4OSA_DEBUG_IF1((M4OSA_NULL==pStreamContext), 0, "invalid context pointer"); + + return (M4OSA_UInt8*)((M4OSA_UInt8*)pStreamContext->mPbitStream + \ + pStreamContext->mIndex * sizeof(M4OSA_UInt32) + \ + pStreamContext->mBitIndex/8) ; +} + +static M4OSA_Int32 VideoEditor3gpReader_BitStreamParserGetSize(void* pContext) { + VideoEditor3gpReader_BitStreamParserContext* pStreamContext = + (VideoEditor3gpReader_BitStreamParserContext*)pContext; + M4OSA_DEBUG_IF1((M4OSA_NULL==pStreamContext), 0, "invalid context pointer"); + + return pStreamContext->mSize; +} + + +static void VideoEditor3gpReader_MPEG4BitStreamParserInit(void** pContext, + void* pBitStream, M4OSA_Int32 size) { + VideoEditor3gpReader_BitStreamParserInit(pContext, pBitStream, size); +} +static M4OSA_Int32 VideoEditor3gpReader_GetMpegLengthFromInteger(void* pContext, + M4OSA_UInt32 val) { + M4OSA_UInt32 length=0; + M4OSA_UInt32 numBytes=0; + M4OSA_UInt32 b=0; + + M4OSA_DEBUG_IF1((M4OSA_NULL==pContext), 0, "invalid context pointer"); + + /* the length is encoded as a sequence of bytes. The highest bit is used + to indicate that the length continues on the next byte. + + The length can be: 0x80 0x80 0x80 0x22 + of just 0x22 (highest bit not set) + + */ + + do { + b=(val & ((0xff)<< (8 * numBytes)))>> (8 * numBytes); + length=(length << 7) | (b & 0x7f); + numBytes++; + } while ((b & 0x80) && numBytes < 4); + + return length; +} + +/** + ******************************************************************************* + * @brief Decode an MPEG4 Systems descriptor size from an encoded SDL size data + * @note The value is read from the current bitstream location. + * @param pContext (IN/OUT) BitStreamParser context. + * @return Size in a human readable form + ******************************************************************************* +*/ +static M4OSA_Int32 VideoEditor3gpReader_GetMpegLengthFromStream(void* pContext){ + M4OSA_UInt32 length=0; + M4OSA_UInt32 numBytes=0; + M4OSA_UInt32 b=0; + + M4OSA_DEBUG_IF1((M4OSA_NULL==pContext), 0, "invalid context pointer"); + + /* the length is encoded as a sequence of bytes. The highest bit is used + to indicate that the length continues on the next byte. + + The length can be: 0x80 0x80 0x80 0x22 + of just 0x22 (highest bit not set) + */ + + do { + b=VideoEditor3gpReader_BitStreamParserShowBits(pContext, 8); + VideoEditor3gpReader_BitStreamParserFlushBits(pContext, 8); + length=(length << 7) | (b & 0x7f); + numBytes++; + } while ((b & 0x80) && numBytes < 4); + + return length; +} +#endif /* VIDEOEDITOR_BITSTREAM_PARSER */ +/** +************************************************************************ +* @brief create an instance of the 3gp reader + * @note allocates the context + * + * @param pContext: (OUT) pointer on a reader context + * + * @return M4NO_ERROR there is no error + * @return M4ERR_ALLOC a memory allocation has failed + * @return M4ERR_PARAMETER at least one parameter is not valid +************************************************************************ +*/ + +M4OSA_ERR VideoEditor3gpReader_create(M4OSA_Context *pContext) { + VideoEditor3gpReader_Context* pC = NULL; + M4OSA_ERR err = M4NO_ERROR; + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext , M4ERR_PARAMETER); + + LOGV("VideoEditor3gpReader_create begin"); + + /* Context allocation & initialization */ + SAFE_MALLOC(pC, VideoEditor3gpReader_Context, 1, "VideoEditor3gpReader"); + + memset(pC, sizeof(VideoEditor3gpReader_Context), 0); + + pC->mAudioStreamHandler = M4OSA_NULL; + pC->mAudioAu.dataAddress = M4OSA_NULL; + pC->mVideoStreamHandler = M4OSA_NULL; + pC->mVideoAu.dataAddress = M4OSA_NULL; + + pC->mAudioSeeking = M4OSA_FALSE; + pC->mAudioSeekTime = 0; + + pC->mVideoSeeking = M4OSA_FALSE; + pC->mVideoSeekTime = 0; + + M4OSA_INT64_FROM_INT32(pC->mMaxDuration, 0); + *pContext=pC; + +cleanUp: + if ( M4NO_ERROR == err ) { + LOGV("VideoEditor3gpReader_create no error"); + } else { + LOGV("VideoEditor3gpReader_create ERROR 0x%X", err); + } + LOGV("VideoEditor3gpReader_create end "); + return err; +} + +/** +************************************************************************** +* @brief destroy the instance of the 3gp reader +* @note after this call the context is invalid +* @param context: (IN) Context of the reader +* @return M4NO_ERROR there is no error +* @return M4ERR_PARAMETER pContext parameter is not properly set +************************************************************************** +*/ + +M4OSA_ERR VideoEditor3gpReader_destroy(M4OSA_Context pContext) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditor3gpReader_Context* pC = M4OSA_NULL; + + LOGV("VideoEditor3gpReader_destroy begin"); + + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + pC = (VideoEditor3gpReader_Context*)pContext; + + SAFE_FREE(pC->mAudioAu.dataAddress); + pC->mAudioAu.dataAddress = M4OSA_NULL; + SAFE_FREE(pC->mVideoAu.dataAddress); + pC->mVideoAu.dataAddress = M4OSA_NULL; + SAFE_FREE(pC); + pContext = M4OSA_NULL; + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditor3gpReader_destroy no error"); + } + else + { + LOGV("VideoEditor3gpReader_destroy ERROR 0x%X", err); + } + + LOGV("VideoEditor3gpReader_destroy end "); + return err; +} + +/** +************************************************************************ +* @brief open the reader and initializes its created instance +* @note this function open the media file +* @param context: (IN) Context of the reader +* @param pFileDescriptor: (IN) Pointer to proprietary data identifying +* the media to open +* @return M4NO_ERROR there is no error +* @return M4ERR_PARAMETER the context is NULL +************************************************************************ +*/ + +M4OSA_ERR VideoEditor3gpReader_open(M4OSA_Context pContext, + M4OSA_Void* pFileDescriptor) { + VideoEditor3gpReader_Context* pC = (VideoEditor3gpReader_Context*)pContext; + M4OSA_ERR err = M4NO_ERROR; + + LOGV("VideoEditor3gpReader_open start "); + M4OSA_DEBUG_IF1((M4OSA_NULL == pC), M4ERR_PARAMETER, + "VideoEditor3gpReader_open: invalid context pointer"); + M4OSA_DEBUG_IF1((M4OSA_NULL == pFileDescriptor), M4ERR_PARAMETER, + "VideoEditor3gpReader_open: invalid pointer pFileDescriptor"); + + LOGV("VideoEditor3gpReader_open Datasource start %s", + (char*)pFileDescriptor); + pC->mDataSource = DataSource::CreateFromURI((char*)pFileDescriptor); + + if (pC->mDataSource == NULL) { + LOGV("VideoEditor3gpReader_open Datasource error"); + return M4ERR_PARAMETER; + } + + pC->mExtractor = MediaExtractor::Create(pC->mDataSource, + MEDIA_MIMETYPE_CONTAINER_MPEG4); + + if (pC->mExtractor == NULL) { + LOGV("VideoEditor3gpReader_open extractor error"); + return M4ERR_PARAMETER; + } + + LOGV("VideoEditor3gpReader_open end "); + return err; +} + +/** +************************************************************************ +* @brief close the reader +* @note close the 3GP file +* @param context: (IN) Context of the reader +* @return M4NO_ERROR there is no error +* @return M4ERR_PARAMETER the context is NULL +* @return M4ERR_BAD_CONTEXT provided context is not a valid one +************************************************************************ +*/ +M4OSA_ERR VideoEditor3gpReader_close(M4OSA_Context context) { + VideoEditor3gpReader_Context *pC = (VideoEditor3gpReader_Context*)context; + M4READER_AudioSbrUserdata *pAudioSbrUserData; + M4_AccessUnit *pAU; + M4OSA_ERR err = M4NO_ERROR; + + LOGV("VideoEditor3gpReader_close begin"); + + M4OSA_DEBUG_IF1((M4OSA_NULL == pC), M4ERR_PARAMETER, + "VideoEditor3gpReader_close: invalid context pointer"); + + if (pC->mAudioStreamHandler) { + LOGV("VideoEditor3gpReader_close Audio"); + + if (M4OSA_NULL != pC->mAudioStreamHandler->m_pDecoderSpecificInfo) { + M4OSA_free((M4OSA_MemAddr32)pC->mAudioStreamHandler->\ + m_pDecoderSpecificInfo); + pC->mAudioStreamHandler->m_decoderSpecificInfoSize = 0; + pC->mAudioStreamHandler->m_pDecoderSpecificInfo = M4OSA_NULL; + } + + if ((M4DA_StreamTypeAudioAac == pC->mAudioStreamHandler->m_streamType) + && (M4OSA_NULL != pC->mAudioStreamHandler->m_pUserData)) { + pAudioSbrUserData = (M4READER_AudioSbrUserdata*)(\ + pC->mAudioStreamHandler->m_pUserData); + + pAU = (M4_AccessUnit*)pAudioSbrUserData->m_pFirstAU; + if (M4OSA_NULL != pAU) { + M4OSA_free((M4OSA_MemAddr32)pAU); + } + + if (M4OSA_NULL != pAudioSbrUserData->m_pAacDecoderUserConfig) { + M4OSA_free((M4OSA_MemAddr32)pAudioSbrUserData->\ + m_pAacDecoderUserConfig); + } + M4OSA_free((M4OSA_MemAddr32)pAudioSbrUserData); + pC->mAudioStreamHandler->m_pUserData = M4OSA_NULL; + } + + if (pC->mAudioStreamHandler->m_pESDSInfo != M4OSA_NULL) { + M4OSA_free((M4OSA_MemAddr32)pC->mAudioStreamHandler->m_pESDSInfo); + pC->mAudioStreamHandler->m_pESDSInfo = M4OSA_NULL; + pC->mAudioStreamHandler->m_ESDSInfoSize = 0; + } + /* Finally destroy the stream handler */ + M4OSA_free((M4OSA_MemAddr32)pC->mAudioStreamHandler); + pC->mAudioStreamHandler = M4OSA_NULL; + + pC->mAudioSource->stop(); + pC->mAudioSource.clear(); + } + if (pC->mVideoStreamHandler) { + LOGV("VideoEditor3gpReader_close Video "); + + if(M4OSA_NULL != pC->mVideoStreamHandler->m_pDecoderSpecificInfo) { + M4OSA_free((M4OSA_MemAddr32)pC->mVideoStreamHandler->\ + m_pDecoderSpecificInfo); + pC->mVideoStreamHandler->m_decoderSpecificInfoSize = 0; + pC->mVideoStreamHandler->m_pDecoderSpecificInfo = M4OSA_NULL; + } + + if(M4OSA_NULL != pC->mVideoStreamHandler->m_pH264DecoderSpecificInfo) { + M4OSA_free((M4OSA_MemAddr32)pC->mVideoStreamHandler->\ + m_pH264DecoderSpecificInfo); + pC->mVideoStreamHandler->m_H264decoderSpecificInfoSize = 0; + pC->mVideoStreamHandler->m_pH264DecoderSpecificInfo = M4OSA_NULL; + } + + if(pC->mVideoStreamHandler->m_pESDSInfo != M4OSA_NULL) { + M4OSA_free((M4OSA_MemAddr32)pC->mVideoStreamHandler->m_pESDSInfo); + pC->mVideoStreamHandler->m_pESDSInfo = M4OSA_NULL; + pC->mVideoStreamHandler->m_ESDSInfoSize = 0; + } + + /* Finally destroy the stream handler */ + M4OSA_free((M4OSA_MemAddr32)pC->mVideoStreamHandler); + pC->mVideoStreamHandler = M4OSA_NULL; + + pC->mVideoSource->stop(); + pC->mVideoSource.clear(); + } + pC->mDataSource.clear(); + + LOGV("VideoEditor3gpReader_close end"); + return err; +} + +/** +************************************************************************ +* @brief get an option from the 3gp reader +* @note it allows the caller to retrieve a property value: +* +* @param context: (IN) Context of the reader +* @param optionId: (IN) indicates the option to get +* @param pValue: (OUT) pointer to structure or value (allocated +* by user) where option is stored +* +* @return M4NO_ERROR there is no error +* @return M4ERR_BAD_CONTEXT provided context is not a valid one +* @return M4ERR_PARAMETER at least one parameter is not properly set +* @return M4ERR_BAD_OPTION_ID when the option ID is not a valid one +* @return M4ERR_VIDEO_NOT_H263 No video stream H263 in file. +* @return M4ERR_NO_VIDEO_STREAM_RETRIEVED_YET +* Function 3gpReader_getNextStreamHandler must be called before +************************************************************************ +*/ +M4OSA_ERR VideoEditor3gpReader_getOption(M4OSA_Context context, + M4OSA_OptionID optionId, M4OSA_DataOption pValue) { + VideoEditor3gpReader_Context* pC = (VideoEditor3gpReader_Context*)context; + M4OSA_ERR err = M4NO_ERROR; + + LOGV("VideoEditor3gpReader_getOption begin %d", optionId); + + M4OSA_DEBUG_IF1((M4OSA_NULL == pC), M4ERR_PARAMETER, + "invalid context pointer"); + M4OSA_DEBUG_IF1((M4OSA_NULL == pValue), M4ERR_PARAMETER, + "VideoEditor3gpReader_getOption: invalid pointer on value"); + + switch (optionId) { + case M4READER_kOptionID_Duration: + { + LOGV("VideoEditor3gpReader_getOption duration %d",pC->mMaxDuration); + M4OSA_TIME_SET(*(M4OSA_Time*)pValue, pC->mMaxDuration); + } + break; + case M4READER_kOptionID_Version: + /* not used */ + LOGV("VideoEditor3gpReader_getOption: M4READER_kOptionID_Version"); + break; + + case M4READER_kOptionID_Copyright: + /* not used */ + LOGV(">>>>>>> M4READER_kOptionID_Copyright"); + break; + + case M4READER_kOptionID_CreationTime: + /* not used */ + LOGV("VideoEditor3gpReader_getOption M4READER_kOptionID_CreationTime"); + break; + + case M4READER_kOptionID_Bitrate: + { + M4OSA_UInt32* pBitrate = (M4OSA_UInt32*)pValue; + + if (pC->mMaxDuration != 0) { + M4OSA_UInt32 ui32Tmp = (M4OSA_UInt32)pC->mMaxDuration; + *pBitrate = (M4OSA_UInt32)((M4OSA_Double)pC->mFileSize * \ + 8000.0 / (M4OSA_Double)ui32Tmp); + LOGV("3gpReader_getOption bitrate: %d", *pBitrate); + } + *pBitrate = 384000; //check + LOGV("VideoEditor3gpReader_getOption bitrate %ld", *pBitrate); + } + break; + case M4READER_3GP_kOptionID_H263Properties: + { +#if 0 + if(M4OSA_NULL == pC->mVideoStreamHandler) { + LOGV("VideoEditor3gpReader_getOption no videoStream retrieved"); + + err = M4ERR_NO_VIDEO_STREAM_RETRIEVED_YET; + break; + } + if((M4DA_StreamTypeVideoH263 != pC->mVideoStreamHandler->\ + mStreamType) || (pC->mVideoStreamHandler->\ + m_decoderSpecificInfoSize < 7)) { + LOGV("VideoEditor3gpReader_getOption DSI Size %d", + pC->mVideoStreamHandler->m_decoderSpecificInfoSize); + + err = M4ERR_VIDEO_NOT_H263; + break; + } + + /* MAGICAL in the decoder confi H263: the 7th byte is the profile + * number, 6th byte is the level number */ + ((M4READER_3GP_H263Properties *)pValue)->uiProfile = + pC->mVideoStreamHandler->m_pDecoderSpecificInfo[6]; + ((M4READER_3GP_H263Properties *)pValue)->uiLevel = + pC->mVideoStreamHandler->m_pDecoderSpecificInfo[5]; +#endif + LOGV("VideoEditor3gpReader_getOption M4READER_3GP_kOptionID_\ + H263Properties end"); + } + break; + case M4READER_3GP_kOptionID_PurpleLabsDrm: + LOGV("VideoEditor3gpReaderOption M4READER_3GP_kOptionID_PurpleLabsDrm"); + /* not used */ + break; + + case M4READER_kOptionID_GetNumberOfAudioAu: + /* not used */ + LOGV("VideoEditor3gpReadeOption M4READER_kOptionID_GetNumberOfAudioAu"); + break; + + case M4READER_kOptionID_GetNumberOfVideoAu: + /* not used */ + LOGV("VideoEditor3gpReader_getOption :GetNumberOfVideoAu"); + break; + + case M4READER_kOptionID_GetMetadata: + /* not used */ + LOGV("VideoEditor3gpReader_getOption M4READER_kOptionID_GetMetadata"); + break; + + case M4READER_kOptionID_3gpFtypBox: + /* used only for SEMC */ + LOGV("VideoEditor3gpReader_getOption M4READER_kOptionID_3gpFtypBox"); + err = M4ERR_BAD_OPTION_ID; //check this + break; + +#ifdef OPTIONID_GET_NEXT_VIDEO_CTS + case M4READER_3GP_kOptionID_getNextVideoCTS: + /* not used */ + LOGV("VideoEditor3gpReader_getOption: getNextVideoCTS"); + break; +#endif + default: + { + err = M4ERR_BAD_OPTION_ID; + LOGV("VideoEditor3gpReader_getOption M4ERR_BAD_OPTION_ID"); + } + break; + } + LOGV("VideoEditor3gpReader_getOption end: optionID: x%x", optionId); + return err; +} +/** +************************************************************************ +* @brief set an option on the 3gp reader +* @note No option can be set yet. +* @param context: (IN) Context of the reader +* @param optionId: (IN) indicates the option to set +* @param pValue: (IN) pointer to structure or value (allocated +* by user) where option is stored +* @return M4NO_ERROR there is no error +* @return M4ERR_BAD_CONTEXT provided context is not a valid one +* @return M4ERR_PARAMETER at least one parameter is not properly set +* @return M4ERR_BAD_OPTION_ID when the option ID is not a valid one +************************************************************************ +*/ +M4OSA_ERR VideoEditor3gpReader_setOption(M4OSA_Context context, + M4OSA_OptionID optionId, M4OSA_DataOption pValue) { + VideoEditor3gpReader_Context* pC = (VideoEditor3gpReader_Context*)context; + M4OSA_ERR err = M4NO_ERROR; + + /* Check function parameters */ + M4OSA_DEBUG_IF1((M4OSA_NULL == pC), M4ERR_PARAMETER, + "invalid context pointer"); + M4OSA_DEBUG_IF1((M4OSA_NULL == pValue), M4ERR_PARAMETER, + "invalid value pointer"); + + LOGV("VideoEditor3gpReader_setOption begin %d",optionId); + + switch(optionId) { + case M4READER_kOptionID_SetOsaFileReaderFctsPtr: + break; + + case M4READER_3GP_kOptionID_AudioOnly: + break; + + case M4READER_3GP_kOptionID_VideoOnly: + break; + + case M4READER_3GP_kOptionID_FastOpenMode: + break; + + case M4READER_kOptionID_MaxMetadataSize: + break; + + default: + { + LOGV("VideoEditor3gpReader_setOption: returns M4ERR_BAD_OPTION_ID"); + err = M4ERR_BAD_OPTION_ID; + } + break; + } + LOGV("VideoEditor3gpReader_setOption end "); + return err; +} +/** + ************************************************************************ + * @brief fill the access unit structure with initialization values + * @param context: (IN) Context of the reader + * @param pStreamHandler: (IN) pointer to the stream handler to which + * the access unit will be associated + * @param pAccessUnit: (IN/OUT) pointer to the access unit (allocated + * by the caller) to initialize + * @return M4NO_ERROR there is no error + * @return M4ERR_PARAMETER at least one parameter is not properly set + ************************************************************************ +*/ +M4OSA_ERR VideoEditor3gpReader_fillAuStruct(M4OSA_Context context, + M4_StreamHandler *pStreamHandler, M4_AccessUnit *pAccessUnit) { + VideoEditor3gpReader_Context* pC = (VideoEditor3gpReader_Context*)context; + M4OSA_ERR err= M4NO_ERROR; + + M4OSA_DEBUG_IF1((pC == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_fillAuStruct: invalid context"); + M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_fillAuStruc invalid pointer to M4_StreamHandler"); + M4OSA_DEBUG_IF1((pAccessUnit == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_fillAuStruct: invalid pointer to M4_AccessUnit"); + + LOGV("VideoEditor3gpReader_fillAuStruct begin"); + + /* Initialize pAccessUnit structure */ + pAccessUnit->m_size = 0; + pAccessUnit->m_CTS = 0; + pAccessUnit->m_DTS = 0; + pAccessUnit->m_attribute = 0; + pAccessUnit->m_dataAddress = M4OSA_NULL; + pAccessUnit->m_maxsize = pStreamHandler->m_maxAUSize; + pAccessUnit->m_streamID = pStreamHandler->m_streamId; + pAccessUnit->m_structSize = sizeof(M4_AccessUnit); + + LOGV("VideoEditor3gpReader_fillAuStruct end"); + return M4NO_ERROR; +} + +/** +******************************************************************************** +* @brief jump into the stream at the specified time +* @note +* @param context: (IN) Context of the reader +* @param pStreamHandler (IN) the stream handler of the stream to make jump +* @param pTime (I/O)IN the time to jump to (in ms) +* OUT the time to which the stream really jumped +* @return M4NO_ERROR there is no error +* @return M4ERR_PARAMETER at least one parameter is not properly set +******************************************************************************** +*/ +M4OSA_ERR VideoEditor3gpReader_jump(M4OSA_Context context, + M4_StreamHandler *pStreamHandler, M4OSA_Int32* pTime) { + VideoEditor3gpReader_Context* pC = (VideoEditor3gpReader_Context*)context; + M4OSA_ERR err = M4NO_ERROR; + M4SYS_AccessUnit* pAu; + M4OSA_Time time64; + M4OSA_Double timeDouble; + + M4OSA_DEBUG_IF1((pC == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_jump: invalid context"); + M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_jump: invalid pointer to M4_StreamHandler"); + M4OSA_DEBUG_IF1((pTime == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_jump: invalid time pointer"); + + LOGV("VideoEditor3gpReader_jump begin"); + + if (*pTime == (pStreamHandler->m_duration)) { + *pTime -= 1; + } + M4OSA_INT64_FROM_INT32(time64, *pTime); + + LOGV("VideoEditor3gpReader_jump time us %ld ", time64); + + if ((pC->mAudioStreamHandler != M4OSA_NULL) && + (pStreamHandler->m_streamId == pC->mAudioStreamHandler->m_streamId)) + { + pAu = &pC->mAudioAu; + pAu->CTS = time64; + pAu->DTS = time64; + + time64 = time64 * 1000; /* Convert the time into micro sec */ + pC->mAudioSeeking = M4OSA_TRUE; + pC->mAudioSeekTime = time64; + LOGV("VideoEditor3gpReader_jump AUDIO time us %ld ", time64); + } else if ((pC->mVideoStreamHandler != M4OSA_NULL) && + (pStreamHandler->m_streamId == pC->mVideoStreamHandler->m_streamId)) + { + pAu = &pC->mVideoAu; + pAu->CTS = time64; + pAu->DTS = time64; + + time64 = time64 * 1000; /* Convert the time into micro sec */ + pC->mVideoSeeking = M4OSA_TRUE; + pC->mVideoSeekTime = time64; + LOGV("VideoEditor3gpReader_jump VIDEO time us %ld ", time64); + } else { + LOGV("VideoEditor3gpReader_jump passed StreamHandler is not known\n"); + return M4ERR_PARAMETER; + } + time64 = time64 / 1000; /* Convert the time into milli sec */ + LOGV("VideoEditor3gpReader_jump time ms before seekset %ld ", time64); + + M4OSA_INT64_TO_DOUBLE(timeDouble, time64); + *pTime = (M4OSA_Int32)timeDouble; + + LOGV("VideoEditor3gpReader_jump end"); + err = M4NO_ERROR; + return err; +} +/** +******************************************************************************** +* @brief reset the stream, that is seek it to beginning and make it ready +* @note +* @param context: (IN) Context of the reader +* @param pStreamHandler (IN) The stream handler of the stream to reset +* @return M4NO_ERROR there is no error +* @return M4ERR_PARAMETER at least one parameter is not properly set +******************************************************************************** +*/ +M4OSA_ERR VideoEditor3gpReader_reset(M4OSA_Context context, + M4_StreamHandler *pStreamHandler) { + VideoEditor3gpReader_Context* pC = (VideoEditor3gpReader_Context*)context; + M4OSA_ERR err = M4NO_ERROR; + M4SYS_StreamID streamIdArray[2]; + M4SYS_AccessUnit* pAu; + M4OSA_Time time64; + + M4OSA_DEBUG_IF1((pC == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_reset: invalid context"); + M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_reset: invalid pointer to M4_StreamHandler"); + + M4OSA_INT64_FROM_INT32(time64, 0); + + LOGV("VideoEditor3gpReader_reset begin"); + + if (pStreamHandler == (M4_StreamHandler*)pC->mAudioStreamHandler) { + pAu = &pC->mAudioAu; + } else if (pStreamHandler == (M4_StreamHandler*)pC->mVideoStreamHandler) { + pAu = &pC->mVideoAu; + } else { + LOGV("VideoEditor3gpReader_reset passed StreamHandler is not known\n"); + return M4ERR_PARAMETER; + } + + pAu->CTS = time64; + pAu->DTS = time64; + + LOGV("VideoEditor3gpReader_reset end"); + return err; +} + +/** +******************************************************************************** +* @brief Gets an access unit (AU) from the stream handler source. +* @note An AU is the smallest possible amount of data to be decoded by decoder +* +* @param context: (IN) Context of the reader +* @param pStreamHandler (IN) The stream handler of the stream to make jump +* @param pAccessUnit (IO) Pointer to access unit to fill with read data +* @return M4NO_ERROR there is no error +* @return M4ERR_PARAMETER at least one parameter is not properly set +* @returns M4ERR_ALLOC memory allocation failed +* @returns M4WAR_NO_MORE_AU there are no more access unit in the stream +******************************************************************************** +*/ +M4OSA_ERR VideoEditor3gpReader_getNextAu(M4OSA_Context context, + M4_StreamHandler *pStreamHandler, M4_AccessUnit *pAccessUnit) { + VideoEditor3gpReader_Context* pC=(VideoEditor3gpReader_Context*)context; + M4OSA_ERR err = M4NO_ERROR; + M4SYS_AccessUnit* pAu; + int64_t tempTime64 = 0; + MediaBuffer *mMediaBuffer = NULL; + MediaSource::ReadOptions options; + M4OSA_Bool flag = M4OSA_FALSE; + status_t error; + + M4OSA_DEBUG_IF1((pReaderContext == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_getNextAu: invalid context"); + M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_getNextAu: invalid pointer to M4_StreamHandler"); + M4OSA_DEBUG_IF1((pAccessUnit == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_getNextAu: invalid pointer to M4_AccessUnit"); + + LOGV("VideoEditor3gpReader_getNextAu begin"); + + if (pStreamHandler == (M4_StreamHandler*)pC->mAudioStreamHandler) { + LOGV("VideoEditor3gpReader_getNextAu audio stream"); + pAu = &pC->mAudioAu; + if (pC->mAudioSeeking == M4OSA_TRUE) { + LOGV("VideoEditor3gpReader_getNextAu audio seek time: %ld", + pC->mAudioSeekTime); + options.setSeekTo(pC->mAudioSeekTime); + pC->mAudioSource->read(&mMediaBuffer, &options); + + mMediaBuffer->meta_data()->findInt64(kKeyTime, + (int64_t*)&tempTime64); + options.clearSeekTo(); + pC->mAudioSeeking = M4OSA_FALSE; + flag = M4OSA_TRUE; + } else { + LOGV("VideoEditor3gpReader_getNextAu audio no seek:"); + pC->mAudioSource->read(&mMediaBuffer, &options); + if (mMediaBuffer != NULL) { + mMediaBuffer->meta_data()->findInt64(kKeyTime, + (int64_t*)&tempTime64); + } + } + } else if (pStreamHandler == (M4_StreamHandler*)pC->mVideoStreamHandler) { + LOGV("VideoEditor3gpReader_getNextAu video steram "); + pAu = &pC->mVideoAu; + if(pC->mVideoSeeking == M4OSA_TRUE) { + flag = M4OSA_TRUE; + LOGV("VideoEditor3gpReader_getNextAu seek: %ld",pC->mVideoSeekTime); + options.setSeekTo(pC->mVideoSeekTime, + MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); + do + { + if (mMediaBuffer != NULL) { + LOGV("VideoEditor3gpReader_getNextAu free the MediaBuffer"); + mMediaBuffer->release(); + } + error = pC->mVideoSource->read(&mMediaBuffer, &options); + LOGV("VE3gpReader_getNextAu MediaBuffer %x , error %d", + mMediaBuffer, error); + if (mMediaBuffer != NULL) + { + mMediaBuffer->meta_data()->findInt64(kKeyTime, + (int64_t*)&tempTime64); + } else { + break; + } + options.clearSeekTo(); + } while(tempTime64 < pC->mVideoSeekTime); + + LOGV("VE3gpReader_getNextAu: video time with seek = %lld:", + tempTime64); + pC->mVideoSeeking = M4OSA_FALSE; + } else { + LOGV("VideoEditor3gpReader_getNextAu video no seek:"); + pC->mVideoSource->read(&mMediaBuffer, &options); + + if(mMediaBuffer != NULL) { + mMediaBuffer->meta_data()->findInt64(kKeyTime, + (int64_t*)&tempTime64); + LOGV("VE3gpReader_getNextAu: video no seek time = %lld:", + tempTime64); + }else { + LOGV("VE3gpReader_getNextAu:video no seek time buffer is NULL"); + } + } + } else { + LOGV("VideoEditor3gpReader_getNextAu M4ERR_PARAMETER"); + return M4ERR_PARAMETER; + } + + if (mMediaBuffer != NULL) { + if( (pAu->dataAddress == NULL) || (pAu->size < \ + mMediaBuffer->range_length())) { + if(pAu->dataAddress != NULL) { + M4OSA_free((M4OSA_Int32*)pAu->dataAddress); + pAu->dataAddress = NULL; + } + LOGV("Buffer lenght = %d ,%d",(mMediaBuffer->range_length() +\ + 3) & ~0x3,(mMediaBuffer->range_length())); + + pAu->dataAddress = (M4OSA_Int32*)M4OSA_malloc( + (mMediaBuffer->range_length() + 3) & ~0x3,M4READER_3GP, + (M4OSA_Char*)"pAccessUnit->m_dataAddress" ); + if(pAu->dataAddress == NULL) { + LOGV("VideoEditor3gpReader_getNextAu malloc failed"); + return M4ERR_ALLOC; + } + } + pAu->size = mMediaBuffer->range_length(); + + memcpy((M4OSA_MemAddr8)pAu->dataAddress, + (const char *)mMediaBuffer->data() + mMediaBuffer->range_offset(), + mMediaBuffer->range_length()); + + if( (pStreamHandler == (M4_StreamHandler*)pC->mVideoStreamHandler) && + (pStreamHandler->m_streamType == M4DA_StreamTypeVideoMpeg4Avc) ) { + M4OSA_UInt32 size = mMediaBuffer->range_length(); + M4OSA_UInt8 *lbuffer; + + lbuffer = (M4OSA_UInt8 *) pAu->dataAddress; + LOGV("pAccessUnit->m_dataAddress size = %x",size); + + lbuffer[0] = (size >> 24) & 0xFF; + lbuffer[1] = (size >> 16) & 0xFF; + lbuffer[2] = (size >> 8) & 0xFF; + lbuffer[3] = (size) & 0xFF; + } + + pAu->CTS = tempTime64; + + pAu->CTS = pAu->CTS / 1000; //converting the microsec to millisec + LOGV("VideoEditor3gpReader_getNextAu CTS = %ld",pAu->CTS); + + pAu->DTS = pAu->CTS; + pAu->attribute = M4SYS_kFragAttrOk; + mMediaBuffer->release(); + + pAccessUnit->m_dataAddress = (M4OSA_Int8*) pAu->dataAddress; + pAccessUnit->m_size = pAu->size; + pAccessUnit->m_maxsize = pAu->size; + pAccessUnit->m_CTS = pAu->CTS; + pAccessUnit->m_DTS = pAu->DTS; + pAccessUnit->m_attribute = pAu->attribute; + + } else { + LOGV("VideoEditor3gpReader_getNextAu: M4WAR_NO_MORE_AU (EOS) reached"); + pAccessUnit->m_size = 0; + err = M4WAR_NO_MORE_AU; + } + options.clearSeekTo(); + + pAu->nbFrag = 0; + mMediaBuffer = NULL; + LOGV("VideoEditor3gpReader_getNextAu end "); + + return err; +} +/** + ******************************************************************************* + * @brief Split the AVC DSI in its different components and write it in + * ONE memory buffer + * @note + * @param pStreamHandler: (IN/OUT) The MPEG4-AVC stream + * @param pDecoderConfigLocal: (IN) The DSI buffer + * @param decoderConfigSizeLocal: (IN) The DSI buffer size + * @return M4NO_ERROR there is no error + * @return ERR_FILE_SYNTAX_ERROR pDecoderConfigLocal is NULL + ******************************************************************************* +*/ +static M4OSA_ERR VideoEditor3gpReader_AnalyseAvcDsi( + M4_StreamHandler *pStreamHandler, M4OSA_Int32* pDecoderConfigLocal, + M4OSA_Int32 decoderConfigSizeLocal) { + struct _avcSpecificInfo *pAvcSpecInfo = M4OSA_NULL; + M4OSA_UInt32 uiSpecInfoSize; + M4OSA_Context pBitParserContext = M4OSA_NULL; + M4OSA_MemAddr8 pPos; + + /** + * First parsing to get the total allocation size (we must not do + * multiple malloc, but only one instead) */ + { + M4OSA_Int32 val; + M4OSA_UInt32 i,j; + M4OSA_UInt8 nalUnitLength; + M4OSA_UInt8 numOfSequenceParameterSets; + M4OSA_UInt32 uiTotalSizeOfSPS = 0; + M4OSA_UInt8 numOfPictureParameterSets; + M4OSA_UInt32 uiTotalSizeOfPPS = 0; + M4OSA_UInt32 uiSize; + struct _avcSpecificInfo avcSpIf; + + avcSpIf.m_nalUnitLength = 0; + + if (M4OSA_NULL == pDecoderConfigLocal) { + return M4ERR_READER3GP_DECODER_CONFIG_ERROR; + } + + VideoEditor3gpReader_MPEG4BitStreamParserInit(&pBitParserContext, + pDecoderConfigLocal, decoderConfigSizeLocal); + + if (M4OSA_NULL == pBitParserContext) { + return M4ERR_ALLOC; + } + + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + /* 8 bits -- configuration version */ + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + /* 8 bits -- avc profile indication*/ + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + /* 8 bits -- profile compatibility */ + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + /* 8 bits -- avc level indication*/ + val=VideoEditor3gpReader_BitStreamParserShowBits(pBitParserContext, 8); + /* 6 bits reserved 111111b 2 bits length Size minus one*/ + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + /* m_nalUnitLength */ + + nalUnitLength = (M4OSA_UInt8)((val & 0x03) + 1);/*0b11111100*/ + if (nalUnitLength > 4) { + pStreamHandler->m_decoderSpecificInfoSize = 0; + pStreamHandler->m_pDecoderSpecificInfo = M4OSA_NULL; + VideoEditor3gpReader_BitStreamParserCleanUp(pBitParserContext); + } else { + /** + * SPS table */ + val=VideoEditor3gpReader_BitStreamParserShowBits(pBitParserContext, + 8);/* 3 bits-reserved 111b-5 bits number of sequence parameter set*/ + numOfSequenceParameterSets = val & 0x1F; + /*1F instead of E0*/ /*0b11100000*/ /*Number of seq parameter sets*/ + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + for (i=0; i < numOfSequenceParameterSets; i++) { + /** + * Get the size of this element */ + uiSize = + (M4OSA_UInt32)VideoEditor3gpReader_BitStreamParserShowBits( + pBitParserContext, 16); + uiTotalSizeOfSPS += uiSize; + VideoEditor3gpReader_BitStreamParserFlushBits( + pBitParserContext, 16); + /** + *Read the element(dont keep it, we only want size right now) */ + for (j=0; j<uiSize; j++) { + VideoEditor3gpReader_BitStreamParserFlushBits( + pBitParserContext, 8); + } + } + + /** + * SPS table */ + numOfPictureParameterSets=(M4OSA_UInt8)\ + VideoEditor3gpReader_BitStreamParserShowBits(pBitParserContext, + 8); + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + for (i=0; i < numOfPictureParameterSets; i++) { + /** + * Get the size of this element */ + uiSize = (M4OSA_UInt32) + VideoEditor3gpReader_BitStreamParserShowBits( + pBitParserContext, 16); + uiTotalSizeOfPPS += uiSize; + VideoEditor3gpReader_BitStreamParserFlushBits( + pBitParserContext, 16); + /** + *Read the element(dont keep it,we only want size right now)*/ + for (j=0; j<uiSize; j++) { + VideoEditor3gpReader_BitStreamParserFlushBits( + pBitParserContext, 8); + } + } + + /** + * Compute the size of the full buffer */ + uiSpecInfoSize = sizeof(struct _avcSpecificInfo) + + numOfSequenceParameterSets * sizeof(struct _parameterSet) + + /**< size of the table of SPS elements */ + numOfPictureParameterSets * sizeof(struct _parameterSet) + + /**< size of the table of PPS elements */ + uiTotalSizeOfSPS + + uiTotalSizeOfPPS; + /** + * Allocate the buffer */ + pAvcSpecInfo =(struct _avcSpecificInfo*)M4OSA_malloc(uiSpecInfoSize, + M4READER_3GP, (M4OSA_Char*)"MPEG-4 AVC DecoderSpecific"); + if (M4OSA_NULL == pAvcSpecInfo) { + VideoEditor3gpReader_BitStreamParserCleanUp(pBitParserContext); + return M4ERR_ALLOC; + } + + /** + * Set the pointers to the correct part of the buffer */ + pAvcSpecInfo->m_nalUnitLength = nalUnitLength; + pAvcSpecInfo->m_numOfSequenceParameterSets = + numOfSequenceParameterSets; + pAvcSpecInfo->m_numOfPictureParameterSets = + numOfPictureParameterSets; + + /* We place the SPS param sets table after m_pPictureParameterSet */ + pAvcSpecInfo->m_pSequenceParameterSet= (struct _parameterSet*)( + (M4OSA_MemAddr8)(&pAvcSpecInfo->m_pPictureParameterSet) + + sizeof(pAvcSpecInfo->m_pPictureParameterSet)); + /*We place the PPS param sets table after the SPS param sets table*/ + pAvcSpecInfo->m_pPictureParameterSet = (struct _parameterSet*)( + (M4OSA_MemAddr8)(pAvcSpecInfo->m_pSequenceParameterSet) + + (numOfSequenceParameterSets * sizeof(struct _parameterSet))); + /**< The data will be placed after the PPS param sets table */ + pPos = (M4OSA_MemAddr8)pAvcSpecInfo->m_pPictureParameterSet + + (numOfPictureParameterSets * sizeof(struct _parameterSet)); + + /** + * reset the bit parser */ + VideoEditor3gpReader_BitStreamParserCleanUp(pBitParserContext); + } + } + + /** + * Second parsing to copy the data */ + if (M4OSA_NULL != pAvcSpecInfo) { + M4OSA_Int32 i,j; + + VideoEditor3gpReader_MPEG4BitStreamParserInit(&pBitParserContext, + pDecoderConfigLocal, decoderConfigSizeLocal); + + if (M4OSA_NULL == pBitParserContext) { + M4OSA_free((M4OSA_MemAddr32)pAvcSpecInfo); + return M4ERR_ALLOC; + } + + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + /* 8 bits -- configuration version */ + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + /* 8 bits -- avc profile indication*/ + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + /* 8 bits -- profile compatibility */ + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + /* 8 bits -- avc level indication*/ + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + /* m_nalUnitLength */ + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + /* 3 bits -- reserved 111b -- 5 bits number of sequence parameter set*/ + + for (i=0; i < pAvcSpecInfo->m_numOfSequenceParameterSets; i++) { + pAvcSpecInfo->m_pSequenceParameterSet[i].m_length = + (M4OSA_UInt16)VideoEditor3gpReader_BitStreamParserShowBits( + pBitParserContext, 16); + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext,16); + + pAvcSpecInfo->m_pSequenceParameterSet[i].m_pParameterSetUnit = + (M4OSA_UInt8*)pPos; /**< current position in the buffer */ + pPos += pAvcSpecInfo->m_pSequenceParameterSet[i].m_length; + /**< increment the position in the buffer */ + for (j=0; j<pAvcSpecInfo->m_pSequenceParameterSet[i].m_length;j++){ + pAvcSpecInfo->m_pSequenceParameterSet[i].m_pParameterSetUnit[j]= + (M4OSA_UInt8)VideoEditor3gpReader_BitStreamParserShowBits( + pBitParserContext, 8); + VideoEditor3gpReader_BitStreamParserFlushBits( + pBitParserContext, 8); + } + } + + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext, 8); + /* number of pîcture parameter set*/ + + for (i=0; i < pAvcSpecInfo->m_numOfPictureParameterSets; i++) { + pAvcSpecInfo->m_pPictureParameterSet[i].m_length = + (M4OSA_UInt16)VideoEditor3gpReader_BitStreamParserShowBits( + pBitParserContext, 16); + VideoEditor3gpReader_BitStreamParserFlushBits(pBitParserContext,16); + + pAvcSpecInfo->m_pPictureParameterSet[i].m_pParameterSetUnit = + (M4OSA_UInt8*)pPos; /**< current position in the buffer */ + pPos += pAvcSpecInfo->m_pPictureParameterSet[i].m_length; + /**< increment the position in the buffer */ + for (j=0; j<pAvcSpecInfo->m_pPictureParameterSet[i].m_length; j++) { + pAvcSpecInfo->m_pPictureParameterSet[i].m_pParameterSetUnit[j] = + (M4OSA_UInt8)VideoEditor3gpReader_BitStreamParserShowBits( + pBitParserContext, 8); + VideoEditor3gpReader_BitStreamParserFlushBits( + pBitParserContext, 8); + } + } + VideoEditor3gpReader_BitStreamParserCleanUp(pBitParserContext); + pStreamHandler->m_decoderSpecificInfoSize = uiSpecInfoSize; + pStreamHandler->m_pDecoderSpecificInfo = (M4OSA_UInt8*)pAvcSpecInfo; + } + pStreamHandler->m_H264decoderSpecificInfoSize = decoderConfigSizeLocal; + pStreamHandler->m_pH264DecoderSpecificInfo = (M4OSA_UInt8*)M4OSA_malloc( + decoderConfigSizeLocal, M4READER_3GP, + (M4OSA_Char*)"MPEG-4 AVC DecoderSpecific"); + if (M4OSA_NULL == pStreamHandler->m_pH264DecoderSpecificInfo) { + goto cleanup; + } + + M4OSA_memcpy((M4OSA_MemAddr8 ) pStreamHandler->m_pH264DecoderSpecificInfo, + (M4OSA_MemAddr8 )pDecoderConfigLocal, + pStreamHandler->m_H264decoderSpecificInfoSize); + return M4NO_ERROR; +cleanup: + VideoEditor3gpReader_BitStreamParserCleanUp(pBitParserContext); + return M4ERR_READER3GP_DECODER_CONFIG_ERROR; +} +/** +******************************************************************************** +* @brief Get the next stream found in the 3gp file +* @note +* @param context: (IN) Context of the reader +* @param pMediaFamily: OUT) pointer to a user allocated +* M4READER_MediaFamily that will be filled +* with the media family of the found stream +* @param pStreamHandler:(OUT) pointer to StreamHandler that will be allocated +* and filled with the found stream description +* @return M4NO_ERROR there is no error +* @return M4ERR_BAD_CONTEXT provided context is not a valid one +* @return M4ERR_PARAMETER at least one parameter is not properly set +* @return M4WAR_NO_MORE_STREAM no more available stream in the media +******************************************************************************** +*/ +M4OSA_ERR VideoEditor3gpReader_getNextStreamHandler(M4OSA_Context context, + M4READER_MediaFamily *pMediaFamily, + M4_StreamHandler **pStreamHandler) { + VideoEditor3gpReader_Context* pC=(VideoEditor3gpReader_Context*)context; + M4OSA_ERR err = M4NO_ERROR; + M4SYS_StreamID streamIdArray[2]; + M4SYS_StreamDescription streamDesc; + M4_AudioStreamHandler* pAudioStreamHandler; + M4_VideoStreamHandler* pVideoStreamHandler; + M4OSA_Int8 *DecoderSpecificInfo = M4OSA_NULL; + M4OSA_Int32 decoderSpecificInfoSize =0, maxAUSize = 0; + + M4_StreamType streamType = M4DA_StreamTypeUnknown; + M4OSA_UInt8 temp, i, trackCount; + M4OSA_Bool haveAudio = M4OSA_FALSE; + M4OSA_Bool haveVideo = M4OSA_FALSE; + sp<MetaData> meta = NULL; + int64_t Duration = 0; + M4OSA_UInt8* DecoderSpecific = M4OSA_NULL ; + uint32_t type; + const void *data; + size_t size; + const void *codec_specific_data; + size_t codec_specific_data_size; + M4OSA_Int32 ptempTime; + + LOGV("VideoEditor3gpReader_getNextStreamHandler begin"); + + M4OSA_DEBUG_IF1((pC == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_getNextStreamHandler: invalid context"); + M4OSA_DEBUG_IF1((pMediaFamily == 0), M4ERR_PARAMETER, + "getNextStreamHandler: invalid pointer to MediaFamily"); + M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER, + "getNextStreamHandler: invalid pointer to StreamHandler"); + + trackCount = pC->mExtractor->countTracks(); + temp = pC->mCurrTrack; + + if(temp >= trackCount) { + LOGV("VideoEditor3gpReader_getNextStreamHandler error = %d", + M4WAR_NO_MORE_STREAM); + return (M4WAR_NO_MORE_STREAM); + } else { + const char *mime; + meta = pC->mExtractor->getTrackMetaData(temp); + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (!haveVideo && !strncasecmp(mime, "video/", 6)) { + pC->mVideoSource = pC->mExtractor->getTrack(temp); + pC->mVideoSource->start(); + + *pMediaFamily = M4READER_kMediaFamilyVideo; + haveVideo = true; + LOGV("VideoEditor3gpReader_getNextStreamHandler getTrack called"); + if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + streamType = M4DA_StreamTypeVideoMpeg4Avc; + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) { + streamType = M4DA_StreamTypeVideoH263; + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)) { + streamType = M4DA_StreamTypeVideoMpeg4; + } else { + LOGV("VideoEditor3gpReaderGetNextStreamHandler streamTypeNONE"); + } + LOGV("VideoEditor3gpReader_getNextStreamHandler: stream type: %d ", + streamType); + + if(streamType != M4DA_StreamTypeUnknown) { + pC->mStreamType = streamType; + pC->mStreamId = pC->mCurrTrack; + + pVideoStreamHandler = (M4_VideoStreamHandler*)M4OSA_malloc + (sizeof(M4_VideoStreamHandler), M4READER_3GP, + (M4OSA_Char*)"M4_VideoStreamHandler"); + if (M4OSA_NULL == pVideoStreamHandler) { + return M4ERR_ALLOC; + } + pVideoStreamHandler->m_structSize=sizeof(M4_VideoStreamHandler); + + meta->findInt32(kKeyWidth, + (int32_t*)&(pVideoStreamHandler->m_videoWidth)); + meta->findInt32(kKeyHeight, + (int32_t*)&(pVideoStreamHandler->m_videoHeight)); + + (*pStreamHandler) = (M4_StreamHandler*)(pVideoStreamHandler); + meta->findInt64(kKeyDuration, + (int64_t*)&(Duration)); + ((*pStreamHandler)->m_duration) = + (int32_t)((Duration)/1000); // conversion to mS + pC->mMaxDuration = ((*pStreamHandler)->m_duration); + LOGV("VideoEditor3gpReader_getNextStreamHandler m_duration %d", + (*pStreamHandler)->m_duration); + + pC->mFileSize = 0; + + meta->findInt32(kKeyMaxInputSize, (int32_t*)&(maxAUSize)); + if(maxAUSize == 0) { + maxAUSize = 70000; + } + (*pStreamHandler)->m_maxAUSize = maxAUSize; + LOGV("<<<<<<<<<< video: mMaxAUSize from MP4 extractor: %d", + (*pStreamHandler)->m_maxAUSize); + + //check this + pVideoStreamHandler->m_averageFrameRate = 15; + if( (M4DA_StreamTypeVideoH263 == streamType) || + (M4DA_StreamTypeVideoMpeg4Avc == streamType)){ + ((M4_StreamHandler*)pVideoStreamHandler)->m_averageBitRate = + 384000; + } + pC->mVideoStreamHandler = + (M4_StreamHandler*)(pVideoStreamHandler); + + /* Get the DSI info */ + if(M4DA_StreamTypeVideoH263 == streamType) { + if (meta->findData(kKeyESDS, &type, &data, &size)) { + ESDS esds((const char *)data, size); + CHECK_EQ(esds.InitCheck(), OK); + + esds.getCodecSpecificInfo( + &codec_specific_data, &codec_specific_data_size); + (*pStreamHandler)->m_decoderSpecificInfoSize = + codec_specific_data_size; + if ((*pStreamHandler)->m_decoderSpecificInfoSize != 0) { + DecoderSpecific = (M4OSA_UInt8*)M4OSA_malloc( + (*pStreamHandler)->m_decoderSpecificInfoSize, + M4READER_3GP,(M4OSA_Char*)"H263 DSI"); + if (M4OSA_NULL == DecoderSpecific) { + return M4ERR_ALLOC; + } + M4OSA_memcpy((M4OSA_MemAddr8)DecoderSpecific, + (M4OSA_MemAddr8)codec_specific_data, + codec_specific_data_size); + (*pStreamHandler)->m_pDecoderSpecificInfo = + DecoderSpecific; + } + else { + (*pStreamHandler)->m_pDecoderSpecificInfo = + M4OSA_NULL; + } + } else { + LOGV("VE_getNextStreamHandler: H263 dsi not found"); + (*pStreamHandler)->m_pDecoderSpecificInfo = M4OSA_NULL; + (*pStreamHandler)->m_decoderSpecificInfoSize = 0; + (*pStreamHandler)->m_H264decoderSpecificInfoSize = 0; + (*pStreamHandler)->m_pH264DecoderSpecificInfo = + M4OSA_NULL; + (*pStreamHandler)->m_pESDSInfo = M4OSA_NULL; + (*pStreamHandler)->m_ESDSInfoSize = 0; + } + } + else if(M4DA_StreamTypeVideoMpeg4Avc == streamType) { + if(meta->findData(kKeyAVCC, &type, &data, &size)) { + decoderSpecificInfoSize = size; + if (decoderSpecificInfoSize != 0) { + DecoderSpecificInfo = (M4OSA_Int8*)M4OSA_malloc( + decoderSpecificInfoSize, M4READER_3GP, + (M4OSA_Char*)"H264 DecoderSpecific" ); + if (M4OSA_NULL == DecoderSpecificInfo) { + LOGV("VideoEditor3gp_getNextStream is NULL "); + return M4ERR_ALLOC; + } + M4OSA_memcpy((M4OSA_MemAddr8)DecoderSpecificInfo, + (M4OSA_MemAddr8)data, decoderSpecificInfoSize); + } else { + LOGV("DSI Size %d", decoderSpecificInfoSize); + DecoderSpecificInfo = M4OSA_NULL; + } + } + (*pStreamHandler)->m_pESDSInfo = M4OSA_NULL; + (*pStreamHandler)->m_ESDSInfoSize = 0; + + err = VideoEditor3gpReader_AnalyseAvcDsi(*pStreamHandler, + (M4OSA_Int32*)DecoderSpecificInfo, decoderSpecificInfoSize); + + if (M4NO_ERROR != err) { + return err; + } + LOGV("decsize %d, h264decsize %d: %d", (*pStreamHandler)->\ + m_decoderSpecificInfoSize, (*pStreamHandler)->\ + m_H264decoderSpecificInfoSize); + + if(M4OSA_NULL != DecoderSpecificInfo) { + M4OSA_free((M4OSA_MemAddr32)DecoderSpecificInfo); + DecoderSpecificInfo = M4OSA_NULL; + } + } else if( (M4DA_StreamTypeVideoMpeg4 == streamType) ) { + if (meta->findData(kKeyESDS, &type, &data, &size)) { + ESDS esds((const char *)data, size); + CHECK_EQ(esds.InitCheck(), OK); + + (*pStreamHandler)->m_ESDSInfoSize = size; + (*pStreamHandler)->m_pESDSInfo = (M4OSA_UInt8*)\ + M4OSA_malloc((*pStreamHandler)->m_ESDSInfoSize, + M4READER_3GP, (M4OSA_Char*)"H263 DecoderSpecific" ); + if (M4OSA_NULL == (*pStreamHandler)->m_pESDSInfo) { + return M4ERR_ALLOC; + } + M4OSA_memcpy((M4OSA_MemAddr8)(*pStreamHandler)->\ + m_pESDSInfo, (M4OSA_MemAddr8)data, size); + + esds.getCodecSpecificInfo(&codec_specific_data, + &codec_specific_data_size); + LOGV("VE MP4 dsisize: %d, %x", codec_specific_data_size, + codec_specific_data); + + (*pStreamHandler)->m_decoderSpecificInfoSize = + codec_specific_data_size; + if ((*pStreamHandler)->m_decoderSpecificInfoSize != 0) { + DecoderSpecific = (M4OSA_UInt8*)M4OSA_malloc( + (*pStreamHandler)->m_decoderSpecificInfoSize, + M4READER_3GP, (M4OSA_Char*)" DecoderSpecific" ); + if (M4OSA_NULL == DecoderSpecific) { + return M4ERR_ALLOC; + } + M4OSA_memcpy((M4OSA_MemAddr8)DecoderSpecific, + (M4OSA_MemAddr8)codec_specific_data, + codec_specific_data_size); + (*pStreamHandler)->m_pDecoderSpecificInfo = + DecoderSpecific; + } + else { + (*pStreamHandler)->m_pDecoderSpecificInfo = + M4OSA_NULL; + } + (*pStreamHandler)->m_pH264DecoderSpecificInfo = + M4OSA_NULL; + (*pStreamHandler)->m_H264decoderSpecificInfoSize = 0; + } + } else { + LOGV("VideoEditor3gpReader_getNextStream NO video stream"); + return M4ERR_READER_UNKNOWN_STREAM_TYPE; + } + } + else { + LOGV("VideoEditor3gpReader_getNextStream NO video stream"); + return M4ERR_READER_UNKNOWN_STREAM_TYPE; + } + + } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) { + LOGV("VideoEditor3gpReader_getNextStream audio getTrack called"); + pC->mAudioSource = pC->mExtractor->getTrack(pC->mCurrTrack); + pC->mAudioSource->start(); + *pMediaFamily = M4READER_kMediaFamilyAudio; + + if(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) { + streamType = M4DA_StreamTypeAudioAmrNarrowBand; + } else if(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) { + streamType = M4DA_StreamTypeAudioAmrWideBand; + } + else if(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { + streamType = M4DA_StreamTypeAudioAac; + } else { + LOGV("VideoEditor3gpReader_getNextStrea streamtype Unknown "); + } + if(streamType != M4DA_StreamTypeUnknown) { + pC->mStreamType = streamType; + pC->mStreamId = pC->mCurrTrack; + + LOGV("VE streamtype %d ,id %d", streamType, pC->mCurrTrack); + + pAudioStreamHandler = (M4_AudioStreamHandler*)M4OSA_malloc + (sizeof(M4_AudioStreamHandler), M4READER_3GP, + (M4OSA_Char*)"M4_AudioStreamHandler"); + if (M4OSA_NULL == pAudioStreamHandler) { + return M4ERR_ALLOC; + } + pAudioStreamHandler->m_structSize=sizeof(M4_AudioStreamHandler); + pAudioStreamHandler->m_byteSampleSize = 0; + pAudioStreamHandler->m_nbChannels = 0; + pAudioStreamHandler->m_samplingFrequency= 0; + pAudioStreamHandler->m_byteFrameLength = 0; + + (*pStreamHandler) = (M4_StreamHandler*)(pAudioStreamHandler); + pC->mAudioStreamHandler = + (M4_StreamHandler*)(pAudioStreamHandler); + (*pStreamHandler)->m_averageBitRate = 0; + haveAudio = true; + pC->mAudioStreamHandler=(M4_StreamHandler*)pAudioStreamHandler; + pC->mAudioStreamHandler->m_pESDSInfo = M4OSA_NULL; + pC->mAudioStreamHandler->m_ESDSInfoSize = 0; + + meta->findInt32(kKeyMaxInputSize, (int32_t*)&(maxAUSize)); + if(maxAUSize == 0) { + maxAUSize = 70000; + } + (*pStreamHandler)->m_maxAUSize = maxAUSize; + LOGV("VE Audio mMaxAUSize from MP4 extractor: %d", maxAUSize); + } + if((M4DA_StreamTypeAudioAmrNarrowBand == streamType) || + (M4DA_StreamTypeAudioAmrWideBand == streamType)) { + M4OSA_UInt32 freqIndex = 0; /**< AMR NB */ + M4OSA_UInt32 modeSet; + M4OSA_UInt32 i; + M4OSA_Context pBitParserContext = M4OSA_NULL; + + if(M4DA_StreamTypeAudioAmrWideBand == streamType) { + freqIndex = 1; /**< AMR WB */ + } + + if (meta->findData(kKeyESDS, &type, &data, &size)) { + ESDS esds((const char *)data, size); + CHECK_EQ(esds.InitCheck(), OK); + + esds.getCodecSpecificInfo(&codec_specific_data, + &codec_specific_data_size); + (*pStreamHandler)->m_decoderSpecificInfoSize = + codec_specific_data_size; + + if ((*pStreamHandler)->m_decoderSpecificInfoSize != 0) { + DecoderSpecific = (M4OSA_UInt8*)M4OSA_malloc( + (*pStreamHandler)->m_decoderSpecificInfoSize, + M4READER_3GP, (M4OSA_Char*)"H263 DecoderSpecific" ); + if (M4OSA_NULL == DecoderSpecific) { + return M4ERR_ALLOC; + } + M4OSA_memcpy((M4OSA_MemAddr8)DecoderSpecific, + (M4OSA_MemAddr8)codec_specific_data, + codec_specific_data_size); + (*pStreamHandler)->m_pDecoderSpecificInfo = + DecoderSpecific; + } else { + (*pStreamHandler)->m_pDecoderSpecificInfo = M4OSA_NULL; + } + } else { + M4OSA_UChar AmrDsi[] = + {'P','H','L','P',0x00, 0x00, 0x80, 0x00, 0x01,}; + (*pStreamHandler)->m_decoderSpecificInfoSize = 9; + DecoderSpecific = (M4OSA_UInt8*)M4OSA_malloc( + (*pStreamHandler)->m_decoderSpecificInfoSize, + M4READER_3GP, (M4OSA_Char*)"H263 DecoderSpecific" ); + if (M4OSA_NULL == DecoderSpecific) { + return M4ERR_ALLOC; + } + if(freqIndex ==0) { + AmrDsi[8] = 0x01; + } else { + AmrDsi[8] = 0x02; + } + for(i = 0; i< 9; i++) { + DecoderSpecific[i] = AmrDsi[i]; + } + (*pStreamHandler)->m_pDecoderSpecificInfo = DecoderSpecific; + } + (*pStreamHandler)->m_averageBitRate = + VideoEditor3gpReader_AmrBitRate[freqIndex][7]; + } else if((M4DA_StreamTypeAudioAac == streamType)) { + if (meta->findData(kKeyESDS, &type, &data, &size)) { + ESDS esds((const char *)data, size); + CHECK_EQ(esds.InitCheck(), OK); + + (*pStreamHandler)->m_ESDSInfoSize = size; + (*pStreamHandler)->m_pESDSInfo = (M4OSA_UInt8*)M4OSA_malloc( + (*pStreamHandler)->m_ESDSInfoSize, M4READER_3GP, + (M4OSA_Char*)"H263 DecoderSpecific" ); + if (M4OSA_NULL == (*pStreamHandler)->m_pESDSInfo) { + return M4ERR_ALLOC; + } + M4OSA_memcpy((M4OSA_MemAddr8)(*pStreamHandler)->m_pESDSInfo, + (M4OSA_MemAddr8)data, size); + esds.getCodecSpecificInfo(&codec_specific_data, + &codec_specific_data_size); + + LOGV("VEdsi %d,%x",codec_specific_data_size, + codec_specific_data); + + (*pStreamHandler)->m_decoderSpecificInfoSize = + codec_specific_data_size; + if ((*pStreamHandler)->m_decoderSpecificInfoSize != 0) { + DecoderSpecific = (M4OSA_UInt8*)M4OSA_malloc( + (*pStreamHandler)->m_decoderSpecificInfoSize, + M4READER_3GP, (M4OSA_Char*)"H263 DecoderSpecific" ); + if (M4OSA_NULL == DecoderSpecific) { + return M4ERR_ALLOC; + } + M4OSA_memcpy((M4OSA_MemAddr8)DecoderSpecific, + (M4OSA_MemAddr8)codec_specific_data, + codec_specific_data_size); + (*pStreamHandler)->m_pDecoderSpecificInfo = + DecoderSpecific; + } else { + (*pStreamHandler)->m_pDecoderSpecificInfo = M4OSA_NULL; + } + } + } else { + LOGV("VideoEditor3gpReader_getNextStream mStreamType: none "); + return M4ERR_READER_UNKNOWN_STREAM_TYPE; + } + } else { + LOGV("VE noaudio-video stream:pC->mCurrTrack = %d ",pC->mCurrTrack); + pC->mCurrTrack++; //Increment current track to get the next track + return M4ERR_READER_UNKNOWN_STREAM_TYPE; + } + LOGV("VE StreamType: %d, stremhandler %x",streamType, *pStreamHandler ); + (*pStreamHandler)->m_streamType = streamType; + (*pStreamHandler)->m_streamId = pC->mStreamId; + (*pStreamHandler)->m_pUserData = M4OSA_NULL; + (*pStreamHandler)->m_structSize = sizeof(M4_StreamHandler); + (*pStreamHandler)->m_bStreamIsOK = M4OSA_TRUE; + + meta->findInt64(kKeyDuration, + (int64_t*)&(Duration)); + + (*pStreamHandler)->m_duration = (int32_t)(Duration / 1000); + + pC->mMaxDuration = ((*pStreamHandler)->m_duration); + LOGV("VE str duration duration: %d ", (*pStreamHandler)->m_duration); + + /* In AAC case: Put the first AU in pAudioStreamHandler->m_pUserData + *since decoder has to know if stream contains SBR data(Implicit sig) */ + if(M4DA_StreamTypeAudioAac == (*pStreamHandler)->m_streamType) { + M4READER_AudioSbrUserdata* pAudioSbrUserdata; + + pAudioSbrUserdata = (M4READER_AudioSbrUserdata*)M4OSA_malloc( + sizeof(M4READER_AudioSbrUserdata),M4READER_3GP, + (M4OSA_Char*)"M4READER_AudioSbrUserdata"); + if (M4OSA_NULL == pAudioSbrUserdata) { + err = M4ERR_ALLOC; + goto Error; + } + (*pStreamHandler)->m_pUserData = pAudioSbrUserdata; + pAudioSbrUserdata->m_bIsSbrEnabled = M4OSA_FALSE; + + pAudioSbrUserdata->m_pFirstAU = (M4_AccessUnit*)M4OSA_malloc( + sizeof(M4_AccessUnit),M4READER_3GP, (M4OSA_Char*)"1st AAC AU"); + if (M4OSA_NULL == pAudioSbrUserdata->m_pFirstAU) { + pAudioSbrUserdata->m_pAacDecoderUserConfig = M4OSA_NULL; + err = M4ERR_ALLOC; + goto Error; + } + pAudioSbrUserdata->m_pAacDecoderUserConfig = (M4_AacDecoderConfig*)\ + M4OSA_malloc(sizeof(M4_AacDecoderConfig),M4READER_3GP, + (M4OSA_Char*)"m_pAacDecoderUserConfig"); + if (M4OSA_NULL == pAudioSbrUserdata->m_pAacDecoderUserConfig) { + err = M4ERR_ALLOC; + goto Error; + } + } + if(M4DA_StreamTypeAudioAac == (*pStreamHandler)->m_streamType) { + M4_AudioStreamHandler* pAudioStreamHandler = + (M4_AudioStreamHandler*)(*pStreamHandler); + M4READER_AudioSbrUserdata* pUserData = (M4READER_AudioSbrUserdata*)\ + (pAudioStreamHandler->m_basicProperties.m_pUserData); + + err = VideoEditor3gpReader_fillAuStruct(pC, (*pStreamHandler), + (M4_AccessUnit*)pUserData->m_pFirstAU); + if (M4NO_ERROR != err) { + goto Error; + } + err = VideoEditor3gpReader_getNextAu(pC, (*pStreamHandler), + (M4_AccessUnit*)pUserData->m_pFirstAU); + if (M4NO_ERROR != err) { + goto Error; + } + err = VideoEditor3gpReader_reset(pC, (*pStreamHandler)); + if (M4NO_ERROR != err) { + goto Error; + } + } + } + pC->mCurrTrack++; //Increment the current track to get next track + LOGV("pC->mCurrTrack = %d",pC->mCurrTrack); + + if (!haveAudio && !haveVideo) { + *pMediaFamily=M4READER_kMediaFamilyUnknown; + return M4ERR_READER_UNKNOWN_STREAM_TYPE; + } +Error: + LOGV("VideoEditor3gpReader_getNextStreamHandler end error = %d",err); + return err; +} + +M4OSA_ERR VideoEditor3gpReader_getPrevRapTime(M4OSA_Context context, + M4_StreamHandler *pStreamHandler, M4OSA_Int32* pTime) +{ + VideoEditor3gpReader_Context *pC = (VideoEditor3gpReader_Context*)context; + M4OSA_ERR err = M4NO_ERROR; + MediaBuffer *mMediaBuffer = M4OSA_NULL; + MediaSource::ReadOptions options; + M4OSA_Time time64; + int64_t tempTime64 = 0; + status_t error; + + LOGV("VideoEditor3gpReader_getPrevRapTime begin"); + + M4OSA_DEBUG_IF1((pC == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_getPrevRapTime: invalid context"); + M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_getPrevRapTime invalid pointer to StreamHandler"); + M4OSA_DEBUG_IF1((pTime == 0), M4ERR_PARAMETER, + "VideoEditor3gpReader_getPrevRapTime: invalid time pointer"); + if (*pTime == (pStreamHandler->m_duration)) { + *pTime -= 1; + } + M4OSA_INT64_FROM_INT32(time64, *pTime); + time64 = time64 * 1000; + + LOGV("VideoEditor3gpReader_getPrevRapTime seek time: %ld",time64); + options.setSeekTo(time64, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); + error = pC->mVideoSource->read(&mMediaBuffer, &options); + if (error != OK) { + //Can not get the previous Sync. + //Must be end of stream. + return M4WAR_NO_MORE_AU; + } + + mMediaBuffer->meta_data()->findInt64(kKeyTime, (int64_t*)&tempTime64); + LOGV("VideoEditor3gpReader_getPrevRapTime read time %ld, %x", tempTime64, + mMediaBuffer); + + (*pTime) = (tempTime64) / 1000; + + if(mMediaBuffer != M4OSA_NULL) { + LOGV(" mMediaBuffer size = %d length %d", mMediaBuffer->size(), + mMediaBuffer->range_length()); + mMediaBuffer->release(); + mMediaBuffer = M4OSA_NULL; + } + options.clearSeekTo(); + + if(error != OK) { + LOGV("VideoEditor3gpReader_getPrevRapTime end \ + M4WAR_READER_INFORMATION_NOT_PRESENT"); + return M4WAR_READER_INFORMATION_NOT_PRESENT; + } else { + LOGV("VideoEditor3gpReader_getPrevRapTime end: err %x", err); + err = M4NO_ERROR; + return err; + } +} + +extern "C" { +M4OSA_ERR VideoEditor3gpReader_getInterface(M4READER_MediaType *pMediaType, + M4READER_GlobalInterface **pRdrGlobalInterface, + M4READER_DataInterface **pRdrDataInterface) { + + M4OSA_ERR err = M4NO_ERROR; + + VIDEOEDITOR_CHECK(M4OSA_NULL != pMediaType, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pRdrGlobalInterface, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pRdrDataInterface, M4ERR_PARAMETER); + + LOGV("VideoEditor3gpReader_getInterface begin"); + LOGV("VideoEditor3gpReader_getInterface %d 0x%x 0x%x", *pMediaType, + *pRdrGlobalInterface,*pRdrDataInterface); + + SAFE_MALLOC(*pRdrGlobalInterface, M4READER_GlobalInterface, 1, + "VideoEditor3gpReader_getInterface"); + SAFE_MALLOC(*pRdrDataInterface, M4READER_DataInterface, 1, + "VideoEditor3gpReader_getInterface"); + + *pMediaType = M4READER_kMediaType3GPP; + + (*pRdrGlobalInterface)->m_pFctCreate = VideoEditor3gpReader_create; + (*pRdrGlobalInterface)->m_pFctDestroy = VideoEditor3gpReader_destroy; + (*pRdrGlobalInterface)->m_pFctOpen = VideoEditor3gpReader_open; + (*pRdrGlobalInterface)->m_pFctClose = VideoEditor3gpReader_close; + (*pRdrGlobalInterface)->m_pFctGetOption = VideoEditor3gpReader_getOption; + (*pRdrGlobalInterface)->m_pFctSetOption = VideoEditor3gpReader_setOption; + (*pRdrGlobalInterface)->m_pFctGetNextStream = + VideoEditor3gpReader_getNextStreamHandler; + (*pRdrGlobalInterface)->m_pFctFillAuStruct = + VideoEditor3gpReader_fillAuStruct; + (*pRdrGlobalInterface)->m_pFctStart = M4OSA_NULL; + (*pRdrGlobalInterface)->m_pFctStop = M4OSA_NULL; + (*pRdrGlobalInterface)->m_pFctJump = VideoEditor3gpReader_jump; + (*pRdrGlobalInterface)->m_pFctReset = VideoEditor3gpReader_reset; + (*pRdrGlobalInterface)->m_pFctGetPrevRapTime = + VideoEditor3gpReader_getPrevRapTime; + (*pRdrDataInterface)->m_pFctGetNextAu = VideoEditor3gpReader_getNextAu; + (*pRdrDataInterface)->m_readerContext = M4OSA_NULL; + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditor3gpReader_getInterface no error"); + } else { + SAFE_FREE(*pRdrGlobalInterface); + SAFE_FREE(*pRdrDataInterface); + + LOGV("VideoEditor3gpReader_getInterface ERROR 0x%X", err); + } + LOGV("VideoEditor3gpReader_getInterface end"); + return err; +} + +} /* extern "C" */ + +} /* namespace android */ + + diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditorAudioDecoder.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditorAudioDecoder.cpp new file mode 100755 index 0000000..2e88147 --- /dev/null +++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditorAudioDecoder.cpp @@ -0,0 +1,907 @@ +/* + * Copyright (C) 2011 NXP Software + * Copyright (C) 2011 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. + */ +/** +************************************************************************* +* @file VideoEditorAudioDecoder.cpp +* @brief StageFright shell Audio Decoder +************************************************************************* +*/ + +#define LOG_NDEBUG 1 +#define LOG_TAG "VIDEOEDITOR_AUDIODECODER" + +#include "M4OSA_Debug.h" +#include "VideoEditorAudioDecoder.h" +#include "VideoEditorUtils.h" +#include "M4MCS_InternalTypes.h" + +#include "utils/Log.h" +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/OMXClient.h> +#include <media/stagefright/OMXCodec.h> + +/******************** + * DEFINITIONS * + ********************/ +// Version +#define VIDEOEDITOR_AUDIO_DECODER_VERSION_MAJOR 1 +#define VIDEOEDITOR_AUDIO_DECODER_VERSION_MINOR 0 +#define VIDEOEDITOR_AUDIO_DECODER_VERSION_REV 0 + +// Force using software decoder as engine does not support prefetch +#define VIDEOEDITOR_FORCECODEC kSoftwareCodecsOnly + +namespace android { + +struct VideoEditorAudioDecoderSource : public MediaSource { + public: + static sp<VideoEditorAudioDecoderSource> Create( + const sp<MetaData>& format); + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual sp<MetaData> getFormat(); + virtual status_t read(MediaBuffer **buffer, + const ReadOptions *options = NULL); + virtual int32_t storeBuffer(MediaBuffer *buffer); + + protected: + virtual ~VideoEditorAudioDecoderSource(); + + private: + struct MediaBufferChain { + MediaBuffer* buffer; + MediaBufferChain* nextLink; + }; + enum State { + CREATED, + STARTED, + ERROR + }; + VideoEditorAudioDecoderSource(const sp<MetaData>& format); + sp<MetaData> mFormat; + MediaBufferChain* mFirstBufferLink; + MediaBufferChain* mLastBufferLink; + int32_t mNbBuffer; + bool mIsEOS; + State mState; +}; + +sp<VideoEditorAudioDecoderSource> VideoEditorAudioDecoderSource::Create( + const sp<MetaData>& format) { + + sp<VideoEditorAudioDecoderSource> aSource = + new VideoEditorAudioDecoderSource(format); + + return aSource; +} + +VideoEditorAudioDecoderSource::VideoEditorAudioDecoderSource( + const sp<MetaData>& format): + mFormat(format), + mFirstBufferLink(NULL), + mLastBufferLink(NULL), + mNbBuffer(0), + mIsEOS(false), + mState(CREATED) { +} + +VideoEditorAudioDecoderSource::~VideoEditorAudioDecoderSource() { + + if( STARTED == mState ) { + stop(); + } +} + +status_t VideoEditorAudioDecoderSource::start(MetaData *meta) { + status_t err = OK; + + if( CREATED != mState ) { + LOGV("VideoEditorAudioDecoderSource::start: invalid state %d", mState); + return UNKNOWN_ERROR; + } + + mState = STARTED; + +cleanUp: + LOGV("VideoEditorAudioDecoderSource::start END (0x%x)", err); + return err; +} + +status_t VideoEditorAudioDecoderSource::stop() { + status_t err = OK; + int32_t i = 0; + + LOGV("VideoEditorAudioDecoderSource::stop begin"); + + if( STARTED != mState ) { + LOGV("VideoEditorAudioDecoderSource::stop: invalid state %d", mState); + return UNKNOWN_ERROR; + } + + // Release the buffer chain + MediaBufferChain* tmpLink = NULL; + while( mFirstBufferLink ) { + i++; + tmpLink = mFirstBufferLink; + mFirstBufferLink = mFirstBufferLink->nextLink; + delete tmpLink; + } + LOGV("VideoEditorAudioDecoderSource::stop : %d buffer remained", i); + mFirstBufferLink = NULL; + mLastBufferLink = NULL; + + mState = CREATED; + + LOGV("VideoEditorAudioDecoderSource::stop END (0x%x)", err); + return err; +} + +sp<MetaData> VideoEditorAudioDecoderSource::getFormat() { + + LOGV("VideoEditorAudioDecoderSource::getFormat"); + return mFormat; +} + +status_t VideoEditorAudioDecoderSource::read(MediaBuffer **buffer, + const ReadOptions *options) { + MediaSource::ReadOptions readOptions; + status_t err = OK; + MediaBufferChain* tmpLink = NULL; + + LOGV("VideoEditorAudioDecoderSource::read begin"); + + if ( STARTED != mState ) { + LOGV("VideoEditorAudioDecoderSource::read invalid state %d", mState); + return UNKNOWN_ERROR; + } + + // Get a buffer from the chain + if( NULL == mFirstBufferLink ) { + *buffer = NULL; + if( mIsEOS ) { + LOGV("VideoEditorAudioDecoderSource::read : EOS"); + return ERROR_END_OF_STREAM; + } else { + LOGV("VideoEditorAudioDecoderSource::read : no buffer available"); + return UNKNOWN_ERROR; + } + } + *buffer = mFirstBufferLink->buffer; + + tmpLink = mFirstBufferLink; + mFirstBufferLink = mFirstBufferLink->nextLink; + if( NULL == mFirstBufferLink ) { + mLastBufferLink = NULL; + } + delete tmpLink; + mNbBuffer--; + + LOGV("VideoEditorAudioDecoderSource::read END (0x%x)", err); + return err; +} + +int32_t VideoEditorAudioDecoderSource::storeBuffer(MediaBuffer *buffer) { + status_t err = OK; + + LOGV("VideoEditorAudioDecoderSource::storeBuffer begin"); + + // A NULL input buffer means that the end of stream was reached + if( NULL == buffer ) { + mIsEOS = true; + } else { + MediaBufferChain* newLink = new MediaBufferChain; + newLink->buffer = buffer; + newLink->nextLink = NULL; + if( NULL != mLastBufferLink ) { + mLastBufferLink->nextLink = newLink; + } else { + mFirstBufferLink = newLink; + } + mLastBufferLink = newLink; + mNbBuffer++; + } + LOGV("VideoEditorAudioDecoderSource::storeBuffer END"); + return mNbBuffer; +} + +/******************** + * TOOLS * + ********************/ + +M4OSA_ERR VideoEditorAudioDecoder_getBits(M4OSA_Int8* pData, + M4OSA_UInt32 dataSize, M4OSA_UInt8 nbBits, M4OSA_Int32* pResult, + M4OSA_UInt32* pOffset) { + + M4OSA_ERR err = M4NO_ERROR; + M4OSA_UInt32 startByte = 0; + M4OSA_UInt32 startBit = 0; + M4OSA_UInt32 endByte = 0; + M4OSA_UInt32 endBit = 0; + M4OSA_UInt32 currentByte = 0; + M4OSA_UInt32 result = 0; + M4OSA_UInt32 ui32Tmp = 0; + M4OSA_UInt32 ui32Mask = 0; + + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pData, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pOffset, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(32 >= nbBits, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK((*pOffset + nbBits) <= 8*dataSize, M4ERR_PARAMETER); + + LOGV("VideoEditorAudioDecoder_getBits begin"); + + startByte = (*pOffset) >> 3; + endByte = (*pOffset + nbBits) >> 3; + startBit = (*pOffset) % 8; + endBit = (*pOffset + nbBits) % 8; + currentByte = startByte; + + // Extract the requested nunber of bits from memory + while( currentByte <= endByte) { + ui32Mask = 0x000000FF; + if( currentByte == startByte ) { + ui32Mask >>= startBit; + } + ui32Tmp = ui32Mask & ((M4OSA_UInt32)pData[currentByte]); + if( currentByte == endByte ) { + ui32Tmp >>= (8-endBit); + result <<= endBit; + } else { + result <<= 8; + } + result |= ui32Tmp; + currentByte++; + } + + *pResult = result; + *pOffset += nbBits; + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioDecoder_getBits no error"); + } else { + LOGV("VideoEditorAudioDecoder_getBits ERROR 0x%X", err); + } + LOGV("VideoEditorAudioDecoder_getBits end"); + return err; +} + + +#define FREQ_TABLE_SIZE 16 +const M4OSA_UInt32 AD_AAC_FREQ_TABLE[FREQ_TABLE_SIZE] = + {96000, 88200, 64000, 48000, 44100, + 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0}; + + +M4OSA_ERR VideoEditorAudioDecoder_parse_AAC_DSI(M4OSA_Int8* pDSI, + M4OSA_UInt32 dsiSize, AAC_DEC_STREAM_PROPS* pProperties) { + + M4OSA_ERR err = M4NO_ERROR; + M4OSA_UInt32 offset = 0; + M4OSA_Int32 result = 0; + + LOGV("VideoEditorAudioDecoder_parse_AAC_DSI begin"); + + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pDSI, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pProperties, M4ERR_PARAMETER); + + // Get the object type + err = VideoEditorAudioDecoder_getBits(pDSI, dsiSize, 5, &result, &offset); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + switch( result ) { + case 2: + pProperties->aPSPresent = 0; + pProperties->aSBRPresent = 0; + break; + default: + LOGV("parse_AAC_DSI ERROR : object type %d is not supported", + result); + VIDEOEDITOR_CHECK(!"invalid AAC object type", M4ERR_BAD_OPTION_ID); + break; + } + pProperties->aAudioObjectType = (M4OSA_Int32)result; + + // Get the frequency index + err = VideoEditorAudioDecoder_getBits(pDSI, dsiSize, 4, &result, &offset); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + VIDEOEDITOR_CHECK((0 <= result) && (FREQ_TABLE_SIZE > result), + M4ERR_PARAMETER); + pProperties->aSampFreq = AD_AAC_FREQ_TABLE[result]; + pProperties->aExtensionSampFreq = 0; + + // Get the number of channels + err = VideoEditorAudioDecoder_getBits(pDSI, dsiSize, 4, &result, &offset); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + pProperties->aNumChan = (M4OSA_UInt32)result; + + // Set the max PCM samples per channel + pProperties->aMaxPCMSamplesPerCh = (pProperties->aSBRPresent) ? 2048 : 1024; + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioDecoder_parse_AAC_DSI no error"); + } else { + LOGV("VideoEditorAudioDecoder_parse_AAC_DSI ERROR 0x%X", err); + } + LOGV("VideoEditorAudioDecoder_parse_AAC_DSI end"); + return err; +} + +/******************** + * ENGINE INTERFACE * + ********************/ + +/** + ****************************************************************************** + * structure VideoEditorAudioDecoder_Context + * @brief This structure defines the context of the StageFright audio decoder + * shell + ****************************************************************************** +*/ +typedef struct { + M4AD_Type mDecoderType; + M4_AudioStreamHandler* mAudioStreamHandler; + sp<VideoEditorAudioDecoderSource> mDecoderSource; + OMXClient mClient; + sp<MediaSource> mDecoder; + int32_t mNbOutputChannels; + uint32_t mNbInputFrames; + uint32_t mNbOutputFrames; +} VideoEditorAudioDecoder_Context; + +M4OSA_ERR VideoEditorAudioDecoder_destroy(M4AD_Context pContext) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioDecoder_Context* pDecoderContext = M4OSA_NULL; + + LOGV("VideoEditorAudioDecoder_destroy begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + pDecoderContext = (VideoEditorAudioDecoder_Context*)pContext; + + // Stop the graph + if( M4OSA_NULL != pDecoderContext->mDecoder.get() ) { + pDecoderContext->mDecoder->stop(); + } + + // Destroy the graph + pDecoderContext->mDecoderSource.clear(); + pDecoderContext->mDecoder.clear(); + pDecoderContext->mClient.disconnect(); + + SAFE_FREE(pDecoderContext); + pContext = M4OSA_NULL; + LOGV("VideoEditorAudioDecoder_destroy : DONE"); + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioDecoder_destroy no error"); + } else { + LOGV("VideoEditorAudioDecoder_destroy ERROR 0x%X", err); + } + LOGV("VideoEditorAudioDecoder_destroy : end"); + return err; +} + +M4OSA_ERR VideoEditorAudioDecoder_create(M4AD_Type decoderType, + M4AD_Context* pContext, M4_AudioStreamHandler* pStreamHandler, + void* pUserData) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioDecoder_Context* pDecoderContext = M4OSA_NULL; + AAC_DEC_STREAM_PROPS aacProperties; + status_t result = OK; + sp<MetaData> decoderMetaData = NULL; + const char* mime = NULL; + uint32_t codecFlags = 0; + + LOGV("VideoEditorAudioDecoder_create begin: decoderType %d", decoderType); + + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pStreamHandler, M4ERR_PARAMETER); + + // Context allocation & initialization + SAFE_MALLOC(pDecoderContext, VideoEditorAudioDecoder_Context, 1, + "AudioDecoder"); + pDecoderContext->mDecoderType = decoderType; + pDecoderContext->mAudioStreamHandler = pStreamHandler; + + pDecoderContext->mNbInputFrames = 0; + pDecoderContext->mNbOutputFrames = 0; + + LOGV("VideoEditorAudioDecoder_create : maxAUSize %d", + pDecoderContext->mAudioStreamHandler->m_basicProperties.m_maxAUSize); + + // Create the meta data for the decoder + decoderMetaData = new MetaData; + switch( pDecoderContext->mDecoderType ) { + case M4AD_kTypeAMRNB: + // StageFright parameters + mime = MEDIA_MIMETYPE_AUDIO_AMR_NB; + // Engine parameters + pDecoderContext->mAudioStreamHandler->m_byteFrameLength = 160; + pDecoderContext->mAudioStreamHandler->m_byteSampleSize = 16; + pDecoderContext->mAudioStreamHandler->m_samplingFrequency = 8000; + pDecoderContext->mAudioStreamHandler->m_nbChannels = 1; + break; + + case M4AD_kTypeAMRWB: + // StageFright parameters + mime = MEDIA_MIMETYPE_AUDIO_AMR_WB; + + pDecoderContext->mAudioStreamHandler->m_byteFrameLength = 160; + pDecoderContext->mAudioStreamHandler->m_byteSampleSize = 16; + pDecoderContext->mAudioStreamHandler->m_samplingFrequency = 16000; + pDecoderContext->mAudioStreamHandler->m_nbChannels = 1; + break; + + case M4AD_kTypeAAC: + // Reject ADTS & ADIF (or any incorrect type) + VIDEOEDITOR_CHECK(M4DA_StreamTypeAudioAac == + pDecoderContext->mAudioStreamHandler->\ + m_basicProperties.m_streamType,M4ERR_PARAMETER); + + // StageFright parameters + mime = MEDIA_MIMETYPE_AUDIO_AAC; + + decoderMetaData->setData(kKeyESDS, kTypeESDS, + pStreamHandler->m_basicProperties.m_pESDSInfo, + pStreamHandler->m_basicProperties.m_ESDSInfoSize); + + // Engine parameters + // Retrieve sampling frequency and number of channels from the DSI + err = VideoEditorAudioDecoder_parse_AAC_DSI( + (M4OSA_Int8*)pStreamHandler->m_basicProperties.\ + m_pDecoderSpecificInfo, + pStreamHandler->m_basicProperties.m_decoderSpecificInfoSize, + &aacProperties); + + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + pDecoderContext->mAudioStreamHandler->m_byteFrameLength = 1024; + pDecoderContext->mAudioStreamHandler->m_byteSampleSize = 16; + pDecoderContext->mAudioStreamHandler->m_samplingFrequency = + aacProperties.aSampFreq; + pDecoderContext->mAudioStreamHandler->m_nbChannels = + aacProperties.aNumChan; + + // Copy the stream properties into userdata + if( M4OSA_NULL != pUserData ) { + M4OSA_memcpy((M4OSA_MemAddr8)pUserData, + (M4OSA_MemAddr8)&aacProperties, + sizeof(AAC_DEC_STREAM_PROPS)); + } + break; + + case M4AD_kTypeMP3: + // StageFright parameters + mime = MEDIA_MIMETYPE_AUDIO_MPEG; + break; + + default: + VIDEOEDITOR_CHECK(!"AudioDecoder_open : incorrect input format", + M4ERR_STATE); + break; + } + decoderMetaData->setCString(kKeyMIMEType, mime); + decoderMetaData->setInt32(kKeySampleRate, + (int32_t)pDecoderContext->mAudioStreamHandler->m_samplingFrequency); + decoderMetaData->setInt32(kKeyChannelCount, + pDecoderContext->mAudioStreamHandler->m_nbChannels); + decoderMetaData->setInt64(kKeyDuration, + (int64_t)pDecoderContext->mAudioStreamHandler->\ + m_basicProperties.m_duration); + + // Create the decoder source + pDecoderContext->mDecoderSource = VideoEditorAudioDecoderSource::Create( + decoderMetaData); + VIDEOEDITOR_CHECK(NULL != pDecoderContext->mDecoderSource.get(), + M4ERR_STATE); + + // Connect to the OMX client + result = pDecoderContext->mClient.connect(); + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + + // Create the OMX codec +#ifdef VIDEOEDITOR_FORCECODEC + codecFlags |= OMXCodec::VIDEOEDITOR_FORCECODEC; +#endif /* VIDEOEDITOR_FORCECODEC */ + + pDecoderContext->mDecoder = OMXCodec::Create(pDecoderContext->\ + mClient.interface(), + decoderMetaData, false, pDecoderContext->mDecoderSource, NULL, + codecFlags); + VIDEOEDITOR_CHECK(NULL != pDecoderContext->mDecoder.get(), M4ERR_STATE); + + // Get the output channels, the decoder might overwrite the input metadata + pDecoderContext->mDecoder->getFormat()->findInt32(kKeyChannelCount, + &pDecoderContext->mNbOutputChannels); + LOGV("VideoEditorAudioDecoder_create : output chan %d", + pDecoderContext->mNbOutputChannels); + + // Start the decoder + result = pDecoderContext->mDecoder->start(); + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + + *pContext = pDecoderContext; + LOGV("VideoEditorAudioDecoder_create : DONE"); + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioDecoder_create no error"); + } else { + VideoEditorAudioDecoder_destroy(pDecoderContext); + *pContext = M4OSA_NULL; + LOGV("VideoEditorAudioDecoder_create ERROR 0x%X", err); + } + return err; +} + +M4OSA_ERR VideoEditorAudioDecoder_create_AAC(M4AD_Context* pContext, + M4_AudioStreamHandler* pStreamHandler, void* pUserData) { + + return VideoEditorAudioDecoder_create( + M4AD_kTypeAAC, pContext, pStreamHandler,pUserData); +} + + +M4OSA_ERR VideoEditorAudioDecoder_create_AMRNB(M4AD_Context* pContext, + M4_AudioStreamHandler* pStreamHandler, void* pUserData) { + + return VideoEditorAudioDecoder_create( + M4AD_kTypeAMRNB, pContext, pStreamHandler, pUserData); +} + + +M4OSA_ERR VideoEditorAudioDecoder_create_AMRWB(M4AD_Context* pContext, + M4_AudioStreamHandler* pStreamHandler, void* pUserData) { + + return VideoEditorAudioDecoder_create( + M4AD_kTypeAMRWB, pContext, pStreamHandler, pUserData); +} + + +M4OSA_ERR VideoEditorAudioDecoder_create_MP3(M4AD_Context* pContext, + M4_AudioStreamHandler* pStreamHandler, void* pUserData) { + + return VideoEditorAudioDecoder_create( + M4AD_kTypeMP3, pContext, pStreamHandler, pUserData); +} + +M4OSA_ERR VideoEditorAudioDecoder_processInputBuffer( + M4AD_Context pContext, M4AD_Buffer* pInputBuffer) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioDecoder_Context* pDecoderContext = M4OSA_NULL; + MediaBuffer* buffer = NULL; + int32_t nbBuffer = 0; + + LOGV("VideoEditorAudioDecoder_processInputBuffer begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + + pDecoderContext = (VideoEditorAudioDecoder_Context*)pContext; + + if( M4OSA_NULL != pInputBuffer ) { + buffer = new MediaBuffer((size_t)pInputBuffer->m_bufferSize); + M4OSA_memcpy((M4OSA_Int8*)buffer->data() + buffer->range_offset(), + pInputBuffer->m_dataAddress, pInputBuffer->m_bufferSize); + } + nbBuffer = pDecoderContext->mDecoderSource->storeBuffer(buffer); + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioDecoder_processInputBuffer no error"); + } else { + LOGV("VideoEditorAudioDecoder_processInputBuffer ERROR 0x%X", err); + } + LOGV("VideoEditorAudioDecoder_processInputBuffer end"); + return err; +} + +M4OSA_ERR VideoEditorAudioDecoder_processOutputBuffer(M4AD_Context pContext, + MediaBuffer* buffer, M4AD_Buffer* pOuputBuffer) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioDecoder_Context* pDecoderContext = M4OSA_NULL; + int32_t i32Tmp = 0; + int64_t i64Tmp = 0; + status_t result = OK; + + LOGV("VideoEditorAudioDecoder_processOutputBuffer begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != buffer, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pOuputBuffer, M4ERR_PARAMETER); + + pDecoderContext = (VideoEditorAudioDecoder_Context*)pContext; + + // Process the returned data + if( 0 == buffer->range_length() ) { + // Decoder has no data yet, nothing unusual + goto cleanUp; + } + + pDecoderContext->mNbOutputFrames++; + + if( pDecoderContext->mAudioStreamHandler->m_nbChannels == + (M4OSA_UInt32)pDecoderContext->mNbOutputChannels ) { + // Just copy the PCMs + pOuputBuffer->m_bufferSize = (M4OSA_UInt32)buffer->range_length(); + M4OSA_memcpy(pOuputBuffer->m_dataAddress, + ((M4OSA_MemAddr8)buffer->data())+buffer->range_offset(), + buffer->range_length()); + } else if( pDecoderContext->mAudioStreamHandler->m_nbChannels < + (M4OSA_UInt32)pDecoderContext->mNbOutputChannels ) { + // The decoder forces stereo output, downsample + pOuputBuffer->m_bufferSize = (M4OSA_UInt32)(buffer->range_length()/2); + M4OSA_Int16* pDataIn = ((M4OSA_Int16*)buffer->data()) + + buffer->range_offset(); + M4OSA_Int16* pDataOut = (M4OSA_Int16*)pOuputBuffer->m_dataAddress; + M4OSA_Int16* pDataEnd = pDataIn + \ + (buffer->range_length()/sizeof(M4OSA_Int16)); + while( pDataIn < pDataEnd ) { + *pDataOut = *pDataIn; + pDataIn+=2; + pDataOut++; + } + } else { + // The decoder forces mono output, not supported + VIDEOEDITOR_CHECK(M4OSA_FALSE, M4ERR_PARAMETER); + } + +cleanUp: + // Release the buffer + buffer->release(); + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioDecoder_processOutputBuffer no error"); + } else { + pOuputBuffer->m_bufferSize = 0; + LOGV("VideoEditorAudioDecoder_processOutputBuffer ERROR 0x%X", err); + } + LOGV("VideoEditorAudioDecoder_processOutputBuffer end"); + return err; +} + +M4OSA_ERR VideoEditorAudioDecoder_step(M4AD_Context pContext, + M4AD_Buffer* pInputBuffer, M4AD_Buffer* pOutputBuffer, + M4OSA_Bool bJump) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioDecoder_Context* pDecoderContext = M4OSA_NULL; + status_t result = OK; + MediaBuffer* outputBuffer = NULL; + + LOGV("VideoEditorAudioDecoder_step begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + pDecoderContext = (VideoEditorAudioDecoder_Context*)pContext; + pDecoderContext->mNbInputFrames++; + + // Push the input buffer to the decoder source + err = VideoEditorAudioDecoder_processInputBuffer(pDecoderContext, + pInputBuffer); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + + // Read + result = pDecoderContext->mDecoder->read(&outputBuffer, NULL); + if(OK != result) { + LOGE("VideoEditorAudioDecoder_step result = %d",result); + + } + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + + // Convert the PCM buffer + err = VideoEditorAudioDecoder_processOutputBuffer(pDecoderContext, + outputBuffer, pOutputBuffer); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioDecoder_step no error"); + } else { + LOGV("VideoEditorAudioDecoder_step ERROR 0x%X", err); + } + LOGV("VideoEditorAudioDecoder_step end"); + return err; +} + +M4OSA_ERR VideoEditorAudioDecoder_getVersion(M4_VersionInfo* pVersionInfo) { + M4OSA_ERR err = M4NO_ERROR; + + LOGV("VideoEditorAudioDecoder_getVersion begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pVersionInfo, M4ERR_PARAMETER); + + pVersionInfo->m_major = VIDEOEDITOR_AUDIO_DECODER_VERSION_MAJOR; + pVersionInfo->m_minor = VIDEOEDITOR_AUDIO_DECODER_VERSION_MINOR; + pVersionInfo->m_revision = VIDEOEDITOR_AUDIO_DECODER_VERSION_REV; + pVersionInfo->m_structSize = sizeof(M4_VersionInfo); + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioDecoder_getVersion no error"); + } else { + LOGV("VideoEditorAudioDecoder_getVersion ERROR 0x%X", err); + } + LOGV("VideoEditorAudioDecoder_getVersion end"); + return err; +} + +M4OSA_ERR VideoEditorAudioDecoder_setOption(M4AD_Context pContext, + M4OSA_UInt32 optionID, M4OSA_DataOption optionValue) { + + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioDecoder_Context* pDecoderContext = M4OSA_NULL; + + LOGV("VideoEditorAudioDecoder_setOption begin 0x%X", optionID); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + pDecoderContext = (VideoEditorAudioDecoder_Context*)pContext; + + switch( optionID ) { + case M4AD_kOptionID_UserParam: + LOGV("VideoEditorAudioDecodersetOption UserParam is not supported"); + err = M4ERR_NOT_IMPLEMENTED; + break; + default: + LOGV("VideoEditorAudioDecoder_setOption unsupported optionId 0x%X", + optionID); + VIDEOEDITOR_CHECK(M4OSA_FALSE, M4ERR_BAD_OPTION_ID); + break; + } + +cleanUp: + if( ((M4OSA_UInt32)M4NO_ERROR == err) || ((M4OSA_UInt32)M4ERR_NOT_IMPLEMENTED == err) ) { + LOGV("VideoEditorAudioDecoder_setOption error 0x%X", err); + } else { + LOGV("VideoEditorAudioDecoder_setOption ERROR 0x%X", err); + } + LOGV("VideoEditorAudioDecoder_setOption end"); + return err; +} + +M4OSA_ERR VideoEditorAudioDecoder_getOption(M4AD_Context pContext, + M4OSA_UInt32 optionID, M4OSA_DataOption optionValue) { + + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioDecoder_Context* pDecoderContext = M4OSA_NULL; + + LOGV("VideoEditorAudioDecoder_getOption begin: optionID 0x%X", optionID); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + pDecoderContext = (VideoEditorAudioDecoder_Context*)pContext; + + switch( optionID ) { + default: + LOGV("VideoEditorAudioDecoder_getOption unsupported optionId 0x%X", + optionID); + VIDEOEDITOR_CHECK(M4OSA_FALSE, M4ERR_BAD_OPTION_ID); + break; + } + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioDecoder_getOption no error"); + } else { + LOGV("VideoEditorAudioDecoder_getOption ERROR 0x%X", err); + } + LOGV("VideoEditorAudioDecoder_getOption end"); + return err; +} + +M4OSA_ERR VideoEditorAudioDecoder_getInterface(M4AD_Type decoderType, + M4AD_Type* pDecoderType, M4AD_Interface** pDecoderInterface) { + + M4OSA_ERR err = M4NO_ERROR; + + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pDecoderType, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pDecoderInterface, M4ERR_PARAMETER); + + LOGV("VideoEditorAudioDecoder_getInterface begin %d 0x%x 0x%x", + decoderType, pDecoderType, pDecoderInterface); + + SAFE_MALLOC(*pDecoderInterface, M4AD_Interface, 1, + "VideoEditorAudioDecoder"); + + *pDecoderType = decoderType; + + switch( decoderType ) { + case M4AD_kTypeAMRNB: + (*pDecoderInterface)->m_pFctCreateAudioDec = + VideoEditorAudioDecoder_create_AMRNB; + break; + case M4AD_kTypeAMRWB: + (*pDecoderInterface)->m_pFctCreateAudioDec = + VideoEditorAudioDecoder_create_AMRWB; + break; + case M4AD_kTypeAAC: + (*pDecoderInterface)->m_pFctCreateAudioDec = + VideoEditorAudioDecoder_create_AAC; + break; + case M4AD_kTypeMP3: + (*pDecoderInterface)->m_pFctCreateAudioDec = + VideoEditorAudioDecoder_create_MP3; + break; + default: + LOGV("VEAD_getInterface ERROR: unsupported type %d", decoderType); + VIDEOEDITOR_CHECK(M4OSA_FALSE, M4ERR_PARAMETER); + break; + } + (*pDecoderInterface)->m_pFctDestroyAudioDec = + VideoEditorAudioDecoder_destroy; + (*pDecoderInterface)->m_pFctResetAudioDec = M4OSA_NULL; + (*pDecoderInterface)->m_pFctStartAudioDec = M4OSA_NULL; + (*pDecoderInterface)->m_pFctStepAudioDec = + VideoEditorAudioDecoder_step; + (*pDecoderInterface)->m_pFctGetVersionAudioDec = + VideoEditorAudioDecoder_getVersion; + (*pDecoderInterface)->m_pFctSetOptionAudioDec = + VideoEditorAudioDecoder_setOption; + (*pDecoderInterface)->m_pFctGetOptionAudioDec = + VideoEditorAudioDecoder_getOption; + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioDecoder_getInterface no error"); + } else { + *pDecoderInterface = M4OSA_NULL; + LOGV("VideoEditorAudioDecoder_getInterface ERROR 0x%X", err); + } + LOGV("VideoEditorAudioDecoder_getInterface end"); + return err; +} + + +extern "C" { + +M4OSA_ERR VideoEditorAudioDecoder_getInterface_AAC(M4AD_Type* pDecoderType, + M4AD_Interface** pDecoderInterface) { + LOGV("TEST: AAC VideoEditorAudioDecoder_getInterface no error"); + return VideoEditorAudioDecoder_getInterface( + M4AD_kTypeAAC, pDecoderType, pDecoderInterface); +} + +M4OSA_ERR VideoEditorAudioDecoder_getInterface_AMRNB(M4AD_Type* pDecoderType, + M4AD_Interface** pDecoderInterface) { + LOGV("TEST: AMR VideoEditorAudioDecoder_getInterface no error"); + return VideoEditorAudioDecoder_getInterface( + M4AD_kTypeAMRNB, pDecoderType, pDecoderInterface); +} + +M4OSA_ERR VideoEditorAudioDecoder_getInterface_AMRWB(M4AD_Type* pDecoderType, + M4AD_Interface** pDecoderInterface) { + + return VideoEditorAudioDecoder_getInterface( + M4AD_kTypeAMRWB, pDecoderType, pDecoderInterface); +} + +M4OSA_ERR VideoEditorAudioDecoder_getInterface_MP3(M4AD_Type* pDecoderType, + M4AD_Interface** pDecoderInterface) { + + return VideoEditorAudioDecoder_getInterface( + M4AD_kTypeMP3, pDecoderType, pDecoderInterface); +} + +} // extern "C" + +} // namespace android diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditorAudioEncoder.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditorAudioEncoder.cpp new file mode 100755 index 0000000..718881f --- /dev/null +++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditorAudioEncoder.cpp @@ -0,0 +1,739 @@ +/* + * Copyright (C) 2011 NXP Software + * Copyright (C) 2011 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. + */ +/** +************************************************************************* +* @file VideoEditorAudioEncoder.cpp +* @brief StageFright shell Audio Encoder +************************************************************************* +*/ + +#define LOG_NDEBUG 1 +#define LOG_TAG "VIDEOEDITOR_AUDIOENCODER" + +#include "M4OSA_Debug.h" +#include "VideoEditorAudioEncoder.h" +#include "VideoEditorUtils.h" + +#include "utils/Log.h" +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/OMXClient.h> +#include <media/stagefright/OMXCodec.h> + +/*** DEFINITIONS ***/ +// Force using software encoder as engine does not support prefetch +#define VIDEOEDITOR_FORCECODEC kSoftwareCodecsOnly + +namespace android { +struct VideoEditorAudioEncoderSource : public MediaSource { + public: + static sp<VideoEditorAudioEncoderSource> Create(); + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual sp<MetaData> getFormat(); + virtual status_t read(MediaBuffer **buffer, + const ReadOptions *options = NULL); + virtual int32_t storeBuffer(MediaBuffer *buffer); + + protected: + virtual ~VideoEditorAudioEncoderSource(); + + private: + struct MediaBufferChain { + MediaBuffer* buffer; + MediaBufferChain* nextLink; + }; + enum State { + CREATED, + STARTED, + ERROR + }; + VideoEditorAudioEncoderSource(); + MediaBufferChain* mFirstBufferLink; + MediaBufferChain* mLastBufferLink; + int32_t mNbBuffer; + State mState; +}; + +sp<VideoEditorAudioEncoderSource> VideoEditorAudioEncoderSource::Create() { + + LOGV("VideoEditorAudioEncoderSource::Create"); + sp<VideoEditorAudioEncoderSource> aSource = + new VideoEditorAudioEncoderSource(); + + return aSource; +} + +VideoEditorAudioEncoderSource::VideoEditorAudioEncoderSource(): + mFirstBufferLink(NULL), + mLastBufferLink(NULL), + mNbBuffer(0), + mState(CREATED) { + LOGV("VideoEditorAudioEncoderSource::VideoEditorAudioEncoderSource"); +} + + +VideoEditorAudioEncoderSource::~VideoEditorAudioEncoderSource() { + LOGV("VideoEditorAudioEncoderSource::~VideoEditorAudioEncoderSource"); + + if( STARTED == mState ) { + stop(); + } +} + +status_t VideoEditorAudioEncoderSource::start(MetaData *meta) { + status_t err = OK; + + LOGV("VideoEditorAudioEncoderSource::start"); + + if( CREATED != mState ) { + LOGV("VideoEditorAudioEncoderSource::start ERROR : invalid state %d", + mState); + return UNKNOWN_ERROR; + } + + mState = STARTED; + +cleanUp: + LOGV("VideoEditorAudioEncoderSource::start END (0x%x)", err); + return err; +} + +status_t VideoEditorAudioEncoderSource::stop() { + status_t err = OK; + + LOGV("VideoEditorAudioEncoderSource::stop"); + + if( STARTED != mState ) { + LOGV("VideoEditorAudioEncoderSource::stop ERROR: invalid state %d", + mState); + return UNKNOWN_ERROR; + } + + int32_t i = 0; + MediaBufferChain* tmpLink = NULL; + while( mFirstBufferLink ) { + i++; + tmpLink = mFirstBufferLink; + mFirstBufferLink = mFirstBufferLink->nextLink; + delete tmpLink; + } + LOGV("VideoEditorAudioEncoderSource::stop : %d buffer remained", i); + mFirstBufferLink = NULL; + mLastBufferLink = NULL; + + mState = CREATED; + + LOGV("VideoEditorAudioEncoderSource::stop END (0x%x)", err); + return err; +} + +sp<MetaData> VideoEditorAudioEncoderSource::getFormat() { + LOGV("VideoEditorAudioEncoderSource::getFormat"); + + LOGV("VideoEditorAudioEncoderSource::getFormat :THIS IS NOT IMPLEMENTED"); + + return NULL; +} + +status_t VideoEditorAudioEncoderSource::read(MediaBuffer **buffer, + const ReadOptions *options) { + MediaSource::ReadOptions readOptions; + status_t err = OK; + MediaBufferChain* tmpLink = NULL; + + LOGV("VideoEditorAudioEncoderSource::read"); + + if ( STARTED != mState ) { + LOGV("VideoEditorAudioEncoderSource::read ERROR : invalid state %d", + mState); + return UNKNOWN_ERROR; + } + + if( NULL == mFirstBufferLink ) { + *buffer = NULL; + LOGV("VideoEditorAudioEncoderSource::read : EOS"); + return ERROR_END_OF_STREAM; + } + *buffer = mFirstBufferLink->buffer; + + tmpLink = mFirstBufferLink; + mFirstBufferLink = mFirstBufferLink->nextLink; + if( NULL == mFirstBufferLink ) { + mLastBufferLink = NULL; + } + delete tmpLink; + mNbBuffer--; + + LOGV("VideoEditorAudioEncoderSource::read END (0x%x)", err); + return err; +} + +int32_t VideoEditorAudioEncoderSource::storeBuffer(MediaBuffer *buffer) { + status_t err = OK; + + LOGV("VideoEditorAudioEncoderSource::storeBuffer"); + + MediaBufferChain* newLink = new MediaBufferChain; + newLink->buffer = buffer; + newLink->nextLink = NULL; + if( NULL != mLastBufferLink ) { + mLastBufferLink->nextLink = newLink; + } else { + mFirstBufferLink = newLink; + } + mLastBufferLink = newLink; + mNbBuffer++; + + LOGV("VideoEditorAudioEncoderSource::storeBuffer END"); + return mNbBuffer; +} + +/******************** + * ENGINE INTERFACE * + ********************/ +/** + ****************************************************************************** + * structure VideoEditorAudioEncoder_Context + * @brief This structure defines the context of the StageFright audio + * encoder shell + ****************************************************************************** +*/ +typedef struct { + M4ENCODER_AudioFormat mFormat; + M4ENCODER_AudioParams* mCodecParams; + M4ENCODER_AudioDecSpecificInfo mDSI; + sp<VideoEditorAudioEncoderSource> mEncoderSource; + OMXClient mClient; + sp<MediaSource> mEncoder; + uint32_t mNbInputFrames; + uint32_t mNbOutputFrames; + int64_t mFirstOutputCts; + int64_t mLastOutputCts; +} VideoEditorAudioEncoder_Context; + +M4OSA_ERR VideoEditorAudioEncoder_cleanup(M4OSA_Context pContext) { + + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioEncoder_Context* pEncoderContext = M4OSA_NULL; + + LOGV("VideoEditorAudioEncoder_cleanup begin"); + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + pEncoderContext = (VideoEditorAudioEncoder_Context*)pContext; + + SAFE_FREE(pEncoderContext->mDSI.pInfo); + SAFE_FREE(pEncoderContext); + pContext = M4OSA_NULL; + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioEncoder_cleanup no error"); + } else { + LOGV("VideoEditorAudioEncoder_cleanup ERROR 0x%X", err); + } + LOGV("VideoEditorAudioEncoder_cleanup end"); + return err; +} + +M4OSA_ERR VideoEditorAudioEncoder_init(M4ENCODER_AudioFormat format, + M4OSA_Context* pContext, M4OSA_Void* pUserData) { + + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioEncoder_Context* pEncoderContext = M4OSA_NULL; + + LOGV(" VideoEditorAudioEncoder_init begin: format %d", format); + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + SAFE_MALLOC(pEncoderContext, VideoEditorAudioEncoder_Context, 1, + "VideoEditorAudioEncoder"); + pEncoderContext->mFormat = format; + + *pContext = pEncoderContext; + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioEncoder_init no error"); + } else { + VideoEditorAudioEncoder_cleanup(pEncoderContext); + *pContext = M4OSA_NULL; + LOGV("VideoEditorAudioEncoder_init ERROR 0x%X", err); + } + LOGV("VideoEditorAudioEncoder_init end"); + return err; +} + +M4OSA_ERR VideoEditorAudioEncoder_init_AAC(M4OSA_Context* pContext, + M4OSA_Void* pUserData) { + return VideoEditorAudioEncoder_init(M4ENCODER_kAAC, pContext, pUserData); +} + +M4OSA_ERR VideoEditorAudioEncoder_init_AMRNB(M4OSA_Context* pContext, + M4OSA_Void* pUserData) { + return VideoEditorAudioEncoder_init(M4ENCODER_kAMRNB, pContext, pUserData); +} + +M4OSA_ERR VideoEditorAudioEncoder_init_MP3(M4OSA_Context* pContext, + M4OSA_Void* pUserData) { + return VideoEditorAudioEncoder_init(M4ENCODER_kMP3, pContext, pUserData); +} + +M4OSA_ERR VideoEditorAudioEncoder_close(M4OSA_Context pContext) { + + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioEncoder_Context* pEncoderContext = M4OSA_NULL; + + LOGV("VideoEditorAudioEncoder_close begin"); + + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + pEncoderContext = (VideoEditorAudioEncoder_Context*)pContext; + + SAFE_FREE(pEncoderContext->mCodecParams); + + pEncoderContext->mEncoder->stop(); + pEncoderContext->mEncoder.clear(); + pEncoderContext->mClient.disconnect(); + pEncoderContext->mEncoderSource.clear(); + + LOGV("AudioEncoder_close:IN %d frames,OUT %d frames from %lld to %lld", + pEncoderContext->mNbInputFrames, + pEncoderContext->mNbOutputFrames, pEncoderContext->mFirstOutputCts, + pEncoderContext->mLastOutputCts); + + if( pEncoderContext->mNbInputFrames != pEncoderContext->mNbInputFrames ) { + LOGV("VideoEditorAudioEncoder_close:some frames were not encoded %d %d", + pEncoderContext->mNbInputFrames, pEncoderContext->mNbInputFrames); + } + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioEncoder_close no error"); + } else { + LOGV("VideoEditorAudioEncoder_close ERROR 0x%X", err); + } + LOGV("VideoEditorAudioEncoder_close begin end"); + return err; +} + +M4OSA_ERR VideoEditorAudioEncoder_open(M4OSA_Context pContext, + M4ENCODER_AudioParams *pParams, M4ENCODER_AudioDecSpecificInfo *pDSI, + M4OSA_Context pGrabberContext) { + + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioEncoder_Context* pEncoderContext = M4OSA_NULL; + status_t result = OK; + sp<MetaData> encoderMetadata = NULL; + const char* mime = NULL; + int32_t iNbChannel = 0; + uint32_t codecFlags = 0; + + LOGV("VideoEditorAudioEncoder_open begin"); + + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pParams, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pDSI, M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorAudioEncoder_Context*)pContext; + pDSI->pInfo = M4OSA_NULL; + pDSI->infoSize = 0; + + pEncoderContext->mNbInputFrames = 0; + pEncoderContext->mNbOutputFrames = 0; + pEncoderContext->mFirstOutputCts = -1; + pEncoderContext->mLastOutputCts = -1; + + // Allocate & initialize the encoding parameters + LOGV("VideoEditorAudioEncoder_open : params F=%d CN=%d BR=%d F=%d", + pParams->Frequency, pParams->ChannelNum, pParams->Bitrate, + pParams->Format); + SAFE_MALLOC(pEncoderContext->mCodecParams, M4ENCODER_AudioParams, 1, + "VIDEOEDITOR CodecParams"); + pEncoderContext->mCodecParams->Frequency = pParams->Frequency; + pEncoderContext->mCodecParams->ChannelNum = pParams->ChannelNum; + pEncoderContext->mCodecParams->Bitrate = pParams->Bitrate; + pEncoderContext->mCodecParams->Format = pParams->Format; + + // Check output format consistency + VIDEOEDITOR_CHECK(pEncoderContext->mCodecParams->Format == + pEncoderContext->mFormat, M4ERR_PARAMETER); + + /** + * StageFright graph building + */ + // Create the meta data for the encoder + encoderMetadata = new MetaData; + switch( pEncoderContext->mCodecParams->Format ) { + case M4ENCODER_kAAC: + { + mime = MEDIA_MIMETYPE_AUDIO_AAC; + break; + } + case M4ENCODER_kAMRNB: + { + mime = MEDIA_MIMETYPE_AUDIO_AMR_NB; + break; + } + default: + { + VIDEOEDITOR_CHECK(!"AudioEncoder_open : incorrect input format", + M4ERR_PARAMETER); + break; + } + } + encoderMetadata->setCString(kKeyMIMEType, mime); + encoderMetadata->setInt32(kKeySampleRate, + (int32_t)pEncoderContext->mCodecParams->Frequency); + encoderMetadata->setInt32(kKeyBitRate, + (int32_t)pEncoderContext->mCodecParams->Bitrate); + + switch( pEncoderContext->mCodecParams->ChannelNum ) { + case M4ENCODER_kMono: + { + iNbChannel = 1; + break; + } + case M4ENCODER_kStereo: + { + iNbChannel = 2; + break; + } + default: + { + VIDEOEDITOR_CHECK(!"AudioEncoder_open : incorrect channel number", + M4ERR_STATE); + break; + } + } + encoderMetadata->setInt32(kKeyChannelCount, iNbChannel); + + // Create the encoder source + pEncoderContext->mEncoderSource = VideoEditorAudioEncoderSource::Create(); + VIDEOEDITOR_CHECK(NULL != pEncoderContext->mEncoderSource.get(), + M4ERR_STATE); + + // Connect to the OMX client + result = pEncoderContext->mClient.connect(); + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + + // Create the OMX codec +#ifdef VIDEOEDITOR_FORCECODEC + codecFlags |= OMXCodec::VIDEOEDITOR_FORCECODEC; +#endif /* VIDEOEDITOR_FORCECODEC */ + pEncoderContext->mEncoder = OMXCodec::Create( + pEncoderContext->mClient.interface(), encoderMetadata, true, + pEncoderContext->mEncoderSource, NULL, codecFlags); + VIDEOEDITOR_CHECK(NULL != pEncoderContext->mEncoder.get(), M4ERR_STATE); + + // Start the graph + result = pEncoderContext->mEncoder->start(); + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + + // Get AAC DSI, this code can only work with software encoder + if( M4ENCODER_kAAC == pEncoderContext->mCodecParams->Format ) { + int32_t isCodecConfig = 0; + MediaBuffer* buffer = NULL; + + // Read once to get the DSI + result = pEncoderContext->mEncoder->read(&buffer, NULL); + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + VIDEOEDITOR_CHECK(buffer->meta_data()->findInt32(kKeyIsCodecConfig, + &isCodecConfig) && isCodecConfig, M4ERR_STATE); + + // Save the DSI + pEncoderContext->mDSI.infoSize = (M4OSA_UInt32)buffer->range_length(); + SAFE_MALLOC(pEncoderContext->mDSI.pInfo, M4OSA_Int8, + pEncoderContext->mDSI.infoSize, "Encoder header"); + + M4OSA_memcpy(pEncoderContext->mDSI.pInfo, + (M4OSA_MemAddr8)(buffer->data())+buffer->range_offset(), + pEncoderContext->mDSI.infoSize); + + buffer->release(); + *pDSI = pEncoderContext->mDSI; + } + LOGV("VideoEditorAudioEncoder_open : DONE"); + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioEncoder_open no error"); + } else { + VideoEditorAudioEncoder_close(pEncoderContext); + LOGV("VideoEditorAudioEncoder_open ERROR 0x%X", err); + } + LOGV("VideoEditorAudioEncoder_open end"); + return err; +} + +M4OSA_ERR VideoEditorAudioEncoder_processInputBuffer(M4OSA_Context pContext, + M4ENCODER_AudioBuffer* pInBuffer) { + + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioEncoder_Context* pEncoderContext = M4OSA_NULL; + M4OSA_Int8* pData = M4OSA_NULL; + MediaBuffer* buffer = NULL; + int32_t nbBuffer = 0; + + LOGV("VideoEditorAudioEncoder_processInputBuffer begin"); + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorAudioEncoder_Context*)pContext; + + switch( pEncoderContext->mCodecParams->ChannelNum ) { + case M4ENCODER_kMono: + case M4ENCODER_kStereo: + // Let the MediaBuffer own the data so we don't have to free it + buffer = new MediaBuffer((size_t)pInBuffer->pTableBufferSize[0]); + pData = (M4OSA_Int8*)buffer->data() + buffer->range_offset(); + M4OSA_memcpy(pData, pInBuffer->pTableBuffer[0], + pInBuffer->pTableBufferSize[0]); + break; + default: + LOGV("VEAE_processInputBuffer unsupported channel configuration %d", + pEncoderContext->mCodecParams->ChannelNum); + VIDEOEDITOR_CHECK(M4OSA_FALSE, M4ERR_PARAMETER); + break; + } + + LOGV("VideoEditorAudioEncoder_processInputBuffer : store %d bytes", + buffer->range_length()); + // Push the buffer to the source + nbBuffer = pEncoderContext->mEncoderSource->storeBuffer(buffer); + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioEncoder_processInputBuffer no error"); + } else { + if( NULL != buffer ) { + buffer->release(); + } + LOGV("VideoEditorAudioEncoder_processInputBuffer ERROR 0x%X", err); + } + LOGV("VideoEditorAudioEncoder_processInputBuffer end"); + return err; +} + +M4OSA_ERR VideoEditorAudioEncoder_processOutputBuffer(M4OSA_Context pContext, + MediaBuffer* buffer, M4ENCODER_AudioBuffer* pOutBuffer) { + + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioEncoder_Context* pEncoderContext = M4OSA_NULL; + M4OSA_UInt32 Cts = 0; + int32_t i32Tmp = 0; + int64_t i64Tmp = 0; + status_t result = OK; + + LOGV("VideoEditorAudioEncoder_processOutputBuffer begin"); + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != buffer, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pOutBuffer, M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorAudioEncoder_Context*)pContext; + + // Process the returned AU + if( 0 == buffer->range_length() ) { + // Encoder has no data yet, nothing unusual + LOGV("VideoEditorAudioEncoder_processOutputBuffer : buffer is empty"); + pOutBuffer->pTableBufferSize[0] = 0; + goto cleanUp; + } + if( buffer->meta_data()->findInt32(kKeyIsCodecConfig, &i32Tmp) && i32Tmp ) { + /* This should not happen with software encoder, + * DSI was retrieved beforehand */ + VIDEOEDITOR_CHECK(M4OSA_FALSE, M4ERR_STATE); + } else { + // Check the CTS + VIDEOEDITOR_CHECK(buffer->meta_data()->findInt64(kKeyTime, &i64Tmp), + M4ERR_STATE); + Cts = (M4OSA_Int32)(i64Tmp/1000); + + pEncoderContext->mNbOutputFrames++; + if( 0 > pEncoderContext->mFirstOutputCts ) { + pEncoderContext->mFirstOutputCts = i64Tmp; + } + pEncoderContext->mLastOutputCts = i64Tmp; + + // Format the AU + M4OSA_memcpy(pOutBuffer->pTableBuffer[0], + (M4OSA_MemAddr8)(buffer->data())+buffer->range_offset(), + buffer->range_length()); + pOutBuffer->pTableBufferSize[0] = (M4OSA_UInt32)buffer->range_length(); + } + +cleanUp: + // Release the buffer + buffer->release(); + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioEncoder_processOutputBuffer no error"); + } else { + LOGV("VideoEditorAudioEncoder_processOutputBuffer ERROR 0x%X", err); + } + LOGV("VideoEditorAudioEncoder_processOutputBuffer end"); + return err; +} + +M4OSA_ERR VideoEditorAudioEncoder_step(M4OSA_Context pContext, + M4ENCODER_AudioBuffer* pInBuffer, M4ENCODER_AudioBuffer* pOutBuffer) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioEncoder_Context* pEncoderContext = M4OSA_NULL; + status_t result = OK; + MediaBuffer* buffer = NULL; + + LOGV("VideoEditorAudioEncoder_step begin"); + + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pInBuffer, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pOutBuffer, M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorAudioEncoder_Context*)pContext; + pEncoderContext->mNbInputFrames++; + + // Push the input buffer to the encoder source + err = VideoEditorAudioEncoder_processInputBuffer(pEncoderContext,pInBuffer); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + + // Read + result = pEncoderContext->mEncoder->read(&buffer, NULL); + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + + // Provide the encoded AU to the writer + err = VideoEditorAudioEncoder_processOutputBuffer(pEncoderContext, buffer, + pOutBuffer); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioEncoder_step no error"); + } else { + LOGV("VideoEditorAudioEncoder_step ERROR 0x%X", err); + } + LOGV("VideoEditorAudioEncoder_step end"); + return err; +} + +M4OSA_ERR VideoEditorAudioEncoder_getOption(M4OSA_Context pContext, + M4OSA_OptionID optionID, M4OSA_DataOption* optionValue) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorAudioEncoder_Context* pEncoderContext = M4OSA_NULL; + + LOGV("VideoEditorAudioEncoder_getOption begin optionID 0x%X", optionID); + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorAudioEncoder_Context*)pContext; + + switch( optionID ) { + default: + LOGV("VideoEditorAudioEncoder_getOption: unsupported optionId 0x%X", + optionID); + VIDEOEDITOR_CHECK(M4OSA_FALSE, M4ERR_BAD_OPTION_ID); + break; + } + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioEncoder_getOption no error"); + } else { + LOGV("VideoEditorAudioEncoder_getOption ERROR 0x%X", err); + } + LOGV("VideoEditorAudioEncoder_getOption end"); + return err; +} + +M4OSA_ERR VideoEditorAudioEncoder_getInterface( + M4ENCODER_AudioFormat format, M4ENCODER_AudioFormat* pFormat, + M4ENCODER_AudioGlobalInterface** pEncoderInterface) { + M4OSA_ERR err = M4NO_ERROR; + + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pFormat, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pEncoderInterface, M4ERR_PARAMETER); + + LOGV("VideoEditorAudioEncoder_getInterface 0x%x 0x%x",pFormat, + pEncoderInterface); + SAFE_MALLOC(*pEncoderInterface, M4ENCODER_AudioGlobalInterface, 1, + "AudioEncoder"); + + *pFormat = format; + + switch( format ) { + case M4ENCODER_kAAC: + { + (*pEncoderInterface)->pFctInit = VideoEditorAudioEncoder_init_AAC; + break; + } + case M4ENCODER_kAMRNB: + { + (*pEncoderInterface)->pFctInit = VideoEditorAudioEncoder_init_AMRNB; + break; + } + case M4ENCODER_kMP3: + { + (*pEncoderInterface)->pFctInit = VideoEditorAudioEncoder_init_MP3; + break; + } + default: + { + LOGV("VideoEditorAudioEncoder_getInterface: unsupported format %d", + format); + VIDEOEDITOR_CHECK(M4OSA_FALSE, M4ERR_PARAMETER); + break; + } + } + (*pEncoderInterface)->pFctCleanUp = VideoEditorAudioEncoder_cleanup; + (*pEncoderInterface)->pFctOpen = VideoEditorAudioEncoder_open; + (*pEncoderInterface)->pFctClose = VideoEditorAudioEncoder_close; + (*pEncoderInterface)->pFctStep = VideoEditorAudioEncoder_step; + (*pEncoderInterface)->pFctGetOption = VideoEditorAudioEncoder_getOption; + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorAudioEncoder_getInterface no error"); + } else { + *pEncoderInterface = M4OSA_NULL; + LOGV("VideoEditorAudioEncoder_getInterface ERROR 0x%X", err); + } + return err; +} +extern "C" { + +M4OSA_ERR VideoEditorAudioEncoder_getInterface_AAC( + M4ENCODER_AudioFormat* pFormat, + M4ENCODER_AudioGlobalInterface** pEncoderInterface) { + return VideoEditorAudioEncoder_getInterface( + M4ENCODER_kAAC, pFormat, pEncoderInterface); +} + +M4OSA_ERR VideoEditorAudioEncoder_getInterface_AMRNB( + M4ENCODER_AudioFormat* pFormat, + M4ENCODER_AudioGlobalInterface** pEncoderInterface) { + + return VideoEditorAudioEncoder_getInterface( + M4ENCODER_kAMRNB, pFormat, pEncoderInterface); +} + +M4OSA_ERR VideoEditorAudioEncoder_getInterface_MP3( + M4ENCODER_AudioFormat* pFormat, + M4ENCODER_AudioGlobalInterface** pEncoderInterface) { + LOGV("VideoEditorAudioEncoder_getInterface_MP3 no error"); + + return VideoEditorAudioEncoder_getInterface( + M4ENCODER_kMP3, pFormat, pEncoderInterface); +} + +} // extern "C" + +} // namespace android diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditorBuffer.c b/libvideoeditor/vss/stagefrightshells/src/VideoEditorBuffer.c new file mode 100755 index 0000000..9f50a58 --- /dev/null +++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditorBuffer.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2011 NXP Software + * Copyright (C) 2011 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. + */ +/** +************************************************************************* +* @file VideoEditorBuffer.c +* @brief StageFright shell Buffer +************************************************************************* +*/ +#undef M4OSA_TRACE_LEVEL +#define M4OSA_TRACE_LEVEL 1 + +#include "VideoEditorBuffer.h" +#include "utils/Log.h" + +#define VIDEOEDITOR_BUFFEPOOL_MAX_NAME_SIZE 40 + +#define VIDEOEDITOR_SAFE_FREE(p) \ +{ \ + if(M4OSA_NULL != p) \ + { \ + M4OSA_free((M4OSA_MemAddr32)p); \ + p = M4OSA_NULL; \ + } \ +} + +/** + ************************************************************************ + M4OSA_ERR VIDEOEDITOR_BUFFER_allocatePool(VIDEOEDITOR_BUFFER_Pool** ppool, + * M4OSA_UInt32 nbBuffers) + * @brief Allocate a pool of nbBuffers buffers + * + * @param ppool : IN The buffer pool to create + * @param nbBuffers : IN The number of buffers in the pool + * @param poolName : IN a name given to the pool + * @return Error code + ************************************************************************ +*/ +M4OSA_ERR VIDEOEDITOR_BUFFER_allocatePool(VIDEOEDITOR_BUFFER_Pool** ppool, + M4OSA_UInt32 nbBuffers, M4OSA_Char* poolName) +{ + M4OSA_ERR lerr = M4NO_ERROR; + VIDEOEDITOR_BUFFER_Pool* pool; + + LOGV("VIDEOEDITOR_BUFFER_allocatePool : ppool = 0x%x nbBuffers = %d ", + ppool, nbBuffers); + + pool = M4OSA_NULL; + pool = (VIDEOEDITOR_BUFFER_Pool*)M4OSA_malloc( + sizeof(VIDEOEDITOR_BUFFER_Pool), VIDEOEDITOR_BUFFER_EXTERNAL, + (M4OSA_Char*)("VIDEOEDITOR_BUFFER_allocatePool: pool")); + if (M4OSA_NULL == pool) + { + lerr = M4ERR_ALLOC; + goto VIDEOEDITOR_BUFFER_allocatePool_Cleanup; + } + + LOGV("VIDEOEDITOR_BUFFER_allocatePool : Allocating Pool buffers"); + pool->pNXPBuffer = M4OSA_NULL; + pool->pNXPBuffer = (VIDEOEDITOR_BUFFER_Buffer*)M4OSA_malloc( + sizeof(VIDEOEDITOR_BUFFER_Buffer)*nbBuffers, + VIDEOEDITOR_BUFFER_EXTERNAL, + (M4OSA_Char*)("BUFFER_allocatePool: pNXPBuffer")); + if(M4OSA_NULL == pool->pNXPBuffer) + { + lerr = M4ERR_ALLOC; + goto VIDEOEDITOR_BUFFER_allocatePool_Cleanup; + } + + LOGV("VIDEOEDITOR_BUFFER_allocatePool : Allocating Pool name buffer"); + pool->poolName = M4OSA_NULL; + pool->poolName = (M4OSA_Char*)M4OSA_malloc( + VIDEOEDITOR_BUFFEPOOL_MAX_NAME_SIZE,VIDEOEDITOR_BUFFER_EXTERNAL, + (M4OSA_Char*)("VIDEOEDITOR_BUFFER_allocatePool: poolname")); + if(pool->poolName == M4OSA_NULL) + { + lerr = M4ERR_ALLOC; + goto VIDEOEDITOR_BUFFER_allocatePool_Cleanup; + } + + LOGV("VIDEOEDITOR_BUFFER_allocatePool : Assigning Pool name buffer"); + + M4OSA_memset(pool->poolName, VIDEOEDITOR_BUFFEPOOL_MAX_NAME_SIZE, 0); + M4OSA_memcpy(pool->poolName, poolName, + VIDEOEDITOR_BUFFEPOOL_MAX_NAME_SIZE-1); + + pool->NB = nbBuffers; + +VIDEOEDITOR_BUFFER_allocatePool_Cleanup: + if(M4NO_ERROR != lerr) + { + VIDEOEDITOR_SAFE_FREE(pool->pNXPBuffer); + VIDEOEDITOR_SAFE_FREE(pool->poolName); + VIDEOEDITOR_SAFE_FREE(pool); + } + *ppool = pool; + LOGV("VIDEOEDITOR_BUFFER_allocatePool END"); + + return lerr; +} + +/** + ************************************************************************ + M4OSA_ERR VIDEOEDITOR_BUFFER_freePool(VIDEOEDITOR_BUFFER_Pool* ppool) + * @brief Deallocate a buffer pool + * + * @param ppool : IN The buffer pool to free + * @return Error code + ************************************************************************ +*/ +M4OSA_ERR VIDEOEDITOR_BUFFER_freePool(VIDEOEDITOR_BUFFER_Pool* ppool) +{ + M4OSA_ERR err; + M4OSA_UInt32 j = 0; + + LOGV("VIDEOEDITOR_BUFFER_freePool : ppool = 0x%x", ppool); + + err = M4NO_ERROR; + + for (j = 0; j < ppool->NB; j++) + { + if(M4OSA_NULL != ppool->pNXPBuffer[j].pData) + { + M4OSA_free((M4OSA_MemAddr32)ppool->pNXPBuffer[j].pData); + ppool->pNXPBuffer[j].pData = M4OSA_NULL; + } + } + + if(ppool != M4OSA_NULL) + { + SAFE_FREE(ppool->pNXPBuffer); + SAFE_FREE(ppool->poolName); + SAFE_FREE(ppool); + } + + return(err); +} + +/** + ************************************************************************ + M4OSA_ERR VIDEOEDITOR_BUFFER_getBuffer(VIDEOEDITOR_BUFFER_Pool* ppool, + * VIDEOEDITOR_BUFFER_Buffer** pNXPBuffer) + * @brief Returns a buffer in a given state + * + * @param ppool : IN The buffer pool + * @param desiredState : IN The buffer state + * @param pNXPBuffer : IN The selected buffer + * @return Error code + ************************************************************************ +*/ +M4OSA_ERR VIDEOEDITOR_BUFFER_getBuffer(VIDEOEDITOR_BUFFER_Pool* ppool, + VIDEOEDITOR_BUFFER_State desiredState, + VIDEOEDITOR_BUFFER_Buffer** pNXPBuffer) +{ + M4OSA_ERR err = M4NO_ERROR; + M4OSA_Bool bFound = M4OSA_FALSE; + M4OSA_UInt32 i, ibuf; + + LOGV("VIDEOEDITOR_BUFFER_getBuffer from %s in state=%d", + ppool->poolName, desiredState); + + ibuf = 0; + + for (i=0; i < ppool->NB; i++) + { + bFound = (ppool->pNXPBuffer[i].state == desiredState); + if (bFound) + { + ibuf = i; + break; + } + } + + if(!bFound) + { + LOGV("VIDEOEDITOR_BUFFER_getBuffer No buffer available in state %d", + desiredState); + *pNXPBuffer = M4OSA_NULL; + return M4ERR_NO_BUFFER_AVAILABLE; + } + + /* case where a buffer has been found */ + *pNXPBuffer = &(ppool->pNXPBuffer[ibuf]); + + LOGV("VIDEOEDITOR_BUFFER_getBuffer: idx = %d", ibuf); + + return(err); +} + +M4OSA_ERR VIDEOEDITOR_BUFFER_initPoolBuffers(VIDEOEDITOR_BUFFER_Pool* pool, + M4OSA_UInt32 lSize) +{ + M4OSA_ERR err = M4NO_ERROR; + M4OSA_UInt32 index, j; + + /** + * Initialize all the buffers in the pool */ + for(index = 0; index< pool->NB; index++) + { + pool->pNXPBuffer[index].pData = M4OSA_NULL; + pool->pNXPBuffer[index].pData = (M4OSA_Void*)M4OSA_malloc( + lSize, VIDEOEDITOR_BUFFER_EXTERNAL, + (M4OSA_Char*)("BUFFER_initPoolBuffers: Buffer data")); + if(M4OSA_NULL == pool->pNXPBuffer[index].pData) + { + for (j = 0; j < index; j++) + { + if(M4OSA_NULL != pool->pNXPBuffer[index].pData) + { + M4OSA_free((M4OSA_MemAddr32)pool->pNXPBuffer[index].pData); + pool->pNXPBuffer[index].pData = M4OSA_NULL; + } + } + err = M4ERR_ALLOC; + return err; + } + pool->pNXPBuffer[index].size = 0; + pool->pNXPBuffer[index].state = VIDEOEDITOR_BUFFER_kEmpty; + pool->pNXPBuffer[index].idx = index; + pool->pNXPBuffer[index].buffCTS = -1; + } + return err; +} + +M4OSA_ERR VIDEOEDITOR_BUFFER_getOldestBuffer(VIDEOEDITOR_BUFFER_Pool *pool, + VIDEOEDITOR_BUFFER_State desiredState, + VIDEOEDITOR_BUFFER_Buffer** pNXPBuffer) +{ + M4OSA_ERR err = M4NO_ERROR; + M4OSA_UInt32 index, j; + M4_MediaTime candidateTimeStamp = (M4_MediaTime)0x7ffffff; + M4OSA_Bool bFound = M4OSA_FALSE; + + *pNXPBuffer = M4OSA_NULL; + for(index = 0; index< pool->NB; index++) + { + if(pool->pNXPBuffer[index].state == desiredState) + { + if(pool->pNXPBuffer[index].buffCTS <= candidateTimeStamp) + { + bFound = M4OSA_TRUE; + candidateTimeStamp = pool->pNXPBuffer[index].buffCTS; + *pNXPBuffer = &(pool->pNXPBuffer[index]); + } + } + } + if(M4OSA_FALSE == bFound) + { + LOGV("VIDEOEDITOR_BUFFER_getOldestBuffer WARNING no buffer available"); + err = M4ERR_NO_BUFFER_AVAILABLE; + } + return err; +} diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditorMp3Reader.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditorMp3Reader.cpp new file mode 100755 index 0000000..a07bf47 --- /dev/null +++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditorMp3Reader.cpp @@ -0,0 +1,801 @@ +/* + * Copyright (C) 2011 NXP Software + * Copyright (C) 2011 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. + */ +/** +************************************************************************* +* @file VideoEditorMp3Reader.cpp +* @brief StageFright shell MP3 Reader +************************************************************************* +*/ +#define LOG_NDEBUG 1 +#define LOG_TAG "VIDEOEDITOR_MP3READER" + +/** + * HEADERS + * + */ +#include "M4OSA_Debug.h" +#include "M4SYS_AccessUnit.h" +#include "VideoEditorMp3Reader.h" +#include "VideoEditorUtils.h" + +#include "utils/Log.h" +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/FileSource.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> + +/** + * SOURCE CLASS + */ + +namespace android { +/** + * ENGINE INTERFACE + */ + +/** + ************************************************************************** + * structure VideoEditorMp3Reader_Context + * @brief This structure defines the context of the SF MP3 reader shell. + ************************************************************************** + */ +typedef struct { + sp<DataSource> mDataSource; + sp<MediaExtractor> mExtractor; + sp<MediaSource> mMediaSource; + M4_AudioStreamHandler* mAudioStreamHandler; + M4SYS_AccessUnit mAudioAu; + M4OSA_Time mMaxDuration; + M4OSA_UInt8 mStreamNumber; + M4OSA_Bool mSeeking; + M4OSA_Time mSeekTime; + uint32_t mExtractorFlags; +} VideoEditorMp3Reader_Context; + +/** + **************************************************************************** + * @brief create an instance of the MP3 reader + * @note allocates the context + * + * @param pContext: (OUT) pointer on a reader context + * + * @return M4NO_ERROR there is no error + * @return M4ERR_ALLOC a memory allocation has failed + * @return M4ERR_PARAMETER at least one parameter is not valid + **************************************************************************** +*/ +M4OSA_ERR VideoEditorMp3Reader_create(M4OSA_Context *pContext) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorMp3Reader_Context *pReaderContext = M4OSA_NULL; + + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + LOGV("VideoEditorMp3Reader_create begin"); + + /* Context allocation & initialization */ + SAFE_MALLOC(pReaderContext, VideoEditorMp3Reader_Context, 1, + "VideoEditorMp3Reader"); + + pReaderContext->mAudioStreamHandler = M4OSA_NULL; + pReaderContext->mAudioAu.dataAddress = M4OSA_NULL; + M4OSA_INT64_FROM_INT32(pReaderContext->mMaxDuration, 0); + *pContext = pReaderContext; + +cleanUp: + if (M4NO_ERROR == err) { + LOGV("VideoEditorMp3Reader_create no error"); + } else { + LOGV("VideoEditorMp3Reader_create ERROR 0x%X", err); + } + LOGV("VideoEditorMp3Reader_create end"); + return err; +} + +/** + ******************************************************************************* + * @brief destroy the instance of the MP3 reader + * @note after this call the context is invalid + * @param context: (IN) Context of the reader + * @return M4NO_ERROR there is no error + * @return M4ERR_PARAMETER The input parameter is not properly set + ******************************************************************************* +*/ +M4OSA_ERR VideoEditorMp3Reader_destroy(M4OSA_Context pContext) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorMp3Reader_Context *pReaderContext = + (VideoEditorMp3Reader_Context*)pContext; + + VIDEOEDITOR_CHECK(M4OSA_NULL != pReaderContext, M4ERR_PARAMETER); + LOGV("VideoEditorMp3Reader_destroy begin"); + + SAFE_FREE(pReaderContext); +cleanUp: + if (M4NO_ERROR == err) { + LOGV("VideoEditorMp3Reader_destroy no error"); + } else { + LOGV("VideoEditorMp3Reader_destroy ERROR 0x%X", err); + } + LOGV("VideoEditorMp3Reader_destroy end"); + return err; +} +/** + ****************************************************************************** + * @brief open the reader and initializes its created instance + * @note this function opens the MP3 file + * @param context: (IN) Context of the reader + * @param pFileDescriptor: (IN) Pointer to proprietary data identifying + * the media to open + + * @return M4NO_ERROR there is no error + * @return M4ERR_PARAMETER the context is NULL + * @return M4ERR_BAD_CONTEXT provided context is not a valid one + ****************************************************************************** +*/ +M4OSA_ERR VideoEditorMp3Reader_open(M4OSA_Context context, + M4OSA_Void* pFileDescriptor){ + VideoEditorMp3Reader_Context *pReaderContext = + (VideoEditorMp3Reader_Context*)context; + M4OSA_ERR err = M4NO_ERROR; + + LOGV("VideoEditorMp3Reader_open begin"); + /* Check function parameters*/ + M4OSA_DEBUG_IF1((M4OSA_NULL == pReaderContext), M4ERR_PARAMETER, + "VideoEditorMp3Reader_open: invalid context pointer"); + M4OSA_DEBUG_IF1((M4OSA_NULL == pFileDescriptor), M4ERR_PARAMETER, + "VideoEditorMp3Reader_open: invalid pointer pFileDescriptor"); + + LOGV("VideoEditorMp3Reader_open Datasource start %s", + (char*)pFileDescriptor); + pReaderContext->mDataSource = DataSource::CreateFromURI( + (char*)pFileDescriptor); + LOGV("VideoEditorMp3Reader_open Datasource end"); + + if (pReaderContext->mDataSource == NULL) { + LOGV("VideoEditorMp3Reader_open Datasource error"); + return UNKNOWN_ERROR; + } + + LOGV("VideoEditorMp3Reader_open extractor start"); + pReaderContext->mExtractor = MediaExtractor::Create( + pReaderContext->mDataSource,MEDIA_MIMETYPE_AUDIO_MPEG); + LOGV("VideoEditorMp3Reader_open extractor end"); + + if (pReaderContext->mExtractor == NULL) { + LOGV("VideoEditorMp3Reader_open extractor error"); + return UNKNOWN_ERROR; + } + pReaderContext->mStreamNumber = 0; + + LOGV("VideoEditorMp3Reader_open end"); + return err; +} +/** + ************************************************************************** + * @brief close the reader + * @note this function closes the MP3 reader + * @param context: (IN) Context of the reader + * @return M4NO_ERROR there is no error + * @return M4ERR_PARAMETER the context is NULL + ************************************************************************** +*/ +M4OSA_ERR VideoEditorMp3Reader_close(M4OSA_Context context) { + VideoEditorMp3Reader_Context *pReaderContext = + (VideoEditorMp3Reader_Context*)context; + M4OSA_ERR err = M4NO_ERROR; + + LOGV("VideoEditorMp3Reader_close begin"); + /* Check function parameters */ + M4OSA_DEBUG_IF1((M4OSA_NULL == pReaderContext), M4ERR_PARAMETER, + "VideoEditorMp3Reader_close: invalid context pointer"); + + if (pReaderContext->mAudioStreamHandler != NULL) { + if (M4OSA_NULL != pReaderContext->mAudioStreamHandler->\ + m_basicProperties.m_pDecoderSpecificInfo) { + M4OSA_free((M4OSA_MemAddr32)pReaderContext->mAudioStreamHandler->\ + m_basicProperties.m_pDecoderSpecificInfo); + pReaderContext->mAudioStreamHandler->m_basicProperties.\ + m_decoderSpecificInfoSize = 0; + pReaderContext->mAudioStreamHandler->m_basicProperties.\ + m_pDecoderSpecificInfo = M4OSA_NULL; + } + + /* Finally destroy the stream handler */ + M4OSA_free((M4OSA_MemAddr32)pReaderContext->mAudioStreamHandler); + pReaderContext->mAudioStreamHandler = M4OSA_NULL; + + if (pReaderContext->mAudioAu.dataAddress != NULL) { + M4OSA_free((M4OSA_MemAddr32)pReaderContext->mAudioAu.dataAddress); + pReaderContext->mAudioAu.dataAddress = NULL; + } + } + + pReaderContext->mMediaSource->stop(); + pReaderContext->mMediaSource.clear(); + pReaderContext->mDataSource.clear(); + + LOGV("VideoEditorMp3Reader_close end "); + return err; +} +/** + ****************************************************************************** + * @brief get an option value from the reader + * @note + * it allows the caller to retrieve a property value: + * + * @param context: (IN) Context of the reader + * @param optionId: (IN) indicates the option to get + * @param pValue: (OUT) pointer to structure or value (allocated + * by user) where option is stored + * + * @return M4NO_ERROR there is no error + * @return M4ERR_PARAMETER at least one parameter is not properly set + * @return M4ERR_BAD_OPTION_ID when the option ID is not a valid one + ****************************************************************************** +*/ +M4OSA_ERR VideoEditorMp3Reader_getOption(M4OSA_Context context, + M4OSA_OptionID optionId, M4OSA_DataOption pValue) { + VideoEditorMp3Reader_Context *pReaderContext = + (VideoEditorMp3Reader_Context*)context; + M4OSA_ERR err = M4NO_ERROR; + + LOGV("VideoEditorMp3Reader_getOption begin: optionId= %d ",(int)optionId); + + M4OSA_DEBUG_IF1((M4OSA_NULL == pReaderContext), M4ERR_PARAMETER, + "invalid value pointer"); + M4OSA_DEBUG_IF1((M4OSA_NULL == pValue), M4ERR_PARAMETER, + "invalid value pointer"); + + switch(optionId) { + case M4READER_kOptionID_Duration: + { + LOGV("Mp3Reader duration=%ld",pReaderContext->mMaxDuration); + M4OSA_TIME_SET(*(M4OSA_Time*)pValue, pReaderContext->mMaxDuration); + } + break; + + case M4READER_kOptionID_Bitrate: + { + M4OSA_UInt32* pBitrate = (M4OSA_UInt32*)pValue; + if (M4OSA_NULL != pReaderContext->mAudioStreamHandler) { + *pBitrate = pReaderContext->mAudioStreamHandler->\ + m_basicProperties.m_averageBitRate; + } else { + pBitrate = 0; + err = M4ERR_PARAMETER; + } + } + break; + + case M4READER_kOptionID_Mp3Id3v1Tag: + break; + + case M4READER_kOptionID_Mp3Id3v2Tag: + break; + + case M4READER_kOptionID_GetMetadata: + break; + + default : + { + LOGV("VideoEditorMp3Reader_getOption: M4ERR_BAD_OPTION_ID"); + err = M4ERR_BAD_OPTION_ID; + } + } + LOGV("VideoEditorMp3Reader_getOption end "); + return err; +} +/** + ****************************************************************************** + * @brief set an option value of the reader + * @note + * it allows the caller to set a property value: + * + * @param context: (IN) Context of the reader + * @param optionId: (IN) Identifier indicating the option to set + * @param pValue: (IN) Pointer to structure or value (allocated + * by user) where option is stored + * + * @return M4NO_ERROR There is no error + * @return M4ERR_BAD_OPTION_ID The option ID is not a valid one + * @return M4ERR_STATE State automaton is not applied + * @return M4ERR_PARAMETER The option parameter is invalid + ****************************************************************************** +*/ +M4OSA_ERR VideoEditorMp3Reader_setOption(M4OSA_Context context, + M4OSA_OptionID optionId, M4OSA_DataOption pValue) { + VideoEditorMp3Reader_Context *pReaderContext = + (VideoEditorMp3Reader_Context*)context; + M4OSA_ERR err = M4NO_ERROR; + + LOGV("VideoEditorMp3Reader_Context begin: optionId: %d Value: %d ", + (int)optionId,(int)pValue); + + M4OSA_DEBUG_IF1((M4OSA_NULL == pReaderContext), M4ERR_PARAMETER, + "invalid context pointer"); + M4OSA_DEBUG_IF1((M4OSA_NULL == pValue), M4ERR_PARAMETER, + "invalid value pointer"); + + switch(optionId) { + case M4READER_kOptionID_SetOsaFileReaderFctsPtr: + default : + { + err = M4NO_ERROR; + } + } + LOGV("VideoEditorMp3Reader_Context end "); + return err; +} +/** + ****************************************************************************** + * @brief jump into the stream at the specified time + * @note + * @param context: (IN) Context of the reader + * @param pStreamHandler(IN) stream description of the stream to make jump + * @param pTime (I/O)IN:the time to jump to (in ms) + * OUT: the time to which the stream really jumped + * @return M4NO_ERROR there is no error + * @return M4ERR_PARAMETER at least one parameter is not properly set + ****************************************************************************** +*/ +M4OSA_ERR VideoEditorMp3Reader_jump(M4OSA_Context context, + M4_StreamHandler *pStreamHandler, M4OSA_Int32* pTime) { + VideoEditorMp3Reader_Context *pReaderContext = + (VideoEditorMp3Reader_Context*)context; + M4SYS_StreamID streamIdArray[2]; + M4OSA_ERR err = M4NO_ERROR; + M4SYS_AccessUnit* pAu; + M4OSA_Time time64; + M4OSA_Double timeDouble; + + LOGV("VideoEditorMp3Reader_jump begin"); + M4OSA_DEBUG_IF1((pReaderContext == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_jump: invalid context"); + M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_jump: invalid pointer to M4_StreamHandler"); + M4OSA_DEBUG_IF1((pTime == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_jump: invalid time pointer"); + + M4OSA_INT64_FROM_INT32(time64, *pTime); + + if(pStreamHandler == (M4_StreamHandler*)pReaderContext->\ + mAudioStreamHandler){ + pAu = &pReaderContext->mAudioAu; + } else { + LOGV("VideoEditorMp3Reader_jump: passed StreamHandler is not known"); + return M4ERR_PARAMETER; + } + + streamIdArray[0] = pStreamHandler->m_streamId; + streamIdArray[1] = 0; + + LOGV("VideoEditorMp3Reader_jump time ms %ld ", time64); + + pAu->CTS = time64; + pAu->DTS = time64; + + time64 = time64 * 1000; /* Convert the time into micro sec */ + LOGV("VideoEditorMp3Reader_jump time us %ld ", time64); + + pReaderContext->mSeeking = M4OSA_TRUE; + pReaderContext->mSeekTime = time64; + + time64 = time64 / 1000; /* Convert the time into milli sec */ + M4OSA_INT64_TO_DOUBLE(timeDouble, time64); + *pTime = (M4OSA_Int32)timeDouble; + LOGV("VideoEditorMp3Reader_jump end "); + return err; +} +/** + ******************************************************************************* + * @brief Get the next stream found in the media file + * + * @param context: (IN) Context of the reader + * @param pMediaFamily: (OUT) pointer to a user allocated + * M4READER_MediaFamily that will be filled with + * the media family of the found stream + * @param pStreamHandler: (OUT) pointer to a stream handler that will be + * allocated and filled with stream description + * + * @return M4NO_ERROR there is no error + * @return M4WAR_NO_MORE_STREAM no more available stream in the media + * @return M4ERR_PARAMETER at least one parameter is not properly set + ******************************************************************************* +*/ +M4OSA_ERR VideoEditorMp3Reader_getNextStream(M4OSA_Context context, + M4READER_MediaFamily *pMediaFamily, + M4_StreamHandler **pStreamHandlerParam) { + VideoEditorMp3Reader_Context *pReaderContext = + (VideoEditorMp3Reader_Context*)context; + M4OSA_ERR err = M4NO_ERROR; + M4SYS_StreamID streamIdArray[2]; + M4SYS_StreamDescription streamDesc; + M4_AudioStreamHandler* pAudioStreamHandler; + M4_StreamHandler* pStreamHandler; + M4OSA_UInt8 type, temp; + M4OSA_Bool haveAudio = M4OSA_FALSE; + sp<MetaData> meta = NULL; + int64_t Duration; + + LOGV("VideoEditorMp3Reader_getNextStream begin"); + M4OSA_DEBUG_IF1((pReaderContext == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_getNextStream: invalid context"); + M4OSA_DEBUG_IF1((pMediaFamily == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_getNextStream: invalid pointer to MediaFamily"); + M4OSA_DEBUG_IF1((pStreamHandlerParam == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_getNextStream: invalid pointer to StreamHandler"); + + LOGV("VideoEditorMp3Reader_getNextStream stream number = %d", + pReaderContext->mStreamNumber); + if (pReaderContext->mStreamNumber >= 1) { + LOGV("VideoEditorMp3Reader_getNextStream max number of stream reached"); + return M4WAR_NO_MORE_STREAM; + } + pReaderContext->mStreamNumber = pReaderContext->mStreamNumber + 1; + LOGV("VideoEditorMp3Reader_getNextStream number of Tracks%d", + pReaderContext->mExtractor->countTracks()); + for (temp = 0; temp < pReaderContext->mExtractor->countTracks(); temp++) { + meta = pReaderContext->mExtractor->getTrackMetaData(temp); + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (!haveAudio && !strncasecmp(mime, "audio/", 6)) { + pReaderContext->mMediaSource = + pReaderContext->mExtractor->getTrack(temp); + pReaderContext->mMediaSource->start(); + haveAudio = true; + } + + if (haveAudio) { + break; + } + } + + if (!haveAudio) { + LOGV("VideoEditorMp3Reader_getNextStream no more stream "); + pReaderContext->mDataSource.clear(); + return M4WAR_NO_MORE_STREAM; + } + + pReaderContext->mExtractorFlags = pReaderContext->mExtractor->flags(); + *pMediaFamily = M4READER_kMediaFamilyAudio; + + streamDesc.duration = meta->findInt64(kKeyDuration, &Duration); + streamDesc.duration = (M4OSA_Time)Duration/1000; + + meta->findInt32(kKeyBitRate, (int32_t*)&streamDesc.averageBitrate); + meta->findInt32(kKeySampleRate, (int32_t*)&streamDesc.timeScale); + LOGV("Bitrate = %d, SampleRate = %d duration = %lld", + streamDesc.averageBitrate,streamDesc.timeScale,Duration/1000); + + streamDesc.streamType = M4SYS_kMP3; + streamDesc.profileLevel = 0xFF ; + streamDesc.streamID = pReaderContext->mStreamNumber; + streamDesc.decoderSpecificInfo = M4OSA_NULL; + streamDesc.decoderSpecificInfoSize = 0; + streamDesc.maxBitrate = streamDesc.averageBitrate; + + /* Allocate the audio stream handler and set its parameters */ + pAudioStreamHandler = (M4_AudioStreamHandler*)M4OSA_malloc( + sizeof(M4_AudioStreamHandler), M4READER_MP3, + (M4OSA_Char*)"M4_AudioStreamHandler"); + + if (pAudioStreamHandler == M4OSA_NULL) { + LOGV("VideoEditorMp3Reader_getNextStream malloc failed"); + pReaderContext->mMediaSource->stop(); + pReaderContext->mMediaSource.clear(); + pReaderContext->mDataSource.clear(); + + return M4ERR_ALLOC; + } + pStreamHandler =(M4_StreamHandler*)(pAudioStreamHandler); + *pStreamHandlerParam = pStreamHandler; + pReaderContext->mAudioStreamHandler = pAudioStreamHandler; + + pAudioStreamHandler->m_structSize = sizeof(M4_AudioStreamHandler); + + if (meta == NULL) { + LOGV("VideoEditorMp3Reader_getNextStream meta is NULL"); + } + + pAudioStreamHandler->m_samplingFrequency = streamDesc.timeScale; + pStreamHandler->m_pDecoderSpecificInfo = + (M4OSA_UInt8*)(streamDesc.decoderSpecificInfo); + pStreamHandler->m_decoderSpecificInfoSize = + streamDesc.decoderSpecificInfoSize; + + meta->findInt32(kKeyChannelCount, + (int32_t*)&pAudioStreamHandler->m_nbChannels); + pAudioStreamHandler->m_byteFrameLength = 1152; + pAudioStreamHandler->m_byteSampleSize = 2; + + pStreamHandler->m_pUserData = NULL; + pStreamHandler->m_streamId = streamDesc.streamID; + pStreamHandler->m_duration = streamDesc.duration; + pReaderContext->mMaxDuration = streamDesc.duration; + pStreamHandler->m_averageBitRate = streamDesc.averageBitrate; + + pStreamHandler->m_maxAUSize = 0; + pStreamHandler->m_streamType = M4DA_StreamTypeAudioMp3; + + LOGV("VideoEditorMp3Reader_getNextStream end "); + return err; +} + +/** + ******************************************************************************* + * @brief fill the access unit structure with initialization values + * @param context: (IN) Context of the reader + * @param pStreamHandler: (IN) pointer to the stream handler to which + * the access unit will be associated + * @param pAccessUnit: (IN/OUT) pointer to the access unit (allocated by + * the caller) to initialize + * @return M4NO_ERROR there is no error + * @return M4ERR_PARAMETER at least one parameter is not properly set + ******************************************************************************* +*/ +M4OSA_ERR VideoEditorMp3Reader_fillAuStruct(M4OSA_Context context, + M4_StreamHandler *pStreamHandler, M4_AccessUnit *pAccessUnit) { + VideoEditorMp3Reader_Context *pReaderContext = + (VideoEditorMp3Reader_Context*)context; + M4SYS_AccessUnit *pAu; + + M4OSA_DEBUG_IF1((pReaderContext == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_fillAuStruct: invalid context"); + M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_fillAuStruct invalid pointer to StreamHandler"); + M4OSA_DEBUG_IF1((pAccessUnit == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_fillAuStruct: invalid pointer to M4_AccessUnit"); + + LOGV("VideoEditorMp3Reader_fillAuStruct start "); + if(pStreamHandler == (M4_StreamHandler*)pReaderContext->\ + mAudioStreamHandler){ + pAu = &pReaderContext->mAudioAu; + } else { + LOGV("VideoEditorMp3Reader_fillAuStruct StreamHandler is not known"); + return M4ERR_PARAMETER; + } + + /* Initialize pAu structure */ + pAu->dataAddress = M4OSA_NULL; + pAu->size = 0; + pAu->CTS = 0; + pAu->DTS = 0; + pAu->attribute = 0; + pAu->nbFrag = 0; + + /* Initialize pAccessUnit structure */ + pAccessUnit->m_size = 0; + pAccessUnit->m_CTS = 0; + pAccessUnit->m_DTS = 0; + pAccessUnit->m_attribute = 0; + pAccessUnit->m_dataAddress = M4OSA_NULL; + pAccessUnit->m_maxsize = pStreamHandler->m_maxAUSize; + pAccessUnit->m_streamID = pStreamHandler->m_streamId; + pAccessUnit->m_structSize = sizeof(M4_AccessUnit); + + LOGV("VideoEditorMp3Reader_fillAuStruct end"); + return M4NO_ERROR; +} + +/** + ******************************************************************************* + * @brief reset the stream, i.e seek it to the beginning + * @note + * @param context: (IN) Context of the reader + * @param pStreamHandler (IN) The stream handler of the stream to reset + * @return M4NO_ERROR there is no error + * @return M4ERR_PARAMETER at least one parameter is not properly set + ******************************************************************************* +*/ +M4OSA_ERR VideoEditorMp3Reader_reset(M4OSA_Context context, + M4_StreamHandler *pStreamHandler) { + VideoEditorMp3Reader_Context *pReaderContext = + (VideoEditorMp3Reader_Context*)context; + + M4OSA_ERR err = M4NO_ERROR; + M4SYS_StreamID streamIdArray[2]; + M4SYS_AccessUnit* pAu; + M4OSA_Time time64; + + LOGV("VideoEditorMp3Reader_reset start"); + M4OSA_DEBUG_IF1((pReaderContext == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_reset: invalid context"); + M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_reset: invalid pointer to M4_StreamHandler"); + + M4OSA_INT64_FROM_INT32(time64, 0); + + if (pStreamHandler == (M4_StreamHandler*)pReaderContext->\ + mAudioStreamHandler) { + pAu = &pReaderContext->mAudioAu; + } else { + LOGV("VideoEditorMp3Reader_reset StreamHandler is not known"); + return M4ERR_PARAMETER; + } + streamIdArray[0] = pStreamHandler->m_streamId; + streamIdArray[1] = 0; + pAu->CTS = time64; + pAu->DTS = time64; + + pReaderContext->mSeeking = M4OSA_TRUE; + pReaderContext->mSeekTime = time64; + + LOGV("VideoEditorMp3Reader_reset end"); + return err; +} +/** + ******************************************************************************* + * @brief Gets an access unit (AU) from the stream handler source. + * @note AU is the smallest possible amount of data to be decoded by decoder + * + * @param context: (IN) Context of the reader + * @param pStreamHandler (IN) The stream handler of the stream to make jump + * @param pAccessUnit (I/O)Pointer to an access unit to fill with read data + * @return M4NO_ERROR there is no error + * @return M4ERR_PARAMETER at least one parameter is not properly set + * @returns M4ERR_ALLOC memory allocation failed + * @returns M4WAR_NO_MORE_AU there are no more access unit in the stream + ******************************************************************************* +*/ +M4OSA_ERR VideoEditorMp3Reader_getNextAu(M4OSA_Context context, + M4_StreamHandler *pStreamHandler, M4_AccessUnit *pAccessUnit) { + VideoEditorMp3Reader_Context *pReaderContext = + (VideoEditorMp3Reader_Context*)context; + M4OSA_ERR err = M4NO_ERROR; + M4SYS_AccessUnit* pAu; + MediaBuffer *mAudioBuffer; + MediaSource::ReadOptions options; + + LOGV("VideoEditorMp3Reader_getNextAu start"); + M4OSA_DEBUG_IF1((pReaderContext == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_getNextAu: invalid context"); + M4OSA_DEBUG_IF1((pStreamHandler == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_getNextAu: invalid pointer to M4_StreamHandler"); + M4OSA_DEBUG_IF1((pAccessUnit == 0), M4ERR_PARAMETER, + "VideoEditorMp3Reader_getNextAu: invalid pointer to M4_AccessUnit"); + + if (pStreamHandler == (M4_StreamHandler*)pReaderContext->\ + mAudioStreamHandler) { + pAu = &pReaderContext->mAudioAu; + } else { + LOGV("VideoEditorMp3Reader_getNextAu: StreamHandler is not known\n"); + return M4ERR_PARAMETER; + } + + if (pReaderContext->mSeeking) { + options.setSeekTo(pReaderContext->mSeekTime); + } + + pReaderContext->mMediaSource->read(&mAudioBuffer, &options); + + if (mAudioBuffer != NULL) { + if ((pAu->dataAddress == NULL) || + (pAu->size < mAudioBuffer->range_length())) { + if (pAu->dataAddress != NULL) { + M4OSA_free((M4OSA_Int32*)pAu->dataAddress); + pAu->dataAddress = NULL; + } + pAu->dataAddress = (M4OSA_Int32*)M4OSA_malloc( + (mAudioBuffer->range_length() + 3) & ~0x3, + M4READER_MP3, (M4OSA_Char*)"pAccessUnit->m_dataAddress" ); + + if (pAu->dataAddress == NULL) { + LOGV("VideoEditorMp3Reader_getNextAu malloc failed"); + pReaderContext->mMediaSource->stop(); + pReaderContext->mMediaSource.clear(); + pReaderContext->mDataSource.clear(); + + return M4ERR_ALLOC; + } + } + pAu->size = mAudioBuffer->range_length(); + memcpy((M4OSA_MemAddr8)pAu->dataAddress, + (const char *)mAudioBuffer->data() + mAudioBuffer->range_offset(), + mAudioBuffer->range_length()); + + mAudioBuffer->meta_data()->findInt64(kKeyTime, (int64_t*)&pAu->CTS); + + + pAu->CTS = pAu->CTS / 1000; /*converting the microsec to millisec */ + pAu->DTS = pAu->CTS; + pAu->attribute = M4SYS_kFragAttrOk; + mAudioBuffer->release(); + + LOGV("VideoEditorMp3Reader_getNextAu AU CTS = %ld",pAu->CTS); + + pAccessUnit->m_dataAddress = (M4OSA_Int8*) pAu->dataAddress; + pAccessUnit->m_size = pAu->size; + pAccessUnit->m_CTS = pAu->CTS; + pAccessUnit->m_DTS = pAu->DTS; + pAccessUnit->m_attribute = pAu->attribute; + } else { + LOGV("VideoEditorMp3Reader_getNextAu EOS reached."); + pAccessUnit->m_size=0; + err = M4WAR_NO_MORE_AU; + } + pAu->nbFrag = 0; + + options.clearSeekTo(); + pReaderContext->mSeeking = M4OSA_FALSE; + mAudioBuffer = NULL; + LOGV("VideoEditorMp3Reader_getNextAu end"); + + return err; +} + +extern "C" { + +M4OSA_ERR VideoEditorMp3Reader_getInterface( + M4READER_MediaType *pMediaType, + M4READER_GlobalInterface **pRdrGlobalInterface, + M4READER_DataInterface **pRdrDataInterface) { + M4OSA_ERR err = M4NO_ERROR; + + LOGV("VideoEditorMp3Reader_getInterface: begin"); + /* Input parameters check */ + VIDEOEDITOR_CHECK(M4OSA_NULL != pMediaType, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pRdrGlobalInterface, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pRdrDataInterface, M4ERR_PARAMETER); + + SAFE_MALLOC(*pRdrGlobalInterface, M4READER_GlobalInterface, 1, + "VideoEditorMp3Reader_getInterface"); + SAFE_MALLOC(*pRdrDataInterface, M4READER_DataInterface, 1, + "VideoEditorMp3Reader_getInterface"); + + *pMediaType = M4READER_kMediaTypeMP3; + + (*pRdrGlobalInterface)->m_pFctCreate = VideoEditorMp3Reader_create; + (*pRdrGlobalInterface)->m_pFctDestroy = VideoEditorMp3Reader_destroy; + (*pRdrGlobalInterface)->m_pFctOpen = VideoEditorMp3Reader_open; + (*pRdrGlobalInterface)->m_pFctClose = VideoEditorMp3Reader_close; + (*pRdrGlobalInterface)->m_pFctGetOption = VideoEditorMp3Reader_getOption; + (*pRdrGlobalInterface)->m_pFctSetOption = VideoEditorMp3Reader_setOption; + (*pRdrGlobalInterface)->m_pFctGetNextStream = + VideoEditorMp3Reader_getNextStream; + (*pRdrGlobalInterface)->m_pFctFillAuStruct = + VideoEditorMp3Reader_fillAuStruct; + (*pRdrGlobalInterface)->m_pFctStart = M4OSA_NULL; + (*pRdrGlobalInterface)->m_pFctStop = M4OSA_NULL; + (*pRdrGlobalInterface)->m_pFctJump = VideoEditorMp3Reader_jump; + (*pRdrGlobalInterface)->m_pFctReset = VideoEditorMp3Reader_reset; + (*pRdrGlobalInterface)->m_pFctGetPrevRapTime = M4OSA_NULL; + + (*pRdrDataInterface)->m_pFctGetNextAu = VideoEditorMp3Reader_getNextAu; + (*pRdrDataInterface)->m_readerContext = M4OSA_NULL; + +cleanUp: + if( M4NO_ERROR == err ) + { + LOGV("VideoEditorMp3Reader_getInterface no error"); + } + else + { + SAFE_FREE(*pRdrGlobalInterface); + SAFE_FREE(*pRdrDataInterface); + + LOGV("VideoEditorMp3Reader_getInterface ERROR 0x%X", err); + } + LOGV("VideoEditorMp3Reader_getInterface: end"); + return err; +} +} /* extern "C" */ +} /* namespace android */ diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditorUtils.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditorUtils.cpp new file mode 100755 index 0000000..733c5d6 --- /dev/null +++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditorUtils.cpp @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2011 NXP Software + * Copyright (C) 2011 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. + */ + +/** +************************************************************************* +* @file VideoEditorUtils.cpp +* @brief StageFright shell Utilities +************************************************************************* +*/ +#define LOG_NDEBUG 0 +#define LOG_TAG "SF_utils" +#include "utils/Log.h" + +#include "VideoEditorUtils.h" + +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/OMXCodec.h> + +/* Android includes*/ +#include <utils/Log.h> +#include <memory.h> + +/*---------------------*/ +/* DEBUG LEVEL SETUP */ +/*---------------------*/ +#define LOG1 LOGE /*ERRORS Logging*/ +#define LOG2 LOGI /*WARNING Logging*/ +#define LOG3 //LOGV /*COMMENTS Logging*/ + +namespace android { + +void displayMetaData(const sp<MetaData> meta) { + + const char* charData; + int32_t int32Data; + int64_t int64Data; + uint32_t type; + const void* data; + void* ptr; + size_t size; + + if (meta->findCString(kKeyMIMEType, &charData)) { + LOG1("displayMetaData kKeyMIMEType %s", charData); + } + if (meta->findInt32(kKeyWidth, &int32Data)) { + LOG1("displayMetaData kKeyWidth %d", int32Data); + } + if (meta->findInt32(kKeyHeight, &int32Data)) { + LOG1("displayMetaData kKeyHeight %d", int32Data); + } + if (meta->findInt32(kKeyIFramesInterval, &int32Data)) { + LOG1("displayMetaData kKeyIFramesInterval %d", int32Data); + } + if (meta->findInt32(kKeyStride, &int32Data)) { + LOG1("displayMetaData kKeyStride %d", int32Data); + } + if (meta->findInt32(kKeySliceHeight, &int32Data)) { + LOG1("displayMetaData kKeySliceHeight %d", int32Data); + } + if (meta->findInt32(kKeyChannelCount, &int32Data)) { + LOG1("displayMetaData kKeyChannelCount %d", int32Data); + } + if (meta->findInt32(kKeySampleRate, &int32Data)) { + LOG1("displayMetaData kKeySampleRate %d", int32Data); + } + if (meta->findInt32(kKeyBitRate, &int32Data)) { + LOG1("displayMetaData kKeyBitRate %d", int32Data); + } + if (meta->findData(kKeyESDS, &type, &data, &size)) { + LOG1("displayMetaData kKeyESDS type=%d size=%d", type, size); + } + if (meta->findData(kKeyAVCC, &type, &data, &size)) { + LOG1("displayMetaData kKeyAVCC data=0x%X type=%d size=%d", + *((unsigned int*)data), type, size); + } + if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) { + LOG1("displayMetaData kKeyVorbisInfo type=%d size=%d", type, size); + } + if (meta->findData(kKeyVorbisBooks, &type, &data, &size)) { + LOG1("displayMetaData kKeyVorbisBooks type=%d size=%d", type, size); + } + if (meta->findInt32(kKeyWantsNALFragments, &int32Data)) { + LOG1("displayMetaData kKeyWantsNALFragments %d", int32Data); + } + if (meta->findInt32(kKeyIsSyncFrame, &int32Data)) { + LOG1("displayMetaData kKeyIsSyncFrame %d", int32Data); + } + if (meta->findInt32(kKeyIsCodecConfig, &int32Data)) { + LOG1("displayMetaData kKeyIsCodecConfig %d", int32Data); + } + if (meta->findInt64(kKeyTime, &int64Data)) { + LOG1("displayMetaData kKeyTime %lld", int64Data); + } + if (meta->findInt32(kKeyDuration, &int32Data)) { + LOG1("displayMetaData kKeyDuration %d", int32Data); + } + if (meta->findInt32(kKeyColorFormat, &int32Data)) { + LOG1("displayMetaData kKeyColorFormat %d", int32Data); + } + if (meta->findPointer(kKeyPlatformPrivate, &ptr)) { + LOG1("displayMetaData kKeyPlatformPrivate pointer=0x%x", (int32_t) ptr); + } + if (meta->findCString(kKeyDecoderComponent, &charData)) { + LOG1("displayMetaData kKeyDecoderComponent %s", charData); + } + if (meta->findInt32(kKeyBufferID, &int32Data)) { + LOG1("displayMetaData kKeyBufferID %d", int32Data); + } + if (meta->findInt32(kKeyMaxInputSize, &int32Data)) { + LOG1("displayMetaData kKeyMaxInputSize %d", int32Data); + } + if (meta->findInt64(kKeyThumbnailTime, &int64Data)) { + LOG1("displayMetaData kKeyThumbnailTime %lld", int64Data); + } + if (meta->findCString(kKeyAlbum, &charData)) { + LOG1("displayMetaData kKeyAlbum %s", charData); + } + if (meta->findCString(kKeyArtist, &charData)) { + LOG1("displayMetaData kKeyArtist %s", charData); + } + if (meta->findCString(kKeyAlbumArtist, &charData)) { + LOG1("displayMetaData kKeyAlbumArtist %s", charData); + } + if (meta->findCString(kKeyComposer, &charData)) { + LOG1("displayMetaData kKeyComposer %s", charData); + } + if (meta->findCString(kKeyGenre, &charData)) { + LOG1("displayMetaData kKeyGenre %s", charData); + } + if (meta->findCString(kKeyTitle, &charData)) { + LOG1("displayMetaData kKeyTitle %s", charData); + } + if (meta->findCString(kKeyYear, &charData)) { + LOG1("displayMetaData kKeyYear %s", charData); + } + if (meta->findData(kKeyAlbumArt, &type, &data, &size)) { + LOG1("displayMetaData kKeyAlbumArt type=%d size=%d", type, size); + } + if (meta->findCString(kKeyAlbumArtMIME, &charData)) { + LOG1("displayMetaData kKeyAlbumArtMIME %s", charData); + } + if (meta->findCString(kKeyAuthor, &charData)) { + LOG1("displayMetaData kKeyAuthor %s", charData); + } + if (meta->findCString(kKeyCDTrackNumber, &charData)) { + LOG1("displayMetaData kKeyCDTrackNumber %s", charData); + } + if (meta->findCString(kKeyDiscNumber, &charData)) { + LOG1("displayMetaData kKeyDiscNumber %s", charData); + } + if (meta->findCString(kKeyDate, &charData)) { + LOG1("displayMetaData kKeyDate %s", charData); + } + if (meta->findCString(kKeyWriter, &charData)) { + LOG1("displayMetaData kKeyWriter %s", charData); + } + if (meta->findInt32(kKeyTimeScale, &int32Data)) { + LOG1("displayMetaData kKeyTimeScale %d", int32Data); + } + if (meta->findInt32(kKeyVideoProfile, &int32Data)) { + LOG1("displayMetaData kKeyVideoProfile %d", int32Data); + } + if (meta->findInt32(kKeyVideoLevel, &int32Data)) { + LOG1("displayMetaData kKeyVideoLevel %d", int32Data); + } + if (meta->findInt32(kKey64BitFileOffset, &int32Data)) { + LOG1("displayMetaData kKey64BitFileOffset %d", int32Data); + } + if (meta->findInt32(kKeyFileType, &int32Data)) { + LOG1("displayMetaData kKeyFileType %d", int32Data); + } + if (meta->findInt64(kKeyTrackTimeStatus, &int64Data)) { + LOG1("displayMetaData kKeyTrackTimeStatus %lld", int64Data); + } + if (meta->findInt32(kKeyNotRealTime, &int32Data)) { + LOG1("displayMetaData kKeyNotRealTime %d", int32Data); + } +} + +/** + * This code was extracted from StageFright MPEG4 writer + * Is is used to parse and format the AVC codec specific info received + * from StageFright encoders + */ +static const uint8_t kNalUnitTypeSeqParamSet = 0x07; +static const uint8_t kNalUnitTypePicParamSet = 0x08; +struct AVCParamSet { + AVCParamSet(uint16_t length, const uint8_t *data) + : mLength(length), mData(data) {} + + uint16_t mLength; + const uint8_t *mData; +}; +struct AVCCodecSpecificContext { + List<AVCParamSet> mSeqParamSets; + List<AVCParamSet> mPicParamSets; + uint8_t mProfileIdc; + uint8_t mProfileCompatible; + uint8_t mLevelIdc; +}; + +const uint8_t *parseParamSet(AVCCodecSpecificContext* pC, + const uint8_t *data, size_t length, int type, size_t *paramSetLen) { + CHECK(type == kNalUnitTypeSeqParamSet || + type == kNalUnitTypePicParamSet); + + size_t bytesLeft = length; + while (bytesLeft > 4 && + memcmp("\x00\x00\x00\x01", &data[length - bytesLeft], 4)) { + --bytesLeft; + } + if (bytesLeft <= 4) { + bytesLeft = 0; // Last parameter set + } + const uint8_t *nextStartCode = &data[length - bytesLeft]; + *paramSetLen = nextStartCode - data; + if (*paramSetLen == 0) { + LOGE("Param set is malformed, since its length is 0"); + return NULL; + } + + AVCParamSet paramSet(*paramSetLen, data); + if (type == kNalUnitTypeSeqParamSet) { + if (*paramSetLen < 4) { + LOGE("Seq parameter set malformed"); + return NULL; + } + if (pC->mSeqParamSets.empty()) { + pC->mProfileIdc = data[1]; + pC->mProfileCompatible = data[2]; + pC->mLevelIdc = data[3]; + } else { + if (pC->mProfileIdc != data[1] || + pC->mProfileCompatible != data[2] || + pC->mLevelIdc != data[3]) { + LOGV("Inconsistent profile/level found in seq parameter sets"); + return NULL; + } + } + pC->mSeqParamSets.push_back(paramSet); + } else { + pC->mPicParamSets.push_back(paramSet); + } + return nextStartCode; +} + +status_t buildAVCCodecSpecificData(uint8_t **pOutputData, size_t *pOutputSize, + const uint8_t *data, size_t size, MetaData *param) +{ + //LOGV("buildAVCCodecSpecificData"); + + if ( (pOutputData == NULL) || (pOutputSize == NULL) ) { + LOGE("output is invalid"); + return ERROR_MALFORMED; + } + + if (*pOutputData != NULL) { + LOGE("Already have codec specific data"); + return ERROR_MALFORMED; + } + + if (size < 4) { + LOGE("Codec specific data length too short: %d", size); + return ERROR_MALFORMED; + } + + // Data is in the form of AVCCodecSpecificData + if (memcmp("\x00\x00\x00\x01", data, 4)) { + // 2 bytes for each of the parameter set length field + // plus the 7 bytes for the header + if (size < 4 + 7) { + LOGE("Codec specific data length too short: %d", size); + return ERROR_MALFORMED; + } + + *pOutputSize = size; + *pOutputData = (uint8_t*)malloc(size); + memcpy(*pOutputData, data, size); + return OK; + } + + AVCCodecSpecificContext ctx; + uint8_t *outputData = NULL; + size_t outputSize = 0; + + // Check if the data is valid + uint8_t type = kNalUnitTypeSeqParamSet; + bool gotSps = false; + bool gotPps = false; + const uint8_t *tmp = data; + const uint8_t *nextStartCode = data; + size_t bytesLeft = size; + size_t paramSetLen = 0; + outputSize = 0; + while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) { + type = (*(tmp + 4)) & 0x1F; + if (type == kNalUnitTypeSeqParamSet) { + if (gotPps) { + LOGE("SPS must come before PPS"); + return ERROR_MALFORMED; + } + if (!gotSps) { + gotSps = true; + } + nextStartCode = parseParamSet(&ctx, tmp + 4, bytesLeft - 4, type, + ¶mSetLen); + } else if (type == kNalUnitTypePicParamSet) { + if (!gotSps) { + LOGE("SPS must come before PPS"); + return ERROR_MALFORMED; + } + if (!gotPps) { + gotPps = true; + } + nextStartCode = parseParamSet(&ctx, tmp + 4, bytesLeft - 4, type, + ¶mSetLen); + } else { + LOGE("Only SPS and PPS Nal units are expected"); + return ERROR_MALFORMED; + } + + if (nextStartCode == NULL) { + return ERROR_MALFORMED; + } + + // Move on to find the next parameter set + bytesLeft -= nextStartCode - tmp; + tmp = nextStartCode; + outputSize += (2 + paramSetLen); + } + + { + // Check on the number of seq parameter sets + size_t nSeqParamSets = ctx.mSeqParamSets.size(); + if (nSeqParamSets == 0) { + LOGE("Cound not find sequence parameter set"); + return ERROR_MALFORMED; + } + + if (nSeqParamSets > 0x1F) { + LOGE("Too many seq parameter sets (%d) found", nSeqParamSets); + return ERROR_MALFORMED; + } + } + + { + // Check on the number of pic parameter sets + size_t nPicParamSets = ctx.mPicParamSets.size(); + if (nPicParamSets == 0) { + LOGE("Cound not find picture parameter set"); + return ERROR_MALFORMED; + } + if (nPicParamSets > 0xFF) { + LOGE("Too many pic parameter sets (%d) found", nPicParamSets); + return ERROR_MALFORMED; + } + } + + { + // Check on the profiles + // These profiles requires additional parameter set extensions + if (ctx.mProfileIdc == 100 || ctx.mProfileIdc == 110 || + ctx.mProfileIdc == 122 || ctx.mProfileIdc == 144) { + LOGE("Sorry, no support for profile_idc: %d!", ctx.mProfileIdc); + return BAD_VALUE; + } + } + + // ISO 14496-15: AVC file format + outputSize += 7; // 7 more bytes in the header + outputData = (uint8_t *)malloc(outputSize); + uint8_t *header = outputData; + header[0] = 1; // version + header[1] = ctx.mProfileIdc; // profile indication + header[2] = ctx.mProfileCompatible; // profile compatibility + header[3] = ctx.mLevelIdc; + + // 6-bit '111111' followed by 2-bit to lengthSizeMinuusOne + int32_t use2ByteNalLength = 0; + if (param && + param->findInt32(kKey2ByteNalLength, &use2ByteNalLength) && + use2ByteNalLength) { + header[4] = 0xfc | 1; // length size == 2 bytes + } else { + header[4] = 0xfc | 3; // length size == 4 bytes + } + + // 3-bit '111' followed by 5-bit numSequenceParameterSets + int nSequenceParamSets = ctx.mSeqParamSets.size(); + header[5] = 0xe0 | nSequenceParamSets; + header += 6; + for (List<AVCParamSet>::iterator it = ctx.mSeqParamSets.begin(); + it != ctx.mSeqParamSets.end(); ++it) { + // 16-bit sequence parameter set length + uint16_t seqParamSetLength = it->mLength; + header[0] = seqParamSetLength >> 8; + header[1] = seqParamSetLength & 0xff; + //LOGE("### SPS %d %d %d", seqParamSetLength, header[0], header[1]); + + // SPS NAL unit (sequence parameter length bytes) + memcpy(&header[2], it->mData, seqParamSetLength); + header += (2 + seqParamSetLength); + } + + // 8-bit nPictureParameterSets + int nPictureParamSets = ctx.mPicParamSets.size(); + header[0] = nPictureParamSets; + header += 1; + for (List<AVCParamSet>::iterator it = ctx.mPicParamSets.begin(); + it != ctx.mPicParamSets.end(); ++it) { + // 16-bit picture parameter set length + uint16_t picParamSetLength = it->mLength; + header[0] = picParamSetLength >> 8; + header[1] = picParamSetLength & 0xff; +//LOGE("### PPS %d %d %d", picParamSetLength, header[0], header[1]); + + // PPS Nal unit (picture parameter set length bytes) + memcpy(&header[2], it->mData, picParamSetLength); + header += (2 + picParamSetLength); + } + + *pOutputSize = outputSize; + *pOutputData = outputData; + return OK; +} +}// namespace android diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditorVideoDecoder.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditorVideoDecoder.cpp new file mode 100755 index 0000000..b362197 --- /dev/null +++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditorVideoDecoder.cpp @@ -0,0 +1,1404 @@ +/* + * Copyright (C) 2011 NXP Software + * Copyright (C) 2011 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. + */ +/** +************************************************************************* +* @file VideoEditorVideoDecoder.cpp +* @brief StageFright shell video decoder +************************************************************************* +*/ +#define LOG_NDEBUG 1 +#define LOG_TAG "VIDEOEDITOR_VIDEODECODER" + +/******************* + * HEADERS * + *******************/ + +#include "VideoEditorVideoDecoder_internal.h" +#include "VideoEditorUtils.h" +#include "M4VD_Tools.h" + +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MediaDefs.h> + +/******************** + * DEFINITIONS * + ********************/ +#define OMX_QCOM_COLOR_FormatYVU420SemiPlanar 0x7FA30C00 +#define MAX_DEC_BUFFERS 10 + +/******************** + * SOURCE CLASS * + ********************/ +using namespace android; + +class VideoEditorVideoDecoderSource : public MediaSource { + public: + VideoEditorVideoDecoderSource(const sp<MetaData> &format, + VIDEOEDITOR_CodecType codecType, void *decoderShellContext); + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual sp<MetaData> getFormat(); + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options = NULL); + + protected : + virtual ~VideoEditorVideoDecoderSource(); + + private: + sp<MetaData> mFormat; + MediaBuffer* mBuffer; + MediaBufferGroup* mGroup; + Mutex mLock; + VideoEditorVideoDecoder_Context* mpDecShellContext; + int32_t mMaxAUSize; + bool mStarted; + VIDEOEDITOR_CodecType mCodecType; + VideoEditorVideoDecoderSource(const MediaSource &); + VideoEditorVideoDecoderSource &operator=(const MediaSource &); +}; + +VideoEditorVideoDecoderSource::VideoEditorVideoDecoderSource( + const sp<MetaData> &format, VIDEOEDITOR_CodecType codecType, + void *decoderShellContext) : + mFormat(format), + mBuffer(NULL), + mGroup(NULL), + mStarted(false), + mCodecType(codecType) { + mpDecShellContext = (VideoEditorVideoDecoder_Context*) decoderShellContext; +} + +VideoEditorVideoDecoderSource::~VideoEditorVideoDecoderSource() { + if (mStarted == true) { + stop(); + } +} + +status_t VideoEditorVideoDecoderSource::start( + MetaData *params) { + + LOGV("VideoEditorVideoDecoderSource::start() begin "); + if (!mStarted) { + if(mFormat->findInt32(kKeyMaxInputSize, &mMaxAUSize) == false) { + LOGW("FATAL: Should never happen "); + mMaxAUSize = 10000; + } + + mGroup = new MediaBufferGroup; + if(mGroup == NULL) { + LOGE("FATAL: memory limitation ! "); + return NO_MEMORY; + } + LOGV("VideoEditorVideoDecoderSource:adding buffer to group MaxSize= %d", + mMaxAUSize); + mGroup->add_buffer(new MediaBuffer(mMaxAUSize)); + + mStarted = true; + } + LOGV("VideoEditorVideoDecoderSource::start() end OK"); + return OK; +} + +status_t VideoEditorVideoDecoderSource::stop() { + int ref_count = 0; + int i; + + LOGV("VideoEditorVideoDecoderSource::stop() begin"); + if (mStarted) { + if(mBuffer != NULL) { + ref_count = mBuffer->refcount(); + LOGV("MediaBuffer refcount is %d",ref_count); + for (i=0; i< ref_count; i++) { + mBuffer->release(); + } + + mBuffer = NULL; + } + delete mGroup; + mGroup = NULL; + mStarted = false; + } + LOGV("VideoEditorVideoDecoderSource::stop() end"); + return OK; +} + +sp<MetaData> VideoEditorVideoDecoderSource::getFormat() { + Mutex::Autolock autolock(mLock); + + return mFormat; +} + +status_t VideoEditorVideoDecoderSource::read(MediaBuffer** buffer_out, + const ReadOptions *options) { + + Mutex::Autolock autolock(mLock); + //We donot use read options on decoder hence dont impliment this option here + M4_AccessUnit* pAccessUnit = mpDecShellContext->m_pNextAccessUnitToDecode; + M4OSA_UInt32 lSize = 0; + M4OSA_ERR lerr = M4NO_ERROR; + int64_t frameTime; + + *buffer_out = NULL; + + LOGV("VideoEditorVideoDecoderSource::read begin"); + + if (options) { + int64_t time = 0; + ReadOptions::SeekMode mode = ReadOptions::SEEK_CLOSEST_SYNC; + bool hasOptions = FALSE; + hasOptions = options->getSeekTo(&time, &mode); + if (hasOptions) { + LOGV("VideoEditorVideoDecoderSource: Options is not NULL %lld %d", + time, mode); + } else { + LOGV("VideoEditorVideoDecoderSource: Options is not NULL ****"); + } + } + lerr = mGroup->acquire_buffer(&mBuffer); + if (lerr != OK) { + return lerr; + } + LOGV("VideoEditorVideoDecoderSource: got a buffer from group"); + + if (mStarted) { + //getNext AU from reader. + lerr = mpDecShellContext->m_pReader->m_pFctGetNextAu( + mpDecShellContext->m_pReader->m_readerContext, + (M4_StreamHandler*)mpDecShellContext->m_pVideoStreamhandler, + pAccessUnit); + if (lerr == M4WAR_NO_DATA_YET) { + LOGV("VideoEditorVideoDecoderSource::read() M4WAR_NO_DATA_YET"); + mBuffer->set_range(0, 0); + mBuffer->meta_data()->clear(); + + *buffer_out = mBuffer; + } + if (lerr == M4WAR_NO_MORE_AU) { + LOGV("VideoEditorVideoDecoderSource::read() returning err = " + "ERROR_END_OF_STREAM;"); + *buffer_out = NULL; + return ERROR_END_OF_STREAM; + } + LOGV("VideoEditorVideoDecoderSource: getNextAU succesful ts = %lf", + pAccessUnit->m_CTS); + + //copy the reader AU buffer to mBuffer + lSize = (pAccessUnit->m_size > (M4OSA_UInt32)mMaxAUSize)\ + ? (M4OSA_UInt32)mMaxAUSize : pAccessUnit->m_size; + LOGV("VideoDecoderSource:Read() copying AU to i/p buffer of decoder," + "Bufer Add = 0x%x, size = %d", mBuffer->data(), lSize); + M4OSA_memcpy((M4OSA_MemAddr8)mBuffer->data(),pAccessUnit->m_dataAddress, + lSize); + + mBuffer->set_range(0, lSize); + mBuffer->meta_data()->clear(); + frameTime = (int64_t)pAccessUnit->m_CTS; + mBuffer->meta_data()->setInt64(kKeyTime, (int64_t)frameTime*1000); + + // Replace the AU start code for H264 + if (VIDEOEDITOR_kH264VideoDec == mCodecType) { + uint8_t *data =(uint8_t *)mBuffer->data() + mBuffer->range_offset(); + data[0]=0; + data[1]=0; + data[2]=0; + data[3]=1; + } + mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, + (pAccessUnit->m_attribute == 0x04)? 1 : 0); + *buffer_out = mBuffer; + } + LOGV("VideoEditorVideoDecoderSource::read end"); + return OK; +} +/******************** + * TOOLS * + ********************/ + +static M4OSA_UInt32 VideoEditorVideoDecoder_GetBitsFromMemory( + VIDEOEDITOR_VIDEO_Bitstream_ctxt* parsingCtxt, M4OSA_UInt32 nb_bits) { + return (M4VD_Tools_GetBitsFromMemory((M4VS_Bitstream_ctxt*) parsingCtxt, + nb_bits)); +} + +M4OSA_ERR VideoEditorVideoDecoder_internalParseVideoDSI(M4OSA_UInt8* pVol, + M4OSA_Int32 aVolSize, M4DECODER_MPEG4_DecoderConfigInfo* pDci, + M4DECODER_VideoSize* pVideoSize) { + + VIDEOEDITOR_VIDEO_Bitstream_ctxt parsingCtxt; + M4OSA_UInt32 code, j; + M4OSA_MemAddr8 start; + M4OSA_UInt8 i; + M4OSA_UInt32 time_incr_length; + M4OSA_UInt8 vol_verid=0, b_hierarchy_type; + + /* Parsing variables */ + M4OSA_UInt8 video_object_layer_shape = 0; + M4OSA_UInt8 sprite_enable = 0; + M4OSA_UInt8 reduced_resolution_vop_enable = 0; + M4OSA_UInt8 scalability = 0; + M4OSA_UInt8 enhancement_type = 0; + M4OSA_UInt8 complexity_estimation_disable = 0; + M4OSA_UInt8 interlaced = 0; + M4OSA_UInt8 sprite_warping_points = 0; + M4OSA_UInt8 sprite_brightness_change = 0; + M4OSA_UInt8 quant_precision = 0; + + /* Fill the structure with default parameters */ + pVideoSize->m_uiWidth = 0; + pVideoSize->m_uiHeight = 0; + + pDci->uiTimeScale = 0; + pDci->uiProfile = 0; + pDci->uiUseOfResynchMarker = 0; + pDci->bDataPartition = M4OSA_FALSE; + pDci->bUseOfRVLC = M4OSA_FALSE; + + /* Reset the bitstream context */ + parsingCtxt.stream_byte = 0; + parsingCtxt.stream_index = 8; + parsingCtxt.in = (M4OSA_MemAddr8) pVol; + + start = (M4OSA_MemAddr8) pVol; + + /* Start parsing */ + while (parsingCtxt.in - start < aVolSize) { + code = VideoEditorVideoDecoder_GetBitsFromMemory(&parsingCtxt, 8); + if (code == 0) { + code = VideoEditorVideoDecoder_GetBitsFromMemory(&parsingCtxt, 8); + if (code == 0) { + code = VideoEditorVideoDecoder_GetBitsFromMemory(&parsingCtxt,8); + if (code == 1) { + /* start code found */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 8); + + /* ----- 0x20..0x2F : video_object_layer_start_code ----- */ + + if ((code > 0x1F) && (code < 0x30)) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1); + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 8); + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1); + if (code == 1) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 4); + vol_verid = (M4OSA_UInt8)code; + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 3); + } + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 4); + if (code == 15) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 16); + } + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1); + if (code == 1) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 3); + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1); + if (code == 1) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 32); + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 31); + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 16); + } + } + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 2); + /* Need to save it for vop parsing */ + video_object_layer_shape = (M4OSA_UInt8)code; + + if (code != 0) { + return 0; /* only rectangular case supported */ + } + + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1); + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 16); + pDci->uiTimeScale = code; + + /* Computes time increment length */ + j = code - 1; + for (i = 0; (i < 32) && (j != 0); j >>=1) { + i++; + } + time_incr_length = (i == 0) ? 1 : i; + + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1); + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1); + if (code == 1) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, time_incr_length); + } + + if(video_object_layer_shape != 1) { /* 1 = Binary */ + if(video_object_layer_shape == 0) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* Marker bit */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 13);/* Width */ + pVideoSize->m_uiWidth = code; + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* Marker bit */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 13);/* Height */ + pVideoSize->m_uiHeight = code; + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* Marker bit */ + } + } + + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* interlaced */ + interlaced = (M4OSA_UInt8)code; + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* OBMC disable */ + + if(vol_verid == 1) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* sprite enable */ + sprite_enable = (M4OSA_UInt8)code; + } else { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 2);/* sprite enable */ + sprite_enable = (M4OSA_UInt8)code; + } + if ((sprite_enable == 1) || (sprite_enable == 2)) { + if (sprite_enable != 2) { + + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 13);/* sprite width */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* Marker bit */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 13);/* sprite height */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* Marker bit */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 13);/* sprite l coordinate */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* Marker bit */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 13);/* sprite top coordinate */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* Marker bit */ + } + + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 6);/* sprite warping points */ + sprite_warping_points = (M4OSA_UInt8)code; + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 2);/* sprite warping accuracy */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* sprite brightness change */ + sprite_brightness_change = (M4OSA_UInt8)code; + if (sprite_enable != 2) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1); + } + } + if ((vol_verid != 1) && (video_object_layer_shape != 0)){ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* sadct disable */ + } + + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1); /* not 8 bits */ + if (code) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 4);/* quant precision */ + quant_precision = (M4OSA_UInt8)code; + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 4);/* bits per pixel */ + } + + /* greyscale not supported */ + if(video_object_layer_shape == 3) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 3); + } + + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* quant type */ + if (code) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* load intra quant mat */ + if (code) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 8);/* */ + i = 1; + while (i < 64) { + code = + VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 8); + if (code == 0) { + break; + } + i++; + } + } + + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* load non intra quant mat */ + if (code) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 8);/* */ + i = 1; + while (i < 64) { + code = + VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 8); + if (code == 0) { + break; + } + i++; + } + } + } + + if (vol_verid != 1) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* quarter sample */ + } + + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* complexity estimation disable */ + complexity_estimation_disable = (M4OSA_UInt8)code; + if (!code) { + //return M4ERR_NOT_IMPLEMENTED; + } + + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* resync marker disable */ + pDci->uiUseOfResynchMarker = (code) ? 0 : 1; + + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* data partitionned */ + pDci->bDataPartition = (code) ? M4OSA_TRUE : M4OSA_FALSE; + if (code) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* reversible VLC */ + pDci->bUseOfRVLC = (code) ? M4OSA_TRUE : M4OSA_FALSE; + } + + if (vol_verid != 1) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* newpred */ + if (code) { + //return M4ERR_PARAMETER; + } + + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1); + reduced_resolution_vop_enable = (M4OSA_UInt8)code; + } + + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* scalability */ + scalability = (M4OSA_UInt8)code; + if (code) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* hierarchy type */ + b_hierarchy_type = (M4OSA_UInt8)code; + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 4);/* ref layer id */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* ref sampling direct */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 5);/* hor sampling factor N */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 5);/* hor sampling factor M */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 5);/* vert sampling factor N */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 5);/* vert sampling factor M */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* enhancement type */ + enhancement_type = (M4OSA_UInt8)code; + if ((!b_hierarchy_type) && + (video_object_layer_shape == 1)) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* use ref shape */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* use ref texture */ + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 5); + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 5); + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 5); + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 5); + } + } + break; + } + + /* ----- 0xB0 : visual_object_sequence_start_code ----- */ + + else if(code == 0xB0) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 8);/* profile_and_level_indication */ + pDci->uiProfile = (M4OSA_UInt8)code; + } + + /* ----- 0xB5 : visual_object_start_code ----- */ + + else if(code == 0xB5) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 1);/* is object layer identifier */ + if (code == 1) { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 4); /* visual object verid */ + vol_verid = (M4OSA_UInt8)code; + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 3); + } else { + code = VideoEditorVideoDecoder_GetBitsFromMemory( + &parsingCtxt, 7); /* Realign on byte */ + vol_verid = 1; + } + } + + /* ----- end ----- */ + } else { + if ((code >> 2) == 0x20) { + /* H263 ...-> wrong*/ + break; + } + } + } + } + } + return M4NO_ERROR; +} + +M4VIFI_UInt8 M4VIFI_SemiplanarYVU420toYUV420(void *user_data, + M4VIFI_UInt8 *inyuv, M4VIFI_ImagePlane *PlaneOut ) { + M4VIFI_UInt8 return_code = M4VIFI_OK; + M4VIFI_UInt8 *outyuv = + ((M4VIFI_UInt8*)&(PlaneOut[0].pac_data[PlaneOut[0].u_topleft])); + int32_t width = PlaneOut[0].u_width; + int32_t height = PlaneOut[0].u_height; + + int32_t outYsize = width * height; + uint32_t *outy = (uint32_t *) outyuv; + uint16_t *outcb = + (uint16_t *) &(PlaneOut[1].pac_data[PlaneOut[1].u_topleft]); + uint16_t *outcr = + (uint16_t *) &(PlaneOut[2].pac_data[PlaneOut[2].u_topleft]); + + /* Y copying */ + memcpy(outy, inyuv, outYsize); + + /* U & V copying */ + uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize); + for (int32_t i = height >> 1; i > 0; --i) { + for (int32_t j = width >> 2; j > 0; --j) { + uint32_t temp = *inyuv_4++; + uint32_t tempU = temp & 0xFF; + tempU = tempU | ((temp >> 8) & 0xFF00); + + uint32_t tempV = (temp >> 8) & 0xFF; + tempV = tempV | ((temp >> 16) & 0xFF00); + + // Flip U and V + *outcb++ = tempV; + *outcr++ = tempU; + } + } + return return_code; +} + +M4OSA_ERR VideoEditorVideoDecoder_ParseAVCDSI(M4OSA_UInt8* pDSI, + M4OSA_Int32 DSISize, M4DECODER_AVCProfileLevel *profile) { + M4OSA_ERR err = M4NO_ERROR; + M4OSA_Bool NALSPS_and_Profile0Found = M4OSA_FALSE; + M4OSA_UInt16 index; + M4OSA_Bool constraintSet3; + + for(index = 0; index < (DSISize-1); index++) { + if(((pDSI[index] & 0x1f) == 0x07) && (pDSI[index+1] == 0x42)) { + NALSPS_and_Profile0Found = M4OSA_TRUE; + break; + } + } + if(M4OSA_FALSE == NALSPS_and_Profile0Found) { + LOGV("VideoEditorVideoDecoder_ParseAVCDSI: index bad = %d", index); + *profile = M4DECODER_AVC_kProfile_and_Level_Out_Of_Range; + } else { + LOGV("VideoEditorVideoDecoder_ParseAVCDSI: index = %d", index); + constraintSet3 = (pDSI[index+2] & 0x10); + LOGV("VideoEditorVideoDecoder_ParseAVCDSI: level = %d", pDSI[index+3]); + switch(pDSI[index+3]) { + case 10: + *profile = M4DECODER_AVC_kProfile_0_Level_1; + break; + case 11: + if(constraintSet3) { + *profile = M4DECODER_AVC_kProfile_0_Level_1b; + } else { + *profile = M4DECODER_AVC_kProfile_0_Level_1_1; + } + break; + case 12: + *profile = M4DECODER_AVC_kProfile_0_Level_1_2; + break; + case 13: + *profile = M4DECODER_AVC_kProfile_0_Level_1_3; + break; + case 20: + *profile = M4DECODER_AVC_kProfile_0_Level_2; + break; + case 21: + *profile = M4DECODER_AVC_kProfile_0_Level_2_1; + break; + case 22: + *profile = M4DECODER_AVC_kProfile_0_Level_2_2; + break; + case 30: + *profile = M4DECODER_AVC_kProfile_0_Level_3; + break; + case 31: + *profile = M4DECODER_AVC_kProfile_0_Level_3_1; + break; + case 32: + *profile = M4DECODER_AVC_kProfile_0_Level_3_2; + break; + case 40: + *profile = M4DECODER_AVC_kProfile_0_Level_4; + break; + case 41: + *profile = M4DECODER_AVC_kProfile_0_Level_4_1; + break; + case 42: + *profile = M4DECODER_AVC_kProfile_0_Level_4_2; + break; + case 50: + *profile = M4DECODER_AVC_kProfile_0_Level_5; + break; + case 51: + *profile = M4DECODER_AVC_kProfile_0_Level_5_1; + break; + default: + *profile = M4DECODER_AVC_kProfile_and_Level_Out_Of_Range; + } + } + return err; +} +/******************** + * ENGINE INTERFACE * + ********************/ +M4OSA_ERR VideoEditorVideoDecoder_configureFromMetadata(M4OSA_Context pContext, + MetaData* meta) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoDecoder_Context* pDecShellContext = M4OSA_NULL; + bool success = OK; + int32_t width = 0; + int32_t height = 0; + int32_t frameSize = 0; + + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != meta, M4ERR_PARAMETER); + + LOGV("VideoEditorVideoDecoder_configureFromMetadata begin"); + + pDecShellContext = (VideoEditorVideoDecoder_Context*)pContext; + + // Get the parameters + success = meta->findInt32(kKeyWidth, &width); + success &= meta->findInt32(kKeyHeight, &height); + VIDEOEDITOR_CHECK(TRUE == success, M4ERR_PARAMETER); + + LOGV("VideoDecoder_configureFromMetadata : W=%d H=%d", width, height); + VIDEOEDITOR_CHECK((0 != width) && (0 != height), M4ERR_PARAMETER); + + LOGV("VideoDecoder_configureFromMetadata : W=%d H=%d", width, height); + + if( (M4OSA_NULL != pDecShellContext->m_pDecBufferPool) && + (pDecShellContext->m_pVideoStreamhandler->m_videoWidth == \ + (uint32_t)width) && + (pDecShellContext->m_pVideoStreamhandler->m_videoHeight == \ + (uint32_t)height) ) { + // No need to reconfigure + goto cleanUp; + } + LOGV("VideoDecoder_configureFromMetadata reset: W=%d H=%d", width, height); + // Update the stream handler parameters + pDecShellContext->m_pVideoStreamhandler->m_videoWidth = width; + pDecShellContext->m_pVideoStreamhandler->m_videoHeight = height; + frameSize = (width * height * 3) / 2; + + // Configure the buffer pool + if( M4OSA_NULL != pDecShellContext->m_pDecBufferPool ) { + LOGV("VideoDecoder_configureFromMetadata : reset the buffer pool"); + VIDEOEDITOR_BUFFER_freePool(pDecShellContext->m_pDecBufferPool); + pDecShellContext->m_pDecBufferPool = M4OSA_NULL; + } + err = VIDEOEDITOR_BUFFER_allocatePool(&pDecShellContext->m_pDecBufferPool, + MAX_DEC_BUFFERS, (M4OSA_Char*)"VIDEOEDITOR_DecodedBufferPool"); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + err = VIDEOEDITOR_BUFFER_initPoolBuffers(pDecShellContext->m_pDecBufferPool, + frameSize + width * 2); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoDecoder_configureFromMetadata no error"); + } else { + if( M4OSA_NULL != pDecShellContext->m_pDecBufferPool ) { + VIDEOEDITOR_BUFFER_freePool(pDecShellContext->m_pDecBufferPool); + pDecShellContext->m_pDecBufferPool = M4OSA_NULL; + } + LOGV("VideoEditorVideoDecoder_configureFromMetadata ERROR 0x%X", err); + } + LOGV("VideoEditorVideoDecoder_configureFromMetadata end"); + return err; +} + +M4OSA_ERR VideoEditorVideoDecoder_destroy(M4OSA_Context pContext) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoDecoder_Context* pDecShellContext = + (VideoEditorVideoDecoder_Context*)pContext; + + // Input parameters check + LOGV("VideoEditorVideoDecoder_destroy begin"); + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + // Destroy the graph + if( pDecShellContext->mVideoDecoder != NULL ) { + LOGV("### VideoEditorVideoDecoder_destroy : releasing decoder"); + pDecShellContext->mVideoDecoder->stop(); + pDecShellContext->mVideoDecoder.clear(); + } + pDecShellContext->mClient.disconnect(); + pDecShellContext->mReaderSource.clear(); + + // Release memory + if( pDecShellContext->m_pDecBufferPool != M4OSA_NULL ) { + VIDEOEDITOR_BUFFER_freePool(pDecShellContext->m_pDecBufferPool); + pDecShellContext->m_pDecBufferPool = M4OSA_NULL; + } + SAFE_FREE(pDecShellContext); + pContext = NULL; + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoDecoder_destroy no error"); + } else { + LOGV("VideoEditorVideoDecoder_destroy ERROR 0x%X", err); + } + LOGV("VideoEditorVideoDecoder_destroy end"); + return err; +} + +M4OSA_ERR VideoEditorVideoDecoder_create(M4OSA_Context *pContext, + M4_StreamHandler *pStreamHandler, + M4READER_DataInterface *pReaderDataInterface, + M4_AccessUnit *pAccessUnit, M4OSA_Void *pUserData) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoDecoder_Context* pDecShellContext = M4OSA_NULL; + status_t status = OK; + bool success = TRUE; + int32_t colorFormat = 0; + M4OSA_UInt32 size = 0; + sp<MetaData> decoderMetadata = NULL; + + LOGV("VideoEditorVideoDecoder_create begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pStreamHandler, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pReaderDataInterface, M4ERR_PARAMETER); + + // Context allocation & initialization + SAFE_MALLOC(pDecShellContext, VideoEditorVideoDecoder_Context, 1, + "VideoEditorVideoDecoder"); + pDecShellContext->m_pVideoStreamhandler = + (M4_VideoStreamHandler*)pStreamHandler; + pDecShellContext->m_pNextAccessUnitToDecode = pAccessUnit; + pDecShellContext->m_pReader = pReaderDataInterface; + pDecShellContext->m_lastDecodedCTS = -1; + pDecShellContext->m_lastRenderCts = -1; + switch( pStreamHandler->m_streamType ) { + case M4DA_StreamTypeVideoH263: + pDecShellContext->mDecoderType = VIDEOEDITOR_kH263VideoDec; + break; + case M4DA_StreamTypeVideoMpeg4: + pDecShellContext->mDecoderType = VIDEOEDITOR_kMpeg4VideoDec; + // Parse the VOL header + err = VideoEditorVideoDecoder_internalParseVideoDSI( + (M4OSA_UInt8*)pDecShellContext->m_pVideoStreamhandler->\ + m_basicProperties.m_pDecoderSpecificInfo, + pDecShellContext->m_pVideoStreamhandler->\ + m_basicProperties.m_decoderSpecificInfoSize, + &pDecShellContext->m_Dci, &pDecShellContext->m_VideoSize); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + break; + case M4DA_StreamTypeVideoMpeg4Avc: + pDecShellContext->mDecoderType = VIDEOEDITOR_kH264VideoDec; + break; + default: + VIDEOEDITOR_CHECK(!"VideoDecoder_create : incorrect stream type", + M4ERR_PARAMETER); + break; + } + + pDecShellContext->mNbInputFrames = 0; + pDecShellContext->mFirstInputCts = -1.0; + pDecShellContext->mLastInputCts = -1.0; + pDecShellContext->mNbRenderedFrames = 0; + pDecShellContext->mFirstRenderedCts = -1.0; + pDecShellContext->mLastRenderedCts = -1.0; + pDecShellContext->mNbOutputFrames = 0; + pDecShellContext->mFirstOutputCts = -1; + pDecShellContext->mLastOutputCts = -1; + + /** + * StageFright graph building + */ + decoderMetadata = new MetaData; + switch( pDecShellContext->mDecoderType ) { + case VIDEOEDITOR_kH263VideoDec: + decoderMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263); + break; + case VIDEOEDITOR_kMpeg4VideoDec: + decoderMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); + decoderMetadata->setData(kKeyESDS, kTypeESDS, + pStreamHandler->m_pESDSInfo, + pStreamHandler->m_ESDSInfoSize); + break; + case VIDEOEDITOR_kH264VideoDec: + decoderMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); + decoderMetadata->setData(kKeyAVCC, kTypeAVCC, + pStreamHandler->m_pH264DecoderSpecificInfo, + pStreamHandler->m_H264decoderSpecificInfoSize); + break; + default: + VIDEOEDITOR_CHECK(!"VideoDecoder_create : incorrect stream type", + M4ERR_PARAMETER); + break; + } + + decoderMetadata->setInt32(kKeyMaxInputSize, pStreamHandler->m_maxAUSize); + decoderMetadata->setInt32(kKeyWidth, + pDecShellContext->m_pVideoStreamhandler->m_videoWidth); + decoderMetadata->setInt32(kKeyHeight, + pDecShellContext->m_pVideoStreamhandler->m_videoHeight); + + // Create the decoder source + pDecShellContext->mReaderSource = new VideoEditorVideoDecoderSource( + decoderMetadata, pDecShellContext->mDecoderType, + (void *)pDecShellContext); + VIDEOEDITOR_CHECK(NULL != pDecShellContext->mReaderSource.get(), + M4ERR_SF_DECODER_RSRC_FAIL); + + // Connect to the OMX client + status = pDecShellContext->mClient.connect(); + VIDEOEDITOR_CHECK(OK == status, M4ERR_SF_DECODER_RSRC_FAIL); + + // Create the decoder + pDecShellContext->mVideoDecoder = OMXCodec::Create( + pDecShellContext->mClient.interface(), + decoderMetadata, false, pDecShellContext->mReaderSource); + VIDEOEDITOR_CHECK(NULL != pDecShellContext->mVideoDecoder.get(), + M4ERR_SF_DECODER_RSRC_FAIL); + + + // Get the output color format + success = pDecShellContext->mVideoDecoder->getFormat()->findInt32( + kKeyColorFormat, &colorFormat); + VIDEOEDITOR_CHECK(TRUE == success, M4ERR_PARAMETER); + pDecShellContext->decOuputColorFormat = (OMX_COLOR_FORMATTYPE)colorFormat; + + pDecShellContext->mVideoDecoder->getFormat()->setInt32(kKeyWidth, + pDecShellContext->m_pVideoStreamhandler->m_videoWidth); + pDecShellContext->mVideoDecoder->getFormat()->setInt32(kKeyHeight, + pDecShellContext->m_pVideoStreamhandler->m_videoHeight); + + // Configure the buffer pool from the metadata + err = VideoEditorVideoDecoder_configureFromMetadata(pDecShellContext, + pDecShellContext->mVideoDecoder->getFormat().get()); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + + // Start the graph + status = pDecShellContext->mVideoDecoder->start(); + VIDEOEDITOR_CHECK(OK == status, M4ERR_SF_DECODER_RSRC_FAIL); + + *pContext = (M4OSA_Context)pDecShellContext; + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoDecoder_create no error"); + } else { + VideoEditorVideoDecoder_destroy(pDecShellContext); + *pContext = M4OSA_NULL; + LOGV("VideoEditorVideoDecoder_create ERROR 0x%X", err); + } + LOGV("VideoEditorVideoDecoder_create : DONE"); + return err; +} + +M4OSA_ERR VideoEditorVideoDecoder_getOption(M4OSA_Context context, + M4OSA_OptionID optionId, M4OSA_DataOption pValue) { + M4OSA_ERR lerr = M4NO_ERROR; + VideoEditorVideoDecoder_Context* pDecShellContext = + (VideoEditorVideoDecoder_Context*) context; + M4_VersionInfo* pVersionInfo; + M4DECODER_VideoSize* pVideoSize; + M4OSA_UInt32* pNextFrameCts; + M4OSA_UInt32 *plastDecodedFrameCts; + M4DECODER_AVCProfileLevel* profile; + M4DECODER_MPEG4_DecoderConfigInfo* pDecConfInfo; + + LOGV("VideoEditorVideoDecoder_getOption begin"); + + switch (optionId) { + case M4DECODER_kOptionID_AVCLastDecodedFrameCTS: + plastDecodedFrameCts = (M4OSA_UInt32 *) pValue; + *plastDecodedFrameCts = pDecShellContext->m_lastDecodedCTS; + break; + + case M4DECODER_kOptionID_Version: + pVersionInfo = (M4_VersionInfo*)pValue; + + pVersionInfo->m_major = VIDEOEDITOR_VIDEC_SHELL_VER_MAJOR; + pVersionInfo->m_minor= VIDEOEDITOR_VIDEC_SHELL_VER_MINOR; + pVersionInfo->m_revision = VIDEOEDITOR_VIDEC_SHELL_VER_REVISION; + pVersionInfo->m_structSize=sizeof(M4_VersionInfo); + break; + + case M4DECODER_kOptionID_VideoSize: + /** Only VPS uses this Option ID. */ + pVideoSize = (M4DECODER_VideoSize*)pValue; + pDecShellContext->mVideoDecoder->getFormat()->findInt32(kKeyWidth, + (int32_t*)(&pVideoSize->m_uiWidth)); + pDecShellContext->mVideoDecoder->getFormat()->findInt32(kKeyHeight, + (int32_t*)(&pVideoSize->m_uiHeight)); + LOGV("VideoEditorVideoDecoder_getOption : W=%d H=%d", + pVideoSize->m_uiWidth, pVideoSize->m_uiHeight); + break; + + case M4DECODER_kOptionID_NextRenderedFrameCTS: + /** How to get this information. SF decoder does not provide this. * + ** Let us provide last decoded frame CTS as of now. * + ** Only VPS uses this Option ID. */ + pNextFrameCts = (M4OSA_UInt32 *)pValue; + *pNextFrameCts = pDecShellContext->m_lastDecodedCTS; + break; + case M4DECODER_kOptionID_AVCProfileAndLevel: + profile = (M4DECODER_AVCProfileLevel *) pValue; + VideoEditorVideoDecoder_ParseAVCDSI ( + pDecShellContext->m_pVideoStreamhandler->\ + m_basicProperties.m_pDecoderSpecificInfo, + pDecShellContext->m_pVideoStreamhandler->\ + m_basicProperties.m_decoderSpecificInfoSize, + profile); + break; + case M4DECODER_MPEG4_kOptionID_DecoderConfigInfo: + if(pDecShellContext->mDecoderType == VIDEOEDITOR_kMpeg4VideoDec) { + (*(M4DECODER_MPEG4_DecoderConfigInfo*)pValue) = + pDecShellContext->m_Dci; + } + break; + default: + lerr = M4ERR_BAD_OPTION_ID; + break; + + } + + LOGV("VideoEditorVideoDecoder_getOption: end with err = 0x%x", lerr); + return lerr; +} + +M4OSA_ERR VideoEditorVideoDecoder_setOption(M4OSA_Context context, + M4OSA_OptionID optionId, M4OSA_DataOption pValue) { + M4OSA_ERR lerr = M4NO_ERROR; + VideoEditorVideoDecoder_Context *pDecShellContext = + (VideoEditorVideoDecoder_Context*) context; + + LOGV("VideoEditorVideoDecoder_setOption begin"); + + switch (optionId) { + case M4DECODER_kOptionID_OutputFilter: { + M4DECODER_OutputFilter* pOutputFilter = + (M4DECODER_OutputFilter*) pValue; + pDecShellContext->m_pFilter = + (M4VIFI_PlanConverterFunctionType*)pOutputFilter->\ + m_pFilterFunction; + pDecShellContext->m_pFilterUserData = + pOutputFilter->m_pFilterUserData; + } + break; + case M4DECODER_kOptionID_DeblockingFilter: + break; + default: + lerr = M4ERR_BAD_CONTEXT; + break; + } + + LOGV("VideoEditorVideoDecoder_setOption: end with err = 0x%x", lerr); + return lerr; +} + +M4OSA_ERR VideoEditorVideoDecoder_decode(M4OSA_Context context, + M4_MediaTime* pTime, M4OSA_Bool bJump) { + M4OSA_ERR lerr = M4NO_ERROR; + VideoEditorVideoDecoder_Context* pDecShellContext = + (VideoEditorVideoDecoder_Context*) context; + int64_t lFrameTime; + VIDEOEDITOR_BUFFER_Buffer* tmpDecBuffer; + MediaSource::ReadOptions decShellOptions; + MediaBuffer* pDecoderBuffer = NULL; + status_t errStatus; + + + LOGV("VideoEditorVideoDecoder_decode begin"); + + if( M4OSA_TRUE == pDecShellContext->mReachedEOS ) { + // Do not call read(), it could lead to a freeze + LOGV("VideoEditorVideoDecoder_decode : EOS already reached"); + lerr = M4WAR_NO_MORE_AU; + goto VIDEOEDITOR_VideoDecode_cleanUP; + } + if(pDecShellContext->m_lastDecodedCTS >= *pTime) { + LOGV("VideoDecoder_decode: Already decoded up to this time CTS = %lf.", + pDecShellContext->m_lastDecodedCTS); + goto VIDEOEDITOR_VideoDecode_cleanUP; + } + if(M4OSA_TRUE == bJump) { + LOGV("VideoEditorVideoDecoder_decode: Jump called"); + pDecShellContext->m_lastDecodedCTS = -1; + pDecShellContext->m_lastRenderCts = -1; + } + + pDecShellContext->mNbInputFrames++; + if (0 > pDecShellContext->mFirstInputCts){ + pDecShellContext->mFirstInputCts = *pTime; + } + pDecShellContext->mLastInputCts = *pTime; + + while (pDecShellContext->m_lastDecodedCTS < *pTime) { + LOGV("VideoEditorVideoDecoder_decode, frameCTS = %lf, DecodeUpTo = %lf", + pDecShellContext->m_lastDecodedCTS, *pTime); + lerr = VIDEOEDITOR_BUFFER_getBuffer(pDecShellContext->m_pDecBufferPool, + VIDEOEDITOR_BUFFER_kEmpty, &tmpDecBuffer); + if (lerr == (M4OSA_UInt32)M4ERR_NO_BUFFER_AVAILABLE) { + lerr = VIDEOEDITOR_BUFFER_getOldestBuffer( + pDecShellContext->m_pDecBufferPool, + VIDEOEDITOR_BUFFER_kFilled, &tmpDecBuffer); + tmpDecBuffer->state = VIDEOEDITOR_BUFFER_kEmpty; + lerr = M4NO_ERROR; + } + + if (lerr != M4NO_ERROR) { + goto VIDEOEDITOR_VideoDecode_cleanUP; + } + + if (pDecoderBuffer != NULL) { + pDecoderBuffer->release(); + pDecoderBuffer = NULL; + } + + decShellOptions.reset(); + errStatus = pDecShellContext->mVideoDecoder->read(&pDecoderBuffer, + &decShellOptions); + if (errStatus == ERROR_END_OF_STREAM) { + LOGV("End of stream reached, returning M4WAR_NO_MORE_AU "); + pDecShellContext->mReachedEOS = M4OSA_TRUE; + lerr = M4WAR_NO_MORE_AU; + goto VIDEOEDITOR_VideoDecode_cleanUP; + } else if ( INFO_FORMAT_CHANGED == errStatus ) { + LOGV("VideoDecoder_decode:source returns INFO_FORMAT_CHANGED:TODO"); + +#if 1 + LOGV("VideoDecoder_decode : source returns INFO_FORMAT_CHANGED"); + lerr = VideoEditorVideoDecoder_configureFromMetadata( + pDecShellContext, + pDecShellContext->mVideoDecoder->getFormat().get()); + if( M4NO_ERROR != lerr ) { + LOGV("!!! VideoEditorVideoDecoder_decode ERROR : " + "VideoDecoder_configureFromMetadata returns 0x%X", lerr); + break; + } +#endif + continue; + } + + if( 0 < pDecoderBuffer->range_length() ) { + LOGV("VIDEOEDITOR_VideoDecoder frame buffer size = %d", + pDecoderBuffer->range_length()); + + pDecoderBuffer->meta_data()->findInt64(kKeyTime, &lFrameTime); + pDecShellContext->m_lastDecodedCTS = (M4_MediaTime)(lFrameTime/1000); + LOGV("VideoEditorVideoDecoder_decode,decoded frametime = %lf,size = %d", + (M4_MediaTime)lFrameTime, pDecoderBuffer->size() ); + + switch ( pDecShellContext->decOuputColorFormat ) { + case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: { + M4VIFI_ImagePlane tmpPlane[3]; + // Prepare the output image for conversion + if( pDecoderBuffer->range_length() != ( + pDecShellContext->m_pVideoStreamhandler->m_videoWidth * + pDecShellContext->m_pVideoStreamhandler->m_videoHeight \ + * 3)/2 ) { + LOGV("VideoEditorVideoDecoder_decod invalid frame size S=%d" + "W=%d H=%d", pDecoderBuffer->range_length(), + pDecShellContext->m_pVideoStreamhandler->m_videoWidth, + pDecShellContext->m_pVideoStreamhandler->m_videoHeight); + lerr = M4ERR_PARAMETER; + goto VIDEOEDITOR_VideoDecode_cleanUP; + } + tmpPlane[0].u_width = + pDecShellContext->m_pVideoStreamhandler->m_videoWidth; + tmpPlane[0].u_height = + pDecShellContext->m_pVideoStreamhandler->m_videoHeight; + tmpPlane[0].u_topleft = 0; + tmpPlane[0].u_stride = tmpPlane[0].u_width; + tmpPlane[0].pac_data = (M4VIFI_UInt8*)tmpDecBuffer->pData; + tmpPlane[1].u_width = tmpPlane[0].u_width/2; + tmpPlane[1].u_height = tmpPlane[0].u_height/2; + tmpPlane[1].u_topleft = 0; + tmpPlane[1].u_stride = tmpPlane[0].u_stride/2; + tmpPlane[1].pac_data = tmpPlane[0].pac_data + + (tmpPlane[0].u_stride * tmpPlane[0].u_height); + tmpPlane[2].u_width = tmpPlane[1].u_width; + tmpPlane[2].u_height = tmpPlane[1].u_height; + tmpPlane[2].u_topleft = 0; + tmpPlane[2].u_stride = tmpPlane[1].u_stride; + tmpPlane[2].pac_data = tmpPlane[1].pac_data + + (tmpPlane[1].u_stride * tmpPlane[1].u_height); + M4VIFI_SemiplanarYVU420toYUV420(M4OSA_NULL, + (M4VIFI_UInt8 *)pDecoderBuffer->data() + \ + pDecoderBuffer->range_offset(), &tmpPlane[0]); + break; + } + case OMX_COLOR_FormatYUV420Planar: + M4OSA_memcpy((M4OSA_MemAddr8)tmpDecBuffer->pData, + (M4OSA_MemAddr8) pDecoderBuffer->data() + + pDecoderBuffer->range_offset(), + (M4OSA_UInt32)pDecoderBuffer->range_length()); + break; + default: + LOGV("VideoDecoder_decode: unexpected color format 0x%X", + pDecShellContext->decOuputColorFormat); + return M4ERR_PARAMETER; + } + + tmpDecBuffer->buffCTS = pDecShellContext->m_lastDecodedCTS; + tmpDecBuffer->state = VIDEOEDITOR_BUFFER_kFilled; + tmpDecBuffer->size = pDecoderBuffer->size(); + + } else { + LOGV("VideoEditorVideoDecoder_decode : empty buffer was returned"); + } + } + pDecShellContext->mNbOutputFrames++; + if ( 0 > pDecShellContext->mFirstOutputCts ) { + pDecShellContext->mFirstOutputCts = *pTime; + } + pDecShellContext->mLastOutputCts = *pTime; + +VIDEOEDITOR_VideoDecode_cleanUP: + *pTime = pDecShellContext->m_lastDecodedCTS; + if (pDecoderBuffer != NULL) { + pDecoderBuffer->release(); + pDecoderBuffer = NULL; + } + + LOGV("VideoEditorVideoDecoder_decode: end with 0x%x", lerr); + return lerr; +} + +M4OSA_ERR VideoEditorVideoDecoder_render(M4OSA_Context context, + M4_MediaTime* pTime, M4VIFI_ImagePlane* pOutputPlane, + M4OSA_Bool bForceRender) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoDecoder_Context* pDecShellContext = + (VideoEditorVideoDecoder_Context*) context; + M4OSA_UInt32 lindex, i; + M4OSA_UInt8* p_buf_src, *p_buf_dest; + M4VIFI_ImagePlane tmpPlaneIn, tmpPlaneOut; + VIDEOEDITOR_BUFFER_Buffer* pTmpVIDEOEDITORBuffer, *pRenderVIDEOEDITORBuffer + = M4OSA_NULL; + M4_MediaTime candidateTimeStamp = -1; + M4OSA_Bool bFound = M4OSA_FALSE; + + LOGV("VideoEditorVideoDecoder_render begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != context, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pTime, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pOutputPlane, M4ERR_PARAMETER); + + // The output buffer is already allocated, just copy the data + if ( (*pTime <= pDecShellContext->m_lastRenderCts) && + (M4OSA_FALSE == bForceRender) ) { + LOGV("VIDEOEDITOR_VIDEO_render Frame in the past"); + err = M4WAR_VIDEORENDERER_NO_NEW_FRAME; + goto cleanUp; + } + LOGV("VideoDecoder_render: lastRendered time = %lf,requested render time = " + "%lf", pDecShellContext->m_lastRenderCts, *pTime); + + /** + * Find the buffer appropriate for rendering. */ + for (i=0; i < pDecShellContext->m_pDecBufferPool->NB; i++) { + pTmpVIDEOEDITORBuffer = &pDecShellContext->m_pDecBufferPool\ + ->pNXPBuffer[i]; + if (pTmpVIDEOEDITORBuffer->state == VIDEOEDITOR_BUFFER_kFilled) { + /** Free all those buffers older than last rendered frame. */ + if (pTmpVIDEOEDITORBuffer->buffCTS < pDecShellContext->\ + m_lastRenderCts) { + pTmpVIDEOEDITORBuffer->state = VIDEOEDITOR_BUFFER_kEmpty; + } + + /** Get the buffer with appropriate timestamp */ + if ( (pTmpVIDEOEDITORBuffer->buffCTS >= pDecShellContext->\ + m_lastRenderCts) && + (pTmpVIDEOEDITORBuffer->buffCTS <= *pTime) && + (pTmpVIDEOEDITORBuffer->buffCTS > candidateTimeStamp)) { + bFound = M4OSA_TRUE; + pRenderVIDEOEDITORBuffer = pTmpVIDEOEDITORBuffer; + candidateTimeStamp = pTmpVIDEOEDITORBuffer->buffCTS; + LOGV("VideoDecoder_render: found a buffer with timestamp = %lf", + candidateTimeStamp); + } + } + } + if (M4OSA_FALSE == bFound) { + err = M4WAR_VIDEORENDERER_NO_NEW_FRAME; + goto cleanUp; + } + + LOGV("VideoEditorVideoDecoder_render 3 ouput %d %d %d %d", + pOutputPlane[0].u_width, pOutputPlane[0].u_height, + pOutputPlane[0].u_topleft, pOutputPlane[0].u_stride); + + pDecShellContext->m_lastRenderCts = candidateTimeStamp; + + if( M4OSA_NULL != pDecShellContext->m_pFilter ) { + // Filtering was requested + M4VIFI_ImagePlane tmpPlane[3]; + // Prepare the output image for conversion + tmpPlane[0].u_width = + pDecShellContext->m_pVideoStreamhandler->m_videoWidth; + tmpPlane[0].u_height = + pDecShellContext->m_pVideoStreamhandler->m_videoHeight; + tmpPlane[0].u_topleft = 0; + tmpPlane[0].u_stride = tmpPlane[0].u_width; + tmpPlane[0].pac_data = (M4VIFI_UInt8*)pRenderVIDEOEDITORBuffer->pData; + tmpPlane[1].u_width = tmpPlane[0].u_width/2; + tmpPlane[1].u_height = tmpPlane[0].u_height/2; + tmpPlane[1].u_topleft = 0; + tmpPlane[1].u_stride = tmpPlane[0].u_stride/2; + tmpPlane[1].pac_data = tmpPlane[0].pac_data + + (tmpPlane[0].u_stride * tmpPlane[0].u_height); + tmpPlane[2].u_width = tmpPlane[1].u_width; + tmpPlane[2].u_height = tmpPlane[1].u_height; + tmpPlane[2].u_topleft = 0; + tmpPlane[2].u_stride = tmpPlane[1].u_stride; + tmpPlane[2].pac_data = tmpPlane[1].pac_data + + (tmpPlane[1].u_stride * tmpPlane[1].u_height); + + LOGV("VideoEditorVideoDecoder_render w = %d H = %d", + tmpPlane[0].u_width,tmpPlane[0].u_height); + pDecShellContext->m_pFilter(M4OSA_NULL, &tmpPlane[0], pOutputPlane); + } else { + // Just copy the YUV420P buffer + M4OSA_MemAddr8 tempBuffPtr = + (M4OSA_MemAddr8)pRenderVIDEOEDITORBuffer->pData; + M4OSA_UInt32 tempWidth = + pDecShellContext->m_pVideoStreamhandler->m_videoWidth; + M4OSA_UInt32 tempHeight = + pDecShellContext->m_pVideoStreamhandler->m_videoHeight; + + M4OSA_memcpy((M4OSA_MemAddr8) pOutputPlane[0].pac_data, tempBuffPtr, + tempWidth * tempHeight); + tempBuffPtr += (tempWidth * tempHeight); + M4OSA_memcpy((M4OSA_MemAddr8) pOutputPlane[1].pac_data, tempBuffPtr, + (tempWidth/2) * (tempHeight/2)); + tempBuffPtr += ((tempWidth/2) * (tempHeight/2)); + M4OSA_memcpy((M4OSA_MemAddr8) pOutputPlane[2].pac_data, tempBuffPtr, + (tempWidth/2) * (tempHeight/2)); + } + + pDecShellContext->mNbRenderedFrames++; + if ( 0 > pDecShellContext->mFirstRenderedCts ) { + pDecShellContext->mFirstRenderedCts = *pTime; + } + pDecShellContext->mLastRenderedCts = *pTime; + +cleanUp: + if( M4NO_ERROR == err ) { + *pTime = pDecShellContext->m_lastRenderCts; + LOGV("VideoEditorVideoDecoder_render no error"); + } else { + LOGV("VideoEditorVideoDecoder_render ERROR 0x%X", err); + } + LOGV("VideoEditorVideoDecoder_render end"); + return err; +} + +M4OSA_ERR VideoEditorVideoDecoder_getInterface(M4DECODER_VideoType decoderType, + M4DECODER_VideoType *pDecoderType, M4OSA_Context *pDecInterface) { + M4DECODER_VideoInterface* pDecoderInterface = M4OSA_NULL; + + pDecoderInterface = (M4DECODER_VideoInterface*)M4OSA_malloc( + sizeof(M4DECODER_VideoInterface), M4DECODER_EXTERNAL, + (M4OSA_Char*)"VideoEditorVideoDecoder_getInterface" ); + if (M4OSA_NULL == pDecoderInterface) { + return M4ERR_ALLOC; + } + + *pDecoderType = decoderType; + + pDecoderInterface->m_pFctCreate = VideoEditorVideoDecoder_create; + pDecoderInterface->m_pFctDestroy = VideoEditorVideoDecoder_destroy; + pDecoderInterface->m_pFctGetOption = VideoEditorVideoDecoder_getOption; + pDecoderInterface->m_pFctSetOption = VideoEditorVideoDecoder_setOption; + pDecoderInterface->m_pFctDecode = VideoEditorVideoDecoder_decode; + pDecoderInterface->m_pFctRender = VideoEditorVideoDecoder_render; + + *pDecInterface = (M4OSA_Context)pDecoderInterface; + return M4NO_ERROR; +} + +extern "C" { + +M4OSA_ERR VideoEditorVideoDecoder_getInterface_MPEG4( + M4DECODER_VideoType *pDecoderType, M4OSA_Context *pDecInterface) { + return VideoEditorVideoDecoder_getInterface(M4DECODER_kVideoTypeMPEG4, + pDecoderType, pDecInterface); +} + +M4OSA_ERR VideoEditorVideoDecoder_getInterface_H264( + M4DECODER_VideoType *pDecoderType, M4OSA_Context *pDecInterface) { + return VideoEditorVideoDecoder_getInterface(M4DECODER_kVideoTypeAVC, + pDecoderType, pDecInterface); + +} + +} // extern "C" diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditorVideoEncoder.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditorVideoEncoder.cpp new file mode 100755 index 0000000..0813b5c --- /dev/null +++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditorVideoEncoder.cpp @@ -0,0 +1,1288 @@ +/* + * Copyright (C) 2011 NXP Software + * Copyright (C) 2011 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. + */ +/** +************************************************************************* +* @file VideoEditorVideoEncoder.cpp +* @brief StageFright shell video encoder +************************************************************************* +*/ +#define LOG_NDEBUG 1 +#define LOG_TAG "VIDEOEDITOR_VIDEOENCODER" + +/******************* + * HEADERS * + *******************/ +#include "M4OSA_Debug.h" +#include "M4SYS_AccessUnit.h" +#include "VideoEditorVideoEncoder.h" +#include "VideoEditorUtils.h" + +#include "utils/Log.h" +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/OMXClient.h> +#include <media/stagefright/OMXCodec.h> +#include "OMX_Video.h" + +/******************** + * DEFINITIONS * + ********************/ + +// Minimum number of buffer in the source in order to allow encoding +#define VIDEOEDITOR_MIN_BUFFER_NB 15 + +// Not enough source buffers available +#define M4WAR_SF_LOW_BUFFER M4OSA_ERR_CREATE(M4_WAR, 0xFF, 0x00001) + +// Encoder color format +#define VIDEOEDITOR_ENCODER_COLOR_FORMAT OMX_COLOR_FormatYUV420Planar + +// Force using hardware encoder +#define VIDEOEDITOR_FORCECODEC kHardwareCodecsOnly + +// Force Encoder to produce a DSI by sending fake input frames upon creation +#define VIDEOEDITOR_ENCODER_GET_DSI_AT_CREATION + +#if defined(VIDEOEDITOR_ENCODER_GET_DSI_AT_CREATION) && \ + !defined(VIDEOEDITOR_FORCECODEC) + #error "Cannot force DSI retrieval if codec type is not fixed" +#endif + +/******************** + * SOURCE CLASS * + ********************/ + +namespace android { + +struct VideoEditorVideoEncoderSource : public MediaSource { + public: + static sp<VideoEditorVideoEncoderSource> Create(); + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual sp<MetaData> getFormat(); + virtual status_t read(MediaBuffer **buffer, + const ReadOptions *options = NULL); + virtual int32_t storeBuffer(MediaBuffer *buffer); + + protected: + virtual ~VideoEditorVideoEncoderSource(); + + private: + struct MediaBufferChain { + MediaBuffer* buffer; + MediaBufferChain* nextLink; + }; + enum State { + CREATED, + STARTED, + ERROR + }; + VideoEditorVideoEncoderSource(); + MediaBufferChain* mFirstBufferLink; + MediaBufferChain* mLastBufferLink; + int32_t mNbBuffer; + bool mIsEOS; + State mState; +}; + +sp<VideoEditorVideoEncoderSource> VideoEditorVideoEncoderSource::Create() { +
+ sp<VideoEditorVideoEncoderSource> aSource = + new VideoEditorVideoEncoderSource(); + return aSource; +} + +VideoEditorVideoEncoderSource::VideoEditorVideoEncoderSource(): + mFirstBufferLink(NULL), + mLastBufferLink(NULL), + mNbBuffer(0), + mIsEOS(false), + mState(CREATED) { + LOGV("VideoEditorVideoEncoderSource::VideoEditorVideoEncoderSource"); +} + +VideoEditorVideoEncoderSource::~VideoEditorVideoEncoderSource() { +
+ // Safety clean up + if( STARTED == mState ) { + stop(); + } +} + +status_t VideoEditorVideoEncoderSource::start(MetaData *meta) { + status_t err = OK; + + LOGV("VideoEditorVideoEncoderSource::start() begin"); + + if( CREATED != mState ) { + LOGV("VideoEditorVideoEncoderSource::start: invalid state %d", mState); + return UNKNOWN_ERROR; + } + mState = STARTED; + + LOGV("VideoEditorVideoEncoderSource::start() END (0x%x)", err); + return err; +} + +status_t VideoEditorVideoEncoderSource::stop() { + status_t err = OK; + + LOGV("VideoEditorVideoEncoderSource::stop() begin"); + + if( STARTED != mState ) { + LOGV("VideoEditorVideoEncoderSource::stop: invalid state %d", mState); + return UNKNOWN_ERROR; + } + + // Release the buffer chain + int32_t i = 0; + MediaBufferChain* tmpLink = NULL; + while( mFirstBufferLink ) { + i++; + tmpLink = mFirstBufferLink; + mFirstBufferLink = mFirstBufferLink->nextLink; + delete tmpLink; + } + LOGV("VideoEditorVideoEncoderSource::stop : %d buffer remained", i); + mFirstBufferLink = NULL; + mLastBufferLink = NULL; + + mState = CREATED; + + LOGV("VideoEditorVideoEncoderSource::stop() END (0x%x)", err); + return err; +} + +sp<MetaData> VideoEditorVideoEncoderSource::getFormat() { + + LOGW("VideoEditorVideoEncoderSource::getFormat:THIS IS NOT IMPLEMENTED"); + return NULL; +}
+ +status_t VideoEditorVideoEncoderSource::read(MediaBuffer **buffer, + const ReadOptions *options) { + MediaSource::ReadOptions readOptions; + status_t err = OK; + MediaBufferChain* tmpLink = NULL; + + LOGV("VideoEditorVideoEncoderSource::read() begin"); + + if ( STARTED != mState ) { + LOGV("VideoEditorVideoEncoderSource::read: invalid state %d", mState); + return UNKNOWN_ERROR; + } + + // Get a buffer from the chain + if ( NULL == mFirstBufferLink ) { + *buffer = NULL; + if( mIsEOS ) { + LOGV("VideoEditorVideoEncoderSource::read : EOS"); + return ERROR_END_OF_STREAM; + } else { + LOGV("VideoEditorVideoEncoderSource::read: no buffer available"); + return ERROR_END_OF_STREAM; + } + } + *buffer = mFirstBufferLink->buffer; + tmpLink = mFirstBufferLink; + mFirstBufferLink = mFirstBufferLink->nextLink;
+ + if ( NULL == mFirstBufferLink ) { + mLastBufferLink = NULL; + } + delete tmpLink; + mNbBuffer--; + + LOGV("VideoEditorVideoEncoderSource::read() END (0x%x)", err); + return err; +} + +int32_t VideoEditorVideoEncoderSource::storeBuffer(MediaBuffer *buffer) { + status_t err = OK; + + LOGV("VideoEditorVideoEncoderSource::storeBuffer() begin"); + + if( NULL == buffer ) { + LOGV("VideoEditorVideoEncoderSource::storeBuffer : reached EOS"); + mIsEOS = true; + } else { + MediaBufferChain* newLink = new MediaBufferChain; + newLink->buffer = buffer; + newLink->nextLink = NULL; + if( NULL != mLastBufferLink ) { + mLastBufferLink->nextLink = newLink; + } else { + mFirstBufferLink = newLink; + } + mLastBufferLink = newLink; + mNbBuffer++; + } + LOGV("VideoEditorVideoEncoderSource::storeBuffer() end"); + return mNbBuffer; +}
+ +/** + ****************************************************************************** + * structure VideoEditorVideoEncoder_Context + * @brief This structure defines the context of the StageFright video encoder + * shell + ****************************************************************************** +*/ +typedef enum { + CREATED = 0x1, + OPENED = 0x2, + STARTED = 0x4, + BUFFERING = 0x8, + READING = 0x10 +} VideoEditorVideoEncoder_State;
+ +typedef struct { + VideoEditorVideoEncoder_State mState; + M4ENCODER_Format mFormat; + M4WRITER_DataInterface* mWriterDataInterface; + M4VPP_apply_fct* mPreProcFunction; + M4VPP_Context mPreProcContext; + M4SYS_AccessUnit* mAccessUnit; + M4ENCODER_Params* mCodecParams; + M4ENCODER_Header mHeader; + H264MCS_ProcessEncodedNALU_fct* mH264NALUPostProcessFct; + M4OSA_Context mH264NALUPostProcessCtx; + M4OSA_UInt32 mLastCTS; + sp<VideoEditorVideoEncoderSource> mEncoderSource; + OMXClient mClient; + sp<MediaSource> mEncoder; + OMX_COLOR_FORMATTYPE mEncoderColorFormat; + + uint32_t mNbInputFrames; + double mFirstInputCts; + double mLastInputCts; + uint32_t mNbOutputFrames; + int64_t mFirstOutputCts; + int64_t mLastOutputCts; + +} VideoEditorVideoEncoder_Context; + +/******************** + * TOOLS * + ********************/ + +M4OSA_ERR VideoEditorVideoEncoder_getDSI(M4ENCODER_Context pContext, + sp<MetaData> metaData) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoEncoder_Context* pEncoderContext = M4OSA_NULL; + status_t result = OK; + int32_t nbBuffer = 0; + int32_t stride = 0; + int32_t height = 0; + int32_t framerate = 0; + int32_t isCodecConfig = 0; + size_t size = 0; + uint32_t codecFlags = 0; + MediaBuffer* inputBuffer = NULL; + MediaBuffer* outputBuffer = NULL; + sp<VideoEditorVideoEncoderSource> encoderSource = NULL; + sp<MediaSource> encoder = NULL;; + OMXClient client; +
+ LOGV("VideoEditorVideoEncoder_getDSI begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != metaData.get(), M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorVideoEncoder_Context*)pContext; + VIDEOEDITOR_CHECK(CREATED == pEncoderContext->mState, M4ERR_STATE); + + // Create the encoder source + encoderSource = VideoEditorVideoEncoderSource::Create(); + VIDEOEDITOR_CHECK(NULL != encoderSource.get(), M4ERR_STATE); + + // Connect to the OMX client + result = client.connect(); + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + + // Create the OMX codec + // VIDEOEDITOR_FORCECODEC MUST be defined here + codecFlags |= OMXCodec::VIDEOEDITOR_FORCECODEC; + encoder = OMXCodec::Create(client.interface(), metaData, true, + encoderSource, NULL, codecFlags); + VIDEOEDITOR_CHECK(NULL != encoder.get(), M4ERR_STATE); + + /** + * Send fake frames and retrieve the DSI + */ + // Send a fake frame to the source + metaData->findInt32(kKeyStride, &stride); + metaData->findInt32(kKeyHeight, &height); + metaData->findInt32(kKeySampleRate, &framerate); + size = (size_t)(stride*height*3)/2; + inputBuffer = new MediaBuffer(size); + inputBuffer->meta_data()->setInt64(kKeyTime, 0); + nbBuffer = encoderSource->storeBuffer(inputBuffer); + encoderSource->storeBuffer(NULL); // Signal EOS + + // Call read once to get the DSI + result = encoder->start();; + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + result = encoder->read(&outputBuffer, NULL); + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + VIDEOEDITOR_CHECK(outputBuffer->meta_data()->findInt32( + kKeyIsCodecConfig, &isCodecConfig) && isCodecConfig, M4ERR_STATE); + + VIDEOEDITOR_CHECK(M4OSA_NULL == pEncoderContext->mHeader.pBuf, M4ERR_STATE); + if ( M4ENCODER_kH264 == pEncoderContext->mFormat ) { + // For H264, format the DSI + result = buildAVCCodecSpecificData( + (uint8_t**)(&(pEncoderContext->mHeader.pBuf)), + (size_t*)(&(pEncoderContext->mHeader.Size)), + (const uint8_t*)outputBuffer->data() + outputBuffer->range_offset(), + outputBuffer->range_length(), encoder->getFormat().get()); + outputBuffer->release(); + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + } else { + // For MPEG4, just copy the DSI + pEncoderContext->mHeader.Size = + (M4OSA_UInt32)outputBuffer->range_length(); + SAFE_MALLOC(pEncoderContext->mHeader.pBuf, M4OSA_Int8, + pEncoderContext->mHeader.Size, "Encoder header"); + M4OSA_memcpy(pEncoderContext->mHeader.pBuf, + (M4OSA_MemAddr8)(outputBuffer->data())+outputBuffer->range_offset(), + pEncoderContext->mHeader.Size); + outputBuffer->release(); + } + + result = encoder->stop(); + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + +cleanUp: + // Destroy the graph + if ( encoder != NULL ) { encoder.clear(); } + client.disconnect(); + if ( encoderSource != NULL ) { encoderSource.clear(); } + if ( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoEncoder_getDSI no error"); + } else { + LOGV("VideoEditorVideoEncoder_getDSI ERROR 0x%X", err); + } + LOGV("VideoEditorVideoEncoder_getDSI end"); + return err; +} +/******************** + * ENGINE INTERFACE * + ********************/ + +M4OSA_ERR VideoEditorVideoEncoder_cleanup(M4ENCODER_Context pContext) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoEncoder_Context* pEncoderContext = M4OSA_NULL; +
+ LOGV("VideoEditorVideoEncoder_cleanup begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorVideoEncoder_Context*)pContext; + VIDEOEDITOR_CHECK(CREATED == pEncoderContext->mState, M4ERR_STATE); + + // Release memory + SAFE_FREE(pEncoderContext->mHeader.pBuf); + SAFE_FREE(pEncoderContext); + pContext = M4OSA_NULL; + +cleanUp: + if ( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoEncoder_cleanup no error"); + } else { + LOGV("VideoEditorVideoEncoder_cleanup ERROR 0x%X", err); + }
+ LOGV("VideoEditorVideoEncoder_cleanup end"); + return err; +} + +M4OSA_ERR VideoEditorVideoEncoder_init(M4ENCODER_Format format, + M4ENCODER_Context* pContext, + M4WRITER_DataInterface* pWriterDataInterface, + M4VPP_apply_fct* pVPPfct, M4VPP_Context pVPPctxt, + M4OSA_Void* pExternalAPI, M4OSA_Void* pUserData) {
+ + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoEncoder_Context* pEncoderContext = M4OSA_NULL; +
+ LOGV("VideoEditorVideoEncoder_init begin: format %d", format); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pWriterDataInterface, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pVPPfct, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pVPPctxt, M4ERR_PARAMETER); + + // Context allocation & initialization + SAFE_MALLOC(pEncoderContext, VideoEditorVideoEncoder_Context, 1, + "VideoEditorVideoEncoder"); + pEncoderContext->mState = CREATED; + pEncoderContext->mFormat = format; + pEncoderContext->mWriterDataInterface = pWriterDataInterface; + pEncoderContext->mPreProcFunction = pVPPfct; + pEncoderContext->mPreProcContext = pVPPctxt; + + *pContext = pEncoderContext; + +cleanUp: + if ( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoEncoder_init no error"); + } else { + VideoEditorVideoEncoder_cleanup(pEncoderContext); + *pContext = M4OSA_NULL; + LOGV("VideoEditorVideoEncoder_init ERROR 0x%X", err); + }
+ LOGV("VideoEditorVideoEncoder_init end"); + return err; +} + +M4OSA_ERR VideoEditorVideoEncoder_init_H263(M4ENCODER_Context* pContext, + M4WRITER_DataInterface* pWriterDataInterface, M4VPP_apply_fct* pVPPfct, + M4VPP_Context pVPPctxt, M4OSA_Void* pExternalAPI, M4OSA_Void* pUserData) + {
+ + return VideoEditorVideoEncoder_init(M4ENCODER_kH263, pContext, + pWriterDataInterface, pVPPfct, pVPPctxt, pExternalAPI, pUserData); +} + + +M4OSA_ERR VideoEditorVideoEncoder_init_MPEG4(M4ENCODER_Context* pContext, + M4WRITER_DataInterface* pWriterDataInterface, M4VPP_apply_fct* pVPPfct, + M4VPP_Context pVPPctxt, M4OSA_Void* pExternalAPI, M4OSA_Void* pUserData) + {
+ + return VideoEditorVideoEncoder_init(M4ENCODER_kMPEG4, pContext, + pWriterDataInterface, pVPPfct, pVPPctxt, pExternalAPI, pUserData); +} + + +M4OSA_ERR VideoEditorVideoEncoder_init_H264(M4ENCODER_Context* pContext, + M4WRITER_DataInterface* pWriterDataInterface, M4VPP_apply_fct* pVPPfct, + M4VPP_Context pVPPctxt, M4OSA_Void* pExternalAPI, M4OSA_Void* pUserData) + {
+ + return VideoEditorVideoEncoder_init(M4ENCODER_kH264, pContext, + pWriterDataInterface, pVPPfct, pVPPctxt, pExternalAPI, pUserData); +} + +M4OSA_ERR VideoEditorVideoEncoder_close(M4ENCODER_Context pContext) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoEncoder_Context* pEncoderContext = M4OSA_NULL; +
+ LOGV("VideoEditorVideoEncoder_close begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorVideoEncoder_Context*)pContext; + VIDEOEDITOR_CHECK(OPENED == pEncoderContext->mState, M4ERR_STATE); + + // Release memory + SAFE_FREE(pEncoderContext->mCodecParams); + + // Destroy the graph + pEncoderContext->mEncoder.clear(); + pEncoderContext->mClient.disconnect(); + pEncoderContext->mEncoderSource.clear(); + + // Set the new state + pEncoderContext->mState = CREATED; + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoEncoder_close no error"); + } else { + LOGV("VideoEditorVideoEncoder_close ERROR 0x%X", err); + }
+ LOGV("VideoEditorVideoEncoder_close end"); + return err; +} + + +M4OSA_ERR VideoEditorVideoEncoder_open(M4ENCODER_Context pContext, + M4SYS_AccessUnit* pAU, M4OSA_Void* pParams) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoEncoder_Context* pEncoderContext = M4OSA_NULL; + M4ENCODER_Params* pCodecParams = M4OSA_NULL; + status_t result = OK; + sp<MetaData> encoderMetadata = NULL; + const char* mime = NULL; + int32_t iProfile = 0; + int32_t iFrameRate = 0; + uint32_t codecFlags = 0; +
+ LOGV(">>> VideoEditorVideoEncoder_open begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pAU, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pParams, M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorVideoEncoder_Context*)pContext; + pCodecParams = (M4ENCODER_Params*)pParams; + VIDEOEDITOR_CHECK(CREATED == pEncoderContext->mState, M4ERR_STATE); + + // Context initialization + pEncoderContext->mAccessUnit = pAU; + + // Allocate & initialize the encoding parameters + SAFE_MALLOC(pEncoderContext->mCodecParams, M4ENCODER_Params, 1, + "VideoEditorVideoEncoder"); + + + pEncoderContext->mCodecParams->InputFormat = pCodecParams->InputFormat; + pEncoderContext->mCodecParams->InputFrameWidth = + pCodecParams->InputFrameWidth; + pEncoderContext->mCodecParams->InputFrameHeight = + pCodecParams->InputFrameHeight; + pEncoderContext->mCodecParams->FrameWidth = pCodecParams->FrameWidth; + pEncoderContext->mCodecParams->FrameHeight = pCodecParams->FrameHeight; + pEncoderContext->mCodecParams->Bitrate = pCodecParams->Bitrate; + pEncoderContext->mCodecParams->FrameRate = pCodecParams->FrameRate; + pEncoderContext->mCodecParams->Format = pCodecParams->Format; + + // Check output format consistency and resolution + VIDEOEDITOR_CHECK( + pEncoderContext->mCodecParams->Format == pEncoderContext->mFormat, + M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(0 == pEncoderContext->mCodecParams->FrameWidth % 16, + M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(0 == pEncoderContext->mCodecParams->FrameHeight % 16, + M4ERR_PARAMETER); + + /** + * StageFright graph building + */ + + // Create the meta data for the encoder + encoderMetadata = new MetaData; + switch( pEncoderContext->mCodecParams->Format ) { + case M4ENCODER_kH263: + mime = MEDIA_MIMETYPE_VIDEO_H263; + iProfile = OMX_VIDEO_H263ProfileBaseline; + break; + case M4ENCODER_kMPEG4: + mime = MEDIA_MIMETYPE_VIDEO_MPEG4; + iProfile = OMX_VIDEO_MPEG4ProfileSimple; + break; + case M4ENCODER_kH264: + mime = MEDIA_MIMETYPE_VIDEO_AVC; + iProfile = OMX_VIDEO_AVCProfileBaseline; + break; + default: + VIDEOEDITOR_CHECK(!"VideoEncoder_open : incorrect input format", + M4ERR_PARAMETER); + break; + } + encoderMetadata->setCString(kKeyMIMEType, mime); + encoderMetadata->setInt32(kKeyVideoProfile, iProfile); + encoderMetadata->setInt32(kKeyWidth, + (int32_t)pEncoderContext->mCodecParams->FrameWidth); + encoderMetadata->setInt32(kKeyStride, + (int32_t)pEncoderContext->mCodecParams->FrameWidth); + encoderMetadata->setInt32(kKeyHeight, + (int32_t)pEncoderContext->mCodecParams->FrameHeight); + encoderMetadata->setInt32(kKeySliceHeight, + (int32_t)pEncoderContext->mCodecParams->FrameHeight); + + switch( pEncoderContext->mCodecParams->FrameRate ) { + case M4ENCODER_k5_FPS: iFrameRate = 5; break; + case M4ENCODER_k7_5_FPS: iFrameRate = 8; break; + case M4ENCODER_k10_FPS: iFrameRate = 10; break; + case M4ENCODER_k12_5_FPS: iFrameRate = 13; break; + case M4ENCODER_k15_FPS: iFrameRate = 15; break; + case M4ENCODER_k20_FPS: iFrameRate = 20; break; + case M4ENCODER_k25_FPS: iFrameRate = 25; break; + case M4ENCODER_k30_FPS: iFrameRate = 30; break; + case M4ENCODER_kVARIABLE_FPS: + iFrameRate = 30; + LOGI("Frame rate set to M4ENCODER_kVARIABLE_FPS: set to 30"); + break; + case M4ENCODER_kUSE_TIMESCALE: + iFrameRate = 30; + LOGI("Frame rate set to M4ENCODER_kUSE_TIMESCALE: set to 30"); + break; + + default: + VIDEOEDITOR_CHECK(!"VideoEncoder_open:incorrect framerate", + M4ERR_STATE); + break; + } + encoderMetadata->setInt32(kKeyFrameRate, iFrameRate); + encoderMetadata->setInt32(kKeyBitRate, + (int32_t)pEncoderContext->mCodecParams->Bitrate); + encoderMetadata->setInt32(kKeyIFramesInterval, 1); + + pEncoderContext->mEncoderColorFormat = VIDEOEDITOR_ENCODER_COLOR_FORMAT; + encoderMetadata->setInt32(kKeyColorFormat, + pEncoderContext->mEncoderColorFormat); + +#ifdef VIDEOEDITOR_ENCODER_GET_DSI_AT_CREATION + // Get the encoder DSI + err = VideoEditorVideoEncoder_getDSI(pEncoderContext, encoderMetadata); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); +#endif /* VIDEOEDITOR_ENCODER_GET_DSI_AT_CREATION */ + + // Create the encoder source + pEncoderContext->mEncoderSource = VideoEditorVideoEncoderSource::Create(); + VIDEOEDITOR_CHECK( + NULL != pEncoderContext->mEncoderSource.get(), M4ERR_STATE); + + // Connect to the OMX client + result = pEncoderContext->mClient.connect(); + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + + // Create the OMX codec +#ifdef VIDEOEDITOR_FORCECODEC + codecFlags |= OMXCodec::VIDEOEDITOR_FORCECODEC; +#endif /* VIDEOEDITOR_FORCECODEC */ + pEncoderContext->mEncoder = OMXCodec::Create( + pEncoderContext->mClient.interface(), encoderMetadata, true, + pEncoderContext->mEncoderSource, NULL, codecFlags); + VIDEOEDITOR_CHECK(NULL != pEncoderContext->mEncoder.get(), M4ERR_STATE); + LOGV("VideoEditorVideoEncoder_open : DONE"); + + // Set the new state + pEncoderContext->mState = OPENED; + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoEncoder_open no error"); + } else { + VideoEditorVideoEncoder_close(pEncoderContext); + LOGV("VideoEditorVideoEncoder_open ERROR 0x%X", err); + }
+ LOGV("VideoEditorVideoEncoder_open end"); + return err; +} + +M4OSA_ERR VideoEditorVideoEncoder_processOutputBuffer( + M4ENCODER_Context pContext, MediaBuffer* buffer); +M4OSA_ERR VideoEditorVideoEncoder_processInputBuffer( + M4ENCODER_Context pContext, M4OSA_Double Cts, + M4OSA_Bool bReachedEOS) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoEncoder_Context* pEncoderContext = M4OSA_NULL; + M4VIFI_ImagePlane pOutPlane[3]; + MediaBuffer* buffer = NULL; + int32_t nbBuffer = 0; +
+ LOGV("VideoEditorVideoEncoder_processInputBuffer begin: cts %f", Cts); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorVideoEncoder_Context*)pContext; + pOutPlane[0].pac_data = M4OSA_NULL; + pOutPlane[1].pac_data = M4OSA_NULL; + pOutPlane[2].pac_data = M4OSA_NULL; + + if ( M4OSA_FALSE == bReachedEOS ) { + M4OSA_UInt32 sizeY = pEncoderContext->mCodecParams->FrameWidth * + pEncoderContext->mCodecParams->FrameHeight; + M4OSA_UInt32 sizeU = sizeY >> 2; + M4OSA_UInt32 size = sizeY + 2*sizeU; + M4OSA_UInt8* pData = M4OSA_NULL; + buffer = new MediaBuffer((size_t)size); + pData = (M4OSA_UInt8*)buffer->data() + buffer->range_offset(); + + // Prepare the output image for pre-processing + pOutPlane[0].u_width = pEncoderContext->mCodecParams->FrameWidth; + pOutPlane[0].u_height = pEncoderContext->mCodecParams->FrameHeight; + pOutPlane[0].u_topleft = 0; + pOutPlane[0].u_stride = pOutPlane[0].u_width; + pOutPlane[1].u_width = pOutPlane[0].u_width/2; + pOutPlane[1].u_height = pOutPlane[0].u_height/2; + pOutPlane[1].u_topleft = 0; + pOutPlane[1].u_stride = pOutPlane[0].u_stride/2; + pOutPlane[2].u_width = pOutPlane[1].u_width; + pOutPlane[2].u_height = pOutPlane[1].u_height; + pOutPlane[2].u_topleft = 0; + pOutPlane[2].u_stride = pOutPlane[1].u_stride; + + switch( pEncoderContext->mEncoderColorFormat ) { + case OMX_COLOR_FormatYUV420Planar: + pOutPlane[0].pac_data = pData; + pOutPlane[1].pac_data = pData + sizeY; + pOutPlane[2].pac_data = pData + sizeY + sizeU; + break; + case OMX_COLOR_FormatYUV420SemiPlanar: + pOutPlane[0].pac_data = pData; + SAFE_MALLOC(pOutPlane[1].pac_data, M4VIFI_UInt8, + pOutPlane[1].u_height*pOutPlane[1].u_stride,"OutputPlaneU"); + SAFE_MALLOC(pOutPlane[2].pac_data, M4VIFI_UInt8, + pOutPlane[2].u_height*pOutPlane[2].u_stride,"OutputPlaneV"); + break; + default: + LOGV("VideoEditorVideoEncoder_processInputBuffer : unsupported " + "color format 0x%X", pEncoderContext->mEncoderColorFormat); + VIDEOEDITOR_CHECK(M4OSA_FALSE, M4ERR_PARAMETER); + break; + } + + // Apply pre-processing + err = pEncoderContext->mPreProcFunction( + pEncoderContext->mPreProcContext, M4OSA_NULL, pOutPlane); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + + // Convert to MediaBuffer format if necessary + if( OMX_COLOR_FormatYUV420SemiPlanar == \ + pEncoderContext->mEncoderColorFormat ) { + M4OSA_UInt8* pTmpData = M4OSA_NULL; + pTmpData = pData + sizeY; + // Highly unoptimized copy... + for( M4OSA_UInt32 i=0; i<sizeU; i++ ) { + *pTmpData = pOutPlane[2].pac_data[i]; pTmpData++; + *pTmpData = pOutPlane[1].pac_data[i]; pTmpData++; + } + } + + // Set the metadata + buffer->meta_data()->setInt64(kKeyTime, (int64_t)(Cts*1000)); + } + + // Push the buffer to the source, a NULL buffer, notifies the source of EOS + nbBuffer = pEncoderContext->mEncoderSource->storeBuffer(buffer); + if ( VIDEOEDITOR_MIN_BUFFER_NB > nbBuffer ) { + LOGV("VideoEncoder_processInputBuffer not enough source buffer" + "%d", nbBuffer); + err = M4WAR_SF_LOW_BUFFER; + } + +cleanUp: + if ( OMX_COLOR_FormatYUV420SemiPlanar == \ + pEncoderContext->mEncoderColorFormat ) { + // Y plane has not been allocated + if ( pOutPlane[1].pac_data ) {
+ SAFE_FREE(pOutPlane[1].pac_data);
+ } + if ( pOutPlane[2].pac_data ) {
+ SAFE_FREE(pOutPlane[2].pac_data);
+ } + } + if ( (M4NO_ERROR == err) || (M4WAR_SF_LOW_BUFFER == err) ) { + LOGV("VideoEditorVideoEncoder_processInputBuffer error 0x%X", err); + } else { + if( NULL != buffer ) { + buffer->release(); + } + LOGV("VideoEditorVideoEncoder_processInputBuffer ERROR 0x%X", err); + }
+ LOGV("VideoEditorVideoEncoder_processInputBuffer end"); + return err; +} + +M4OSA_ERR VideoEditorVideoEncoder_processOutputBuffer( + M4ENCODER_Context pContext, MediaBuffer* buffer) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoEncoder_Context* pEncoderContext = M4OSA_NULL; + M4OSA_UInt32 Cts = 0; + int32_t i32Tmp = 0; + int64_t i64Tmp = 0; + status_t result = OK; +
+ LOGV("VideoEditorVideoEncoder_processOutputBuffer begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != buffer, M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorVideoEncoder_Context*)pContext; + + // Process the returned AU + if ( 0 == buffer->range_length() ) { + // Encoder has no data yet, nothing unusual + LOGV("VideoEditorVideoEncoder_processOutputBuffer : buffer is empty"); + goto cleanUp; + } + VIDEOEDITOR_CHECK(0 == ((M4OSA_UInt32)buffer->data())%4, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(buffer->meta_data().get(), M4ERR_PARAMETER); + if ( buffer->meta_data()->findInt32(kKeyIsCodecConfig, &i32Tmp) && i32Tmp ){ +#if 1 + { // Display the DSI + LOGV("VideoEditorVideoEncoder_processOutputBuffer DSI %d", + buffer->range_length()); + uint8_t* tmp = (uint8_t*)(buffer->data()); + for( uint32_t i=0; i<buffer->range_length(); i++ ) { + LOGV("DSI [%d] %.2X", i, tmp[i]); + } + } +#endif + +#ifndef VIDEOEDITOR_ENCODER_GET_DSI_AT_CREATION + VIDEOEDITOR_CHECK(M4OSA_NULL == pEncoderContext->mHeader.pBuf, + M4ERR_STATE); + if ( M4ENCODER_kH264 == pEncoderContext->mFormat ) { + result = buildAVCCodecSpecificData( + (uint8_t**)(&(pEncoderContext->mHeader.pBuf)), + (size_t*)(&(pEncoderContext->mHeader.Size)), + (const uint8_t *)buffer->data() + buffer->range_offset(), + buffer->range_length(), + pEncoderContext->mEncoder->getFormat().get()); + } else { + pEncoderContext->mHeader.Size = + (M4OSA_UInt32)buffer->range_length(); + SAFE_MALLOC(pEncoderContext->mHeader.pBuf, M4OSA_Int8, + pEncoderContext->mHeader.Size, "Encoder header"); + M4OSA_memcpy(pEncoderContext->mHeader.pBuf, + (M4OSA_MemAddr8)(buffer->data())+buffer->range_offset(), + pEncoderContext->mHeader.Size); + } +#endif /* VIDEOEDITOR_ENCODER_GET_DSI_AT_CREATION */ + } else { + // Check the CTS + VIDEOEDITOR_CHECK(buffer->meta_data()->findInt64(kKeyTime, &i64Tmp), + M4ERR_STATE); + + pEncoderContext->mNbOutputFrames++; + if ( 0 > pEncoderContext->mFirstOutputCts ) {
+ pEncoderContext->mFirstOutputCts = i64Tmp;
+ } + pEncoderContext->mLastOutputCts = i64Tmp; + + Cts = (M4OSA_Int32)(i64Tmp/1000); + LOGV("[TS_CHECK] VI/ENC WRITE frame %d @ %lld -> %d (last %d)", + pEncoderContext->mNbOutputFrames, i64Tmp, Cts, + pEncoderContext->mLastCTS); + if ( Cts < pEncoderContext->mLastCTS ) { + LOGV("VideoEncoder_processOutputBuffer WARNING : Cts is going " + "backwards %d < %d", Cts, pEncoderContext->mLastCTS); + goto cleanUp; + } + LOGV("VideoEditorVideoEncoder_processOutputBuffer : %d %d", + Cts, pEncoderContext->mLastCTS); + + // Retrieve the AU container + err = pEncoderContext->mWriterDataInterface->pStartAU( + pEncoderContext->mWriterDataInterface->pWriterContext, + pEncoderContext->mAccessUnit->stream->streamID, + pEncoderContext->mAccessUnit); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + + // Format the AU + VIDEOEDITOR_CHECK( + buffer->range_length() <= pEncoderContext->mAccessUnit->size, + M4ERR_PARAMETER); + // Remove H264 AU start code + if ( M4ENCODER_kH264 == pEncoderContext->mFormat ) { + if (!memcmp((const uint8_t *)buffer->data() + \ + buffer->range_offset(), "\x00\x00\x00\x01", 4) ) { + buffer->set_range(buffer->range_offset() + 4, + buffer->range_length() - 4); + } + } + + if ( (M4ENCODER_kH264 == pEncoderContext->mFormat) && + (M4OSA_NULL != pEncoderContext->mH264NALUPostProcessFct) ) { + // H264 trimming case, NALU post processing is needed + M4OSA_Int32 outputSize = pEncoderContext->mAccessUnit->size; + err = pEncoderContext->mH264NALUPostProcessFct( + pEncoderContext->mH264NALUPostProcessCtx, + (M4OSA_UInt8*)buffer->data()+buffer->range_offset(), + buffer->range_length(), + (M4OSA_UInt8*)pEncoderContext->mAccessUnit->dataAddress, + &outputSize); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + pEncoderContext->mAccessUnit->size = (M4OSA_UInt32)outputSize; + } else { + // The AU can just be copied + M4OSA_memcpy((M4OSA_MemAddr8)pEncoderContext->mAccessUnit->\ + dataAddress, (M4OSA_MemAddr8)(buffer->data())+buffer->\ + range_offset(), buffer->range_length()); + pEncoderContext->mAccessUnit->size = + (M4OSA_UInt32)buffer->range_length(); + } + + if ( buffer->meta_data()->findInt32(kKeyIsSyncFrame,&i32Tmp) && i32Tmp){ + pEncoderContext->mAccessUnit->attribute = AU_RAP; + } else { + pEncoderContext->mAccessUnit->attribute = AU_B_Frame; + } + pEncoderContext->mLastCTS = Cts; + pEncoderContext->mAccessUnit->CTS = Cts; + pEncoderContext->mAccessUnit->DTS = Cts; + + LOGV("VideoEditorVideoEncoder_processOutputBuffer: AU @ 0x%X 0x%X %d %d",
+ pEncoderContext->mAccessUnit->dataAddress, + *pEncoderContext->mAccessUnit->dataAddress,
+ pEncoderContext->mAccessUnit->size, + pEncoderContext->mAccessUnit->CTS); + + // Write the AU + err = pEncoderContext->mWriterDataInterface->pProcessAU( + pEncoderContext->mWriterDataInterface->pWriterContext, + pEncoderContext->mAccessUnit->stream->streamID, + pEncoderContext->mAccessUnit); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + } + +cleanUp: + buffer->release(); + if( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoEncoder_processOutputBuffer no error"); + } else { + SAFE_FREE(pEncoderContext->mHeader.pBuf); + pEncoderContext->mHeader.Size = 0; + LOGV("VideoEditorVideoEncoder_processOutputBuffer ERROR 0x%X", err); + }
+ LOGV("VideoEditorVideoEncoder_processOutputBuffer end"); + return err; +} + +M4OSA_ERR VideoEditorVideoEncoder_encode(M4ENCODER_Context pContext, + M4VIFI_ImagePlane* pInPlane, M4OSA_Double Cts, + M4ENCODER_FrameMode FrameMode) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoEncoder_Context* pEncoderContext = M4OSA_NULL; + status_t result = OK; + MediaBuffer* outputBuffer = NULL; +
+ LOGV("VideoEditorVideoEncoder_encode 0x%X %f %d", pInPlane, Cts, FrameMode); + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorVideoEncoder_Context*)pContext; + if ( STARTED == pEncoderContext->mState ) {
+ pEncoderContext->mState = BUFFERING;
+ } + VIDEOEDITOR_CHECK( + (BUFFERING | READING) & pEncoderContext->mState, M4ERR_STATE); + + pEncoderContext->mNbInputFrames++; + if ( 0 > pEncoderContext->mFirstInputCts ) {
+ pEncoderContext->mFirstInputCts = Cts;
+ } + pEncoderContext->mLastInputCts = Cts; + + LOGV("VideoEditorVideoEncoder_encode 0x%X %d %f (%d)", pInPlane, FrameMode, + Cts, pEncoderContext->mLastCTS); + + // Push the input buffer to the encoder source + err = VideoEditorVideoEncoder_processInputBuffer(pEncoderContext, Cts, + M4OSA_FALSE); + VIDEOEDITOR_CHECK((M4NO_ERROR == err) || (M4WAR_SF_LOW_BUFFER == err), err); + + // Notify the source in case of EOS + if ( M4ENCODER_kLastFrame == FrameMode ) { + err = VideoEditorVideoEncoder_processInputBuffer( + pEncoderContext, 0, M4OSA_TRUE); + VIDEOEDITOR_CHECK((M4NO_ERROR == err) || (M4WAR_SF_LOW_BUFFER == err), + err); + } + + if ( BUFFERING == pEncoderContext->mState ) { + if ( M4WAR_SF_LOW_BUFFER == err ) { + // Insufficient prefetch, do not encode + err = M4NO_ERROR; + goto cleanUp; + } else { + // Prefetch is complete, start reading + pEncoderContext->mState = READING; + } + } + // Read + result = pEncoderContext->mEncoder->read(&outputBuffer, NULL); + if( OK != result ) {
+ LOGV("VideoEditorVideoEncoder_encode: encoder returns 0x%X", result);
+ }
+ + if( ERROR_END_OF_STREAM == result ) { + if( outputBuffer != NULL ) {
+ LOGV("VideoEditorVideoEncoder_encode : EOS w/ buffer");
+ } + VIDEOEDITOR_CHECK(0 == VIDEOEDITOR_MIN_BUFFER_NB, M4ERR_STATE); + // No output provided here, just exit + goto cleanUp; + } + VIDEOEDITOR_CHECK((OK == result) || (ERROR_END_OF_STREAM == result), + M4ERR_STATE); + + // Provide the encoded AU to the writer + err = VideoEditorVideoEncoder_processOutputBuffer(pEncoderContext, + outputBuffer); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoEncoder_encode no error"); + } else { + LOGV("VideoEditorVideoEncoder_encode ERROR 0x%X", err); + }
+ LOGV("VideoEditorVideoEncoder_encode end"); + return err; +} + +M4OSA_ERR VideoEditorVideoEncoder_start(M4ENCODER_Context pContext) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoEncoder_Context* pEncoderContext = M4OSA_NULL; + status_t result = OK; +
+ LOGV("VideoEditorVideoEncoder_start begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorVideoEncoder_Context*)pContext; + VIDEOEDITOR_CHECK(OPENED == pEncoderContext->mState, M4ERR_STATE); + + pEncoderContext->mNbInputFrames = 0; + pEncoderContext->mFirstInputCts = -1.0; + pEncoderContext->mLastInputCts = -1.0; + pEncoderContext->mNbOutputFrames = 0; + pEncoderContext->mFirstOutputCts = -1; + pEncoderContext->mLastOutputCts = -1; + + result = pEncoderContext->mEncoder->start(); + VIDEOEDITOR_CHECK(OK == result, M4ERR_STATE); + + // Set the new state + pEncoderContext->mState = STARTED; + +cleanUp: + if ( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoEncoder_start no error"); + } else { + LOGV("VideoEditorVideoEncoder_start ERROR 0x%X", err); + }
+ LOGV("VideoEditorVideoEncoder_start end"); + return err; +} + +M4OSA_ERR VideoEditorVideoEncoder_stop(M4ENCODER_Context pContext) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoEncoder_Context* pEncoderContext = M4OSA_NULL; + MediaBuffer* outputBuffer = NULL; + status_t result = OK; +
+ LOGV("VideoEditorVideoEncoder_stop begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + pEncoderContext = (VideoEditorVideoEncoder_Context*)pContext; + + // Process the remaining buffers if necessary + if ( (BUFFERING | READING) & pEncoderContext->mState ) { + // Send EOS again just in case + err = VideoEditorVideoEncoder_processInputBuffer(pEncoderContext, 0, + M4OSA_TRUE); + VIDEOEDITOR_CHECK((M4NO_ERROR == err) || (M4WAR_SF_LOW_BUFFER == err), + err); + while( OK == result ) { + result = pEncoderContext->mEncoder->read(&outputBuffer, NULL); + if ( OK == result ) { + err = VideoEditorVideoEncoder_processOutputBuffer( + pEncoderContext, outputBuffer); + VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); + } + } + pEncoderContext->mState = STARTED; + } + + // Stop the graph module if necessary + if ( STARTED == pEncoderContext->mState ) { + pEncoderContext->mEncoder->stop(); + pEncoderContext->mState = OPENED; + } + + if ( pEncoderContext->mNbInputFrames != pEncoderContext->mNbInputFrames ) { + LOGV("VideoEditorVideoEncoder_stop: some frames were not encoded %d %d", + pEncoderContext->mNbInputFrames, pEncoderContext->mNbInputFrames); + } + +cleanUp: + if ( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoEncoder_stop no error"); + } else { + LOGV("VideoEditorVideoEncoder_stop ERROR 0x%X", err); + }
+ LOGV("VideoEditorVideoEncoder_stop end"); + return err; +} + +M4OSA_ERR VideoEditorVideoEncoder_regulBitRate(M4ENCODER_Context pContext) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoEncoder_Context* pEncoderContext = M4OSA_NULL; +
+ LOGV("VideoEditorVideoEncoder_regulBitRate begin"); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + pEncoderContext = (VideoEditorVideoEncoder_Context*)pContext; + + LOGV("VideoEditorVideoEncoder_regulBitRate : THIS IS NOT IMPLEMENTED"); + +cleanUp: + if ( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoEncoder_regulBitRate no error"); + } else { + LOGV("VideoEditorVideoEncoder_regulBitRate ERROR 0x%X", err); + }
+ LOGV("VideoEditorVideoEncoder_regulBitRate end"); + return err; +} + +M4OSA_ERR VideoEditorVideoEncoder_setOption(M4ENCODER_Context pContext, + M4OSA_UInt32 optionID, M4OSA_DataOption optionValue) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoEncoder_Context* pEncoderContext = M4OSA_NULL; +
+ LOGV("VideoEditorVideoEncoder_setOption start optionID 0x%X", optionID); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + + pEncoderContext = (VideoEditorVideoEncoder_Context*)pContext; + + switch( optionID ) { + case M4ENCODER_kOptionID_SetH264ProcessNALUfctsPtr: + pEncoderContext->mH264NALUPostProcessFct = + (H264MCS_ProcessEncodedNALU_fct*)optionValue; + break; + case M4ENCODER_kOptionID_H264ProcessNALUContext: + pEncoderContext->mH264NALUPostProcessCtx = + (M4OSA_Context)optionValue; + break; + default: + LOGV("VideoEditorVideoEncoder_setOption: unsupported optionId 0x%X", + optionID); + VIDEOEDITOR_CHECK(M4OSA_FALSE, M4ERR_BAD_OPTION_ID); + break; + } + +cleanUp: + if ( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoEncoder_setOption no error"); + } else { + LOGV("VideoEditorVideoEncoder_setOption ERROR 0x%X", err); + }
+ LOGV("VideoEditorVideoEncoder_setOption end"); + return err; +} + +M4OSA_ERR VideoEditorVideoEncoder_getOption(M4ENCODER_Context pContext, + M4OSA_UInt32 optionID, M4OSA_DataOption optionValue) { + M4OSA_ERR err = M4NO_ERROR; + VideoEditorVideoEncoder_Context* pEncoderContext = M4OSA_NULL; +
+ LOGV("VideoEditorVideoEncoder_getOption begin optinId 0x%X", optionID); + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); + pEncoderContext = (VideoEditorVideoEncoder_Context*)pContext; + + switch( optionID ) { + case M4ENCODER_kOptionID_EncoderHeader: + VIDEOEDITOR_CHECK( + M4OSA_NULL != pEncoderContext->mHeader.pBuf, M4ERR_STATE); + *(M4ENCODER_Header**)optionValue = &(pEncoderContext->mHeader); + break; + default: + LOGV("VideoEditorVideoEncoder_getOption: unsupported optionId 0x%X", + optionID); + VIDEOEDITOR_CHECK(M4OSA_FALSE, M4ERR_BAD_OPTION_ID); + break; + } + +cleanUp: + if ( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoEncoder_getOption no error"); + } else { + LOGV("VideoEditorVideoEncoder_getOption ERROR 0x%X", err); + } + return err; +}
+ +M4OSA_ERR VideoEditorVideoEncoder_getInterface(M4ENCODER_Format format, + M4ENCODER_Format* pFormat, + M4ENCODER_GlobalInterface** pEncoderInterface, M4ENCODER_OpenMode mode){ + M4OSA_ERR err = M4NO_ERROR; + + // Input parameters check + VIDEOEDITOR_CHECK(M4OSA_NULL != pFormat, M4ERR_PARAMETER); + VIDEOEDITOR_CHECK(M4OSA_NULL != pEncoderInterface, M4ERR_PARAMETER); + + LOGV("VideoEditorVideoEncoder_getInterface begin 0x%x 0x%x %d", pFormat, + pEncoderInterface, mode); + + SAFE_MALLOC(*pEncoderInterface, M4ENCODER_GlobalInterface, 1, + "VideoEditorVideoEncoder"); + + *pFormat = format; + + switch( format ) { + case M4ENCODER_kH263:
+ {
+ (*pEncoderInterface)->pFctInit = + VideoEditorVideoEncoder_init_H263;
+ break;
+ } + case M4ENCODER_kMPEG4:
+ {
+ (*pEncoderInterface)->pFctInit = + VideoEditorVideoEncoder_init_MPEG4;
+ break;
+ } + case M4ENCODER_kH264:
+ {
+ (*pEncoderInterface)->pFctInit = + VideoEditorVideoEncoder_init_H264;
+ break;
+ } + default: + LOGV("VideoEditorVideoEncoder_getInterface : unsupported format %d", + format); + VIDEOEDITOR_CHECK(M4OSA_FALSE, M4ERR_PARAMETER); + break; + } + (*pEncoderInterface)->pFctOpen = VideoEditorVideoEncoder_open; + (*pEncoderInterface)->pFctStart = VideoEditorVideoEncoder_start; + (*pEncoderInterface)->pFctStop = VideoEditorVideoEncoder_stop; + (*pEncoderInterface)->pFctPause = M4OSA_NULL; + (*pEncoderInterface)->pFctResume = M4OSA_NULL; + (*pEncoderInterface)->pFctClose = VideoEditorVideoEncoder_close; + (*pEncoderInterface)->pFctCleanup = VideoEditorVideoEncoder_cleanup; + (*pEncoderInterface)->pFctRegulBitRate = + VideoEditorVideoEncoder_regulBitRate; + (*pEncoderInterface)->pFctEncode = VideoEditorVideoEncoder_encode; + (*pEncoderInterface)->pFctSetOption = VideoEditorVideoEncoder_setOption; + (*pEncoderInterface)->pFctGetOption = VideoEditorVideoEncoder_getOption; + +cleanUp: + if( M4NO_ERROR == err ) { + LOGV("VideoEditorVideoEncoder_getInterface no error"); + } else { + *pEncoderInterface = M4OSA_NULL; + LOGV("VideoEditorVideoEncoder_getInterface ERROR 0x%X", err); + } + return err; +} + +extern "C" { + +M4OSA_ERR VideoEditorVideoEncoder_getInterface_H263(M4ENCODER_Format* pFormat, + M4ENCODER_GlobalInterface** pEncoderInterface, M4ENCODER_OpenMode mode){ + return VideoEditorVideoEncoder_getInterface(M4ENCODER_kH263, pFormat, + pEncoderInterface, mode); +} + +M4OSA_ERR VideoEditorVideoEncoder_getInterface_MPEG4(M4ENCODER_Format* pFormat, + M4ENCODER_GlobalInterface** pEncoderInterface, M4ENCODER_OpenMode mode){ + return VideoEditorVideoEncoder_getInterface(M4ENCODER_kMPEG4, pFormat, + pEncoderInterface, mode); +} + +M4OSA_ERR VideoEditorVideoEncoder_getInterface_H264(M4ENCODER_Format* pFormat, + M4ENCODER_GlobalInterface** pEncoderInterface, M4ENCODER_OpenMode mode){ + return VideoEditorVideoEncoder_getInterface(M4ENCODER_kH264, pFormat, + pEncoderInterface, mode); + +} + +} // extern "C" + +} // namespace android |