int CDVDAudioCodecAndroidMediaCodec::GetData(uint8_t** dst) { m_bufferUsed = 0; int64_t timeout_us = 10000; CJNIMediaCodecBufferInfo bufferInfo; int index = m_codec->dequeueOutputBuffer(bufferInfo, timeout_us); if (xbmc_jnienv()->ExceptionCheck()) { std::string err = CJNIBase::ExceptionToString(); CLog::Log(LOGERROR, "CDVDAudioCodecAndroidMediaCodec::GetData ExceptionCheck; dequeueOutputBuffer \n %s", err.c_str()); return 0; } if (index >= 0) { // some devices will return a valid index // before signaling INFO_OUTPUT_BUFFERS_CHANGED which // is used to setup m_output, D'uh. setup m_output here. if (m_output.empty()) { m_output = m_codec->getOutputBuffers(); if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDAudioCodecAndroidMediaCodec::GetData ExceptionCheck: getOutputBuffers"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); return 0; } } int flags = bufferInfo.flags(); if (flags & CJNIMediaCodec::BUFFER_FLAG_SYNC_FRAME) CLog::Log(LOGDEBUG, "CDVDAudioCodecAndroidMediaCodec:: BUFFER_FLAG_SYNC_FRAME"); if (flags & CJNIMediaCodec::BUFFER_FLAG_CODEC_CONFIG) CLog::Log(LOGDEBUG, "CDVDAudioCodecAndroidMediaCodec:: BUFFER_FLAG_CODEC_CONFIG"); if (flags & CJNIMediaCodec::BUFFER_FLAG_END_OF_STREAM) { CLog::Log(LOGDEBUG, "CDVDAudioCodecAndroidMediaCodec:: BUFFER_FLAG_END_OF_STREAM"); m_codec->releaseOutputBuffer(index, false); if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDAudioCodecAndroidMediaCodec::GetData ExceptionCheck: releaseOutputBuffer"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); return 0; } return 0; } int size = bufferInfo.size(); int offset = bufferInfo.offset(); if (!m_output[index].isDirect()) CLog::Log(LOGWARNING, "CDVDAudioCodecAndroidMediaCodec:: m_output[index].isDirect == false"); if (size && m_output[index].capacity()) { uint8_t *src_ptr = (uint8_t*)xbmc_jnienv()->GetDirectBufferAddress(m_output[index].get_raw()); src_ptr += offset; if (size > m_bufferSize) { m_bufferSize = size; m_buffer = (uint8_t*)realloc(m_buffer, m_bufferSize); } memcpy(m_buffer, src_ptr, size); m_bufferUsed = size; } else return 0; m_codec->releaseOutputBuffer(index, false); if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDAudioCodecAndroidMediaCodec::GetData ExceptionCheck: releaseOutputBuffer"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); } #ifdef DEBUG_VERBOSE CLog::Log(LOGDEBUG, "CDVDAudioCodecAndroidMediaCodec::GetData " "index(%d), size(%d)", index, m_bufferUsed); #endif // always, check/clear jni exceptions. if (xbmc_jnienv()->ExceptionCheck()) xbmc_jnienv()->ExceptionClear(); } else if (index == CJNIMediaCodec::INFO_OUTPUT_BUFFERS_CHANGED) { m_output = m_codec->getOutputBuffers(); if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDAudioCodecAndroidMediaCodec::GetData(INFO_OUTPUT_BUFFERS_CHANGED) ExceptionCheck: getOutputBuffers"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); } } else if (index == CJNIMediaCodec::INFO_OUTPUT_FORMAT_CHANGED) { CJNIMediaFormat mediaformat = m_codec->getOutputFormat(); if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDAudioCodecAndroidMediaCodec::GetData(INFO_OUTPUT_FORMAT_CHANGED) ExceptionCheck: getOutputBuffers"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); } ConfigureOutputFormat(&mediaformat); } else if (index == CJNIMediaCodec::INFO_TRY_AGAIN_LATER) { // normal dequeueOutputBuffer timeout, ignore it. m_bufferUsed = 0; } else { // we should never get here CLog::Log(LOGERROR, "CDVDAudioCodecAndroidMediaCodec::GetData unknown index(%d)", index); } *dst = m_buffer; return m_bufferUsed; }
int CDVDVideoCodecAndroidMediaCodec::GetOutputPicture(void) { int rtn = 0; int64_t timeout_us = 50000; CJNIMediaCodecBufferInfo bufferInfo; int index = m_codec->dequeueOutputBuffer(bufferInfo, timeout_us); if (xbmc_jnienv()->ExceptionOccurred()) { CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture ExceptionOccurred"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); return 0; } if (index >= 0) { if (m_drop) { m_codec->releaseOutputBuffer(index, false); if (xbmc_jnienv()->ExceptionOccurred()) xbmc_jnienv()->ExceptionClear(); return 0; } // some devices will return a valid index // before signaling INFO_OUTPUT_BUFFERS_CHANGED which // is used to setup m_output, D'uh. setup m_output here. if (m_output.empty()) { m_output = m_codec->getOutputBuffers(); FlushInternal(); } 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()->ExceptionOccurred()) xbmc_jnienv()->ExceptionClear(); return 0; } if (!m_render_sw) { m_videobuffer.mediacodec = m_inflight[index]->Retain(); m_videobuffer.mediacodec->Validate(true); } else { int size = bufferInfo.size(); int offset = bufferInfo.offset(); if (!m_output[index].isDirect()) CLog::Log(LOGWARNING, "CDVDVideoCodecAndroidMediaCodec:: m_output[index].isDirect == false"); if (size && m_output[index].capacity()) { uint8_t *src_ptr = (uint8_t*)xbmc_jnienv()->GetDirectBufferAddress(m_output[index].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); } 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; /* CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture " "index(%d), pts(%f)", index, m_videobuffer.pts); */ // always, check/clear jni exceptions. if (xbmc_jnienv()->ExceptionOccurred()) xbmc_jnienv()->ExceptionClear(); rtn = 1; } else if (index == CJNIMediaCodec::INFO_OUTPUT_BUFFERS_CHANGED) { m_output = m_codec->getOutputBuffers(); FlushInternal(); } else if (index == CJNIMediaCodec::INFO_OUTPUT_FORMAT_CHANGED) { CJNIMediaFormat mediaformat = m_codec->getOutputFormat(); ConfigureOutputFormat(&mediaformat); } else if (index == CJNIMediaCodec::INFO_TRY_AGAIN_LATER) { // normal dequeueOutputBuffer timeout, ignore it. rtn = -1; } else { // we should never get here CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture unknown index(%d)", index); } return rtn; }
bool CDVDAudioCodecAndroidMediaCodec::ConfigureMediaCodec(void) { // setup a MediaFormat to match the audio content, // used by codec during configure CJNIMediaFormat mediaformat = CJNIMediaFormat::createAudioFormat( m_mime.c_str(), m_hints.samplerate, m_hints.channels); // handle codec extradata if (m_hints.extrasize) { size_t size = m_hints.extrasize; void *src_ptr = m_hints.extradata; // Allocate a byte buffer via allocateDirect in java instead of NewDirectByteBuffer, // since the latter doesn't allocate storage of its own, and we don't know how long // the codec uses the buffer. CJNIByteBuffer bytebuffer = CJNIByteBuffer::allocateDirect(size); void *dts_ptr = xbmc_jnienv()->GetDirectBufferAddress(bytebuffer.get_raw()); memcpy(dts_ptr, src_ptr, size); // codec will automatically handle buffers as extradata // using entries with keys "csd-0", "csd-1", etc. mediaformat.setByteBuffer("csd-0", bytebuffer); } else if (m_hints.codec == AV_CODEC_ID_AAC || m_hints.codec == AV_CODEC_ID_AAC_LATM) { mediaformat.setInteger(CJNIMediaFormat::KEY_IS_ADTS, 1); } // configure and start the codec. // use the MediaFormat that we have setup. // use a null MediaCrypto, our content is not encrypted. // use a null Surface int flags = 0; CJNIMediaCrypto crypto(jni::jhobject(NULL)); CJNISurface surface(jni::jhobject(NULL)); m_codec->configure(mediaformat, surface, crypto, flags); // always, check/clear jni exceptions. if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDAudioCodecAndroidMediaCodec::ExceptionCheck: configure"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); return false; } m_codec->start(); // always, check/clear jni exceptions. if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDAudioCodecAndroidMediaCodec::ExceptionCheck: start"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); return false; } // There is no guarantee we'll get an INFO_OUTPUT_FORMAT_CHANGED (up to Android 4.3) // Configure the output with defaults ConfigureOutputFormat(&mediaformat); return true; }
bool CDVDVideoCodecAndroidMediaCodec::ConfigureMediaCodec(void) { // setup a MediaFormat to match the video content, // used by codec during configure CJNIMediaFormat mediaformat = CJNIMediaFormat::createVideoFormat( m_mime.c_str(), m_hints.width, m_hints.height); // some android devices forget to default the demux input max size mediaformat.setInteger(CJNIMediaFormat::KEY_MAX_INPUT_SIZE, 0); // handle codec extradata if (m_hints.extrasize) { size_t size = m_hints.extrasize; void *src_ptr = m_hints.extradata; if (m_bitstream) { size = m_bitstream->GetExtraSize(); src_ptr = m_bitstream->GetExtraData(); } // Allocate a byte buffer via allocateDirect in java instead of NewDirectByteBuffer, // since the latter doesn't allocate storage of its own, and we don't know how long // the codec uses the buffer. CJNIByteBuffer bytebuffer = CJNIByteBuffer::allocateDirect(size); void *dts_ptr = xbmc_jnienv()->GetDirectBufferAddress(bytebuffer.get_raw()); memcpy(dts_ptr, src_ptr, size); // codec will automatically handle buffers as extradata // using entries with keys "csd-0", "csd-1", etc. mediaformat.setByteBuffer("csd-0", bytebuffer); } InitSurfaceTexture(); // configure and start the codec. // use the MediaFormat that we have setup. // use a null MediaCrypto, our content is not encrypted. // use a null Surface, we will extract the video picture data manually. int flags = 0; CJNIMediaCrypto crypto(jni::jhobject(NULL)); // our jni gets upset if we do this a different // way, do not mess with it. if (m_render_sw) { CJNISurface surface(jni::jhobject(NULL)); m_codec->configure(mediaformat, surface, crypto, flags); } else { m_codec->configure(mediaformat, *m_surface, crypto, flags); } // always, check/clear jni exceptions. if (xbmc_jnienv()->ExceptionOccurred()) { CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::ExceptionOccurred: configure"); xbmc_jnienv()->ExceptionClear(); return false; } m_codec->start(); // always, check/clear jni exceptions. if (xbmc_jnienv()->ExceptionOccurred()) { CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::ExceptionOccurred: start"); xbmc_jnienv()->ExceptionClear(); return false; } // There is no guarantee we'll get an INFO_OUTPUT_FORMAT_CHANGED (up to Android 4.3) // Configure the output with defaults ConfigureOutputFormat(&mediaformat); return true; }