void SoftHEVC::onQueueFilled(OMX_U32 portIndex) { UNUSED(portIndex); if (mSignalledError) { return; } if (mOutputPortSettingsChange != NONE) { return; } if (NULL == mCodecCtx) { if (OK != initDecoder()) { ALOGE("Failed to initialize decoder"); notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); mSignalledError = true; return; } } if (outputBufferWidth() != mStride) { /* Set the run-time (dynamic) parameters */ mStride = outputBufferWidth(); setParams(mStride); } List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex); List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex); while (!outQueue.empty()) { BufferInfo *inInfo; OMX_BUFFERHEADERTYPE *inHeader; BufferInfo *outInfo; OMX_BUFFERHEADERTYPE *outHeader; size_t timeStampIx; inInfo = NULL; inHeader = NULL; if (!mIsInFlush) { if (!inQueue.empty()) { inInfo = *inQueue.begin(); inHeader = inInfo->mHeader; } else { break; } } outInfo = *outQueue.begin(); outHeader = outInfo->mHeader; outHeader->nFlags = 0; outHeader->nTimeStamp = 0; outHeader->nOffset = 0; if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) { mReceivedEOS = true; if (inHeader->nFilledLen == 0) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); inHeader = NULL; setFlushMode(); } } /* Get a free slot in timestamp array to hold input timestamp */ { size_t i; timeStampIx = 0; for (i = 0; i < MAX_TIME_STAMPS; i++) { if (!mTimeStampsValid[i]) { timeStampIx = i; break; } } if (inHeader != NULL) { mTimeStampsValid[timeStampIx] = true; mTimeStamps[timeStampIx] = inHeader->nTimeStamp; } } { ivd_video_decode_ip_t s_dec_ip; ivd_video_decode_op_t s_dec_op; WORD32 timeDelay, timeTaken; size_t sizeY, sizeUV; if (!setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx)) { ALOGE("Decoder arg setup failed"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } GETTIME(&mTimeStart, NULL); /* Compute time elapsed between end of previous decode() * to start of current decode() */ TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); IV_API_CALL_STATUS_T status; status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); bool unsupportedResolution = (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & 0xFF)); /* Check for unsupported dimensions */ if (unsupportedResolution) { ALOGE("Unsupported resolution : %dx%d", mWidth, mHeight); notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); mSignalledError = true; return; } bool allocationFailed = (IVD_MEM_ALLOC_FAILED == (s_dec_op.u4_error_code & 0xFF)); if (allocationFailed) { ALOGE("Allocation failure in decoder"); notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); mSignalledError = true; return; } bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF)); getVUIParams(); GETTIME(&mTimeEnd, NULL); /* Compute time taken for decode() */ TIME_DIFF(mTimeStart, mTimeEnd, timeTaken); ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay, s_dec_op.u4_num_bytes_consumed); if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) { mFlushNeeded = true; } if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) { /* If the input did not contain picture data, then ignore * the associated timestamp */ mTimeStampsValid[timeStampIx] = false; } // If the decoder is in the changing resolution mode and there is no output present, // that means the switching is done and it's ready to reset the decoder and the plugin. if (mChangingResolution && !s_dec_op.u4_output_present) { mChangingResolution = false; resetDecoder(); resetPlugin(); mStride = outputBufferWidth(); setParams(mStride); continue; } if (resChanged) { mChangingResolution = true; if (mFlushNeeded) { setFlushMode(); } continue; } // Combine the resolution change and coloraspects change in one PortSettingChange event // if necessary. if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) { uint32_t width = s_dec_op.u4_pic_wd; uint32_t height = s_dec_op.u4_pic_ht; bool portWillReset = false; handlePortSettingsChange(&portWillReset, width, height); if (portWillReset) { resetDecoder(); resetPlugin(); return; } } else if (mUpdateColorAspects) { notify(OMX_EventPortSettingsChanged, kOutputPortIndex, kDescribeColorAspectsIndex, NULL); mUpdateColorAspects = false; return; } if (s_dec_op.u4_output_present) { outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * 3) / 2; outHeader->nTimeStamp = mTimeStamps[s_dec_op.u4_ts]; mTimeStampsValid[s_dec_op.u4_ts] = false; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; } else if (mIsInFlush) { /* If in flush mode and no output is returned by the codec, * then come out of flush mode */ mIsInFlush = false; /* If EOS was recieved on input port and there is no output * from the codec, then signal EOS on output port */ if (mReceivedEOS) { outHeader->nFilledLen = 0; outHeader->nFlags |= OMX_BUFFERFLAG_EOS; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; resetPlugin(); } } } /* If input EOS is seen and decoder is not in flush mode, * set the decoder in flush mode. * There can be a case where EOS is sent along with last picture data * In that case, only after decoding that input data, decoder has to be * put in flush. This case is handled here */ if (mReceivedEOS && !mIsInFlush) { setFlushMode(); } // TODO: Handle more than one picture data if (inHeader != NULL) { inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); inInfo = NULL; notifyEmptyBufferDone(inHeader); inHeader = NULL; } } }
void SoftAVC::onQueueFilled(OMX_U32 /* portIndex */) { if (mSignalledError || mOutputPortSettingsChange != NONE) { return; } if (mEOSStatus == OUTPUT_FRAMES_FLUSHED) { return; } List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex); List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex); if (mHeadersDecoded) { // Dequeue any already decoded output frames to free up space // in the output queue. drainAllOutputBuffers(false /* eos */); } H264SwDecRet ret = H264SWDEC_PIC_RDY; bool portWillReset = false; while ((mEOSStatus != INPUT_DATA_AVAILABLE || !inQueue.empty()) && outQueue.size() == kNumOutputBuffers) { if (mEOSStatus == INPUT_EOS_SEEN) { drainAllOutputBuffers(true /* eos */); return; } BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; ++mPicId; OMX_BUFFERHEADERTYPE *header = new OMX_BUFFERHEADERTYPE; memset(header, 0, sizeof(OMX_BUFFERHEADERTYPE)); header->nTimeStamp = inHeader->nTimeStamp; header->nFlags = inHeader->nFlags; if (header->nFlags & OMX_BUFFERFLAG_EOS) { mEOSStatus = INPUT_EOS_SEEN; } mPicToHeaderMap.add(mPicId, header); inQueue.erase(inQueue.begin()); H264SwDecInput inPicture; H264SwDecOutput outPicture; memset(&inPicture, 0, sizeof(inPicture)); inPicture.dataLen = inHeader->nFilledLen; inPicture.pStream = inHeader->pBuffer + inHeader->nOffset; inPicture.picId = mPicId; inPicture.intraConcealmentMethod = 1; H264SwDecPicture decodedPicture; while (inPicture.dataLen > 0) { ret = H264SwDecDecode(mHandle, &inPicture, &outPicture); if (ret == H264SWDEC_HDRS_RDY_BUFF_NOT_EMPTY || ret == H264SWDEC_PIC_RDY_BUFF_NOT_EMPTY) { inPicture.dataLen -= (u32)(outPicture.pStrmCurrPos - inPicture.pStream); inPicture.pStream = outPicture.pStrmCurrPos; if (ret == H264SWDEC_HDRS_RDY_BUFF_NOT_EMPTY) { mHeadersDecoded = true; H264SwDecInfo decoderInfo; CHECK(H264SwDecGetInfo(mHandle, &decoderInfo) == H264SWDEC_OK); SoftVideoDecoderOMXComponent::CropSettingsMode cropSettingsMode = handleCropParams(decoderInfo); handlePortSettingsChange( &portWillReset, decoderInfo.picWidth, decoderInfo.picHeight, cropSettingsMode); } } else { if (portWillReset) { if (H264SwDecNextPicture(mHandle, &decodedPicture, 0) == H264SWDEC_PIC_RDY) { // Save this output buffer; otherwise, it will be // lost during dynamic port reconfiguration because // OpenMAX client will delete _all_ output buffers // in the process. saveFirstOutputBuffer( decodedPicture.picId, (uint8_t *)decodedPicture.pOutputPicture); } } inPicture.dataLen = 0; if (ret < 0) { ALOGE("Decoder failed: %d", ret); notify(OMX_EventError, OMX_ErrorUndefined, ERROR_MALFORMED, NULL); mSignalledError = true; return; } } } inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); if (portWillReset) { return; } if (mFirstPicture && !outQueue.empty()) { drainOneOutputBuffer(mFirstPictureId, mFirstPicture); delete[] mFirstPicture; mFirstPicture = NULL; mFirstPictureId = -1; } drainAllOutputBuffers(false /* eos */); } }
void SoftAVC::onQueueFilled(OMX_U32 portIndex) { UNUSED(portIndex); if (mSignalledError) { return; } if (mOutputPortSettingsChange != NONE) { return; } List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex); List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex); /* If input EOS is seen and decoder is not in flush mode, * set the decoder in flush mode. * There can be a case where EOS is sent along with last picture data * In that case, only after decoding that input data, decoder has to be * put in flush. This case is handled here */ if (mReceivedEOS && !mIsInFlush) { setFlushMode(); } while (!outQueue.empty()) { BufferInfo *inInfo; OMX_BUFFERHEADERTYPE *inHeader; BufferInfo *outInfo; OMX_BUFFERHEADERTYPE *outHeader; size_t timeStampIx; inInfo = NULL; inHeader = NULL; if (!mIsInFlush) { if (!inQueue.empty()) { inInfo = *inQueue.begin(); inHeader = inInfo->mHeader; if (inHeader == NULL) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; continue; } } else { break; } } outInfo = *outQueue.begin(); outHeader = outInfo->mHeader; outHeader->nFlags = 0; outHeader->nTimeStamp = 0; outHeader->nOffset = 0; if (inHeader != NULL) { if (inHeader->nFilledLen == 0) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); if (!(inHeader->nFlags & OMX_BUFFERFLAG_EOS)) { continue; } mReceivedEOS = true; inHeader = NULL; setFlushMode(); } else if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { mReceivedEOS = true; } } // When there is an init required and the decoder is not in flush mode, // update output port's definition and reinitialize decoder. if (mInitNeeded && !mIsInFlush) { bool portWillReset = false; status_t err = reInitDecoder(mNewWidth, mNewHeight); if (err != OK) { notify(OMX_EventError, OMX_ErrorUnsupportedSetting, err, NULL); mSignalledError = true; return; } handlePortSettingsChange(&portWillReset, mNewWidth, mNewHeight); return; } /* Get a free slot in timestamp array to hold input timestamp */ { size_t i; timeStampIx = 0; for (i = 0; i < MAX_TIME_STAMPS; i++) { if (!mTimeStampsValid[i]) { timeStampIx = i; break; } } if (inHeader != NULL) { mTimeStampsValid[timeStampIx] = true; mTimeStamps[timeStampIx] = inHeader->nTimeStamp; } } { ivd_video_decode_ip_t s_dec_ip; ivd_video_decode_op_t s_dec_op; WORD32 timeDelay, timeTaken; size_t sizeY, sizeUV; setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx); // If input dump is enabled, then write to file DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes); GETTIME(&mTimeStart, NULL); /* Compute time elapsed between end of previous decode() * to start of current decode() */ TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); IV_API_CALL_STATUS_T status; status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); bool unsupportedDimensions = (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & 0xFF)); bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF)); bool unsupportedLevel = (IH264D_UNSUPPORTED_LEVEL == (s_dec_op.u4_error_code & 0xFF)); GETTIME(&mTimeEnd, NULL); /* Compute time taken for decode() */ TIME_DIFF(mTimeStart, mTimeEnd, timeTaken); PRINT_TIME("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay, s_dec_op.u4_num_bytes_consumed); if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) { mFlushNeeded = true; } if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) { /* If the input did not contain picture data, then ignore * the associated timestamp */ mTimeStampsValid[timeStampIx] = false; } // This is needed to handle CTS DecoderTest testCodecResetsH264WithoutSurface, // which is not sending SPS/PPS after port reconfiguration and flush to the codec. if (unsupportedDimensions && !mFlushNeeded) { bool portWillReset = false; mNewWidth = s_dec_op.u4_pic_wd; mNewHeight = s_dec_op.u4_pic_ht; status_t err = reInitDecoder(mNewWidth, mNewHeight); if (err != OK) { notify(OMX_EventError, OMX_ErrorUnsupportedSetting, err, NULL); mSignalledError = true; return; } handlePortSettingsChange(&portWillReset, mNewWidth, mNewHeight); setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx); ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); return; } if (unsupportedLevel && !mFlushNeeded) { mNewLevel = 51; status_t err = reInitDecoder(mNewWidth, mNewHeight); if (err != OK) { notify(OMX_EventError, OMX_ErrorUnsupportedSetting, err, NULL); mSignalledError = true; return; } setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx); ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); return; } // If the decoder is in the changing resolution mode and there is no output present, // that means the switching is done and it's ready to reset the decoder and the plugin. if (mChangingResolution && !s_dec_op.u4_output_present) { mChangingResolution = false; resetDecoder(); resetPlugin(); continue; } if (unsupportedDimensions || resChanged) { mChangingResolution = true; if (mFlushNeeded) { setFlushMode(); } if (unsupportedDimensions) { mNewWidth = s_dec_op.u4_pic_wd; mNewHeight = s_dec_op.u4_pic_ht; mInitNeeded = true; } continue; } if (unsupportedLevel) { if (mFlushNeeded) { setFlushMode(); } mNewLevel = 51; mInitNeeded = true; continue; } if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) { uint32_t width = s_dec_op.u4_pic_wd; uint32_t height = s_dec_op.u4_pic_ht; bool portWillReset = false; handlePortSettingsChange(&portWillReset, width, height); if (portWillReset) { resetDecoder(); return; } } if (s_dec_op.u4_output_present) { outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * 3) / 2; outHeader->nTimeStamp = mTimeStamps[s_dec_op.u4_ts]; mTimeStampsValid[s_dec_op.u4_ts] = false; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; } else { /* If in flush mode and no output is returned by the codec, * then come out of flush mode */ mIsInFlush = false; /* If EOS was recieved on input port and there is no output * from the codec, then signal EOS on output port */ if (mReceivedEOS) { outHeader->nFilledLen = 0; outHeader->nFlags |= OMX_BUFFERFLAG_EOS; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; resetPlugin(); } } } if (inHeader != NULL) { inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); inInfo = NULL; notifyEmptyBufferDone(inHeader); inHeader = NULL; } } }
void SoftMPEG2::onQueueFilled(OMX_U32 portIndex) { UNUSED(portIndex); if (mOutputPortSettingsChange != NONE) { return; } List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex); List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex); /* If input EOS is seen and decoder is not in flush mode, * set the decoder in flush mode. * There can be a case where EOS is sent along with last picture data * In that case, only after decoding that input data, decoder has to be * put in flush. This case is handled here */ if (mReceivedEOS && !mIsInFlush) { setFlushMode(); } while (!outQueue.empty()) { BufferInfo *inInfo; OMX_BUFFERHEADERTYPE *inHeader; BufferInfo *outInfo; OMX_BUFFERHEADERTYPE *outHeader; size_t timeStampIx; inInfo = NULL; inHeader = NULL; if (!mIsInFlush) { if (!inQueue.empty()) { inInfo = *inQueue.begin(); inHeader = inInfo->mHeader; } else { break; } } outInfo = *outQueue.begin(); outHeader = outInfo->mHeader; outHeader->nFlags = 0; outHeader->nTimeStamp = 0; outHeader->nOffset = 0; if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) { mReceivedEOS = true; if (inHeader->nFilledLen == 0) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); inHeader = NULL; setFlushMode(); } } // When there is an init required and the decoder is not in flush mode, // update output port's definition and reinitialize decoder. if (mInitNeeded && !mIsInFlush) { bool portWillReset = false; handlePortSettingsChange(&portWillReset, mNewWidth, mNewHeight); CHECK_EQ(reInitDecoder(), (status_t)OK); return; } /* Get a free slot in timestamp array to hold input timestamp */ { size_t i; timeStampIx = 0; for (i = 0; i < MAX_TIME_STAMPS; i++) { if (!mTimeStampsValid[i]) { timeStampIx = i; break; } } if (inHeader != NULL) { mTimeStampsValid[timeStampIx] = true; mTimeStamps[timeStampIx] = inHeader->nTimeStamp; } } { ivd_video_decode_ip_t s_dec_ip; ivd_video_decode_op_t s_dec_op; WORD32 timeDelay, timeTaken; size_t sizeY, sizeUV; setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx); // If input dump is enabled, then write to file DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes); if (s_dec_ip.u4_num_Bytes > 0) { char *ptr = (char *)s_dec_ip.pv_stream_buffer; } GETTIME(&mTimeStart, NULL); /* Compute time elapsed between end of previous decode() * to start of current decode() */ TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); IV_API_CALL_STATUS_T status; status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); bool unsupportedDimensions = (IMPEG2D_UNSUPPORTED_DIMENSIONS == s_dec_op.u4_error_code); bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF)); GETTIME(&mTimeEnd, NULL); /* Compute time taken for decode() */ TIME_DIFF(mTimeStart, mTimeEnd, timeTaken); ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay, s_dec_op.u4_num_bytes_consumed); if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) { mFlushNeeded = true; } if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) { /* If the input did not contain picture data, then ignore * the associated timestamp */ mTimeStampsValid[timeStampIx] = false; } // This is needed to handle CTS DecoderTest testCodecResetsMPEG2WithoutSurface, // which is not sending SPS/PPS after port reconfiguration and flush to the codec. if (unsupportedDimensions && !mFlushNeeded) { bool portWillReset = false; handlePortSettingsChange(&portWillReset, s_dec_op.u4_pic_wd, s_dec_op.u4_pic_ht); CHECK_EQ(reInitDecoder(), (status_t)OK); setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx); ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); return; } // If the decoder is in the changing resolution mode and there is no output present, // that means the switching is done and it's ready to reset the decoder and the plugin. if (mChangingResolution && !s_dec_op.u4_output_present) { mChangingResolution = false; resetDecoder(); resetPlugin(); continue; } if (unsupportedDimensions || resChanged) { mChangingResolution = true; if (mFlushNeeded) { setFlushMode(); } if (unsupportedDimensions) { mNewWidth = s_dec_op.u4_pic_wd; mNewHeight = s_dec_op.u4_pic_ht; mInitNeeded = true; } continue; } if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) { uint32_t width = s_dec_op.u4_pic_wd; uint32_t height = s_dec_op.u4_pic_ht; bool portWillReset = false; handlePortSettingsChange(&portWillReset, width, height); if (portWillReset) { resetDecoder(); return; } } if (s_dec_op.u4_output_present) { size_t timeStampIdx; outHeader->nFilledLen = (mWidth * mHeight * 3) / 2; timeStampIdx = getMinTimestampIdx(mTimeStamps, mTimeStampsValid); outHeader->nTimeStamp = mTimeStamps[timeStampIdx]; mTimeStampsValid[timeStampIdx] = false; /* mWaitForI waits for the first I picture. Once made FALSE, it has to remain false till explicitly set to TRUE. */ mWaitForI = mWaitForI && !(IV_I_FRAME == s_dec_op.e_pic_type); if (mWaitForI) { s_dec_op.u4_output_present = false; } else { ALOGV("Output timestamp: %lld, res: %ux%u", (long long)outHeader->nTimeStamp, mWidth, mHeight); DUMP_TO_FILE(mOutFile, outHeader->pBuffer, outHeader->nFilledLen); outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; } } else { /* If in flush mode and no output is returned by the codec, * then come out of flush mode */ mIsInFlush = false; /* If EOS was recieved on input port and there is no output * from the codec, then signal EOS on output port */ if (mReceivedEOS) { outHeader->nFilledLen = 0; outHeader->nFlags |= OMX_BUFFERFLAG_EOS; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; resetPlugin(); } } } // TODO: Handle more than one picture data if (inHeader != NULL) { inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); inInfo = NULL; notifyEmptyBufferDone(inHeader); inHeader = NULL; } } }
void SoftHEVC::onQueueFilled(OMX_U32 portIndex) { UNUSED(portIndex); if (mOutputPortSettingsChange != NONE) { return; } List<BufferInfo *> &inQueue = getPortQueue(kInputPortIndex); List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex); /* If input EOS is seen and decoder is not in flush mode, * set the decoder in flush mode. * There can be a case where EOS is sent along with last picture data * In that case, only after decoding that input data, decoder has to be * put in flush. This case is handled here */ if (mReceivedEOS && !mIsInFlush) { setFlushMode(); } while (!outQueue.empty()) { BufferInfo *inInfo; OMX_BUFFERHEADERTYPE *inHeader; BufferInfo *outInfo; OMX_BUFFERHEADERTYPE *outHeader; size_t timeStampIx; inInfo = NULL; inHeader = NULL; if (!mIsInFlush) { if (!inQueue.empty()) { inInfo = *inQueue.begin(); inHeader = inInfo->mHeader; } else { break; } } outInfo = *outQueue.begin(); outHeader = outInfo->mHeader; outHeader->nFlags = 0; outHeader->nTimeStamp = 0; outHeader->nOffset = 0; if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) { ALOGD("EOS seen on input"); mReceivedEOS = true; if (inHeader->nFilledLen == 0) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); inHeader = NULL; setFlushMode(); } } // When there is an init required and the decoder is not in flush mode, // update output port's definition and reinitialize decoder. if (mInitNeeded && !mIsInFlush) { bool portWillReset = false; handlePortSettingsChange(&portWillReset, mNewWidth, mNewHeight); CHECK_EQ(reInitDecoder(), (status_t)OK); return; } /* Get a free slot in timestamp array to hold input timestamp */ { size_t i; timeStampIx = 0; for (i = 0; i < MAX_TIME_STAMPS; i++) { if (!mTimeStampsValid[i]) { timeStampIx = i; break; } } if (inHeader != NULL) { mTimeStampsValid[timeStampIx] = true; mTimeStamps[timeStampIx] = inHeader->nTimeStamp; } } { ivd_video_decode_ip_t s_dec_ip; ivd_video_decode_op_t s_dec_op; WORD32 timeDelay, timeTaken; size_t sizeY, sizeUV; setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx); GETTIME(&mTimeStart, NULL); /* Compute time elapsed between end of previous decode() * to start of current decode() */ TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); IV_API_CALL_STATUS_T status; status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); //Fixed if ( mWidth != s_dec_op.u4_pic_wd || mHeight != s_dec_op.u4_pic_ht) { ALOGE("mWidth %d, mHeight %d -> mNewWidth %d, mNewHeight %d", mWidth, mHeight, mNewWidth, mNewHeight); if(mFlushOutBuffer) { ivd_aligned_free(mFlushOutBuffer); mFlushOutBuffer = NULL; } uint32_t bufferSize = s_dec_op.u4_pic_wd * s_dec_op.u4_pic_ht * 3 / 2; mFlushOutBuffer = (uint8_t *)ivd_aligned_malloc(128, bufferSize); if (NULL == mFlushOutBuffer) { ALOGE("Could not allocate flushOutputBuffer of size %zu", bufferSize); return; } ALOGE("re-alloc mFlushOutBuffer"); } // FIXME: Compare |status| to IHEVCD_UNSUPPORTED_DIMENSIONS, which is not one of the // IV_API_CALL_STATUS_T, seems be wrong. But this is what the decoder returns right now. // The decoder should be fixed so that |u4_error_code| instead of |status| returns // IHEVCD_UNSUPPORTED_DIMENSIONS. bool unsupportedDimensions = ((IHEVCD_UNSUPPORTED_DIMENSIONS == status) || (IHEVCD_UNSUPPORTED_DIMENSIONS == s_dec_op.u4_error_code)); bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF)); GETTIME(&mTimeEnd, NULL); /* Compute time taken for decode() */ TIME_DIFF(mTimeStart, mTimeEnd, timeTaken); ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay, s_dec_op.u4_num_bytes_consumed); if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) { mFlushNeeded = true; } if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) { /* If the input did not contain picture data, then ignore * the associated timestamp */ mTimeStampsValid[timeStampIx] = false; } // This is needed to handle CTS DecoderTest testCodecResetsHEVCWithoutSurface, // which is not sending SPS/PPS after port reconfiguration and flush to the codec. if (unsupportedDimensions && !mFlushNeeded) { bool portWillReset = false; handlePortSettingsChange(&portWillReset, s_dec_op.u4_pic_wd, s_dec_op.u4_pic_ht); CHECK_EQ(reInitDecoder(), (status_t)OK); setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx); ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); return; } // If the decoder is in the changing resolution mode and there is no output present, // that means the switching is done and it's ready to reset the decoder and the plugin. if (mChangingResolution && !s_dec_op.u4_output_present) { mChangingResolution = false; resetDecoder(); resetPlugin(); continue; } if (unsupportedDimensions || resChanged) { mChangingResolution = true; if (mFlushNeeded) { setFlushMode(); } if (unsupportedDimensions) { mNewWidth = s_dec_op.u4_pic_wd; mNewHeight = s_dec_op.u4_pic_ht; mInitNeeded = true; } continue; } if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) { uint32_t width = s_dec_op.u4_pic_wd; uint32_t height = s_dec_op.u4_pic_ht; bool portWillReset = false; handlePortSettingsChange(&portWillReset, width, height); if (portWillReset) { resetDecoder(); return; } } if (s_dec_op.u4_output_present) { outHeader->nFilledLen = (mWidth * mHeight * 3) / 2; outHeader->nTimeStamp = mTimeStamps[s_dec_op.u4_ts]; mTimeStampsValid[s_dec_op.u4_ts] = false; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; } else { /* If in flush mode and no output is returned by the codec, * then come out of flush mode */ mIsInFlush = false; /* If EOS was recieved on input port and there is no output * from the codec, then signal EOS on output port */ if (mReceivedEOS) { outHeader->nFilledLen = 0; outHeader->nFlags |= OMX_BUFFERFLAG_EOS; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; resetPlugin(); } } } // TODO: Handle more than one picture data if (inHeader != NULL) { inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); inInfo = NULL; notifyEmptyBufferDone(inHeader); inHeader = NULL; } } }
void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) { if (mOutputPortSettingsChange != NONE) { return; } List<BufferInfo *> &inQueue = getPortQueue(0); List<BufferInfo *> &outQueue = getPortQueue(1); bool EOSseen = false; while (!inQueue.empty() && !outQueue.empty()) { BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { EOSseen = true; if (inHeader->nFilledLen == 0) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); outHeader->nFilledLen = 0; outHeader->nFlags = OMX_BUFFERFLAG_EOS; outQueue.erase(outQueue.begin()); outInfo->mOwnedByUs = false; notifyFillBufferDone(outHeader); return; } } if (mImg == NULL) { if (vpx_codec_decode( (vpx_codec_ctx_t *)mCtx, inHeader->pBuffer + inHeader->nOffset, inHeader->nFilledLen, NULL, 0)) { ALOGE("on2 decoder failed to decode frame."); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); return; } vpx_codec_iter_t iter = NULL; mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); } if (mImg != NULL) { CHECK_EQ(mImg->fmt, VPX_IMG_FMT_I420); uint32_t width = mImg->d_w; uint32_t height = mImg->d_h; bool portWillReset = false; handlePortSettingsChange(&portWillReset, width, height); if (portWillReset) { return; } outHeader->nOffset = 0; outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * 3) / 2; outHeader->nFlags = EOSseen ? OMX_BUFFERFLAG_EOS : 0; outHeader->nTimeStamp = inHeader->nTimeStamp; if (outHeader->nAllocLen >= outHeader->nFilledLen) { uint8_t *dst = outHeader->pBuffer; const uint8_t *srcY = (const uint8_t *)mImg->planes[VPX_PLANE_Y]; const uint8_t *srcU = (const uint8_t *)mImg->planes[VPX_PLANE_U]; const uint8_t *srcV = (const uint8_t *)mImg->planes[VPX_PLANE_V]; size_t srcYStride = mImg->stride[VPX_PLANE_Y]; size_t srcUStride = mImg->stride[VPX_PLANE_U]; size_t srcVStride = mImg->stride[VPX_PLANE_V]; copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride); } else { ALOGE("b/27597103, buffer too small"); outHeader->nFilledLen = 0; } mImg = NULL; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; } inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); inInfo = NULL; notifyEmptyBufferDone(inHeader); inHeader = NULL; } }
void SoftMPEG4::onQueueFilled(OMX_U32 /* portIndex */) { if (mSignalledError || mOutputPortSettingsChange != NONE) { return; } List<BufferInfo *> &inQueue = getPortQueue(0); List<BufferInfo *> &outQueue = getPortQueue(1); while (!inQueue.empty() && outQueue.size() == kNumOutputBuffers) { BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; if (inHeader == NULL) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; continue; } PortInfo *port = editPortInfo(1); OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(mNumSamplesOutput & 1).mHeader; if (inHeader->nFilledLen == 0) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); ++mInputBufferCount; if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { outHeader->nFilledLen = 0; outHeader->nFlags = OMX_BUFFERFLAG_EOS; List<BufferInfo *>::iterator it = outQueue.begin(); while ((*it)->mHeader != outHeader) { ++it; } BufferInfo *outInfo = *it; outInfo->mOwnedByUs = false; outQueue.erase(it); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; } return; } uint8_t *bitstream = inHeader->pBuffer + inHeader->nOffset; uint32_t *start_code = (uint32_t *)bitstream; bool volHeader = *start_code == 0xB0010000; if (volHeader) { PVCleanUpVideoDecoder(mHandle); mInitialized = false; } if (!mInitialized) { uint8_t *vol_data[1]; int32_t vol_size = 0; vol_data[0] = NULL; if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) || volHeader) { vol_data[0] = bitstream; vol_size = inHeader->nFilledLen; } MP4DecodingMode mode = (mMode == MODE_MPEG4) ? MPEG4_MODE : H263_MODE; Bool success = PVInitVideoDecoder( mHandle, vol_data, &vol_size, 1, outputBufferWidth(), outputBufferHeight(), mode); if (!success) { ALOGW("PVInitVideoDecoder failed. Unsupported content?"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle); if (mode != actualMode) { notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } PVSetPostProcType((VideoDecControls *) mHandle, 0); bool hasFrameData = false; if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); inInfo = NULL; notifyEmptyBufferDone(inHeader); inHeader = NULL; } else if (volHeader) { hasFrameData = true; } mInitialized = true; if (mode == MPEG4_MODE && handlePortSettingsChange()) { return; } if (!hasFrameData) { continue; } } if (!mFramesConfigured) { PortInfo *port = editPortInfo(1); OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(1).mHeader; OMX_U32 yFrameSize = sizeof(uint8) * mHandle->size; if ((outHeader->nAllocLen < yFrameSize) || (outHeader->nAllocLen - yFrameSize < yFrameSize / 2)) { ALOGE("Too small output buffer for reference frame: %lu bytes", (unsigned long)outHeader->nAllocLen); android_errorWriteLog(0x534e4554, "30033990"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } PVSetReferenceYUV(mHandle, outHeader->pBuffer); mFramesConfigured = true; } uint32_t useExtTimestamp = (inHeader->nOffset == 0); // decoder deals in ms (int32_t), OMX in us (int64_t) // so use fake timestamp instead uint32_t timestamp = 0xFFFFFFFF; if (useExtTimestamp) { mPvToOmxTimeMap.add(mPvTime, inHeader->nTimeStamp); timestamp = mPvTime; mPvTime++; } int32_t bufferSize = inHeader->nFilledLen; int32_t tmp = bufferSize; OMX_U32 frameSize; OMX_U64 yFrameSize = (OMX_U64)mWidth * (OMX_U64)mHeight; if (yFrameSize > ((OMX_U64)UINT32_MAX / 3) * 2) { ALOGE("Frame size too large"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } frameSize = (OMX_U32)(yFrameSize + (yFrameSize / 2)); if (outHeader->nAllocLen < frameSize) { android_errorWriteLog(0x534e4554, "27833616"); ALOGE("Insufficient output buffer size"); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } // The PV decoder is lying to us, sometimes it'll claim to only have // consumed a subset of the buffer when it clearly consumed all of it. // ignore whatever it says... if (PVDecodeVideoFrame( mHandle, &bitstream, ×tamp, &tmp, &useExtTimestamp, outHeader->pBuffer) != PV_TRUE) { ALOGE("failed to decode video frame."); notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); mSignalledError = true; return; } // H263 doesn't have VOL header, the frame size information is in short header, i.e. the // decoder may detect size change after PVDecodeVideoFrame. if (handlePortSettingsChange()) { return; } // decoder deals in ms, OMX in us. outHeader->nTimeStamp = mPvToOmxTimeMap.valueFor(timestamp); mPvToOmxTimeMap.removeItem(timestamp); inHeader->nOffset += bufferSize; inHeader->nFilledLen = 0; if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { outHeader->nFlags = OMX_BUFFERFLAG_EOS; } else { outHeader->nFlags = 0; } if (inHeader->nFilledLen == 0) { inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); inInfo = NULL; notifyEmptyBufferDone(inHeader); inHeader = NULL; } ++mInputBufferCount; outHeader->nOffset = 0; outHeader->nFilledLen = frameSize; List<BufferInfo *>::iterator it = outQueue.begin(); while ((*it)->mHeader != outHeader) { ++it; } BufferInfo *outInfo = *it; outInfo->mOwnedByUs = false; outQueue.erase(it); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; ++mNumSamplesOutput; } }