| /* |
| * Copyright (c) 2009-2011 Intel Corporation. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "VideoDecoderBase.h" |
| #include "VideoDecoderTrace.h" |
| #include <string.h> |
| #include <va/va_android.h> |
| #include <va/va_tpi.h> |
| #ifdef __SSE4_1__ |
| #include "use_util_sse4.h" |
| #endif |
| |
| #define INVALID_PTS ((uint64_t)-1) |
| #define MAXIMUM_POC 0x7FFFFFFF |
| #define MINIMUM_POC 0x80000000 |
| #define ANDROID_DISPLAY_HANDLE 0x18C34078 |
| |
| VideoDecoderBase::VideoDecoderBase(const char *mimeType, _vbp_parser_type type) |
| : mInitialized(false), |
| mLowDelay(false), |
| mStoreMetaData(false), |
| mDisplay(NULL), |
| mVADisplay(NULL), |
| mVAContext(VA_INVALID_ID), |
| mVAConfig(VA_INVALID_ID), |
| mVAStarted(false), |
| mCurrentPTS(INVALID_PTS), |
| mAcquiredBuffer(NULL), |
| mLastReference(NULL), |
| mForwardReference(NULL), |
| mDecodingFrame(false), |
| mSizeChanged(false), |
| mShowFrame(true), |
| mOutputWindowSize(OUTPUT_WINDOW_SIZE), |
| mRotationDegrees(0), |
| mErrReportEnabled(false), |
| mWiDiOn(false), |
| mRawOutput(false), |
| mManageReference(true), |
| mOutputMethod(OUTPUT_BY_PCT), |
| mNumSurfaces(0), |
| mSurfaceBuffers(NULL), |
| mOutputHead(NULL), |
| mOutputTail(NULL), |
| mSurfaces(NULL), |
| mVASurfaceAttrib(NULL), |
| mSurfaceUserPtr(NULL), |
| mSurfaceAcquirePos(0), |
| mNextOutputPOC(MINIMUM_POC), |
| mParserType(type), |
| mParserHandle(NULL), |
| mSignalBufferSize(0) { |
| |
| memset(&mVideoFormatInfo, 0, sizeof(VideoFormatInfo)); |
| memset(&mConfigBuffer, 0, sizeof(mConfigBuffer)); |
| for (int i = 0; i < MAX_GRAPHIC_BUFFER_NUM; i++) { |
| mSignalBufferPre[i] = NULL; |
| } |
| pthread_mutex_init(&mLock, NULL); |
| pthread_mutex_init(&mFormatLock, NULL); |
| mVideoFormatInfo.mimeType = strdup(mimeType); |
| mUseGEN = false; |
| mMetaDataBuffersNum = 0; |
| mLibHandle = NULL; |
| mParserOpen = NULL; |
| mParserClose = NULL; |
| mParserParse = NULL; |
| mParserQuery = NULL; |
| mParserFlush = NULL; |
| mParserUpdate = NULL; |
| } |
| |
| VideoDecoderBase::~VideoDecoderBase() { |
| pthread_mutex_destroy(&mLock); |
| pthread_mutex_destroy(&mFormatLock); |
| stop(); |
| free(mVideoFormatInfo.mimeType); |
| } |
| |
| Decode_Status VideoDecoderBase::start(VideoConfigBuffer *buffer) { |
| if (buffer == NULL) { |
| return DECODE_INVALID_DATA; |
| } |
| |
| if (mParserHandle != NULL) { |
| WTRACE("Decoder has already started."); |
| return DECODE_SUCCESS; |
| } |
| mLibHandle = dlopen("libmixvbp.so", RTLD_NOW); |
| if (mLibHandle == NULL) { |
| return DECODE_NO_PARSER; |
| } |
| mParserOpen = (OpenFunc)dlsym(mLibHandle, "vbp_open"); |
| mParserClose = (CloseFunc)dlsym(mLibHandle, "vbp_close"); |
| mParserParse = (ParseFunc)dlsym(mLibHandle, "vbp_parse"); |
| mParserQuery = (QueryFunc)dlsym(mLibHandle, "vbp_query"); |
| mParserFlush = (FlushFunc)dlsym(mLibHandle, "vbp_flush"); |
| if (mParserOpen == NULL || mParserClose == NULL || mParserParse == NULL |
| || mParserQuery == NULL || mParserFlush == NULL) { |
| return DECODE_NO_PARSER; |
| } |
| #if (defined USE_AVC_SHORT_FORMAT || defined USE_SLICE_HEADER_PARSING) |
| mParserUpdate = (UpdateFunc)dlsym(mLibHandle, "vbp_update"); |
| if (mParserUpdate == NULL) { |
| return DECODE_NO_PARSER; |
| } |
| #endif |
| if ((int32_t)mParserType != VBP_INVALID) { |
| ITRACE("mParserType = %d", mParserType); |
| if (mParserOpen(mParserType, &mParserHandle) != VBP_OK) { |
| ETRACE("Failed to open VBP parser."); |
| return DECODE_NO_PARSER; |
| } |
| } |
| // keep a copy of configure buffer, meta data only. It can be used to override VA setup parameter. |
| mConfigBuffer = *buffer; |
| mConfigBuffer.data = NULL; |
| mConfigBuffer.size = 0; |
| |
| mVideoFormatInfo.width = buffer->width; |
| mVideoFormatInfo.height = buffer->height; |
| if (buffer->flag & USE_NATIVE_GRAPHIC_BUFFER) { |
| mVideoFormatInfo.surfaceWidth = buffer->graphicBufferWidth; |
| mVideoFormatInfo.surfaceHeight = buffer->graphicBufferHeight; |
| } |
| mLowDelay = buffer->flag & WANT_LOW_DELAY; |
| mStoreMetaData = buffer->flag & WANT_STORE_META_DATA; |
| mRawOutput = buffer->flag & WANT_RAW_OUTPUT; |
| if (mRawOutput) { |
| WTRACE("Output is raw data."); |
| } |
| |
| return DECODE_SUCCESS; |
| } |
| |
| |
| Decode_Status VideoDecoderBase::reset(VideoConfigBuffer *buffer) { |
| if (buffer == NULL) { |
| return DECODE_INVALID_DATA; |
| } |
| |
| // if VA is already started, terminate VA as graphic buffers are reallocated by omxcodec |
| terminateVA(); |
| |
| // reset the mconfigBuffer to pass it for startVA. |
| mConfigBuffer = *buffer; |
| mConfigBuffer.data = NULL; |
| mConfigBuffer.size = 0; |
| |
| mVideoFormatInfo.width = buffer->width; |
| mVideoFormatInfo.height = buffer->height; |
| if (buffer->flag & USE_NATIVE_GRAPHIC_BUFFER) { |
| mVideoFormatInfo.surfaceWidth = buffer->graphicBufferWidth; |
| mVideoFormatInfo.surfaceHeight = buffer->graphicBufferHeight; |
| } |
| mVideoFormatInfo.actualBufferNeeded = mConfigBuffer.surfaceNumber; |
| mLowDelay = buffer->flag & WANT_LOW_DELAY; |
| mStoreMetaData = buffer->flag & WANT_STORE_META_DATA; |
| mMetaDataBuffersNum = 0; |
| mRawOutput = buffer->flag & WANT_RAW_OUTPUT; |
| if (mRawOutput) { |
| WTRACE("Output is raw data."); |
| } |
| return DECODE_SUCCESS; |
| } |
| |
| |
| |
| void VideoDecoderBase::stop(void) { |
| terminateVA(); |
| |
| mCurrentPTS = INVALID_PTS; |
| mAcquiredBuffer = NULL; |
| mLastReference = NULL; |
| mForwardReference = NULL; |
| mDecodingFrame = false; |
| mSizeChanged = false; |
| |
| // private variables |
| mLowDelay = false; |
| mStoreMetaData = false; |
| mRawOutput = false; |
| mNumSurfaces = 0; |
| mSurfaceAcquirePos = 0; |
| mNextOutputPOC = MINIMUM_POC; |
| mVideoFormatInfo.valid = false; |
| if (mParserHandle){ |
| mParserClose(mParserHandle); |
| mParserHandle = NULL; |
| } |
| if (mLibHandle) { |
| dlclose(mLibHandle); |
| mLibHandle = NULL; |
| } |
| } |
| |
| void VideoDecoderBase::flush(void) { |
| if (mVAStarted == false) { |
| // nothing to flush at this stage |
| return; |
| } |
| |
| endDecodingFrame(true); |
| |
| VideoSurfaceBuffer *p = mOutputHead; |
| // check if there's buffer with DRC flag in the output queue |
| while (p) { |
| if (p->renderBuffer.flag & IS_RESOLUTION_CHANGE) { |
| mSizeChanged = true; |
| break; |
| } |
| p = p->next; |
| } |
| // avoid setting mSurfaceAcquirePos to 0 as it may cause tearing |
| // (surface is still being rendered) |
| mSurfaceAcquirePos = (mSurfaceAcquirePos + 1) % mNumSurfaces; |
| mNextOutputPOC = MINIMUM_POC; |
| mCurrentPTS = INVALID_PTS; |
| mAcquiredBuffer = NULL; |
| mLastReference = NULL; |
| mForwardReference = NULL; |
| mOutputHead = NULL; |
| mOutputTail = NULL; |
| mDecodingFrame = false; |
| |
| // flush vbp parser |
| if (mParserHandle && (mParserFlush(mParserHandle) != VBP_OK)) { |
| WTRACE("Failed to flush parser. Continue"); |
| } |
| |
| // initialize surface buffer without resetting mapped/raw data |
| initSurfaceBuffer(false); |
| |
| } |
| |
| void VideoDecoderBase::freeSurfaceBuffers(void) { |
| if (mVAStarted == false) { |
| // nothing to free surface buffers at this stage |
| return; |
| } |
| |
| pthread_mutex_lock(&mLock); |
| |
| endDecodingFrame(true); |
| |
| // if VA is already started, terminate VA as graphic buffers are reallocated by omxcodec |
| terminateVA(); |
| |
| pthread_mutex_unlock(&mLock); |
| } |
| |
| const VideoFormatInfo* VideoDecoderBase::getFormatInfo(void) { |
| if ((mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER) && mStoreMetaData) { |
| // Do nothing here, just to avoid thread |
| // contention in updateFormatInfo() |
| pthread_mutex_lock(&mFormatLock); |
| pthread_mutex_unlock(&mFormatLock); |
| } |
| |
| return &mVideoFormatInfo; |
| } |
| |
| int VideoDecoderBase::getOutputQueueLength(void) { |
| VideoSurfaceBuffer *p = mOutputHead; |
| |
| int i = 0; |
| while (p) { |
| p = p->next; |
| i++; |
| } |
| |
| return i; |
| } |
| |
| const VideoRenderBuffer* VideoDecoderBase::getOutput(bool draining, VideoErrorBuffer *outErrBuf) { |
| if (mVAStarted == false) { |
| return NULL; |
| } |
| bool useGraphicBuffer = mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER; |
| |
| if (draining) { |
| // complete decoding the last frame and ignore return |
| endDecodingFrame(false); |
| } |
| |
| if (mOutputHead == NULL) { |
| return NULL; |
| } |
| |
| // output by position (the first buffer) |
| VideoSurfaceBuffer *outputByPos = mOutputHead; |
| |
| if (mLowDelay) { |
| mOutputHead = mOutputHead->next; |
| if (mOutputHead == NULL) { |
| mOutputTail = NULL; |
| } |
| vaSetTimestampForSurface(mVADisplay, outputByPos->renderBuffer.surface, outputByPos->renderBuffer.timeStamp); |
| if (useGraphicBuffer && !mUseGEN) { |
| vaSyncSurface(mVADisplay, outputByPos->renderBuffer.surface); |
| fillDecodingErrors(&(outputByPos->renderBuffer)); |
| } |
| if (draining && mOutputTail == NULL) { |
| outputByPos->renderBuffer.flag |= IS_EOS; |
| } |
| drainDecodingErrors(outErrBuf, &(outputByPos->renderBuffer)); |
| |
| return &(outputByPos->renderBuffer); |
| } |
| |
| VideoSurfaceBuffer *output = NULL; |
| if (mOutputMethod == OUTPUT_BY_POC) { |
| output = findOutputByPoc(draining); |
| } else if (mOutputMethod == OUTPUT_BY_PCT) { |
| output = findOutputByPct(draining); |
| } else { |
| ETRACE("Invalid output method."); |
| return NULL; |
| } |
| |
| if (output == NULL) { |
| return NULL; |
| } |
| |
| if (output != outputByPos) { |
| // remove this output from middle or end of the list |
| VideoSurfaceBuffer *p = outputByPos; |
| while (p->next != output) { |
| p = p->next; |
| } |
| p->next = output->next; |
| if (mOutputTail == output) { |
| mOutputTail = p; |
| } |
| } else { |
| // remove this output from head of the list |
| mOutputHead = mOutputHead->next; |
| if (mOutputHead == NULL) { |
| mOutputTail = NULL; |
| } |
| } |
| //VTRACE("Output POC %d for display (pts = %.2f)", output->pictureOrder, output->renderBuffer.timeStamp/1E6); |
| vaSetTimestampForSurface(mVADisplay, output->renderBuffer.surface, output->renderBuffer.timeStamp); |
| |
| if (useGraphicBuffer && !mUseGEN) { |
| vaSyncSurface(mVADisplay, output->renderBuffer.surface); |
| fillDecodingErrors(&(output->renderBuffer)); |
| } |
| |
| if (draining && mOutputTail == NULL) { |
| output->renderBuffer.flag |= IS_EOS; |
| } |
| |
| drainDecodingErrors(outErrBuf, &(output->renderBuffer)); |
| |
| return &(output->renderBuffer); |
| } |
| |
| VideoSurfaceBuffer* VideoDecoderBase::findOutputByPts() { |
| // output by presentation time stamp - buffer with the smallest time stamp is output |
| VideoSurfaceBuffer *p = mOutputHead; |
| VideoSurfaceBuffer *outputByPts = NULL; |
| uint64_t pts = INVALID_PTS; |
| do { |
| if ((uint64_t)(p->renderBuffer.timeStamp) <= pts) { |
| // find buffer with the smallest PTS |
| pts = p->renderBuffer.timeStamp; |
| outputByPts = p; |
| } |
| p = p->next; |
| } while (p != NULL); |
| |
| return outputByPts; |
| } |
| |
| VideoSurfaceBuffer* VideoDecoderBase::findOutputByPct(bool draining) { |
| // output by picture coding type (PCT) |
| // if there is more than one reference frame, the first reference frame is ouput, otherwise, |
| // output non-reference frame if there is any. |
| |
| VideoSurfaceBuffer *p = mOutputHead; |
| VideoSurfaceBuffer *outputByPct = NULL; |
| int32_t reference = 0; |
| do { |
| if (p->referenceFrame) { |
| reference++; |
| if (reference > 1) { |
| // mOutputHead must be a reference frame |
| outputByPct = mOutputHead; |
| break; |
| } |
| } else { |
| // first non-reference frame |
| outputByPct = p; |
| break; |
| } |
| p = p->next; |
| } while (p != NULL); |
| |
| if (outputByPct == NULL && draining) { |
| outputByPct = mOutputHead; |
| } |
| return outputByPct; |
| } |
| |
| #if 0 |
| VideoSurfaceBuffer* VideoDecoderBase::findOutputByPoc(bool draining) { |
| // output by picture order count (POC) |
| // Output criteria: |
| // if there is IDR frame (POC == 0), all the frames before IDR must be output; |
| // Otherwise, if draining flag is set or list is full, frame with the least POC is output; |
| // Otherwise, NOTHING is output |
| |
| int32_t dpbFullness = 0; |
| for (int32_t i = 0; i < mNumSurfaces; i++) { |
| // count num of reference frames |
| if (mSurfaceBuffers[i].asReferernce) { |
| dpbFullness++; |
| } |
| } |
| |
| if (mAcquiredBuffer && mAcquiredBuffer->asReferernce) { |
| // frame is being decoded and is not ready for output yet |
| dpbFullness--; |
| } |
| |
| VideoSurfaceBuffer *p = mOutputHead; |
| while (p != NULL) { |
| // count dpbFullness with non-reference frame in the output queue |
| if (p->asReferernce == false) { |
| dpbFullness++; |
| } |
| p = p->next; |
| } |
| |
| Retry: |
| p = mOutputHead; |
| VideoSurfaceBuffer *outputByPoc = NULL; |
| int32_t count = 0; |
| int32_t poc = MAXIMUM_POC; |
| |
| do { |
| if (p->pictureOrder == 0) { |
| // output picture with the least POC before IDR |
| if (outputByPoc != NULL) { |
| mNextOutputPOC = outputByPoc->pictureOrder + 1; |
| return outputByPoc; |
| } else { |
| mNextOutputPOC = MINIMUM_POC; |
| } |
| } |
| |
| // POC of the output candidate must not be less than mNextOutputPOC |
| if (p->pictureOrder < mNextOutputPOC) { |
| break; |
| } |
| |
| if (p->pictureOrder < poc) { |
| // update the least POC. |
| poc = p->pictureOrder; |
| outputByPoc = p; |
| } |
| count++; |
| p = p->next; |
| } while (p != NULL && count < mOutputWindowSize); |
| |
| if (draining == false && dpbFullness < mOutputWindowSize) { |
| // list is not full and we are not in draining state |
| // if DPB is already full, one frame must be output |
| return NULL; |
| } |
| |
| if (outputByPoc == NULL) { |
| mNextOutputPOC = MINIMUM_POC; |
| goto Retry; |
| } |
| |
| // for debugging purpose |
| if (outputByPoc->pictureOrder != 0 && outputByPoc->pictureOrder < mNextOutputPOC) { |
| ETRACE("Output POC is not incremental, expected %d, actual %d", mNextOutputPOC, outputByPoc->pictureOrder); |
| //gaps_in_frame_num_value_allowed_flag is not currently supported |
| } |
| |
| mNextOutputPOC = outputByPoc->pictureOrder + 1; |
| |
| return outputByPoc; |
| } |
| #else |
| VideoSurfaceBuffer* VideoDecoderBase::findOutputByPoc(bool draining) { |
| VideoSurfaceBuffer *output = NULL; |
| VideoSurfaceBuffer *p = mOutputHead; |
| int32_t count = 0; |
| int32_t poc = MAXIMUM_POC; |
| VideoSurfaceBuffer *outputleastpoc = mOutputHead; |
| do { |
| count++; |
| if (p->pictureOrder == 0) { |
| // any picture before this POC (new IDR) must be output |
| if (output == NULL) { |
| mNextOutputPOC = MINIMUM_POC; |
| // looking for any POC with negative value |
| } else { |
| mNextOutputPOC = output->pictureOrder + 1; |
| break; |
| } |
| } |
| if (p->pictureOrder < poc && p->pictureOrder >= mNextOutputPOC) { |
| // this POC meets ouput criteria. |
| poc = p->pictureOrder; |
| output = p; |
| outputleastpoc = p; |
| } |
| if (poc == mNextOutputPOC || count == mOutputWindowSize) { |
| if (output != NULL) { |
| // this indicates two cases: |
| // 1) the next output POC is found. |
| // 2) output queue is full and there is at least one buffer meeting the output criteria. |
| mNextOutputPOC = output->pictureOrder + 1; |
| break; |
| } else { |
| // this indicates output queue is full and no buffer in the queue meets the output criteria |
| // restart processing as queue is FULL and output criteria is changed. (next output POC is 0) |
| mNextOutputPOC = MINIMUM_POC; |
| count = 0; |
| poc = MAXIMUM_POC; |
| p = mOutputHead; |
| continue; |
| } |
| } |
| if (p->next == NULL) { |
| output = NULL; |
| } |
| |
| p = p->next; |
| } while (p != NULL); |
| |
| if (draining == true && output == NULL) { |
| output = outputleastpoc; |
| } |
| |
| return output; |
| } |
| #endif |
| |
| bool VideoDecoderBase::checkBufferAvail(void) { |
| if (!mInitialized) { |
| if ((mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER) == 0) { |
| return true; |
| } |
| for (int i = 0; i < MAX_GRAPHIC_BUFFER_NUM; i++) { |
| if (mSignalBufferPre[i] != NULL) { |
| return true; |
| } |
| } |
| return false; |
| } |
| // check whether there is buffer available for decoding |
| // TODO: check frame being referenced for frame skipping |
| VideoSurfaceBuffer *buffer = NULL; |
| for (int32_t i = 0; i < mNumSurfaces; i++) { |
| buffer = mSurfaceBuffers + i; |
| |
| if (buffer->asReferernce == false && |
| buffer->renderBuffer.renderDone == true) { |
| querySurfaceRenderStatus(buffer); |
| if (buffer->renderBuffer.driverRenderDone == true) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| Decode_Status VideoDecoderBase::acquireSurfaceBuffer(void) { |
| if (mVAStarted == false) { |
| return DECODE_FAIL; |
| } |
| |
| if (mAcquiredBuffer != NULL) { |
| ETRACE("mAcquiredBuffer is not NULL. Implementation bug."); |
| return DECODE_FAIL; |
| } |
| |
| int nextAcquire = mSurfaceAcquirePos; |
| VideoSurfaceBuffer *acquiredBuffer = NULL; |
| bool acquired = false; |
| |
| while (acquired == false) { |
| acquiredBuffer = mSurfaceBuffers + nextAcquire; |
| |
| querySurfaceRenderStatus(acquiredBuffer); |
| |
| if (acquiredBuffer->asReferernce == false && acquiredBuffer->renderBuffer.renderDone == true && acquiredBuffer->renderBuffer.driverRenderDone == true) { |
| // this is potential buffer for acquisition. Check if it is referenced by other surface for frame skipping |
| VideoSurfaceBuffer *temp; |
| acquired = true; |
| for (int i = 0; i < mNumSurfaces; i++) { |
| if (i == nextAcquire) { |
| continue; |
| } |
| temp = mSurfaceBuffers + i; |
| // use mSurfaces[nextAcquire] instead of acquiredBuffer->renderBuffer.surface as its the actual surface to use. |
| if (temp->renderBuffer.surface == mSurfaces[nextAcquire] && |
| temp->renderBuffer.renderDone == false) { |
| ITRACE("Surface is referenced by other surface buffer."); |
| acquired = false; |
| break; |
| } |
| } |
| } |
| if (acquired) { |
| break; |
| } |
| nextAcquire++; |
| if (nextAcquire == mNumSurfaces) { |
| nextAcquire = 0; |
| } |
| if (nextAcquire == mSurfaceAcquirePos) { |
| return DECODE_NO_SURFACE; |
| } |
| } |
| |
| if (acquired == false) { |
| return DECODE_NO_SURFACE; |
| } |
| |
| mAcquiredBuffer = acquiredBuffer; |
| mSurfaceAcquirePos = nextAcquire; |
| |
| // set surface again as surface maybe reset by skipped frame. |
| // skipped frame is a "non-coded frame" and decoder needs to duplicate the previous reference frame as the output. |
| mAcquiredBuffer->renderBuffer.surface = mSurfaces[mSurfaceAcquirePos]; |
| if (mSurfaceUserPtr && mAcquiredBuffer->mappedData) { |
| mAcquiredBuffer->mappedData->data = mSurfaceUserPtr[mSurfaceAcquirePos]; |
| } |
| mAcquiredBuffer->renderBuffer.timeStamp = INVALID_PTS; |
| mAcquiredBuffer->renderBuffer.display = mVADisplay; |
| mAcquiredBuffer->renderBuffer.flag = 0; |
| mAcquiredBuffer->renderBuffer.renderDone = false; |
| mAcquiredBuffer->asReferernce = false; |
| mAcquiredBuffer->renderBuffer.errBuf.errorNumber = 0; |
| mAcquiredBuffer->renderBuffer.errBuf.timeStamp = INVALID_PTS; |
| |
| return DECODE_SUCCESS; |
| } |
| |
| Decode_Status VideoDecoderBase::outputSurfaceBuffer(void) { |
| Decode_Status status; |
| if (mAcquiredBuffer == NULL) { |
| ETRACE("mAcquiredBuffer is NULL. Implementation bug."); |
| return DECODE_FAIL; |
| } |
| |
| if (mRawOutput) { |
| status = getRawDataFromSurface(); |
| CHECK_STATUS(); |
| } |
| |
| // frame is successfly decoded to the current surface, it is ready for output |
| if (mShowFrame) { |
| mAcquiredBuffer->renderBuffer.renderDone = false; |
| } else { |
| mAcquiredBuffer->renderBuffer.renderDone = true; |
| } |
| |
| // decoder must set "asReference and referenceFrame" flags properly |
| |
| // update reference frames |
| if (mAcquiredBuffer->referenceFrame) { |
| if (mManageReference) { |
| // managing reference for MPEG4/H.263/WMV. |
| // AVC should manage reference frame in a different way |
| if (mForwardReference != NULL) { |
| // this foward reference is no longer needed |
| mForwardReference->asReferernce = false; |
| } |
| // Forware reference for either P or B frame prediction |
| mForwardReference = mLastReference; |
| mAcquiredBuffer->asReferernce = true; |
| } |
| |
| // the last reference frame. |
| mLastReference = mAcquiredBuffer; |
| } |
| // add to the output list |
| if (mShowFrame) { |
| if (mOutputHead == NULL) { |
| mOutputHead = mAcquiredBuffer; |
| } else { |
| mOutputTail->next = mAcquiredBuffer; |
| } |
| mOutputTail = mAcquiredBuffer; |
| mOutputTail->next = NULL; |
| } |
| |
| //VTRACE("Pushing POC %d to queue (pts = %.2f)", mAcquiredBuffer->pictureOrder, mAcquiredBuffer->renderBuffer.timeStamp/1E6); |
| |
| mAcquiredBuffer = NULL; |
| mSurfaceAcquirePos = (mSurfaceAcquirePos + 1 ) % mNumSurfaces; |
| return DECODE_SUCCESS; |
| } |
| |
| Decode_Status VideoDecoderBase::releaseSurfaceBuffer(void) { |
| if (mAcquiredBuffer == NULL) { |
| // this is harmless error |
| return DECODE_SUCCESS; |
| } |
| |
| // frame is not decoded to the acquired buffer, current surface is invalid, and can't be output. |
| mAcquiredBuffer->asReferernce = false; |
| mAcquiredBuffer->renderBuffer.renderDone = true; |
| mAcquiredBuffer = NULL; |
| return DECODE_SUCCESS; |
| } |
| |
| void VideoDecoderBase::flushSurfaceBuffers(void) { |
| endDecodingFrame(true); |
| VideoSurfaceBuffer *p = NULL; |
| while (mOutputHead) { |
| mOutputHead->renderBuffer.renderDone = true; |
| p = mOutputHead; |
| mOutputHead = mOutputHead->next; |
| p->next = NULL; |
| } |
| mOutputHead = NULL; |
| mOutputTail = NULL; |
| } |
| |
| Decode_Status VideoDecoderBase::endDecodingFrame(bool dropFrame) { |
| Decode_Status status = DECODE_SUCCESS; |
| VAStatus vaStatus; |
| |
| if (mDecodingFrame == false) { |
| if (mAcquiredBuffer != NULL) { |
| //ETRACE("mAcquiredBuffer is not NULL. Implementation bug."); |
| releaseSurfaceBuffer(); |
| status = DECODE_FAIL; |
| } |
| return status; |
| } |
| // return through exit label to reset mDecodingFrame |
| if (mAcquiredBuffer == NULL) { |
| ETRACE("mAcquiredBuffer is NULL. Implementation bug."); |
| status = DECODE_FAIL; |
| goto exit; |
| } |
| |
| vaStatus = vaEndPicture(mVADisplay, mVAContext); |
| if (vaStatus != VA_STATUS_SUCCESS) { |
| releaseSurfaceBuffer(); |
| ETRACE("vaEndPicture failed. vaStatus = %d", vaStatus); |
| status = DECODE_DRIVER_FAIL; |
| goto exit; |
| } |
| |
| if (dropFrame) { |
| // we are asked to drop this decoded picture |
| VTRACE("Frame dropped in endDecodingFrame"); |
| vaStatus = vaSyncSurface(mVADisplay, mAcquiredBuffer->renderBuffer.surface); |
| releaseSurfaceBuffer(); |
| goto exit; |
| } |
| status = outputSurfaceBuffer(); |
| // fall through |
| exit: |
| mDecodingFrame = false; |
| return status; |
| } |
| |
| |
| Decode_Status VideoDecoderBase::setupVA(uint32_t numSurface, VAProfile profile, uint32_t numExtraSurface) { |
| VAStatus vaStatus = VA_STATUS_SUCCESS; |
| Decode_Status status; |
| |
| if (mVAStarted) { |
| return DECODE_SUCCESS; |
| } |
| |
| mRotationDegrees = 0; |
| if (mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER){ |
| #ifdef TARGET_HAS_ISV |
| if (mVideoFormatInfo.actualBufferNeeded > mConfigBuffer.surfaceNumber - mConfigBuffer.vppBufferNum) |
| #else |
| if (mVideoFormatInfo.actualBufferNeeded > mConfigBuffer.surfaceNumber) |
| #endif |
| return DECODE_FORMAT_CHANGE; |
| |
| numSurface = mConfigBuffer.surfaceNumber; |
| // if format has been changed in USE_NATIVE_GRAPHIC_BUFFER mode, |
| // we can not setupVA here when the graphic buffer resolution is smaller than the resolution decoder really needs |
| if (mSizeChanged) { |
| if (mStoreMetaData || (!mStoreMetaData && (mVideoFormatInfo.surfaceWidth < mVideoFormatInfo.width || mVideoFormatInfo.surfaceHeight < mVideoFormatInfo.height))) { |
| mSizeChanged = false; |
| return DECODE_FORMAT_CHANGE; |
| } |
| } |
| } |
| |
| // TODO: validate profile |
| if (numSurface == 0) { |
| return DECODE_FAIL; |
| } |
| |
| if (mConfigBuffer.flag & HAS_MINIMUM_SURFACE_NUMBER) { |
| if (numSurface < mConfigBuffer.surfaceNumber) { |
| WTRACE("surface to allocated %d is less than minimum number required %d", |
| numSurface, mConfigBuffer.surfaceNumber); |
| numSurface = mConfigBuffer.surfaceNumber; |
| } |
| } |
| |
| if (mVADisplay != NULL) { |
| ETRACE("VA is partially started."); |
| return DECODE_FAIL; |
| } |
| |
| // Display is defined as "unsigned int" |
| #ifndef USE_HYBRID_DRIVER |
| mDisplay = new Display; |
| *mDisplay = ANDROID_DISPLAY_HANDLE; |
| #else |
| if (profile >= VAProfileH264Baseline && profile <= VAProfileVC1Advanced) { |
| ITRACE("Using GEN driver"); |
| mDisplay = "libva_driver_name=i965"; |
| mUseGEN = true; |
| } else { |
| ITRACE("Using PVR driver"); |
| mDisplay = "libva_driver_name=pvr"; |
| mUseGEN = false; |
| } |
| #endif |
| mVADisplay = vaGetDisplay(mDisplay); |
| if (mVADisplay == NULL) { |
| ETRACE("vaGetDisplay failed."); |
| return DECODE_DRIVER_FAIL; |
| } |
| |
| int majorVersion, minorVersion; |
| vaStatus = vaInitialize(mVADisplay, &majorVersion, &minorVersion); |
| CHECK_VA_STATUS("vaInitialize"); |
| |
| if ((int32_t)profile != VAProfileSoftwareDecoding) { |
| |
| status = checkHardwareCapability(); |
| CHECK_STATUS("checkHardwareCapability"); |
| |
| #if (defined USE_AVC_SHORT_FORMAT || defined USE_SLICE_HEADER_PARSING) |
| status = getCodecSpecificConfigs(profile, &mVAConfig); |
| CHECK_STATUS("getCodecSpecificAttributes"); |
| #else |
| VAConfigAttrib attrib; |
| //We are requesting RT attributes |
| attrib.type = VAConfigAttribRTFormat; |
| attrib.value = VA_RT_FORMAT_YUV420; |
| |
| vaStatus = vaCreateConfig( |
| mVADisplay, |
| profile, |
| VAEntrypointVLD, |
| &attrib, |
| 1, |
| &mVAConfig); |
| CHECK_VA_STATUS("vaCreateConfig"); |
| #endif |
| } |
| |
| mNumSurfaces = numSurface; |
| mNumExtraSurfaces = numExtraSurface; |
| mSurfaces = new VASurfaceID [mNumSurfaces + mNumExtraSurfaces]; |
| mExtraSurfaces = mSurfaces + mNumSurfaces; |
| for (int i = 0; i < mNumSurfaces + mNumExtraSurfaces; ++i) { |
| mSurfaces[i] = VA_INVALID_SURFACE; |
| } |
| if (mSurfaces == NULL) { |
| return DECODE_MEMORY_FAIL; |
| } |
| |
| setRenderRect(); |
| setColorSpaceInfo(mVideoFormatInfo.colorMatrix, mVideoFormatInfo.videoRange); |
| |
| int32_t format = VA_RT_FORMAT_YUV420; |
| if (mConfigBuffer.flag & WANT_SURFACE_PROTECTION) { |
| #ifndef USE_AVC_SHORT_FORMAT |
| format |= VA_RT_FORMAT_PROTECTED; |
| WTRACE("Surface is protected."); |
| #endif |
| } |
| if (mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER) { |
| if (!mStoreMetaData) { |
| VASurfaceAttrib attribs[2]; |
| mVASurfaceAttrib = new VASurfaceAttribExternalBuffers; |
| if (mVASurfaceAttrib == NULL) { |
| return DECODE_MEMORY_FAIL; |
| } |
| |
| mVASurfaceAttrib->buffers= (unsigned long *)malloc(sizeof(unsigned long)*mNumSurfaces); |
| if (mVASurfaceAttrib->buffers == NULL) { |
| return DECODE_MEMORY_FAIL; |
| } |
| mVASurfaceAttrib->num_buffers = mNumSurfaces; |
| mVASurfaceAttrib->pixel_format = VA_FOURCC_NV12; |
| mVASurfaceAttrib->width = mVideoFormatInfo.surfaceWidth; |
| mVASurfaceAttrib->height = mVideoFormatInfo.surfaceHeight; |
| mVASurfaceAttrib->data_size = mConfigBuffer.graphicBufferHStride * mConfigBuffer.graphicBufferVStride * 1.5; |
| mVASurfaceAttrib->num_planes = 2; |
| mVASurfaceAttrib->pitches[0] = mConfigBuffer.graphicBufferHStride; |
| mVASurfaceAttrib->pitches[1] = mConfigBuffer.graphicBufferHStride; |
| mVASurfaceAttrib->pitches[2] = 0; |
| mVASurfaceAttrib->pitches[3] = 0; |
| mVASurfaceAttrib->offsets[0] = 0; |
| mVASurfaceAttrib->offsets[1] = mConfigBuffer.graphicBufferHStride * mConfigBuffer.graphicBufferVStride; |
| mVASurfaceAttrib->offsets[2] = 0; |
| mVASurfaceAttrib->offsets[3] = 0; |
| mVASurfaceAttrib->private_data = (void *)mConfigBuffer.nativeWindow; |
| mVASurfaceAttrib->flags = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC; |
| if (mConfigBuffer.flag & USE_TILING_MEMORY) |
| mVASurfaceAttrib->flags |= VA_SURFACE_EXTBUF_DESC_ENABLE_TILING; |
| |
| for (int i = 0; i < mNumSurfaces; i++) { |
| mVASurfaceAttrib->buffers[i] = (unsigned long)mConfigBuffer.graphicBufferHandler[i]; |
| } |
| |
| attribs[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType; |
| attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; |
| attribs[0].value.type = VAGenericValueTypeInteger; |
| attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC; |
| |
| attribs[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor; |
| attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; |
| attribs[1].value.type = VAGenericValueTypePointer; |
| attribs[1].value.value.p = (void *)mVASurfaceAttrib; |
| |
| vaStatus = vaCreateSurfaces( |
| mVADisplay, |
| format, |
| mVideoFormatInfo.surfaceWidth, |
| mVideoFormatInfo.surfaceHeight, |
| mSurfaces, |
| mNumSurfaces, |
| attribs, |
| 2); |
| } |
| } else { |
| vaStatus = vaCreateSurfaces( |
| mVADisplay, |
| format, |
| mVideoFormatInfo.width, |
| mVideoFormatInfo.height, |
| mSurfaces, |
| mNumSurfaces, |
| NULL, |
| 0); |
| mVideoFormatInfo.surfaceWidth = mVideoFormatInfo.width; |
| mVideoFormatInfo.surfaceHeight = mVideoFormatInfo.height; |
| } |
| CHECK_VA_STATUS("vaCreateSurfaces"); |
| |
| if (mNumExtraSurfaces != 0) { |
| vaStatus = vaCreateSurfaces( |
| mVADisplay, |
| format, |
| mVideoFormatInfo.surfaceWidth, |
| mVideoFormatInfo.surfaceHeight, |
| mExtraSurfaces, |
| mNumExtraSurfaces, |
| NULL, |
| 0); |
| CHECK_VA_STATUS("vaCreateSurfaces"); |
| } |
| |
| mVideoFormatInfo.surfaceNumber = mNumSurfaces; |
| mVideoFormatInfo.ctxSurfaces = mSurfaces; |
| |
| if ((int32_t)profile != VAProfileSoftwareDecoding) { |
| if (mStoreMetaData) { |
| if (mUseGEN) { |
| vaStatus = vaCreateContext( |
| mVADisplay, |
| mVAConfig, |
| mVideoFormatInfo.surfaceWidth, |
| mVideoFormatInfo.surfaceHeight, |
| 0, |
| NULL, |
| 0, |
| &mVAContext); |
| } else { |
| vaStatus = vaCreateContext( |
| mVADisplay, |
| mVAConfig, |
| mVideoFormatInfo.surfaceWidth, |
| mVideoFormatInfo.surfaceHeight, |
| 0, |
| NULL, |
| mNumSurfaces + mNumExtraSurfaces, |
| &mVAContext); |
| } |
| } else { |
| vaStatus = vaCreateContext( |
| mVADisplay, |
| mVAConfig, |
| mVideoFormatInfo.surfaceWidth, |
| mVideoFormatInfo.surfaceHeight, |
| 0, |
| mSurfaces, |
| mNumSurfaces + mNumExtraSurfaces, |
| &mVAContext); |
| } |
| CHECK_VA_STATUS("vaCreateContext"); |
| } |
| |
| mSurfaceBuffers = new VideoSurfaceBuffer [mNumSurfaces]; |
| if (mSurfaceBuffers == NULL) { |
| return DECODE_MEMORY_FAIL; |
| } |
| initSurfaceBuffer(true); |
| |
| if ((int32_t)profile == VAProfileSoftwareDecoding) { |
| // derive user pointer from surface for direct access |
| status = mapSurface(); |
| CHECK_STATUS("mapSurface") |
| } |
| |
| setRotationDegrees(mConfigBuffer.rotationDegrees); |
| |
| mVAStarted = true; |
| |
| pthread_mutex_lock(&mLock); |
| if (mStoreMetaData) { |
| for (uint32_t i = 0; i < mMetaDataBuffersNum; i++) { |
| status = createSurfaceFromHandle(i); |
| CHECK_STATUS("createSurfaceFromHandle"); |
| mSurfaceBuffers[i].renderBuffer.graphicBufferIndex = i; |
| } |
| } |
| pthread_mutex_unlock(&mLock); |
| |
| return DECODE_SUCCESS; |
| } |
| |
| Decode_Status VideoDecoderBase::terminateVA(void) { |
| mSignalBufferSize = 0; |
| for (int i = 0; i < MAX_GRAPHIC_BUFFER_NUM; i++) { |
| mSignalBufferPre[i] = NULL; |
| } |
| |
| if (mVAStarted == false) { |
| // VA hasn't been started yet |
| return DECODE_SUCCESS; |
| } |
| |
| if (mSurfaceBuffers) { |
| for (int32_t i = 0; i < mNumSurfaces; i++) { |
| if (mSurfaceBuffers[i].renderBuffer.rawData) { |
| if (mSurfaceBuffers[i].renderBuffer.rawData->data) { |
| delete [] mSurfaceBuffers[i].renderBuffer.rawData->data; |
| } |
| delete mSurfaceBuffers[i].renderBuffer.rawData; |
| } |
| if (mSurfaceBuffers[i].mappedData) { |
| // don't delete data pointer as it is mapped from surface |
| delete mSurfaceBuffers[i].mappedData; |
| } |
| } |
| delete [] mSurfaceBuffers; |
| mSurfaceBuffers = NULL; |
| } |
| |
| if (mVASurfaceAttrib) { |
| if (mVASurfaceAttrib->buffers) free(mVASurfaceAttrib->buffers); |
| delete mVASurfaceAttrib; |
| mVASurfaceAttrib = NULL; |
| } |
| |
| |
| if (mSurfaceUserPtr) { |
| delete [] mSurfaceUserPtr; |
| mSurfaceUserPtr = NULL; |
| } |
| |
| if (mSurfaces) { |
| vaDestroySurfaces(mVADisplay, mSurfaces, mStoreMetaData ? mMetaDataBuffersNum : (mNumSurfaces + mNumExtraSurfaces)); |
| delete [] mSurfaces; |
| mSurfaces = NULL; |
| } |
| |
| if (mVAContext != VA_INVALID_ID) { |
| vaDestroyContext(mVADisplay, mVAContext); |
| mVAContext = VA_INVALID_ID; |
| } |
| |
| if (mVAConfig != VA_INVALID_ID) { |
| vaDestroyConfig(mVADisplay, mVAConfig); |
| mVAConfig = VA_INVALID_ID; |
| } |
| |
| if (mVADisplay) { |
| vaTerminate(mVADisplay); |
| mVADisplay = NULL; |
| } |
| |
| if (mDisplay) { |
| #ifndef USE_HYBRID_DRIVER |
| delete mDisplay; |
| #endif |
| mDisplay = NULL; |
| } |
| |
| mVAStarted = false; |
| mInitialized = false; |
| mErrReportEnabled = false; |
| if (mStoreMetaData) { |
| mMetaDataBuffersNum = 0; |
| mSurfaceAcquirePos = 0; |
| } |
| return DECODE_SUCCESS; |
| } |
| |
| Decode_Status VideoDecoderBase::parseBuffer(uint8_t *buffer, int32_t size, bool config, void** vbpData) { |
| // DON'T check if mVAStarted == true |
| if (mParserHandle == NULL) { |
| return DECODE_NO_PARSER; |
| } |
| |
| uint32_t vbpStatus; |
| if (buffer == NULL || size <= 0) { |
| return DECODE_INVALID_DATA; |
| } |
| |
| uint8_t configFlag = config ? 1 : 0; |
| vbpStatus = mParserParse(mParserHandle, buffer, size, configFlag); |
| CHECK_VBP_STATUS("vbp_parse"); |
| |
| vbpStatus = mParserQuery(mParserHandle, vbpData); |
| CHECK_VBP_STATUS("vbp_query"); |
| |
| return DECODE_SUCCESS; |
| } |
| |
| Decode_Status VideoDecoderBase::mapSurface(void) { |
| VAStatus vaStatus = VA_STATUS_SUCCESS; |
| VAImage image; |
| uint8_t *userPtr; |
| mSurfaceUserPtr = new uint8_t* [mNumSurfaces]; |
| if (mSurfaceUserPtr == NULL) { |
| return DECODE_MEMORY_FAIL; |
| } |
| |
| for (int32_t i = 0; i< mNumSurfaces; i++) { |
| vaStatus = vaDeriveImage(mVADisplay, mSurfaces[i], &image); |
| CHECK_VA_STATUS("vaDeriveImage"); |
| vaStatus = vaMapBuffer(mVADisplay, image.buf, (void**)&userPtr); |
| CHECK_VA_STATUS("vaMapBuffer"); |
| mSurfaceUserPtr[i] = userPtr; |
| mSurfaceBuffers[i].mappedData = new VideoFrameRawData; |
| if (mSurfaceBuffers[i].mappedData == NULL) { |
| return DECODE_MEMORY_FAIL; |
| } |
| mSurfaceBuffers[i].mappedData->own = false; // derived from surface so can't be released |
| mSurfaceBuffers[i].mappedData->data = NULL; // specified during acquireSurfaceBuffer |
| mSurfaceBuffers[i].mappedData->fourcc = image.format.fourcc; |
| mSurfaceBuffers[i].mappedData->width = mVideoFormatInfo.width; |
| mSurfaceBuffers[i].mappedData->height = mVideoFormatInfo.height; |
| mSurfaceBuffers[i].mappedData->size = image.data_size; |
| for (int pi = 0; pi < 3; pi++) { |
| mSurfaceBuffers[i].mappedData->pitch[pi] = image.pitches[pi]; |
| mSurfaceBuffers[i].mappedData->offset[pi] = image.offsets[pi]; |
| } |
| // debug information |
| if (image.pitches[0] != image.pitches[1] || |
| image.width != mVideoFormatInfo.width || |
| image.height != mVideoFormatInfo.height || |
| image.offsets[0] != 0) { |
| WTRACE("Unexpected VAImage format, w = %d, h = %d, offset = %d", image.width, image.height, image.offsets[0]); |
| } |
| // TODO: do we need to unmap buffer? |
| //vaStatus = vaUnmapBuffer(mVADisplay, image.buf); |
| //CHECK_VA_STATUS("vaMapBuffer"); |
| vaStatus = vaDestroyImage(mVADisplay,image.image_id); |
| CHECK_VA_STATUS("vaDestroyImage"); |
| |
| } |
| return DECODE_SUCCESS; |
| } |
| |
| Decode_Status VideoDecoderBase::getRawDataFromSurface(VideoRenderBuffer *renderBuffer, uint8_t *pRawData, uint32_t *pSize, bool internal) { |
| if (internal) { |
| if (mAcquiredBuffer == NULL) { |
| return DECODE_FAIL; |
| } |
| renderBuffer = &(mAcquiredBuffer->renderBuffer); |
| } |
| |
| VAStatus vaStatus; |
| VAImage vaImage; |
| vaStatus = vaSyncSurface(renderBuffer->display, renderBuffer->surface); |
| CHECK_VA_STATUS("vaSyncSurface"); |
| |
| vaStatus = vaDeriveImage(renderBuffer->display, renderBuffer->surface, &vaImage); |
| CHECK_VA_STATUS("vaDeriveImage"); |
| |
| void *pBuf = NULL; |
| vaStatus = vaMapBuffer(renderBuffer->display, vaImage.buf, &pBuf); |
| CHECK_VA_STATUS("vaMapBuffer"); |
| |
| |
| // size in NV12 format |
| uint32_t cropWidth = mVideoFormatInfo.width - (mVideoFormatInfo.cropLeft + mVideoFormatInfo.cropRight); |
| uint32_t cropHeight = mVideoFormatInfo.height - (mVideoFormatInfo.cropBottom + mVideoFormatInfo.cropTop); |
| if (strcasecmp(mVideoFormatInfo.mimeType,"video/avc") == 0 || |
| strcasecmp(mVideoFormatInfo.mimeType,"video/avc-secure") == 0 || |
| strcasecmp(mVideoFormatInfo.mimeType,"video/h264") == 0) { |
| cropHeight = mVideoFormatInfo.height; |
| cropWidth = mVideoFormatInfo.width; |
| } |
| int32_t size = cropWidth * cropHeight * 3 / 2; |
| |
| if (internal) { |
| VideoFrameRawData *rawData = NULL; |
| if (renderBuffer->rawData == NULL) { |
| rawData = new VideoFrameRawData; |
| if (rawData == NULL) { |
| return DECODE_MEMORY_FAIL; |
| } |
| memset(rawData, 0, sizeof(VideoFrameRawData)); |
| renderBuffer->rawData = rawData; |
| } else { |
| rawData = renderBuffer->rawData; |
| } |
| |
| if (rawData->data != NULL && rawData->size != size) { |
| delete [] rawData->data; |
| rawData->data = NULL; |
| rawData->size = 0; |
| } |
| if (rawData->data == NULL) { |
| rawData->data = new uint8_t [size]; |
| if (rawData->data == NULL) { |
| return DECODE_MEMORY_FAIL; |
| } |
| } |
| |
| rawData->own = true; // allocated by this library |
| rawData->width = cropWidth; |
| rawData->height = cropHeight; |
| rawData->pitch[0] = cropWidth; |
| rawData->pitch[1] = cropWidth; |
| rawData->pitch[2] = 0; // interleaved U/V, two planes |
| rawData->offset[0] = 0; |
| rawData->offset[1] = cropWidth * cropHeight; |
| rawData->offset[2] = cropWidth * cropHeight * 3 / 2; |
| rawData->size = size; |
| rawData->fourcc = 'NV12'; |
| |
| pRawData = rawData->data; |
| } else { |
| *pSize = size; |
| } |
| |
| if (size == (int32_t)vaImage.data_size) { |
| #ifdef __SSE4_1__ |
| stream_memcpy(pRawData, pBuf, size); |
| #else |
| memcpy(pRawData, pBuf, size); |
| #endif |
| } else { |
| // copy Y data |
| uint8_t *src = (uint8_t*)pBuf; |
| uint8_t *dst = pRawData; |
| uint32_t row = 0; |
| for (row = 0; row < cropHeight; row++) { |
| #ifdef __SSE4_1__ |
| stream_memcpy(dst, src, cropWidth); |
| #else |
| memcpy(dst, src, cropWidth); |
| #endif |
| dst += cropWidth; |
| src += vaImage.pitches[0]; |
| } |
| // copy interleaved V and U data |
| src = (uint8_t*)pBuf + vaImage.offsets[1]; |
| for (row = 0; row < cropHeight / 2; row++) { |
| #ifdef __SSE4_1__ |
| stream_memcpy(dst, src, cropWidth); |
| #else |
| memcpy(dst, src, cropWidth); |
| #endif |
| dst += cropWidth; |
| src += vaImage.pitches[1]; |
| } |
| } |
| |
| vaStatus = vaUnmapBuffer(renderBuffer->display, vaImage.buf); |
| CHECK_VA_STATUS("vaUnmapBuffer"); |
| |
| vaStatus = vaDestroyImage(renderBuffer->display, vaImage.image_id); |
| CHECK_VA_STATUS("vaDestroyImage"); |
| |
| return DECODE_SUCCESS; |
| } |
| |
| Decode_Status VideoDecoderBase::createSurfaceFromHandle(int index) { |
| VAStatus vaStatus = VA_STATUS_SUCCESS; |
| Decode_Status status; |
| |
| int32_t format = VA_RT_FORMAT_YUV420; |
| if (mConfigBuffer.flag & WANT_SURFACE_PROTECTION) { |
| #ifndef USE_AVC_SHORT_FORMAT |
| format |= VA_RT_FORMAT_PROTECTED; |
| WTRACE("Surface is protected."); |
| #endif |
| } |
| VASurfaceAttrib attribs[2]; |
| VASurfaceAttribExternalBuffers surfExtBuf; |
| surfExtBuf.num_buffers = 1; |
| surfExtBuf.pixel_format = VA_FOURCC_NV12; |
| surfExtBuf.width = mVideoFormatInfo.surfaceWidth; |
| surfExtBuf.height = mVideoFormatInfo.surfaceHeight; |
| surfExtBuf.data_size = mConfigBuffer.graphicBufferHStride * mConfigBuffer.graphicBufferVStride * 1.5; |
| surfExtBuf.num_planes = 2; |
| surfExtBuf.pitches[0] = mConfigBuffer.graphicBufferHStride; |
| surfExtBuf.pitches[1] = mConfigBuffer.graphicBufferHStride; |
| surfExtBuf.pitches[2] = 0; |
| surfExtBuf.pitches[3] = 0; |
| surfExtBuf.offsets[0] = 0; |
| surfExtBuf.offsets[1] = mConfigBuffer.graphicBufferHStride * mConfigBuffer.graphicBufferVStride; |
| surfExtBuf.offsets[2] = 0; |
| surfExtBuf.offsets[3] = 0; |
| surfExtBuf.private_data = (void *)mConfigBuffer.nativeWindow; |
| surfExtBuf.flags = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC; |
| if (mConfigBuffer.flag & USE_TILING_MEMORY) { |
| surfExtBuf.flags |= VA_SURFACE_EXTBUF_DESC_ENABLE_TILING; |
| } |
| |
| surfExtBuf.buffers = (long unsigned int*)&(mConfigBuffer.graphicBufferHandler[index]); |
| |
| attribs[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType; |
| attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; |
| attribs[0].value.type = VAGenericValueTypeInteger; |
| attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC; |
| |
| attribs[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor; |
| attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; |
| attribs[1].value.type = VAGenericValueTypePointer; |
| attribs[1].value.value.p = (void *)&surfExtBuf; |
| |
| vaStatus = vaCreateSurfaces( |
| mVADisplay, |
| format, |
| mVideoFormatInfo.surfaceWidth, |
| mVideoFormatInfo.surfaceHeight, |
| &(mSurfaces[index]), |
| 1, |
| attribs, |
| 2); |
| CHECK_VA_STATUS("vaCreateSurfaces"); |
| |
| return DECODE_SUCCESS; |
| } |
| |
| void VideoDecoderBase::initSurfaceBuffer(bool reset) { |
| bool useGraphicBuffer = mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER; |
| if (useGraphicBuffer && reset) { |
| pthread_mutex_lock(&mLock); |
| } |
| for (int32_t i = 0; i < mNumSurfaces; i++) { |
| mSurfaceBuffers[i].renderBuffer.display = mVADisplay; |
| mSurfaceBuffers[i].renderBuffer.surface = VA_INVALID_SURFACE; // set in acquireSurfaceBuffer |
| mSurfaceBuffers[i].renderBuffer.flag = 0; |
| mSurfaceBuffers[i].renderBuffer.scanFormat = VA_FRAME_PICTURE; |
| mSurfaceBuffers[i].renderBuffer.timeStamp = 0; |
| mSurfaceBuffers[i].referenceFrame = false; |
| mSurfaceBuffers[i].asReferernce= false; |
| mSurfaceBuffers[i].pictureOrder = 0; |
| mSurfaceBuffers[i].next = NULL; |
| if (reset == true) { |
| mSurfaceBuffers[i].renderBuffer.rawData = NULL; |
| mSurfaceBuffers[i].mappedData = NULL; |
| } |
| if (useGraphicBuffer) { |
| if (reset) { |
| mSurfaceBuffers[i].renderBuffer.graphicBufferHandle = mConfigBuffer.graphicBufferHandler[i]; |
| mSurfaceBuffers[i].renderBuffer.renderDone = false; //default false |
| for (uint32_t j = 0; j < mSignalBufferSize; j++) { |
| if(mSignalBufferPre[j] != NULL && mSignalBufferPre[j] == mSurfaceBuffers[i].renderBuffer.graphicBufferHandle) { |
| mSurfaceBuffers[i].renderBuffer.renderDone = true; |
| VTRACE("initSurfaceBuffer set renderDone = true index = %d", i); |
| mSignalBufferPre[j] = NULL; |
| break; |
| } |
| } |
| } else { |
| mSurfaceBuffers[i].renderBuffer.renderDone = false; |
| } |
| } else { |
| mSurfaceBuffers[i].renderBuffer.graphicBufferHandle = NULL; |
| mSurfaceBuffers[i].renderBuffer.renderDone = true; |
| } |
| mSurfaceBuffers[i].renderBuffer.graphicBufferIndex = i; |
| } |
| |
| if (useGraphicBuffer && reset) { |
| mInitialized = true; |
| mSignalBufferSize = 0; |
| pthread_mutex_unlock(&mLock); |
| } |
| } |
| |
| Decode_Status VideoDecoderBase::signalRenderDone(void * graphichandler, bool isNew) { |
| Decode_Status status; |
| if (graphichandler == NULL) { |
| return DECODE_SUCCESS; |
| } |
| pthread_mutex_lock(&mLock); |
| bool graphicBufferMode = mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER; |
| if (mStoreMetaData) { |
| if (!graphicBufferMode) { |
| pthread_mutex_unlock(&mLock); |
| return DECODE_SUCCESS; |
| } |
| |
| if ((mMetaDataBuffersNum < mConfigBuffer.surfaceNumber) && isNew) { |
| mConfigBuffer.graphicBufferHandler[mMetaDataBuffersNum] = graphichandler; |
| if (mInitialized) { |
| mSurfaceBuffers[mMetaDataBuffersNum].renderBuffer.graphicBufferHandle = graphichandler; |
| mSurfaceBuffers[mMetaDataBuffersNum].renderBuffer.graphicBufferIndex = mMetaDataBuffersNum; |
| } |
| } |
| } |
| int i = 0; |
| if (!mInitialized) { |
| if (mSignalBufferSize >= MAX_GRAPHIC_BUFFER_NUM) { |
| pthread_mutex_unlock(&mLock); |
| return DECODE_INVALID_DATA; |
| } |
| mSignalBufferPre[mSignalBufferSize++] = graphichandler; |
| VTRACE("SignalRenderDoneFlag mInitialized = false graphichandler = %p, mSignalBufferSize = %d", graphichandler, mSignalBufferSize); |
| } else { |
| if (!graphicBufferMode) { |
| pthread_mutex_unlock(&mLock); |
| return DECODE_SUCCESS; |
| } |
| if (mStoreMetaData) { |
| if ((mMetaDataBuffersNum < mConfigBuffer.surfaceNumber) && isNew) { |
| if (mVAStarted) { |
| status = createSurfaceFromHandle(mMetaDataBuffersNum); |
| CHECK_STATUS("createSurfaceFromHandle") |
| } |
| } |
| } |
| for (i = 0; i < mNumSurfaces; i++) { |
| if (mSurfaceBuffers[i].renderBuffer.graphicBufferHandle == graphichandler) { |
| mSurfaceBuffers[i].renderBuffer.renderDone = true; |
| VTRACE("SignalRenderDoneFlag mInitialized = true index = %d", i); |
| break; |
| } |
| } |
| } |
| |
| if (mStoreMetaData) { |
| if ((mMetaDataBuffersNum < mConfigBuffer.surfaceNumber) && isNew) { |
| mMetaDataBuffersNum++; |
| } |
| } |
| |
| pthread_mutex_unlock(&mLock); |
| |
| return DECODE_SUCCESS; |
| |
| } |
| |
| void VideoDecoderBase::querySurfaceRenderStatus(VideoSurfaceBuffer* surface) { |
| VASurfaceStatus surfStat = VASurfaceReady; |
| VAStatus vaStat = VA_STATUS_SUCCESS; |
| |
| if (!surface) { |
| LOGW("SurfaceBuffer not ready yet"); |
| return; |
| } |
| surface->renderBuffer.driverRenderDone = true; |
| |
| #ifndef USE_GEN_HW |
| if (surface->renderBuffer.surface != VA_INVALID_SURFACE && |
| (mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER)) { |
| |
| vaStat = vaQuerySurfaceStatus(mVADisplay, surface->renderBuffer.surface, &surfStat); |
| |
| if ((vaStat == VA_STATUS_SUCCESS) && (surfStat != VASurfaceReady)) |
| surface->renderBuffer.driverRenderDone = false; |
| |
| } |
| #endif |
| |
| } |
| |
| // This function should be called before start() to load different type of parsers |
| #if (defined USE_AVC_SHORT_FORMAT || defined USE_SLICE_HEADER_PARSING) |
| Decode_Status VideoDecoderBase::setParserType(_vbp_parser_type type) { |
| if ((int32_t)type != VBP_INVALID) { |
| ITRACE("Parser Type = %d", (int32_t)type); |
| mParserType = type; |
| return DECODE_SUCCESS; |
| } else { |
| ETRACE("Invalid parser type = %d", (int32_t)type); |
| return DECODE_NO_PARSER; |
| } |
| } |
| |
| Decode_Status VideoDecoderBase::updateBuffer(uint8_t *buffer, int32_t size, void** vbpData) { |
| if (mParserHandle == NULL) { |
| return DECODE_NO_PARSER; |
| } |
| |
| uint32_t vbpStatus; |
| if (buffer == NULL || size <= 0) { |
| return DECODE_INVALID_DATA; |
| } |
| |
| vbpStatus = mParserUpdate(mParserHandle, buffer, size, vbpData); |
| CHECK_VBP_STATUS("vbp_update"); |
| |
| return DECODE_SUCCESS; |
| } |
| |
| Decode_Status VideoDecoderBase::queryBuffer(void** vbpData) { |
| if (mParserHandle == NULL) { |
| return DECODE_NO_PARSER; |
| } |
| |
| uint32_t vbpStatus; |
| vbpStatus = mParserQuery(mParserHandle, vbpData); |
| CHECK_VBP_STATUS("vbp_query"); |
| |
| return DECODE_SUCCESS; |
| } |
| |
| Decode_Status VideoDecoderBase::getCodecSpecificConfigs(VAProfile profile, VAConfigID *config) { |
| VAStatus vaStatus; |
| VAConfigAttrib attrib; |
| attrib.type = VAConfigAttribRTFormat; |
| attrib.value = VA_RT_FORMAT_YUV420; |
| |
| if (config == NULL) { |
| ETRACE("Invalid parameter!"); |
| return DECODE_FAIL; |
| } |
| |
| vaStatus = vaCreateConfig( |
| mVADisplay, |
| profile, |
| VAEntrypointVLD, |
| &attrib, |
| 1, |
| config); |
| |
| CHECK_VA_STATUS("vaCreateConfig"); |
| |
| return DECODE_SUCCESS; |
| } |
| #endif |
| Decode_Status VideoDecoderBase::checkHardwareCapability() { |
| return DECODE_SUCCESS; |
| } |
| |
| void VideoDecoderBase::drainDecodingErrors(VideoErrorBuffer *outErrBuf, VideoRenderBuffer *currentSurface) { |
| if (mErrReportEnabled && outErrBuf && currentSurface) { |
| memcpy(outErrBuf, &(currentSurface->errBuf), sizeof(VideoErrorBuffer)); |
| |
| currentSurface->errBuf.errorNumber = 0; |
| currentSurface->errBuf.timeStamp = INVALID_PTS; |
| } |
| if (outErrBuf) |
| VTRACE("%s: error number is %d", __FUNCTION__, outErrBuf->errorNumber); |
| } |
| |
| void VideoDecoderBase::fillDecodingErrors(VideoRenderBuffer *currentSurface) { |
| VAStatus ret; |
| |
| if (mErrReportEnabled) { |
| currentSurface->errBuf.timeStamp = currentSurface->timeStamp; |
| // TODO: is 10 a suitable number? |
| VASurfaceDecodeMBErrors *err_drv_output = NULL; |
| ret = vaQuerySurfaceError(mVADisplay, currentSurface->surface, VA_STATUS_ERROR_DECODING_ERROR, (void **)&err_drv_output); |
| if (ret || !err_drv_output) { |
| WTRACE("vaQuerySurfaceError failed."); |
| return; |
| } |
| |
| int offset = 0x1 & currentSurface->errBuf.errorNumber;// offset is either 0 or 1 |
| for (int i = 0; i < MAX_ERR_NUM - offset; i++) { |
| if (err_drv_output[i].status != -1) { |
| currentSurface->errBuf.errorNumber++; |
| currentSurface->errBuf.errorArray[i + offset].type = DecodeMBError; |
| currentSurface->errBuf.errorArray[i + offset].error_data.mb_pos.start_mb = err_drv_output[i].start_mb; |
| currentSurface->errBuf.errorArray[i + offset].error_data.mb_pos.end_mb = err_drv_output[i].end_mb; |
| currentSurface->errBuf.errorArray[i + offset].num_mbs = err_drv_output[i].end_mb - err_drv_output[i].start_mb + 1; |
| ITRACE("Error Index[%d]: type = %d, start_mb = %d, end_mb = %d", |
| currentSurface->errBuf.errorNumber - 1, |
| currentSurface->errBuf.errorArray[i + offset].type, |
| currentSurface->errBuf.errorArray[i + offset].error_data.mb_pos.start_mb, |
| currentSurface->errBuf.errorArray[i + offset].error_data.mb_pos.end_mb); |
| } else break; |
| } |
| ITRACE("%s: error number of current surface is %d, timestamp @%llu", |
| __FUNCTION__, currentSurface->errBuf.errorNumber, currentSurface->timeStamp); |
| } |
| } |
| |
| void VideoDecoderBase::setRotationDegrees(int32_t rotationDegrees) { |
| if (mRotationDegrees == rotationDegrees) { |
| return; |
| } |
| |
| ITRACE("set new rotation degree: %d", rotationDegrees); |
| VADisplayAttribute rotate; |
| rotate.type = VADisplayAttribRotation; |
| rotate.value = VA_ROTATION_NONE; |
| if (rotationDegrees == 0) |
| rotate.value = VA_ROTATION_NONE; |
| else if (rotationDegrees == 90) |
| rotate.value = VA_ROTATION_90; |
| else if (rotationDegrees == 180) |
| rotate.value = VA_ROTATION_180; |
| else if (rotationDegrees == 270) |
| rotate.value = VA_ROTATION_270; |
| |
| VAStatus ret = vaSetDisplayAttributes(mVADisplay, &rotate, 1); |
| if (ret) { |
| ETRACE("Failed to set rotation degree."); |
| } |
| mRotationDegrees = rotationDegrees; |
| } |
| |
| void VideoDecoderBase::setRenderRect() { |
| |
| if (!mVADisplay) |
| return; |
| |
| VAStatus ret; |
| VARectangle rect; |
| rect.x = mVideoFormatInfo.cropLeft; |
| rect.y = mVideoFormatInfo.cropTop; |
| rect.width = mVideoFormatInfo.width - (mVideoFormatInfo.cropLeft + mVideoFormatInfo.cropRight); |
| rect.height = mVideoFormatInfo.height - (mVideoFormatInfo.cropBottom + mVideoFormatInfo.cropTop); |
| if (strcasecmp(mVideoFormatInfo.mimeType,"video/avc") == 0 || |
| strcasecmp(mVideoFormatInfo.mimeType,"video/avc-secure") == 0 || |
| strcasecmp(mVideoFormatInfo.mimeType,"video/h264") == 0) { |
| rect.height = mVideoFormatInfo.height; |
| rect.width = mVideoFormatInfo.width; |
| } |
| |
| VADisplayAttribute render_rect; |
| render_rect.type = VADisplayAttribRenderRect; |
| render_rect.attrib_ptr = ▭ |
| |
| ret = vaSetDisplayAttributes(mVADisplay, &render_rect, 1); |
| if (ret) { |
| ETRACE("Failed to set rotation degree."); |
| } |
| } |
| |
| void VideoDecoderBase::setColorSpaceInfo(int32_t colorMatrix, int32_t videoRange) { |
| ITRACE("set colorMatrix: 0x%x ", colorMatrix); |
| VADisplayAttribute cm; |
| cm.type = VADisplayAttribCSCMatrix; |
| if (colorMatrix == VA_SRC_BT601) { |
| cm.attrib_ptr = &s601; |
| } else if (colorMatrix == VA_SRC_BT709) { |
| cm.attrib_ptr = &s709; |
| } else { |
| // if we can't get the color matrix or it's not BT601 or BT709 |
| // we decide the color matrix according to clip resolution |
| if (mVideoFormatInfo.width < 1280 && mVideoFormatInfo.height < 720) |
| cm.attrib_ptr = &s601; |
| else |
| cm.attrib_ptr = &s709; |
| } |
| |
| VAStatus ret = vaSetDisplayAttributes(mVADisplay, &cm, 1); |
| |
| if (ret) { |
| ETRACE("Failed to set colorMatrix."); |
| } |
| |
| // 1: full range, 0: reduced range |
| ITRACE("set videoRange: %d ", videoRange); |
| VADisplayAttribute vr; |
| vr.type = VADisplayAttribColorRange; |
| vr.value = (videoRange == 1) ? VA_SOURCE_RANGE_FULL : VA_SOURCE_RANGE_REDUCED; |
| |
| ret = vaSetDisplayAttributes(mVADisplay, &vr, 1); |
| |
| if (ret) { |
| ETRACE("Failed to set videoRange."); |
| } |
| } |