/* * 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 #include #include /******************** * 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 &format, VIDEOEDITOR_CodecType codecType, void *decoderShellContext); virtual status_t start(MetaData *params = NULL); virtual status_t stop(); virtual sp getFormat(); virtual status_t read( MediaBuffer **buffer, const ReadOptions *options = NULL); protected : virtual ~VideoEditorVideoDecoderSource(); private: sp 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 &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 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 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; #ifdef QCOM_HARDWARE width = vWidth; height = vHeight; #else width = cropRight - cropLeft + 1; height = cropBottom - cropTop + 1; #endif 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 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 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"