void 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); } m_codec->start(); // always, check/clear jni exceptions. if (xbmc_jnienv()->ExceptionOccurred()) xbmc_jnienv()->ExceptionClear(); }
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 { if (m_render_surface) m_codec->configure(mediaformat, m_videosurface, crypto, flags); else m_codec->configure(mediaformat, *m_surface, crypto, flags); } // always, check/clear jni exceptions. if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::ExceptionCheck: configure"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); return false; } m_state = MEDIACODEC_STATE_CONFIGURED; m_codec->start(); // always, check/clear jni exceptions. if (xbmc_jnienv()->ExceptionCheck()) { CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::ExceptionCheck: start"); xbmc_jnienv()->ExceptionDescribe(); xbmc_jnienv()->ExceptionClear(); return false; } m_state = MEDIACODEC_STATE_FLUSHED; // 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 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; }
void CDVDVideoCodecAndroidMediaCodec::OutputFormatChanged(void) { CJNIMediaFormat mediaformat = m_codec->getOutputFormat(); int width = mediaformat.getInteger("width"); int height = mediaformat.getInteger("height"); int stride = mediaformat.getInteger("stride"); int slice_height= mediaformat.getInteger("slice-height"); int color_format= mediaformat.getInteger("color-format"); int crop_left = mediaformat.getInteger("crop-left"); int crop_top = mediaformat.getInteger("crop-top"); int crop_right = mediaformat.getInteger("crop-right"); int crop_bottom = mediaformat.getInteger("crop-bottom"); CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: " "width(%d), height(%d), stride(%d), slice-height(%d), color-format(%d)", width, height, stride, slice_height, color_format); CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: " "crop-left(%d), crop-top(%d), crop-right(%d), crop-bottom(%d)", crop_left, crop_top, crop_right, crop_bottom); if (!m_render_sw) { CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Direct Surface Rendering"); m_videobuffer.format = RENDER_FMT_MEDIACODEC; } else { // Android device quirks and fixes // Samsung Quirk: ignore width/height/stride/slice: http://code.google.com/p/android/issues/detail?id=37768#c3 if (strstr(m_codecname.c_str(), "OMX.SEC.avc.dec") != NULL || strstr(m_codecname.c_str(), "OMX.SEC.avcdec") != NULL) { width = stride = m_hints.width; height = slice_height = m_hints.height; } if (stride <= width) stride = width; if (slice_height <= height) { slice_height = height; if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar) { // NVidia Tegra 3 on Nexus 7 does not set slice_heights if (strstr(m_codecname.c_str(), "OMX.Nvidia.") != NULL) { slice_height = (((height) + 15) & ~15); CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: NVidia Tegra 3 quirk, slice_height(%d)", slice_height); } } } if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar) { slice_height -= crop_top / 2; // set crop top/left here, since the offset parameter already includes this. // if we would ignore the offset parameter in the BufferInfo, we could just keep // the original slice height and apply the top/left cropping instead. crop_top = 0; crop_left = 0; } // default picture format to none for (int i = 0; i < 4; i++) m_src_offset[i] = m_src_stride[i] = 0; // delete any existing buffers for (int i = 0; i < 4; i++) free(m_videobuffer.data[i]); // setup picture format and data offset vectors if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar) { CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420Planar"); // Y plane m_src_stride[0] = stride; m_src_offset[0] = crop_top * stride; m_src_offset[0]+= crop_left; // U plane m_src_stride[1] = (stride + 1) / 2; // skip over the Y plane m_src_offset[1] = slice_height * stride; // crop_top/crop_left divided by two // because one byte of the U/V planes // corresponds to two pixels horizontally/vertically m_src_offset[1]+= crop_top / 2 * m_src_stride[1]; m_src_offset[1]+= crop_left / 2; // V plane m_src_stride[2] = (stride + 1) / 2; // skip over the Y plane m_src_offset[2] = slice_height * stride; // skip over the U plane m_src_offset[2]+= ((slice_height + 1) / 2) * ((stride + 1) / 2); // crop_top/crop_left divided by two // because one byte of the U/V planes // corresponds to two pixels horizontally/vertically m_src_offset[2]+= crop_top / 2 * m_src_stride[2]; m_src_offset[2]+= crop_left / 2; m_videobuffer.iLineSize[0] = width; // Y m_videobuffer.iLineSize[1] = (width + 1) /2; // U m_videobuffer.iLineSize[2] = (width + 1) /2; // V m_videobuffer.iLineSize[3] = 0; unsigned int iPixels = width * height; unsigned int iChromaPixels = iPixels/4; m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels); m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels); m_videobuffer.data[2] = (uint8_t*)malloc(16 + iChromaPixels); m_videobuffer.data[3] = NULL; m_videobuffer.format = RENDER_FMT_YUV420P; } else if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420SemiPlanar || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_QCOM_FormatYUV420SemiPlanar || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar || color_format == CJNIMediaCodecInfoCodecCapabilities::OMX_QCOM_COLOR_FormatYVU420SemiPlanarInterlace) { CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420SemiPlanar"); // Y plane m_src_stride[0] = stride; m_src_offset[0] = crop_top * stride; m_src_offset[0]+= crop_left; // UV plane m_src_stride[1] = stride; // skip over the Y plane m_src_offset[1] = slice_height * stride; m_src_offset[1]+= crop_top * stride; m_src_offset[1]+= crop_left; m_videobuffer.iLineSize[0] = width; // Y m_videobuffer.iLineSize[1] = width; // UV m_videobuffer.iLineSize[2] = 0; m_videobuffer.iLineSize[3] = 0; unsigned int iPixels = width * height; unsigned int iChromaPixels = iPixels; m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels); m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels); m_videobuffer.data[2] = NULL; m_videobuffer.data[3] = NULL; m_videobuffer.format = RENDER_FMT_NV12; } else { CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Fixme unknown color_format(%d)", color_format); return; } } // picture display width/height include the cropping. m_videobuffer.iDisplayWidth = crop_right + 1 - crop_left; m_videobuffer.iDisplayHeight = crop_bottom + 1 - crop_top; // clear any jni exceptions if (xbmc_jnienv()->ExceptionOccurred()) xbmc_jnienv()->ExceptionClear(); }
void CJNIMediaCodec::configure(const CJNIMediaFormat &format, const CJNISurface &surface, const CJNIMediaCrypto &crypto, int flags) { call_method<void>(m_object, "configure", "(Landroid/media/MediaFormat;Landroid/view/Surface;Landroid/media/MediaCrypto;I)V", format.get_raw(), surface.get_raw(), crypto.get_raw(), flags); }