int CDVDVideoCodecAndroidMediaCodec::Decode(uint8_t *pData, int iSize, double dts, double pts) { if (!m_opened) return VC_ERROR; if (m_state != MEDIACODEC_STATE_RUNNING) CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::Decode current state (%d)", m_state); if (m_hints.ptsinvalid) pts = DVD_NOPTS_VALUE; // Handle input, add demuxer packet to input queue, we must accept it or // it will be discarded as VideoPlayerVideo has no concept of "try again". // we must return VC_BUFFER or VC_PICTURE, default to VC_BUFFER. bool drain = (m_codecControlFlags & DVD_CODEC_CTRL_DRAIN) ? true : false; m_dec_retcode = (drain) ? 0 : VC_BUFFER; if (!pData) { // Check if we have a saved buffer if (m_demux_pkt.pData && m_state != MEDIACODEC_STATE_ENDOFSTREAM) { pData = m_demux_pkt.pData; iSize = m_demux_pkt.iSize; pts = m_demux_pkt.pts; dts = m_demux_pkt.dts; } } else if (m_state == MEDIACODEC_STATE_ENDOFSTREAM) { // We received a packet but already reached EOS. Flush... FlushInternal(); m_codec->flush(); m_state = MEDIACODEC_STATE_FLUSHED; m_dec_retcode |= VC_BUFFER; } // must check for an output picture 1st, // otherwise, mediacodec can stall on some devices. int retgp = GetOutputPicture(); if (retgp > 0) { m_dec_retcode |= VC_PICTURE; m_noPictureLoop = 0; } else if (retgp == -1 || (drain && ++m_noPictureLoop == 10)) // EOS { m_state = MEDIACODEC_STATE_ENDOFSTREAM; m_dec_retcode |= VC_BUFFER; m_noPictureLoop = 0; } if (pData) { // try to fetch an input buffer int64_t timeout_us = 5000; int index = m_codec->dequeueInputBuffer(timeout_us); if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode ExceptionCheck"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); m_dec_retcode = VC_ERROR; } else if (index >= 0) { if (m_state == MEDIACODEC_STATE_FLUSHED) m_state = MEDIACODEC_STATE_RUNNING; if (!(m_state == MEDIACODEC_STATE_FLUSHED || m_state == MEDIACODEC_STATE_RUNNING)) CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode Dequeue: Wrong state (%d)", m_state); // we have an input buffer, fill it. if (pData && m_bitstream) { m_bitstream->Convert(pData, iSize); iSize = m_bitstream->GetConvertSize(); pData = m_bitstream->GetConvertBuffer(); } CJNIByteBuffer buffer = m_codec->getInputBuffer(index); int size = buffer.capacity(); if (iSize > size) { CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode, iSize(%d) > size(%d)", iSize, size); iSize = size; } // fetch a pointer to the ByteBuffer backing store uint8_t *dst_ptr = (uint8_t*)xbmc_jnienv()->GetDirectBufferAddress(buffer.get_raw()); if (pData && dst_ptr) { // Codec specifics switch(m_hints.codec) { case AV_CODEC_ID_VC1: { if (iSize >= 4 && pData[0] == 0x00 && pData[1] == 0x00 && pData[2] == 0x01 && (pData[3] == 0x0d || pData[3] == 0x0f)) memcpy(dst_ptr, pData, iSize); else { dst_ptr[0] = 0x00; dst_ptr[1] = 0x00; dst_ptr[2] = 0x01; dst_ptr[3] = 0x0d; memcpy(dst_ptr+4, pData, iSize); iSize += 4; } break; } default: memcpy(dst_ptr, pData, iSize); break; } } // Translate from VideoPlayer dts/pts to MediaCodec pts, // pts WILL get re-ordered by MediaCodec if needed. // Do not try to pass pts as a unioned double/int64_t, // some android devices will diddle with presentationTimeUs // and you will get NaN back and VideoPlayerVideo will barf. int64_t presentationTimeUs = 0; if (pts != DVD_NOPTS_VALUE) presentationTimeUs = pts; else if (dts != DVD_NOPTS_VALUE) presentationTimeUs = dts; /* CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: " "pts(%f), ipts(%lld), iSize(%d), GetDataSize(%d), loop_cnt(%d)", presentationTimeUs, pts_dtoi(presentationTimeUs), iSize, GetDataSize(), loop_cnt); */ int flags = 0; int offset = 0; m_codec->queueInputBuffer(index, offset, iSize, presentationTimeUs, flags); // clear any jni exceptions, jni gets upset if we do not. if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode ExceptionCheck"); xbmc_jnienv()->ExceptionClear(); } // Free saved buffer it there was one if (m_demux_pkt.pData) { free(m_demux_pkt.pData); memset(&m_demux_pkt, 0, sizeof(m_demux_pkt)); } } else { // We couldn't get an input buffer. Save the packet for next iteration, if it wasn't already if (!m_demux_pkt.pData) { m_demux_pkt.dts = dts; m_demux_pkt.pts = pts; m_demux_pkt.iSize = iSize; m_demux_pkt.pData = (uint8_t*)malloc(iSize); memcpy(m_demux_pkt.pData, pData, iSize); } m_dec_retcode &= ~VC_BUFFER; } } return m_dec_retcode; }
int CDVDVideoCodecAndroidMediaCodec::GetOutputPicture(void) { int rtn = 0; int64_t timeout_us = 10000; CJNIMediaCodecBufferInfo bufferInfo; int index = m_codec->dequeueOutputBuffer(bufferInfo, timeout_us); if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture ExceptionCheck; dequeueOutputBuffer"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); return -2; } if (index >= 0) { int64_t pts= bufferInfo.presentationTimeUs(); m_videobuffer.dts = DVD_NOPTS_VALUE; m_videobuffer.pts = DVD_NOPTS_VALUE; if (pts != AV_NOPTS_VALUE) m_videobuffer.pts = pts; int flags = bufferInfo.flags(); if (flags & CJNIMediaCodec::BUFFER_FLAG_SYNC_FRAME) CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_SYNC_FRAME"); if (flags & CJNIMediaCodec::BUFFER_FLAG_CODEC_CONFIG) CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_CODEC_CONFIG"); if (flags & CJNIMediaCodec::BUFFER_FLAG_END_OF_STREAM) { CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_END_OF_STREAM"); m_codec->releaseOutputBuffer(index, false); if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture ExceptionCheck: releaseOutputBuffer"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); return -2; } return -1; } if (m_drop) { m_codec->releaseOutputBuffer(index, false); if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture ExceptionCheck: releaseOutputBuffer"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); return -2; } m_videobuffer.mediacodec = nullptr; return 1; } if (!m_render_sw) { size_t i = 0; for (; i < m_inflight.size(); ++i) { if (m_inflight[i]->GetIndex() == index) break; } if (i == m_inflight.size()) m_inflight.push_back( new CDVDMediaCodecInfo(index, m_textureId, m_codec, m_surfaceTexture, m_frameAvailable) ); m_videobuffer.mediacodec = m_inflight[i]->Retain(); m_videobuffer.mediacodec->Validate(true); } else { int size = bufferInfo.size(); int offset = bufferInfo.offset(); CJNIByteBuffer buffer = m_codec->getOutputBuffer(index);; if (!buffer.isDirect()) CLog::Log(LOGWARNING, "CDVDVideoCodecAndroidMediaCodec:: buffer.isDirect == false"); if (!buffer.isDirect()) CLog::Log(LOGWARNING, "CDVDVideoCodecAndroidMediaCodec:: m_output[index].isDirect == false"); if (size && buffer.capacity()) { uint8_t *src_ptr = (uint8_t*)xbmc_jnienv()->GetDirectBufferAddress(buffer.get_raw()); src_ptr += offset; int loop_end = 0; if (m_videobuffer.format == RENDER_FMT_NV12) loop_end = 2; else if (m_videobuffer.format == RENDER_FMT_YUV420P) loop_end = 3; for (int i = 0; i < loop_end; i++) { uint8_t *src = src_ptr + m_src_offset[i]; int src_stride = m_src_stride[i]; uint8_t *dst = m_videobuffer.data[i]; int dst_stride = m_videobuffer.iLineSize[i]; int height = m_videobuffer.iHeight; if (i > 0) height = (m_videobuffer.iHeight + 1) / 2; if (src_stride == dst_stride) memcpy(dst, src, dst_stride * height); else for (int j = 0; j < height; j++, src += src_stride, dst += dst_stride) memcpy(dst, src, dst_stride); } } m_codec->releaseOutputBuffer(index, false); } /* CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture " "index(%d), pts(%f)", index, m_videobuffer.pts); */ // always, check/clear jni exceptions. if (xbmc_jnienv()->ExceptionCheck()) xbmc_jnienv()->ExceptionClear(); rtn = 1; } else if (index == CJNIMediaCodec::INFO_OUTPUT_FORMAT_CHANGED) { CJNIMediaFormat mediaformat = m_codec->getOutputFormat(); if (xbmc_jnienv()->ExceptionCheck()) { xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); } ConfigureOutputFormat(&mediaformat); } else if (index == CJNIMediaCodec::INFO_TRY_AGAIN_LATER || index == CJNIMediaCodec::INFO_OUTPUT_BUFFERS_CHANGED) { // ignore rtn = 0; } else { // we should never get here CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture unknown index(%d)", index); rtn = -2; } return rtn; }