| /* |
| * 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/foundation/ADebug.h> |
| #include <media/stagefright/MetaData.h> |
| #include <media/stagefright/MediaDefs.h> |
| /******************** |
| * DEFINITIONS * |
| ********************/ |
| #define MAX_DEC_BUFFERS 10 |
| |
| /******************** |
| * SOURCE CLASS * |
| ********************/ |
| using namespace android; |
| static M4OSA_ERR copyBufferToQueue( |
| VideoEditorVideoDecoder_Context* pDecShellContext, |
| MediaBuffer* pDecodedBuffer); |
| |
| 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; |
| |
| // Don't call me |
| VideoEditorVideoDecoderSource(const VideoEditorVideoDecoderSource &); |
| VideoEditorVideoDecoderSource &operator=( |
| const VideoEditorVideoDecoderSource &); |
| }; |
| |
| 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) { |
| |
| if (!mStarted) { |
| if (mFormat->findInt32(kKeyMaxInputSize, &mMaxAUSize) == false) { |
| ALOGE("Could not find kKeyMaxInputSize"); |
| return ERROR_MALFORMED; |
| } |
| |
| mGroup = new MediaBufferGroup; |
| if (mGroup == NULL) { |
| ALOGE("FATAL: memory limitation ! "); |
| return NO_MEMORY; |
| } |
| |
| mGroup->add_buffer(new MediaBuffer(mMaxAUSize)); |
| |
| mStarted = true; |
| } |
| return OK; |
| } |
| |
| status_t VideoEditorVideoDecoderSource::stop() { |
| if (mStarted) { |
| if (mBuffer != NULL) { |
| |
| // FIXME: |
| // Why do we need to check on the ref count? |
| int ref_count = mBuffer->refcount(); |
| ALOGV("MediaBuffer refcount is %d",ref_count); |
| for (int i = 0; i < ref_count; ++i) { |
| mBuffer->release(); |
| } |
| |
| mBuffer = NULL; |
| } |
| delete mGroup; |
| mGroup = NULL; |
| mStarted = false; |
| } |
| 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); |
| if (options != NULL) { |
| int64_t time_us; |
| MediaSource::ReadOptions::SeekMode mode; |
| options->getSeekTo(&time_us, &mode); |
| if (mode != MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC) { |
| ALOGE("Unexpected read options"); |
| return BAD_VALUE; |
| } |
| |
| M4OSA_ERR err; |
| M4OSA_Int32 rapTime = time_us / 1000; |
| |
| /*--- Retrieve the previous RAP time ---*/ |
| err = mpDecShellContext->m_pReaderGlobal->m_pFctGetPrevRapTime( |
| mpDecShellContext->m_pReader->m_readerContext, |
| (M4_StreamHandler*)mpDecShellContext->m_pVideoStreamhandler, |
| &rapTime); |
| |
| if (err == M4WAR_READER_INFORMATION_NOT_PRESENT) { |
| /* No RAP table, jump backward and predecode */ |
| rapTime -= 40000; |
| if(rapTime < 0) rapTime = 0; |
| } else if (err != OK) { |
| ALOGE("get rap time error = 0x%x\n", (uint32_t)err); |
| return UNKNOWN_ERROR; |
| } |
| |
| err = mpDecShellContext->m_pReaderGlobal->m_pFctJump( |
| mpDecShellContext->m_pReader->m_readerContext, |
| (M4_StreamHandler*)mpDecShellContext->m_pVideoStreamhandler, |
| &rapTime); |
| |
| if (err != OK) { |
| ALOGE("jump err = 0x%x\n", (uint32_t)err); |
| return BAD_VALUE; |
| } |
| } |
| |
| *buffer_out = NULL; |
| |
| M4OSA_ERR lerr = mGroup->acquire_buffer(&mBuffer); |
| if (lerr != OK) { |
| return lerr; |
| } |
| mBuffer->meta_data()->clear(); // clear all the meta data |
| |
| if (mStarted) { |
| //getNext AU from reader. |
| M4_AccessUnit* pAccessUnit = mpDecShellContext->m_pNextAccessUnitToDecode; |
| lerr = mpDecShellContext->m_pReader->m_pFctGetNextAu( |
| mpDecShellContext->m_pReader->m_readerContext, |
| (M4_StreamHandler*)mpDecShellContext->m_pVideoStreamhandler, |
| pAccessUnit); |
| if (lerr == M4WAR_NO_DATA_YET || lerr == M4WAR_NO_MORE_AU) { |
| *buffer_out = NULL; |
| return ERROR_END_OF_STREAM; |
| } |
| |
| //copy the reader AU buffer to mBuffer |
| M4OSA_UInt32 lSize = (pAccessUnit->m_size > (M4OSA_UInt32)mMaxAUSize)\ |
| ? (M4OSA_UInt32)mMaxAUSize : pAccessUnit->m_size; |
| memcpy((void *)mBuffer->data(),(void *)pAccessUnit->m_dataAddress, |
| lSize); |
| |
| mBuffer->set_range(0, lSize); |
| int64_t frameTimeUs = (int64_t) (pAccessUnit->m_CTS * 1000); |
| mBuffer->meta_data()->setInt64(kKeyTime, frameTimeUs); |
| |
| // 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; |
| } |
| return OK; |
| } |
| |
| 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((void *)outy, (void *)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; |
| } |
| void logSupportDecodersAndCapabilities(M4DECODER_VideoDecoders* decoders) { |
| VideoDecoder *pDecoder; |
| VideoComponentCapabilities *pOmxComponents = NULL; |
| VideoProfileLevel *pProfileLevel = NULL; |
| pDecoder = decoders->decoder; |
| for (size_t i = 0; i< decoders->decoderNumber; i++) { |
| ALOGV("Supported Codec[%d] :%d", i, pDecoder->codec); |
| pOmxComponents = pDecoder->component; |
| for(size_t j = 0; j < pDecoder->componentNumber; j++) { |
| pProfileLevel = pOmxComponents->profileLevel; |
| ALOGV("-->component %d", j); |
| for(size_t k = 0; k < pOmxComponents->profileNumber; k++) { |
| ALOGV("-->profile:%ld maxLevel:%ld", pProfileLevel->mProfile, |
| pProfileLevel->mLevel); |
| pProfileLevel++; |
| } |
| pOmxComponents++; |
| } |
| pDecoder++; |
| } |
| } |
| |
| M4OSA_ERR queryVideoDecoderCapabilities |
| (M4DECODER_VideoDecoders** decoders) { |
| M4OSA_ERR err = M4NO_ERROR; |
| const char *kMimeTypes[] = { |
| MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4, |
| MEDIA_MIMETYPE_VIDEO_H263 |
| }; |
| |
| int32_t supportFormats = sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); |
| M4DECODER_VideoDecoders *pDecoders; |
| VideoDecoder *pDecoder; |
| VideoComponentCapabilities *pOmxComponents = NULL; |
| VideoProfileLevel *pProfileLevel = NULL; |
| OMXClient client; |
| status_t status = OK; |
| SAFE_MALLOC(pDecoders, M4DECODER_VideoDecoders, 1, "VideoDecoders"); |
| SAFE_MALLOC(pDecoder, VideoDecoder, supportFormats, |
| "VideoDecoder"); |
| pDecoders->decoder = pDecoder; |
| |
| pDecoders->decoderNumber= supportFormats; |
| status = client.connect(); |
| CHECK(status == OK); |
| for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]); |
| ++k) { |
| Vector<CodecCapabilities> results; |
| CHECK_EQ(QueryCodecs(client.interface(), kMimeTypes[k], |
| true, // queryDecoders |
| &results), (status_t)OK); |
| |
| if (results.size()) { |
| SAFE_MALLOC(pOmxComponents, VideoComponentCapabilities, |
| results.size(), "VideoComponentCapabilities"); |
| ALOGV("K=%d",k); |
| pDecoder->component = pOmxComponents; |
| pDecoder->componentNumber = results.size(); |
| } |
| |
| for (size_t i = 0; i < results.size(); ++i) { |
| ALOGV(" decoder '%s' supports ", |
| results[i].mComponentName.string()); |
| |
| if (results[i].mProfileLevels.size() == 0) { |
| ALOGV("NOTHING.\n"); |
| continue; |
| } |
| |
| #if 0 |
| // FIXME: |
| // We should ignore the software codecs and make IsSoftwareCodec() |
| // part of pubic API from OMXCodec.cpp |
| if (IsSoftwareCodec(results[i].mComponentName.string())) { |
| ALOGV("Ignore software codec %s", results[i].mComponentName.string()); |
| continue; |
| } |
| #endif |
| |
| // Count the supported profiles |
| int32_t profileNumber = 0; |
| int32_t profile = -1; |
| for (size_t j = 0; j < results[i].mProfileLevels.size(); ++j) { |
| const CodecProfileLevel &profileLevel = |
| results[i].mProfileLevels[j]; |
| // FIXME: assume that the profiles are ordered |
| if (profileLevel.mProfile != profile) { |
| profile = profileLevel.mProfile; |
| profileNumber++; |
| } |
| } |
| SAFE_MALLOC(pProfileLevel, VideoProfileLevel, |
| profileNumber, "VideoProfileLevel"); |
| pOmxComponents->profileLevel = pProfileLevel; |
| pOmxComponents->profileNumber = profileNumber; |
| |
| // Get the max Level for each profile. |
| int32_t maxLevel = -1; |
| profile = -1; |
| profileNumber = 0; |
| for (size_t j = 0; j < results[i].mProfileLevels.size(); ++j) { |
| const CodecProfileLevel &profileLevel = |
| results[i].mProfileLevels[j]; |
| if (profile == -1 && maxLevel == -1) { |
| profile = profileLevel.mProfile; |
| maxLevel = profileLevel.mLevel; |
| pProfileLevel->mProfile = profile; |
| pProfileLevel->mLevel = maxLevel; |
| ALOGV("%d profile: %ld, max level: %ld", |
| __LINE__, pProfileLevel->mProfile, pProfileLevel->mLevel); |
| } |
| if (profileLevel.mProfile != profile) { |
| profile = profileLevel.mProfile; |
| maxLevel = profileLevel.mLevel; |
| profileNumber++; |
| pProfileLevel++; |
| pProfileLevel->mProfile = profile; |
| pProfileLevel->mLevel = maxLevel; |
| ALOGV("%d profile: %ld, max level: %ld", |
| __LINE__, pProfileLevel->mProfile, pProfileLevel->mLevel); |
| } else if (profileLevel.mLevel > maxLevel) { |
| maxLevel = profileLevel.mLevel; |
| pProfileLevel->mLevel = maxLevel; |
| ALOGV("%d profile: %ld, max level: %ld", |
| __LINE__, pProfileLevel->mProfile, pProfileLevel->mLevel); |
| } |
| |
| } |
| pOmxComponents++; |
| } |
| if (!strcmp(MEDIA_MIMETYPE_VIDEO_AVC, kMimeTypes[k])) |
| pDecoder->codec = M4DA_StreamTypeVideoMpeg4Avc; |
| if (!strcmp(MEDIA_MIMETYPE_VIDEO_MPEG4, kMimeTypes[k])) |
| pDecoder->codec = M4DA_StreamTypeVideoMpeg4; |
| if (!strcmp(MEDIA_MIMETYPE_VIDEO_H263, kMimeTypes[k])) |
| pDecoder->codec = M4DA_StreamTypeVideoH263; |
| |
| pDecoder++; |
| } |
| |
| logSupportDecodersAndCapabilities(pDecoders); |
| *decoders = pDecoders; |
| cleanUp: |
| 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; |
| int32_t vWidth, vHeight; |
| int32_t cropLeft, cropTop, cropRight, cropBottom; |
| |
| VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); |
| VIDEOEDITOR_CHECK(M4OSA_NULL != meta, M4ERR_PARAMETER); |
| |
| ALOGV("VideoEditorVideoDecoder_configureFromMetadata begin"); |
| |
| pDecShellContext = (VideoEditorVideoDecoder_Context*)pContext; |
| |
| success = meta->findInt32(kKeyWidth, &vWidth); |
| VIDEOEDITOR_CHECK(TRUE == success, M4ERR_PARAMETER); |
| success = meta->findInt32(kKeyHeight, &vHeight); |
| VIDEOEDITOR_CHECK(TRUE == success, M4ERR_PARAMETER); |
| |
| ALOGV("vWidth = %d, vHeight = %d", vWidth, vHeight); |
| |
| pDecShellContext->mGivenWidth = vWidth; |
| pDecShellContext->mGivenHeight = vHeight; |
| |
| if (!meta->findRect( |
| kKeyCropRect, &cropLeft, &cropTop, &cropRight, &cropBottom)) { |
| |
| cropLeft = cropTop = 0; |
| cropRight = vWidth - 1; |
| cropBottom = vHeight - 1; |
| |
| ALOGV("got dimensions only %d x %d", width, height); |
| } else { |
| ALOGV("got crop rect %d, %d, %d, %d", |
| cropLeft, cropTop, cropRight, cropBottom); |
| } |
| |
| pDecShellContext->mCropRect.left = cropLeft; |
| pDecShellContext->mCropRect.right = cropRight; |
| pDecShellContext->mCropRect.top = cropTop; |
| pDecShellContext->mCropRect.bottom = cropBottom; |
| |
| width = cropRight - cropLeft + 1; |
| height = cropBottom - cropTop + 1; |
| |
| ALOGV("VideoDecoder_configureFromMetadata : W=%d H=%d", width, height); |
| VIDEOEDITOR_CHECK((0 != width) && (0 != height), M4ERR_PARAMETER); |
| |
| 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; |
| } |
| ALOGV("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 ) { |
| ALOGV("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 + pDecShellContext->mGivenWidth * 2); |
| |
| VIDEOEDITOR_CHECK(M4NO_ERROR == err, err); |
| |
| cleanUp: |
| if( M4NO_ERROR == err ) { |
| ALOGV("VideoEditorVideoDecoder_configureFromMetadata no error"); |
| } else { |
| if( M4OSA_NULL != pDecShellContext->m_pDecBufferPool ) { |
| VIDEOEDITOR_BUFFER_freePool(pDecShellContext->m_pDecBufferPool); |
| pDecShellContext->m_pDecBufferPool = M4OSA_NULL; |
| } |
| ALOGV("VideoEditorVideoDecoder_configureFromMetadata ERROR 0x%X", err); |
| } |
| ALOGV("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 |
| ALOGV("VideoEditorVideoDecoder_destroy begin"); |
| VIDEOEDITOR_CHECK(M4OSA_NULL != pContext, M4ERR_PARAMETER); |
| |
| // Release the color converter |
| delete pDecShellContext->mI420ColorConverter; |
| |
| // Destroy the graph |
| if( pDecShellContext->mVideoDecoder != NULL ) { |
| ALOGV("### 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 ) { |
| ALOGV("VideoEditorVideoDecoder_destroy no error"); |
| } else { |
| ALOGV("VideoEditorVideoDecoder_destroy ERROR 0x%X", err); |
| } |
| ALOGV("VideoEditorVideoDecoder_destroy end"); |
| return err; |
| } |
| |
| M4OSA_ERR VideoEditorVideoDecoder_create(M4OSA_Context *pContext, |
| M4_StreamHandler *pStreamHandler, |
| M4READER_GlobalInterface *pReaderGlobalInterface, |
| 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; |
| int decoderOutput = OMX_COLOR_FormatYUV420Planar; |
| |
| ALOGV("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_pReaderGlobal = pReaderGlobalInterface; |
| 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; |
| pDecShellContext->m_pDecBufferPool = M4OSA_NULL; |
| |
| // Calculate the interval between two video frames. |
| CHECK(pDecShellContext->m_pVideoStreamhandler->m_averageFrameRate > 0); |
| pDecShellContext->mFrameIntervalMs = |
| 1000.0 / pDecShellContext->m_pVideoStreamhandler->m_averageFrameRate; |
| |
| /** |
| * 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); |
| |
| // Get the color converter |
| pDecShellContext->mI420ColorConverter = new I420ColorConverter; |
| if (pDecShellContext->mI420ColorConverter->isLoaded()) { |
| decoderOutput = pDecShellContext->mI420ColorConverter->getDecoderOutputFormat(); |
| } |
| |
| if (decoderOutput == OMX_COLOR_FormatYUV420Planar) { |
| delete pDecShellContext->mI420ColorConverter; |
| pDecShellContext->mI420ColorConverter = NULL; |
| } |
| |
| ALOGI("decoder output format = 0x%X\n", decoderOutput); |
| |
| // 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 ) { |
| ALOGV("VideoEditorVideoDecoder_create no error"); |
| } else { |
| VideoEditorVideoDecoder_destroy(pDecShellContext); |
| *pContext = M4OSA_NULL; |
| ALOGV("VideoEditorVideoDecoder_create ERROR 0x%X", err); |
| } |
| ALOGV("VideoEditorVideoDecoder_create : DONE"); |
| return err; |
| } |
| |
| M4OSA_ERR VideoEditorVideoSoftwareDecoder_create(M4OSA_Context *pContext, |
| M4_StreamHandler *pStreamHandler, |
| M4READER_GlobalInterface *pReaderGlobalInterface, |
| 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; |
| |
| ALOGV("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_pReaderGlobal = pReaderGlobalInterface; |
| 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; |
| pDecShellContext->m_pDecBufferPool = M4OSA_NULL; |
| |
| /** |
| * 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); |
| |
| ALOGI("Using software codecs only"); |
| // Create the decoder |
| pDecShellContext->mVideoDecoder = OMXCodec::Create( |
| pDecShellContext->mClient.interface(), |
| decoderMetadata, false, pDecShellContext->mReaderSource,NULL,OMXCodec::kSoftwareCodecsOnly); |
| 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 ) { |
| ALOGV("VideoEditorVideoDecoder_create no error"); |
| } else { |
| VideoEditorVideoDecoder_destroy(pDecShellContext); |
| *pContext = M4OSA_NULL; |
| ALOGV("VideoEditorVideoDecoder_create ERROR 0x%X", err); |
| } |
| ALOGV("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; |
| |
| ALOGV("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)); |
| ALOGV("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_MPEG4_kOptionID_DecoderConfigInfo: |
| if(pDecShellContext->mDecoderType == VIDEOEDITOR_kMpeg4VideoDec) { |
| (*(M4DECODER_MPEG4_DecoderConfigInfo*)pValue) = |
| pDecShellContext->m_Dci; |
| } |
| break; |
| default: |
| lerr = M4ERR_BAD_OPTION_ID; |
| break; |
| |
| } |
| |
| ALOGV("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; |
| |
| ALOGV("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; |
| } |
| |
| ALOGV("VideoEditorVideoDecoder_setOption: end with err = 0x%x", lerr); |
| return lerr; |
| } |
| |
| M4OSA_ERR VideoEditorVideoDecoder_decode(M4OSA_Context context, |
| M4_MediaTime* pTime, M4OSA_Bool bJump, M4OSA_UInt32 tolerance) { |
| M4OSA_ERR lerr = M4NO_ERROR; |
| VideoEditorVideoDecoder_Context* pDecShellContext = |
| (VideoEditorVideoDecoder_Context*) context; |
| int64_t lFrameTime; |
| MediaBuffer* pDecoderBuffer = NULL; |
| MediaBuffer* pNextBuffer = NULL; |
| status_t errStatus; |
| bool needSeek = bJump; |
| |
| ALOGV("VideoEditorVideoDecoder_decode begin"); |
| |
| if( M4OSA_TRUE == pDecShellContext->mReachedEOS ) { |
| // Do not call read(), it could lead to a freeze |
| ALOGV("VideoEditorVideoDecoder_decode : EOS already reached"); |
| lerr = M4WAR_NO_MORE_AU; |
| goto VIDEOEDITOR_VideoDecode_cleanUP; |
| } |
| if(pDecShellContext->m_lastDecodedCTS >= *pTime) { |
| ALOGV("VideoDecoder_decode: Already decoded up to this time CTS = %lf.", |
| pDecShellContext->m_lastDecodedCTS); |
| goto VIDEOEDITOR_VideoDecode_cleanUP; |
| } |
| if(M4OSA_TRUE == bJump) { |
| ALOGV("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 (pDecoderBuffer == NULL || pDecShellContext->m_lastDecodedCTS + tolerance < *pTime) { |
| ALOGV("VideoEditorVideoDecoder_decode, frameCTS = %lf, DecodeUpTo = %lf", |
| pDecShellContext->m_lastDecodedCTS, *pTime); |
| |
| // Read the buffer from the stagefright decoder |
| if (needSeek) { |
| MediaSource::ReadOptions options; |
| int64_t time_us = *pTime * 1000; |
| options.setSeekTo(time_us, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); |
| errStatus = pDecShellContext->mVideoDecoder->read(&pNextBuffer, &options); |
| needSeek = false; |
| } else { |
| errStatus = pDecShellContext->mVideoDecoder->read(&pNextBuffer); |
| } |
| |
| // Handle EOS and format change |
| if (errStatus == ERROR_END_OF_STREAM) { |
| ALOGV("End of stream reached, returning M4WAR_NO_MORE_AU "); |
| pDecShellContext->mReachedEOS = M4OSA_TRUE; |
| lerr = M4WAR_NO_MORE_AU; |
| // If we decoded a buffer before EOS, we still need to put it |
| // into the queue. |
| if (pDecoderBuffer && bJump) { |
| copyBufferToQueue(pDecShellContext, pDecoderBuffer); |
| } |
| goto VIDEOEDITOR_VideoDecode_cleanUP; |
| } else if (INFO_FORMAT_CHANGED == errStatus) { |
| ALOGV("VideoDecoder_decode : source returns INFO_FORMAT_CHANGED"); |
| lerr = VideoEditorVideoDecoder_configureFromMetadata( |
| pDecShellContext, |
| pDecShellContext->mVideoDecoder->getFormat().get()); |
| if( M4NO_ERROR != lerr ) { |
| ALOGV("!!! VideoEditorVideoDecoder_decode ERROR : " |
| "VideoDecoder_configureFromMetadata returns 0x%X", lerr); |
| break; |
| } |
| continue; |
| } else if (errStatus != OK) { |
| ALOGE("VideoEditorVideoDecoder_decode ERROR:0x%x(%d)", |
| errStatus,errStatus); |
| lerr = errStatus; |
| goto VIDEOEDITOR_VideoDecode_cleanUP; |
| } |
| |
| // The OMXCodec client should expect to receive 0-length buffers |
| // and drop the 0-length buffers. |
| if (pNextBuffer->range_length() == 0) { |
| pNextBuffer->release(); |
| continue; |
| } |
| |
| // Now we have a good next buffer, release the previous one. |
| if (pDecoderBuffer != NULL) { |
| pDecoderBuffer->release(); |
| pDecoderBuffer = NULL; |
| } |
| pDecoderBuffer = pNextBuffer; |
| |
| // Record the timestamp of last decoded buffer |
| pDecoderBuffer->meta_data()->findInt64(kKeyTime, &lFrameTime); |
| pDecShellContext->m_lastDecodedCTS = (M4_MediaTime)(lFrameTime/1000); |
| ALOGV("VideoEditorVideoDecoder_decode,decoded frametime = %lf,size = %d", |
| (M4_MediaTime)lFrameTime, pDecoderBuffer->size() ); |
| |
| /* |
| * We need to save a buffer if bJump == false to a queue. These |
| * buffers have a timestamp >= the target time, *pTime (for instance, |
| * the transition between two videos, or a trimming postion inside |
| * one video), since they are part of the transition clip or the |
| * trimmed video. |
| * |
| * If *pTime does not have the same value as any of the existing |
| * video frames, we would like to get the buffer right before *pTime |
| * and in the transcoding phrase, this video frame will be encoded |
| * as a key frame and becomes the first video frame for the transition or the |
| * trimmed video to be generated. This buffer must also be queued. |
| * |
| */ |
| int64_t targetTimeMs = |
| pDecShellContext->m_lastDecodedCTS + |
| pDecShellContext->mFrameIntervalMs + |
| tolerance; |
| if (!bJump || targetTimeMs > *pTime) { |
| lerr = copyBufferToQueue(pDecShellContext, pDecoderBuffer); |
| if (lerr != M4NO_ERROR) { |
| goto VIDEOEDITOR_VideoDecode_cleanUP; |
| } |
| } |
| } |
| |
| 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; |
| } |
| |
| ALOGV("VideoEditorVideoDecoder_decode: end with 0x%x", lerr); |
| return lerr; |
| } |
| |
| static M4OSA_ERR copyBufferToQueue( |
| VideoEditorVideoDecoder_Context* pDecShellContext, |
| MediaBuffer* pDecoderBuffer) { |
| |
| M4OSA_ERR lerr = M4NO_ERROR; |
| VIDEOEDITOR_BUFFER_Buffer* tmpDecBuffer; |
| |
| // Get a buffer from the queue |
| 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) return lerr; |
| |
| // Color convert or copy from the given MediaBuffer to our buffer |
| if (pDecShellContext->mI420ColorConverter) { |
| if (pDecShellContext->mI420ColorConverter->convertDecoderOutputToI420( |
| (uint8_t *)pDecoderBuffer->data(),// ?? + pDecoderBuffer->range_offset(), // decoderBits |
| pDecShellContext->mGivenWidth, // decoderWidth |
| pDecShellContext->mGivenHeight, // decoderHeight |
| pDecShellContext->mCropRect, // decoderRect |
| tmpDecBuffer->pData /* dstBits */) < 0) { |
| ALOGE("convertDecoderOutputToI420 failed"); |
| lerr = M4ERR_NOT_IMPLEMENTED; |
| } |
| } else if (pDecShellContext->decOuputColorFormat == OMX_COLOR_FormatYUV420Planar) { |
| int32_t width = pDecShellContext->m_pVideoStreamhandler->m_videoWidth; |
| int32_t height = pDecShellContext->m_pVideoStreamhandler->m_videoHeight; |
| int32_t yPlaneSize = width * height; |
| int32_t uvPlaneSize = width * height / 4; |
| int32_t offsetSrc = 0; |
| |
| if (( width == pDecShellContext->mGivenWidth ) && |
| ( height == pDecShellContext->mGivenHeight )) |
| { |
| M4OSA_MemAddr8 pTmpBuff = (M4OSA_MemAddr8)pDecoderBuffer->data() + pDecoderBuffer->range_offset(); |
| |
| memcpy((void *)tmpDecBuffer->pData, (void *)pTmpBuff, yPlaneSize); |
| |
| offsetSrc += pDecShellContext->mGivenWidth * pDecShellContext->mGivenHeight; |
| memcpy((void *)((M4OSA_MemAddr8)tmpDecBuffer->pData + yPlaneSize), |
| (void *)(pTmpBuff + offsetSrc), uvPlaneSize); |
| |
| offsetSrc += (pDecShellContext->mGivenWidth >> 1) * (pDecShellContext->mGivenHeight >> 1); |
| memcpy((void *)((M4OSA_MemAddr8)tmpDecBuffer->pData + yPlaneSize + uvPlaneSize), |
| (void *)(pTmpBuff + offsetSrc), uvPlaneSize); |
| } |
| else |
| { |
| M4OSA_MemAddr8 pTmpBuff = (M4OSA_MemAddr8)pDecoderBuffer->data() + pDecoderBuffer->range_offset(); |
| M4OSA_MemAddr8 pTmpBuffDst = (M4OSA_MemAddr8)tmpDecBuffer->pData; |
| int32_t index; |
| |
| for ( index = 0; index < height; index++) |
| { |
| memcpy((void *)pTmpBuffDst, (void *)pTmpBuff, width); |
| pTmpBuffDst += width; |
| pTmpBuff += pDecShellContext->mGivenWidth; |
| } |
| |
| pTmpBuff += (pDecShellContext->mGivenWidth * ( pDecShellContext->mGivenHeight - height)); |
| for ( index = 0; index < height >> 1; index++) |
| { |
| memcpy((void *)pTmpBuffDst, (void *)pTmpBuff, width >> 1); |
| pTmpBuffDst += width >> 1; |
| pTmpBuff += pDecShellContext->mGivenWidth >> 1; |
| } |
| |
| pTmpBuff += ((pDecShellContext->mGivenWidth * (pDecShellContext->mGivenHeight - height)) / 4); |
| for ( index = 0; index < height >> 1; index++) |
| { |
| memcpy((void *)pTmpBuffDst, (void *)pTmpBuff, width >> 1); |
| pTmpBuffDst += width >> 1; |
| pTmpBuff += pDecShellContext->mGivenWidth >> 1; |
| } |
| } |
| } else { |
| ALOGE("VideoDecoder_decode: unexpected color format 0x%X", |
| pDecShellContext->decOuputColorFormat); |
| lerr = M4ERR_PARAMETER; |
| } |
| |
| tmpDecBuffer->buffCTS = pDecShellContext->m_lastDecodedCTS; |
| tmpDecBuffer->state = VIDEOEDITOR_BUFFER_kFilled; |
| tmpDecBuffer->size = pDecoderBuffer->size(); |
| |
| 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; |
| |
| ALOGV("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) ) { |
| ALOGV("VIDEOEDITOR_VIDEO_render Frame in the past"); |
| err = M4WAR_VIDEORENDERER_NO_NEW_FRAME; |
| goto cleanUp; |
| } |
| ALOGV("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; |
| ALOGV("VideoDecoder_render: found a buffer with timestamp = %lf", |
| candidateTimeStamp); |
| } |
| } |
| } |
| if (M4OSA_FALSE == bFound) { |
| err = M4WAR_VIDEORENDERER_NO_NEW_FRAME; |
| goto cleanUp; |
| } |
| |
| ALOGV("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); |
| |
| ALOGV("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; |
| |
| memcpy((void *) pOutputPlane[0].pac_data, (void *)tempBuffPtr, |
| tempWidth * tempHeight); |
| tempBuffPtr += (tempWidth * tempHeight); |
| memcpy((void *) pOutputPlane[1].pac_data, (void *)tempBuffPtr, |
| (tempWidth/2) * (tempHeight/2)); |
| tempBuffPtr += ((tempWidth/2) * (tempHeight/2)); |
| memcpy((void *) pOutputPlane[2].pac_data, (void *)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; |
| ALOGV("VideoEditorVideoDecoder_render no error"); |
| } else { |
| ALOGV("VideoEditorVideoDecoder_render ERROR 0x%X", err); |
| } |
| ALOGV("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_32bitAlignedMalloc( |
| 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; |
| } |
| |
| M4OSA_ERR VideoEditorVideoDecoder_getSoftwareInterface(M4DECODER_VideoType decoderType, |
| M4DECODER_VideoType *pDecoderType, M4OSA_Context *pDecInterface) { |
| M4DECODER_VideoInterface* pDecoderInterface = M4OSA_NULL; |
| |
| pDecoderInterface = (M4DECODER_VideoInterface*)M4OSA_32bitAlignedMalloc( |
| sizeof(M4DECODER_VideoInterface), M4DECODER_EXTERNAL, |
| (M4OSA_Char*)"VideoEditorVideoDecoder_getInterface" ); |
| if (M4OSA_NULL == pDecoderInterface) { |
| return M4ERR_ALLOC; |
| } |
| |
| *pDecoderType = decoderType; |
| |
| pDecoderInterface->m_pFctCreate = VideoEditorVideoSoftwareDecoder_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); |
| |
| } |
| |
| M4OSA_ERR VideoEditorVideoDecoder_getSoftwareInterface_MPEG4( |
| M4DECODER_VideoType *pDecoderType, M4OSA_Context *pDecInterface) { |
| return VideoEditorVideoDecoder_getSoftwareInterface(M4DECODER_kVideoTypeMPEG4, |
| pDecoderType, pDecInterface); |
| } |
| |
| M4OSA_ERR VideoEditorVideoDecoder_getSoftwareInterface_H264( |
| M4DECODER_VideoType *pDecoderType, M4OSA_Context *pDecInterface) { |
| return VideoEditorVideoDecoder_getSoftwareInterface(M4DECODER_kVideoTypeAVC, |
| pDecoderType, pDecInterface); |
| |
| } |
| |
| M4OSA_ERR VideoEditorVideoDecoder_getVideoDecodersAndCapabilities( |
| M4DECODER_VideoDecoders** decoders) { |
| return queryVideoDecoderCapabilities(decoders); |
| } |
| |
| } // extern "C" |