int CDVDVideoCodecIMX::Decode(BYTE *pData, int iSize, double dts, double pts) { VpuDecFrameLengthInfo frameLengthInfo; VpuBufferNode inData; VpuDecRetCode ret; int decRet = 0; int retStatus = 0; int demuxer_bytes = iSize; uint8_t *demuxer_content = pData; int retries = 0; int idx; #ifdef IMX_PROFILE static unsigned long long previous, current; unsigned long long before_dec; #endif if (!m_vpuHandle) { VpuOpen(); if (!m_vpuHandle) return VC_ERROR; } for (int i=0; i < m_vpuFrameBufferNum; i++) { if (m_outputBuffers[i]->Rendered()) { ret = m_outputBuffers[i]->ReleaseFramebuffer(&m_vpuHandle); if(ret != VPU_DEC_RET_SUCCESS) { CLog::Log(LOGERROR, "%s: vpu clear frame display failure: ret=%d \r\n",__FUNCTION__,ret); } } } #ifdef IMX_PROFILE current = XbmcThreads::SystemClockMillis(); CLog::Log(LOGDEBUG, "%s - delta time decode : %llu - demux size : %d dts : %f - pts : %f\n", __FUNCTION__, current - previous, iSize, dts, pts); previous = current; #endif if ((pData && iSize) || (m_bytesToBeConsumed)) { if ((m_convert_bitstream) && (iSize)) { // convert demuxer packet from bitstream to bytestream (AnnexB) if (m_converter->Convert(demuxer_content, demuxer_bytes)) { demuxer_content = m_converter->GetConvertBuffer(); demuxer_bytes = m_converter->GetConvertSize(); } else CLog::Log(LOGERROR,"%s - bitstream_convert error", __FUNCTION__); } inData.nSize = demuxer_bytes; inData.pPhyAddr = NULL; inData.pVirAddr = demuxer_content; if ((m_decOpenParam.CodecFormat == VPU_V_MPEG2) || (m_decOpenParam.CodecFormat == VPU_V_VC1_AP)|| (m_decOpenParam.CodecFormat == VPU_V_XVID)) { inData.sCodecData.pData = (unsigned char *)m_hints.extradata; inData.sCodecData.nSize = m_hints.extrasize; } else { inData.sCodecData.pData = NULL; inData.sCodecData.nSize = 0; } while (true) // Decode as long as the VPU consumes data { #ifdef IMX_PROFILE before_dec = XbmcThreads::SystemClockMillis(); #endif if (m_frameReported) m_bytesToBeConsumed += inData.nSize; ret = VPU_DecDecodeBuf(m_vpuHandle, &inData, &decRet); #ifdef IMX_PROFILE CLog::Log(LOGDEBUG, "%s - VPU dec 0x%x decode takes : %lld\n\n", __FUNCTION__, decRet, XbmcThreads::SystemClockMillis() - before_dec); #endif if (ret != VPU_DEC_RET_SUCCESS) { CLog::Log(LOGERROR, "%s - VPU decode failed with error code %d.\n", __FUNCTION__, ret); goto out_error; } if (decRet & VPU_DEC_INIT_OK) // VPU decoding init OK : We can retrieve stream info { ret = VPU_DecGetInitialInfo(m_vpuHandle, &m_initInfo); if (ret == VPU_DEC_RET_SUCCESS) { if (g_advancedSettings.CanLogComponent(LOGVIDEO)) { CLog::Log(LOGDEBUG, "%s - VPU Init Stream Info : %dx%d (interlaced : %d - Minframe : %d)"\ " - Align : %d bytes - crop : %d %d %d %d - Q16Ratio : %x\n", __FUNCTION__, m_initInfo.nPicWidth, m_initInfo.nPicHeight, m_initInfo.nInterlace, m_initInfo.nMinFrameBufferCount, m_initInfo.nAddressAlignment, m_initInfo.PicCropRect.nLeft, m_initInfo.PicCropRect.nTop, m_initInfo.PicCropRect.nRight, m_initInfo.PicCropRect.nBottom, m_initInfo.nQ16ShiftWidthDivHeightRatio); } if (VpuAllocFrameBuffers()) { ret = VPU_DecRegisterFrameBuffer(m_vpuHandle, m_vpuFrameBuffers, m_vpuFrameBufferNum); if (ret != VPU_DEC_RET_SUCCESS) { CLog::Log(LOGERROR, "%s - VPU error while registering frame buffers (%d).\n", __FUNCTION__, ret); goto out_error; } } else { goto out_error; } } else { CLog::Log(LOGERROR, "%s - VPU get initial info failed (%d).\n", __FUNCTION__, ret); goto out_error; } } //VPU_DEC_INIT_OK if (decRet & VPU_DEC_ONE_FRM_CONSUMED) { ret = VPU_DecGetConsumedFrameInfo(m_vpuHandle, &frameLengthInfo); if (ret != VPU_DEC_RET_SUCCESS) { CLog::Log(LOGERROR, "%s - VPU error retireving info about consummed frame (%d).\n", __FUNCTION__, ret); } m_bytesToBeConsumed -= (frameLengthInfo.nFrameLength + frameLengthInfo.nStuffLength); if (frameLengthInfo.pFrame) { idx = VpuFindBuffer(frameLengthInfo.pFrame->pbufY); if (m_bytesToBeConsumed < 50) m_bytesToBeConsumed = 0; if (idx != -1) { if (m_previousPts != DVD_NOPTS_VALUE) { m_outputBuffers[idx]->SetPts(m_previousPts); m_previousPts = DVD_NOPTS_VALUE; } else m_outputBuffers[idx]->SetPts(pts); } else CLog::Log(LOGERROR, "%s - could not find frame buffer\n", __FUNCTION__); } } //VPU_DEC_ONE_FRM_CONSUMED if (decRet & VPU_DEC_OUTPUT_DIS) // Frame ready to be displayed { if (retStatus & VC_PICTURE) CLog::Log(LOGERROR, "%s - Second picture in the same decode call !\n", __FUNCTION__); ret = VPU_DecGetOutputFrame(m_vpuHandle, &m_frameInfo); if(ret != VPU_DEC_RET_SUCCESS) { CLog::Log(LOGERROR, "%s - VPU Cannot get output frame(%d).\n", __FUNCTION__, ret); goto out_error; } // Some codecs (VC1?) lie about their frame size (mod 16). Adjust... m_frameInfo.pExtInfo->nFrmWidth = (((m_frameInfo.pExtInfo->nFrmWidth) + 15) & ~15); m_frameInfo.pExtInfo->nFrmHeight = (((m_frameInfo.pExtInfo->nFrmHeight) + 15) & ~15); retStatus |= VC_PICTURE; } //VPU_DEC_OUTPUT_DIS // According to libfslvpuwrap: If this flag is set then the frame should // be dropped. It is just returned to gather decoder information but not // for display. if (decRet & VPU_DEC_OUTPUT_MOSAIC_DIS) { ret = VPU_DecGetOutputFrame(m_vpuHandle, &m_frameInfo); if(ret != VPU_DEC_RET_SUCCESS) { CLog::Log(LOGERROR, "%s - VPU Cannot get output frame(%d).\n", __FUNCTION__, ret); goto out_error; } // Display frame ret = VPU_DecOutFrameDisplayed(m_vpuHandle, m_frameInfo.pDisplayFrameBuf); if(ret != VPU_DEC_RET_SUCCESS) { CLog::Log(LOGERROR, "%s: VPU Clear frame display failure(%d)\n",__FUNCTION__,ret); goto out_error; } } //VPU_DEC_OUTPUT_MOSAIC_DIS if (decRet & VPU_DEC_OUTPUT_REPEAT) { if (g_advancedSettings.CanLogComponent(LOGVIDEO)) CLog::Log(LOGDEBUG, "%s - Frame repeat.\n", __FUNCTION__); } if (decRet & VPU_DEC_OUTPUT_DROPPED) { if (g_advancedSettings.CanLogComponent(LOGVIDEO)) CLog::Log(LOGDEBUG, "%s - Frame dropped.\n", __FUNCTION__); } if (decRet & VPU_DEC_NO_ENOUGH_BUF) { CLog::Log(LOGERROR, "%s - No frame buffer available.\n", __FUNCTION__); } if (decRet & VPU_DEC_SKIP) { if (g_advancedSettings.CanLogComponent(LOGVIDEO)) CLog::Log(LOGDEBUG, "%s - Frame skipped.\n", __FUNCTION__); } if (decRet & VPU_DEC_FLUSH) { CLog::Log(LOGNOTICE, "%s - VPU requires a flush.\n", __FUNCTION__); Reset(); retStatus = VC_FLUSHED; } if (decRet & VPU_DEC_OUTPUT_EOS) { CLog::Log(LOGNOTICE, "%s - EOS encountered.\n", __FUNCTION__); } if ((decRet & VPU_DEC_NO_ENOUGH_INBUF) || (decRet & VPU_DEC_OUTPUT_DIS)) { // We are done with VPU decoder that time break; } retries++; if (retries >= m_maxVpuDecodeLoops) { CLog::Log(LOGERROR, "%s - Leaving VPU decoding loop after %d iterations\n", __FUNCTION__, m_maxVpuDecodeLoops); break; } if (!(decRet & VPU_DEC_INPUT_USED)) { CLog::Log(LOGERROR, "%s - input not used : addr %p size :%d!\n", __FUNCTION__, inData.pVirAddr, inData.nSize); } // Let's process again as VPU_DEC_NO_ENOUGH_INBUF was not set // and we don't have an image ready if we reach that point inData.pVirAddr = NULL; inData.nSize = 0; } // Decode loop } //(pData && iSize) if (retStatus == 0) { retStatus |= VC_BUFFER; } if (m_bytesToBeConsumed > 0) { // Remember the current pts because the data which has just // been sent to the VPU has not yet been consumed. // This pts is related to the frame that will be consumed // at next call... m_previousPts = pts; } // Store current dts (will be used only if VC_PICTURE is set) m_dts = dts; #ifdef IMX_PROFILE CLog::Log(LOGDEBUG, "%s - returns %x - duration %lld\n", __FUNCTION__, retStatus, XbmcThreads::SystemClockMillis() - previous); #endif return retStatus; out_error: return VC_ERROR; }
ImxVpuDecReturnCodes imx_vpu_dec_decode_frame(ImxVpuDecoder *decoder, ImxVpuEncodedFrame const *encoded_frame, unsigned int *output_code) { VpuDecRetCode ret; VpuBufferNode node; int buf_ret_code; node.pVirAddr = encoded_frame->virtual_address; node.pPhyAddr = 0; /* encoded data is always read from a regular memory block, not a DMA buffer */ node.nSize = encoded_frame->data_size; node.sCodecData.pData = encoded_frame->codec_data; node.sCodecData.nSize = encoded_frame->codec_data_size; decoder->pending_user_data = encoded_frame->user_data; ret = VPU_DecDecodeBuf(decoder->handle, &node, &buf_ret_code); IMX_VPU_LOG("VPU_DecDecodeBuf buf ret code: 0x%x", buf_ret_code); *output_code = dec_convert_outcode(buf_ret_code); if (ret != VPU_DEC_RET_SUCCESS) { IMX_VPU_ERROR("decoding frame failed: %s", imx_vpu_dec_error_string(dec_convert_retcode(ret))); return dec_convert_retcode(ret); } if (decoder->recalculate_num_avail_framebuffers) { decoder->num_available_framebuffers = decoder->num_framebuffers - decoder->num_framebuffers_in_use; IMX_VPU_LOG("recalculated number of available framebuffers to %d", decoder->num_available_framebuffers); decoder->recalculate_num_avail_framebuffers = FALSE; } if (buf_ret_code & VPU_DEC_INIT_OK) { decoder->delay_pending_user_data = TRUE; decoder->last_pending_user_data = decoder->pending_user_data; } if (buf_ret_code & VPU_DEC_FLUSH) { IMX_VPU_INFO("VPU requested a decoder flush"); ret = VPU_DecFlushAll(decoder->handle); if (ret == VPU_DEC_RET_FAILURE_TIMEOUT) { IMX_VPU_WARNING("timeout detected, resetting decoder"); ret = VPU_DecReset(decoder->handle); if (ret != VPU_DEC_RET_SUCCESS) { ImxVpuDecReturnCodes imxret = dec_convert_retcode(ret); IMX_VPU_ERROR("resetting decoder failed: %s", imx_vpu_dec_error_string(imxret)); return imxret; } else *output_code |= IMX_VPU_DEC_OUTPUT_CODE_INTERNAL_RESET; } else if (ret != VPU_DEC_RET_SUCCESS) { ImxVpuDecReturnCodes imxret = dec_convert_retcode(ret); IMX_VPU_ERROR("flushing decoder failed: %s", imx_vpu_dec_error_string(imxret)); return imxret; } else IMX_VPU_INFO("flushed decoder"); } if (buf_ret_code & VPU_DEC_RESOLUTION_CHANGED) { IMX_VPU_INFO("resolution changed - resetting internal states"); *output_code |= IMX_VPU_DEC_OUTPUT_CODE_INITIAL_INFO_AVAILABLE; decoder->delay_pending_user_data = TRUE; decoder->recalculate_num_avail_framebuffers = FALSE; decoder->num_user_data = 0; if (decoder->user_data_for_frames != NULL) IMX_VPU_FREE(decoder->user_data_for_frames, sizeof(void*) * decoder->num_framebuffers); if (decoder->wrapper_framebuffers != NULL) IMX_VPU_FREE(decoder->wrapper_framebuffers, sizeof(VpuFrameBuffer*) * decoder->num_framebuffers); decoder->user_data_for_frames = NULL; decoder->wrapper_framebuffers = NULL; } if (buf_ret_code & VPU_DEC_NO_ENOUGH_INBUF) { /* Not dropping frame here on purpose; the next input frame may * complete the input */ } { void *user_data = decoder->delay_pending_user_data ? decoder->last_pending_user_data : decoder->pending_user_data; /* The first time this location is reached, VPU_DEC_INIT_OK will be set in the output_code. * This implies that the framebuffers have not been allocated and registered yet, * so no user data can be stored yet. * With codec formats that produce consumption info, this is not a problem, because * VPU_DEC_ONE_FRM_CONSUMED will be returned only when framebuffers are present. * But with other formats, an explicit decoder->framebuffers != NULL check is necessary * (see below). The user_data pointer does not get lost; it is stored in last_pending_user_data. */ if ((buf_ret_code & VPU_DEC_ONE_FRM_CONSUMED) && !(buf_ret_code & VPU_DEC_OUTPUT_DROPPED)) { int fb_index; VpuDecFrameLengthInfo consumed_frame_info; ret = VPU_DecGetConsumedFrameInfo(decoder->handle, &consumed_frame_info); if (ret != VPU_DEC_RET_SUCCESS) { ImxVpuDecReturnCodes imxret = dec_convert_retcode(ret); IMX_VPU_ERROR("getting consumed frame info failed: %s", imx_vpu_dec_error_string(imxret)); return imxret; } fb_index = dec_get_wrapper_framebuffer_index(decoder, consumed_frame_info.pFrame); if (consumed_frame_info.pFrame != NULL) { if ((fb_index >= 0) && (fb_index < (int)(decoder->num_framebuffers))) { IMX_VPU_LOG("framebuffer index %d for framebuffer %p user data %p", fb_index, (void *)(consumed_frame_info.pFrame), user_data); decoder->user_data_for_frames[fb_index] = user_data; } else IMX_VPU_ERROR("framebuffer index %d for framebuffer %p user data %p out of bounds", fb_index, (void *)(consumed_frame_info.pFrame), user_data); } else IMX_VPU_WARNING("consumed frame info contains a NULL frame"); } else if (!(decoder->consumption_info_available) && (decoder->framebuffers != NULL)) { if (decoder->num_user_data < (int)(decoder->num_framebuffers)) { decoder->user_data_for_frames[decoder->num_user_data] = user_data; decoder->num_user_data++; IMX_VPU_LOG("user data %p stored as newest", user_data); IMX_VPU_TRACE("incremented number of userdata pointers to %d", decoder->num_user_data); } else IMX_VPU_WARNING("too many user data pointers in memory - cannot store current one"); } decoder->last_pending_user_data = decoder->pending_user_data; decoder->pending_user_data = NULL; } if ((buf_ret_code & VPU_DEC_ONE_FRM_CONSUMED) && !(buf_ret_code & VPU_DEC_OUTPUT_DROPPED)) { decoder->num_available_framebuffers--; decoder->num_times_counter_decremented++; IMX_VPU_LOG("decremented number of available framebuffers to %d (with consumed frame info); number of times decremented is now %d", decoder->num_available_framebuffers, decoder->num_times_counter_decremented); } if (buf_ret_code & VPU_DEC_OUTPUT_NODIS) { if ((encoded_frame->virtual_address != NULL) && (decoder->codec_format == IMX_VPU_CODEC_FORMAT_VP8)) *output_code |= IMX_VPU_DEC_OUTPUT_CODE_DECODE_ONLY; } /* VPU_DEC_NO_ENOUGH_BUF handled by caller - should be treated as an error */ if ((buf_ret_code & VPU_DEC_OUTPUT_DIS) && !(decoder->consumption_info_available)) { decoder->num_available_framebuffers--; decoder->num_times_counter_decremented++; IMX_VPU_LOG("decremented number of available framebuffers to %d (no consumed frame info); number of times decremented is now %d", decoder->num_available_framebuffers, decoder->num_times_counter_decremented); } else if (buf_ret_code & VPU_DEC_OUTPUT_MOSAIC_DIS) { IMX_VPU_TRACE("dropping mosaic frame"); /* mosaic frames do not seem to be useful for anything, so they are just dropped here */ ImxVpuDecReturnCodes imxret; ImxVpuDecodedFrame decoded_frame; if ((imxret = imx_vpu_dec_get_decoded_frame(decoder, &decoded_frame)) != IMX_VPU_DEC_RETURN_CODE_OK) { IMX_VPU_ERROR("error getting output mosaic frame: %s", imx_vpu_dec_error_string(imxret)); return imxret; } if ((imxret = imx_vpu_dec_mark_framebuffer_as_displayed(decoder, decoded_frame.framebuffer)) != IMX_VPU_DEC_RETURN_CODE_OK) { IMX_VPU_ERROR("error marking mosaic frame as displayed: %s", imx_vpu_dec_error_string(imxret)); return imxret; } decoder->dropped_frame_user_data = decoded_frame.user_data; *output_code |= IMX_VPU_DEC_OUTPUT_CODE_DROPPED; } else if (buf_ret_code & VPU_DEC_OUTPUT_DROPPED) { // TODO make this work for formats with consumption info if (decoder->num_user_data > 0) { decoder->dropped_frame_user_data = decoder->user_data_for_frames[0]; decoder->user_data_for_frames[0] = NULL; memmove(decoder->user_data_for_frames, decoder->user_data_for_frames + 1, sizeof(void*) * (decoder->num_user_data - 1)); decoder->num_user_data--; } else decoder->dropped_frame_user_data = NULL; } /* In case the VPU didn't use the input and no consumed frame info is available, * drop the input frame to make sure timestamps are okay * (If consumed frame info is present it is still possible it might be used for input-output frame * associations; unlikely to occur thought) */ if ((encoded_frame->virtual_address != NULL) && !(buf_ret_code & (VPU_DEC_ONE_FRM_CONSUMED | VPU_DEC_INPUT_USED | VPU_DEC_RESOLUTION_CHANGED))) { decoder->dropped_frame_user_data = encoded_frame->user_data; *output_code |= IMX_VPU_DEC_OUTPUT_CODE_DROPPED; } return IMX_VPU_DEC_RETURN_CODE_OK; }