2 * Copyright (C) 2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 // http://developer.android.com/reference/android/media/MediaCodec.html
24 // Android MediaCodec class can be used to access low-level media codec,
25 // i.e. encoder/decoder components. (android.media.MediaCodec). Requires
26 // SDK16+ which is 4.1 Jellybean and above.
29 #include "DVDVideoCodecAndroidMediaCodec.h"
31 #include "Application.h"
32 #include "ApplicationMessenger.h"
34 #include "threads/Atomics.h"
35 #include "utils/BitstreamConverter.h"
36 #include "utils/CPUInfo.h"
37 #include "utils/log.h"
39 #include "android/jni/ByteBuffer.h"
40 #include "android/jni/MediaCodec.h"
41 #include "android/jni/MediaCrypto.h"
42 #include "android/jni/MediaFormat.h"
43 #include "android/jni/MediaCodecList.h"
44 #include "android/jni/MediaCodecInfo.h"
45 #include "android/jni/Surface.h"
46 #include "android/jni/SurfaceTexture.h"
47 #include "android/activity/AndroidFeatures.h"
49 #include <GLES2/gl2.h>
50 #include <GLES2/gl2ext.h>
52 static bool CanSurfaceRenderWhiteList(const std::string &name)
54 // All devices 'should' be capiable of surface rendering
55 // but that seems to be hit or miss as most odd name devices
56 // cannot surface render.
57 static const char *cansurfacerender_decoders[] = {
65 for (const char **ptr = cansurfacerender_decoders; *ptr; ptr++)
67 if (!strnicmp(*ptr, name.c_str(), strlen(*ptr)))
73 static bool IsBlacklisted(const std::string &name)
75 static const char *blacklisted_decoders[] = {
76 // No software decoders
78 // For Rockchip non-standard components
90 for (const char **ptr = blacklisted_decoders; *ptr; ptr++)
92 if (!strnicmp(*ptr, name.c_str(), strlen(*ptr)))
98 static bool IsSupportedColorFormat(int color_format)
100 static const int supported_colorformats[] = {
101 CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar,
102 CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar,
103 CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420SemiPlanar,
104 CJNIMediaCodecInfoCodecCapabilities::COLOR_QCOM_FormatYUV420SemiPlanar,
105 CJNIMediaCodecInfoCodecCapabilities::OMX_QCOM_COLOR_FormatYVU420SemiPlanarInterlace,
108 for (const int *ptr = supported_colorformats; *ptr != -1; ptr++)
110 if (color_format == *ptr)
116 /*****************************************************************************/
117 /*****************************************************************************/
118 class CNULL_Listener : public CJNISurfaceTextureOnFrameAvailableListener
121 CNULL_Listener() : CJNISurfaceTextureOnFrameAvailableListener(jni::jhobject(NULL)) {};
124 virtual void OnFrameAvailable(CJNISurfaceTexture &surface) {};
127 class CDVDMediaCodecOnFrameAvailable : public CEvent, CJNISurfaceTextureOnFrameAvailableListener
130 CDVDMediaCodecOnFrameAvailable(boost::shared_ptr<CJNISurfaceTexture> &surfaceTexture)
131 : m_surfaceTexture(surfaceTexture)
133 m_surfaceTexture->setOnFrameAvailableListener(*this);
136 virtual ~CDVDMediaCodecOnFrameAvailable()
138 // unhook the callback
139 CNULL_Listener null_listener;
140 m_surfaceTexture->setOnFrameAvailableListener(null_listener);
144 virtual void OnFrameAvailable(CJNISurfaceTexture &surface)
150 boost::shared_ptr<CJNISurfaceTexture> m_surfaceTexture;
154 /*****************************************************************************/
155 /*****************************************************************************/
156 CDVDMediaCodecInfo::CDVDMediaCodecInfo(
158 , unsigned int texture
159 , boost::shared_ptr<CJNIMediaCodec> &codec
160 , boost::shared_ptr<CJNISurfaceTexture> &surfacetexture
161 , boost::shared_ptr<CDVDMediaCodecOnFrameAvailable> &frameready
169 , m_surfacetexture(surfacetexture)
170 , m_frameready(frameready)
173 assert(m_index >= 0);
174 assert(m_texture > 0);
175 assert(m_codec != NULL);
176 assert(m_surfacetexture != NULL);
177 assert(m_frameready != NULL);
180 CDVDMediaCodecInfo::~CDVDMediaCodecInfo()
185 CDVDMediaCodecInfo* CDVDMediaCodecInfo::Retain()
187 AtomicIncrement(&m_refs);
192 long CDVDMediaCodecInfo::Release()
194 long count = AtomicDecrement(&m_refs);
197 ReleaseOutputBuffer(false);
204 void CDVDMediaCodecInfo::Validate(bool state)
206 CSingleLock lock(m_section);
211 void CDVDMediaCodecInfo::ReleaseOutputBuffer(bool render)
213 CSingleLock lock(m_section);
218 // release OutputBuffer and render if indicated
219 // then wait for rendered frame to become avaliable.
222 m_frameready->Reset();
224 m_codec->releaseOutputBuffer(m_index, render);
226 if (xbmc_jnienv()->ExceptionOccurred())
228 CLog::Log(LOGERROR, "CDVDMediaCodecInfo::ReleaseOutputBuffer "
229 "ExceptionOccurred render(%d)", render);
230 xbmc_jnienv()->ExceptionDescribe();
231 xbmc_jnienv()->ExceptionClear();
235 int CDVDMediaCodecInfo::GetIndex() const
237 CSingleLock lock(m_section);
242 int CDVDMediaCodecInfo::GetTextureID() const
244 // since m_texture never changes,
245 // we do not need a m_section lock here.
249 void CDVDMediaCodecInfo::GetTransformMatrix(float *textureMatrix)
251 CSingleLock lock(m_section);
256 m_surfacetexture->getTransformMatrix(textureMatrix);
259 void CDVDMediaCodecInfo::UpdateTexImage()
261 CSingleLock lock(m_section);
266 // updateTexImage will check and spew any prior gl errors,
267 // clear them before we call updateTexImage.
270 // this is key, after calling releaseOutputBuffer, we must
271 // wait a little for MediaCodec to render to the surface.
272 // Then we can updateTexImage without delay. If we do not
273 // wait, then video playback gets jerky. To optomize this,
274 // we hook the SurfaceTexture OnFrameAvailable callback
275 // using CJNISurfaceTextureOnFrameAvailableListener and wait
276 // on a CEvent to fire. 20ms seems to be a good max fallback.
277 m_frameready->WaitMSec(20);
279 m_surfacetexture->updateTexImage();
280 if (xbmc_jnienv()->ExceptionOccurred())
282 CLog::Log(LOGERROR, "CDVDMediaCodecInfo::UpdateTexImage updateTexImage:ExceptionOccurred");
283 xbmc_jnienv()->ExceptionDescribe();
284 xbmc_jnienv()->ExceptionClear();
287 m_timestamp = m_surfacetexture->getTimestamp();
288 if (xbmc_jnienv()->ExceptionOccurred())
290 CLog::Log(LOGERROR, "CDVDMediaCodecInfo::UpdateTexImage getTimestamp:ExceptionOccurred");
291 xbmc_jnienv()->ExceptionDescribe();
292 xbmc_jnienv()->ExceptionClear();
296 /*****************************************************************************/
297 /*****************************************************************************/
298 CDVDVideoCodecAndroidMediaCodec::CDVDVideoCodecAndroidMediaCodec()
299 : m_formatname("mediacodec")
306 memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
309 CDVDVideoCodecAndroidMediaCodec::~CDVDVideoCodecAndroidMediaCodec()
314 bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
316 // check for 4.1 Jellybean and above.
317 if (CAndroidFeatures::GetVersion() < 16)
323 switch(m_hints.codec)
325 case AV_CODEC_ID_MPEG2VIDEO:
326 m_mime = "video/mpeg2";
327 m_formatname = "amc-mpeg2";
329 case AV_CODEC_ID_MPEG4:
330 m_mime = "video/mp4v-es";
331 m_formatname = "amc-mpeg4";
333 case AV_CODEC_ID_H263:
334 m_mime = "video/3gpp";
335 m_formatname = "amc-h263";
337 case AV_CODEC_ID_VP3:
338 case AV_CODEC_ID_VP6:
339 case AV_CODEC_ID_VP6F:
340 case AV_CODEC_ID_VP8:
341 //m_mime = "video/x-vp6";
342 //m_mime = "video/x-vp7";
343 m_mime = "video/x-vnd.on2.vp8";
344 m_formatname = "amc-vpX";
346 case AV_CODEC_ID_AVS:
347 case AV_CODEC_ID_CAVS:
348 case AV_CODEC_ID_H264:
349 m_mime = "video/avc";
350 m_formatname = "amc-h264";
351 // check for h264-avcC and convert to h264-annex-b
352 if (m_hints.extradata && *(uint8_t*)m_hints.extradata == 1)
354 m_bitstream = new CBitstreamConverter;
355 if (!m_bitstream->Open(m_hints.codec, (uint8_t*)m_hints.extradata, m_hints.extrasize, true))
357 SAFE_DELETE(m_bitstream);
362 case AV_CODEC_ID_VC1:
363 case AV_CODEC_ID_WMV3:
364 m_mime = "video/wvc1";
365 //m_mime = "video/wmv9";
366 m_formatname = "amc-vc1";
369 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Unknown hints.codec(%d)", hints.codec);
374 // CJNIMediaCodec::createDecoderByXXX doesn't handle errors nicely,
375 // it crashes if the codec isn't found. This is fixed in latest AOSP,
376 // but not in current 4.1 devices. So 1st search for a matching codec, then create it.
377 bool hasSupportedColorFormat = false;
378 int num_codecs = CJNIMediaCodecList::getCodecCount();
379 for (int i = 0; i < num_codecs; i++)
381 CJNIMediaCodecInfo codec_info = CJNIMediaCodecList::getCodecInfoAt(i);
382 if (codec_info.isEncoder())
384 m_codecname = codec_info.getName();
385 if (IsBlacklisted(m_codecname))
388 std::vector<std::string> types = codec_info.getSupportedTypes();
389 // return the 1st one we find, that one is typically 'the best'
390 for (size_t j = 0; j < types.size(); ++j)
392 if (types[j] == m_mime)
394 m_codec = boost::shared_ptr<CJNIMediaCodec>(new CJNIMediaCodec(CJNIMediaCodec::createByCodecName(m_codecname)));
396 CJNIMediaCodecInfoCodecCapabilities codec_caps = codec_info.getCapabilitiesForType(m_mime);
397 std::vector<int> color_formats = codec_caps.colorFormats();
399 // clear any jni exceptions, jni gets upset if we do not.
400 if (xbmc_jnienv()->ExceptionOccurred())
402 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open ExceptionOccurred");
403 xbmc_jnienv()->ExceptionClear();
407 hasSupportedColorFormat = false;
408 for (size_t k = 0; k < color_formats.size(); ++k)
410 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::Open "
411 "m_codecname(%s), colorFormat(%d)", m_codecname.c_str(), color_formats[k]);
412 if (IsSupportedColorFormat(color_formats[k]))
413 hasSupportedColorFormat = true;
423 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Failed to create Android MediaCodec");
424 SAFE_DELETE(m_bitstream);
428 // whitelist of devices that can surface render.
429 m_render_sw = !CanSurfaceRenderWhiteList(m_codecname);
432 if (!hasSupportedColorFormat)
434 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: No supported color format");
436 SAFE_DELETE(m_bitstream);
441 if (!ConfigureMediaCodec())
444 SAFE_DELETE(m_bitstream);
448 // setup a YUV420P DVDVideoPicture buffer.
449 // first make sure all properties are reset.
450 memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
452 m_videobuffer.dts = DVD_NOPTS_VALUE;
453 m_videobuffer.pts = DVD_NOPTS_VALUE;
454 m_videobuffer.color_range = 0;
455 m_videobuffer.color_matrix = 4;
456 m_videobuffer.iFlags = DVP_FLAG_ALLOCATED;
457 m_videobuffer.iWidth = m_hints.width;
458 m_videobuffer.iHeight = m_hints.height;
459 // these will get reset to crop values later
460 m_videobuffer.iDisplayWidth = m_hints.width;
461 m_videobuffer.iDisplayHeight = m_hints.height;
463 CLog::Log(LOGINFO, "CDVDVideoCodecAndroidMediaCodec:: "
464 "Open Android MediaCodec %s", m_codecname.c_str());
471 void CDVDVideoCodecAndroidMediaCodec::Dispose()
475 // release any retained demux packets
476 while (!m_demux.empty())
478 amc_demux &demux_pkt = m_demux.front();
479 free(demux_pkt.pData);
483 // invalidate any inflight outputbuffers, make sure
484 // m_output is empty so we do not create new ones
489 // clear m_videobuffer bits
492 free(m_videobuffer.data[0]), m_videobuffer.data[0] = NULL;
493 free(m_videobuffer.data[1]), m_videobuffer.data[1] = NULL;
494 free(m_videobuffer.data[2]), m_videobuffer.data[2] = NULL;
496 m_videobuffer.iFlags = 0;
497 // m_videobuffer.mediacodec is unioned with m_videobuffer.data[0]
498 // so be very careful when and how you touch it.
499 m_videobuffer.mediacodec = NULL;
507 ReleaseSurfaceTexture();
509 SAFE_DELETE(m_bitstream);
512 int CDVDVideoCodecAndroidMediaCodec::Decode(uint8_t *pData, int iSize, double dts, double pts)
514 // Handle input, add demuxer packet to input queue, we must accept it or
515 // it will be discarded as DVDPlayerVideo has no concept of "try again".
516 // we must return VC_BUFFER or VC_PICTURE, default to VC_BUFFER.
522 if (m_hints.ptsinvalid)
523 pts = DVD_NOPTS_VALUE;
525 // must check for an output picture 1st,
526 // otherwise, mediacodec can stall on some devices.
527 if (GetOutputPicture() > 0)
534 m_bitstream->Convert(pData, iSize);
535 iSize = m_bitstream->GetConvertSize();
536 pData = m_bitstream->GetConvertBuffer();
539 // queue demux pkt in case we cannot get an input buffer
543 demux_pkt.iSize = iSize;
544 demux_pkt.pData = (uint8_t*)malloc(iSize);
545 memcpy(demux_pkt.pData, pData, iSize);
546 m_demux.push(demux_pkt);
548 // try to fetch an input buffer
549 int64_t timeout_us = 5000;
550 int index = m_codec->dequeueInputBuffer(timeout_us);
553 // docs lie, getInputBuffers should be good after
554 // m_codec->start() but the internal refs are not
555 // setup until much later on some devices.
557 m_input = m_codec->getInputBuffers();
559 // we have an input buffer, fill it.
560 int size = m_input[index].capacity();
561 // fetch the front demux packet
562 amc_demux &demux_pkt = m_demux.front();
563 if (demux_pkt.iSize > size)
565 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode, iSize(%d) > size(%d)", iSize, size);
566 demux_pkt.iSize = size;
568 // fetch a pointer to the ByteBuffer backing store
569 void *dst_ptr = xbmc_jnienv()->GetDirectBufferAddress(m_input[index].get_raw());
571 memcpy(dst_ptr, demux_pkt.pData, demux_pkt.iSize);
573 free(demux_pkt.pData);
576 // Translate from dvdplayer dts/pts to MediaCodec pts,
577 // pts WILL get re-ordered by MediaCodec if needed.
578 // Do not try to pass pts as a unioned double/int64_t,
579 // some android devices will diddle with presentationTimeUs
580 // and you will get NaN back and DVDPlayerVideo will barf.
581 int64_t presentationTimeUs = AV_NOPTS_VALUE;
582 if (demux_pkt.pts != DVD_NOPTS_VALUE)
583 presentationTimeUs = demux_pkt.pts;
584 else if (demux_pkt.dts != DVD_NOPTS_VALUE)
585 presentationTimeUs = demux_pkt.dts;
587 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
588 "pts(%f), ipts(%lld), iSize(%d), GetDataSize(%d), loop_cnt(%d)",
589 presentationTimeUs, pts_dtoi(presentationTimeUs), iSize, GetDataSize(), loop_cnt);
593 m_codec->queueInputBuffer(index, offset, demux_pkt.iSize, presentationTimeUs, flags);
594 // clear any jni exceptions, jni gets upset if we do not.
595 if (xbmc_jnienv()->ExceptionOccurred())
597 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode ExceptionOccurred");
598 xbmc_jnienv()->ExceptionClear();
606 void CDVDVideoCodecAndroidMediaCodec::Reset()
611 // dump any pending demux packets
612 while (!m_demux.empty())
614 amc_demux &demux_pkt = m_demux.front();
615 free(demux_pkt.pData);
621 // flush all outputbuffers inflight, they will
622 // become invalid on m_codec->flush and generate
623 // a spew of java exceptions if used
626 // now we can flush the actual MediaCodec object
628 if (xbmc_jnienv()->ExceptionOccurred())
630 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Reset ExceptionOccurred");
631 xbmc_jnienv()->ExceptionClear();
634 // Invalidate our local DVDVideoPicture bits
635 m_videobuffer.pts = DVD_NOPTS_VALUE;
637 m_videobuffer.mediacodec = NULL;
641 bool CDVDVideoCodecAndroidMediaCodec::GetPicture(DVDVideoPicture* pDvdVideoPicture)
646 *pDvdVideoPicture = m_videobuffer;
648 // Invalidate our local DVDVideoPicture bits
649 m_videobuffer.pts = DVD_NOPTS_VALUE;
651 m_videobuffer.mediacodec = NULL;
656 bool CDVDVideoCodecAndroidMediaCodec::ClearPicture(DVDVideoPicture* pDvdVideoPicture)
658 if (pDvdVideoPicture->format == RENDER_FMT_MEDIACODEC)
659 SAFE_RELEASE(pDvdVideoPicture->mediacodec);
660 memset(pDvdVideoPicture, 0x00, sizeof(DVDVideoPicture));
665 void CDVDVideoCodecAndroidMediaCodec::SetDropState(bool bDrop)
669 m_videobuffer.iFlags |= DVP_FLAG_DROPPED;
671 m_videobuffer.iFlags &= ~DVP_FLAG_DROPPED;
674 int CDVDVideoCodecAndroidMediaCodec::GetDataSize(void)
676 // just ignore internal buffering contribution.
680 double CDVDVideoCodecAndroidMediaCodec::GetTimeSize(void)
682 // just ignore internal buffering contribution.
686 unsigned CDVDVideoCodecAndroidMediaCodec::GetAllowedReferences()
691 void CDVDVideoCodecAndroidMediaCodec::FlushInternal()
693 // invalidate any existing inflight buffers and create
694 // new ones to match the number of output buffers
699 for (size_t i = 0; i < m_inflight.size(); i++)
700 m_inflight[i]->Validate(false);
703 for (size_t i = 0; i < m_output.size(); i++)
705 m_inflight.push_back(
706 new CDVDMediaCodecInfo(i, m_textureId, m_codec, m_surfaceTexture, m_frameAvailable)
711 bool CDVDVideoCodecAndroidMediaCodec::ConfigureMediaCodec(void)
713 // setup a MediaFormat to match the video content,
714 // used by codec during configure
715 CJNIMediaFormat mediaformat = CJNIMediaFormat::createVideoFormat(
716 m_mime.c_str(), m_hints.width, m_hints.height);
717 // some android devices forget to default the demux input max size
718 mediaformat.setInteger(CJNIMediaFormat::KEY_MAX_INPUT_SIZE, 0);
720 // handle codec extradata
721 if (m_hints.extrasize)
723 size_t size = m_hints.extrasize;
724 void *src_ptr = m_hints.extradata;
727 size = m_bitstream->GetExtraSize();
728 src_ptr = m_bitstream->GetExtraData();
730 // Allocate a byte buffer via allocateDirect in java instead of NewDirectByteBuffer,
731 // since the latter doesn't allocate storage of its own, and we don't know how long
732 // the codec uses the buffer.
733 CJNIByteBuffer bytebuffer = CJNIByteBuffer::allocateDirect(size);
734 void *dts_ptr = xbmc_jnienv()->GetDirectBufferAddress(bytebuffer.get_raw());
735 memcpy(dts_ptr, src_ptr, size);
736 // codec will automatically handle buffers as extradata
737 // using entries with keys "csd-0", "csd-1", etc.
738 mediaformat.setByteBuffer("csd-0", bytebuffer);
741 InitSurfaceTexture();
743 // configure and start the codec.
744 // use the MediaFormat that we have setup.
745 // use a null MediaCrypto, our content is not encrypted.
746 // use a null Surface, we will extract the video picture data manually.
748 CJNIMediaCrypto crypto(jni::jhobject(NULL));
749 // our jni gets upset if we do this a different
750 // way, do not mess with it.
753 CJNISurface surface(jni::jhobject(NULL));
754 m_codec->configure(mediaformat, surface, crypto, flags);
758 m_codec->configure(mediaformat, *m_surface, crypto, flags);
760 // always, check/clear jni exceptions.
761 if (xbmc_jnienv()->ExceptionOccurred())
763 xbmc_jnienv()->ExceptionClear();
769 // always, check/clear jni exceptions.
770 if (xbmc_jnienv()->ExceptionOccurred())
772 xbmc_jnienv()->ExceptionClear();
779 int CDVDVideoCodecAndroidMediaCodec::GetOutputPicture(void)
783 int64_t timeout_us = 5000;
784 CJNIMediaCodecBufferInfo bufferInfo;
785 int index = m_codec->dequeueOutputBuffer(bufferInfo, timeout_us);
790 m_codec->releaseOutputBuffer(index, false);
791 if (xbmc_jnienv()->ExceptionOccurred())
792 xbmc_jnienv()->ExceptionClear();
796 // some devices will return a valid index
797 // before signaling INFO_OUTPUT_BUFFERS_CHANGED which
798 // is used to setup m_output, D'uh. setup m_output here.
799 if (m_output.empty())
801 m_output = m_codec->getOutputBuffers();
805 int flags = bufferInfo.flags();
806 if (flags & CJNIMediaCodec::BUFFER_FLAG_SYNC_FRAME)
807 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_SYNC_FRAME");
809 if (flags & CJNIMediaCodec::BUFFER_FLAG_CODEC_CONFIG)
810 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_CODEC_CONFIG");
812 if (flags & CJNIMediaCodec::BUFFER_FLAG_END_OF_STREAM)
814 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_END_OF_STREAM");
815 m_codec->releaseOutputBuffer(index, false);
816 if (xbmc_jnienv()->ExceptionOccurred())
817 xbmc_jnienv()->ExceptionClear();
823 m_videobuffer.mediacodec = m_inflight[index]->Retain();
824 m_videobuffer.mediacodec->Validate(true);
828 int size = bufferInfo.size();
829 int offset = bufferInfo.offset();
831 if (!m_output[index].isDirect())
832 CLog::Log(LOGWARNING, "CDVDVideoCodecAndroidMediaCodec:: m_output[index].isDirect == false");
834 if (size && m_output[index].capacity())
836 uint8_t *src_ptr = (uint8_t*)xbmc_jnienv()->GetDirectBufferAddress(m_output[index].get_raw());
840 if (m_videobuffer.format == RENDER_FMT_NV12)
842 else if (m_videobuffer.format == RENDER_FMT_YUV420P)
845 for (int i = 0; i < loop_end; i++)
847 uint8_t *src = src_ptr + m_src_offset[i];
848 int src_stride = m_src_stride[i];
849 uint8_t *dst = m_videobuffer.data[i];
850 int dst_stride = m_videobuffer.iLineSize[i];
852 int height = m_videobuffer.iHeight;
854 height = (m_videobuffer.iHeight + 1) / 2;
856 if (src_stride == dst_stride)
857 memcpy(dst, src, dst_stride * height);
859 for (int j = 0; j < height; j++, src += src_stride, dst += dst_stride)
860 memcpy(dst, src, dst_stride);
863 m_codec->releaseOutputBuffer(index, false);
866 int64_t pts= bufferInfo.presentationTimeUs();
867 m_videobuffer.dts = DVD_NOPTS_VALUE;
868 m_videobuffer.pts = DVD_NOPTS_VALUE;
869 if (pts != AV_NOPTS_VALUE)
870 m_videobuffer.pts = pts;
873 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture "
874 "index(%d), pts(%f)", index, m_videobuffer.pts);
876 // always, check/clear jni exceptions.
877 if (xbmc_jnienv()->ExceptionOccurred())
878 xbmc_jnienv()->ExceptionClear();
882 else if (index == CJNIMediaCodec::INFO_OUTPUT_BUFFERS_CHANGED)
884 m_output = m_codec->getOutputBuffers();
887 else if (index == CJNIMediaCodec::INFO_OUTPUT_FORMAT_CHANGED)
889 OutputFormatChanged();
891 else if (index == CJNIMediaCodec::INFO_TRY_AGAIN_LATER)
893 // normal dequeueOutputBuffer timeout, ignore it.
898 // we should never get here
899 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture unknown index(%d)", index);
905 void CDVDVideoCodecAndroidMediaCodec::OutputFormatChanged(void)
907 CJNIMediaFormat mediaformat = m_codec->getOutputFormat();
909 int width = mediaformat.getInteger("width");
910 int height = mediaformat.getInteger("height");
911 int stride = mediaformat.getInteger("stride");
912 int slice_height= mediaformat.getInteger("slice-height");
913 int color_format= mediaformat.getInteger("color-format");
914 int crop_left = mediaformat.getInteger("crop-left");
915 int crop_top = mediaformat.getInteger("crop-top");
916 int crop_right = mediaformat.getInteger("crop-right");
917 int crop_bottom = mediaformat.getInteger("crop-bottom");
919 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
920 "width(%d), height(%d), stride(%d), slice-height(%d), color-format(%d)",
921 width, height, stride, slice_height, color_format);
922 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
923 "crop-left(%d), crop-top(%d), crop-right(%d), crop-bottom(%d)",
924 crop_left, crop_top, crop_right, crop_bottom);
928 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Direct Surface Rendering");
929 m_videobuffer.format = RENDER_FMT_MEDIACODEC;
933 // Android device quirks and fixes
935 // Samsung Quirk: ignore width/height/stride/slice: http://code.google.com/p/android/issues/detail?id=37768#c3
936 if (strstr(m_codecname.c_str(), "OMX.SEC.avc.dec") != NULL || strstr(m_codecname.c_str(), "OMX.SEC.avcdec") != NULL)
938 width = stride = m_hints.width;
939 height = slice_height = m_hints.height;
943 if (slice_height <= height)
945 slice_height = height;
946 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
948 // NVidia Tegra 3 on Nexus 7 does not set slice_heights
949 if (strstr(m_codecname.c_str(), "OMX.Nvidia.") != NULL)
951 slice_height = (((height) + 15) & ~15);
952 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: NVidia Tegra 3 quirk, slice_height(%d)", slice_height);
956 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar)
958 slice_height -= crop_top / 2;
959 // set crop top/left here, since the offset parameter already includes this.
960 // if we would ignore the offset parameter in the BufferInfo, we could just keep
961 // the original slice height and apply the top/left cropping instead.
966 // default picture format to none
967 for (int i = 0; i < 4; i++)
968 m_src_offset[i] = m_src_stride[i] = 0;
969 // delete any existing buffers
970 for (int i = 0; i < 4; i++)
971 free(m_videobuffer.data[i]);
973 // setup picture format and data offset vectors
974 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
976 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420Planar");
979 m_src_stride[0] = stride;
980 m_src_offset[0] = crop_top * stride;
981 m_src_offset[0]+= crop_left;
984 m_src_stride[1] = (stride + 1) / 2;
985 // skip over the Y plane
986 m_src_offset[1] = slice_height * stride;
987 // crop_top/crop_left divided by two
988 // because one byte of the U/V planes
989 // corresponds to two pixels horizontally/vertically
990 m_src_offset[1]+= crop_top / 2 * m_src_stride[1];
991 m_src_offset[1]+= crop_left / 2;
994 m_src_stride[2] = (stride + 1) / 2;
995 // skip over the Y plane
996 m_src_offset[2] = slice_height * stride;
997 // skip over the U plane
998 m_src_offset[2]+= ((slice_height + 1) / 2) * ((stride + 1) / 2);
999 // crop_top/crop_left divided by two
1000 // because one byte of the U/V planes
1001 // corresponds to two pixels horizontally/vertically
1002 m_src_offset[2]+= crop_top / 2 * m_src_stride[2];
1003 m_src_offset[2]+= crop_left / 2;
1005 m_videobuffer.iLineSize[0] = width; // Y
1006 m_videobuffer.iLineSize[1] = (width + 1) /2; // U
1007 m_videobuffer.iLineSize[2] = (width + 1) /2; // V
1008 m_videobuffer.iLineSize[3] = 0;
1010 unsigned int iPixels = width * height;
1011 unsigned int iChromaPixels = iPixels/4;
1012 m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
1013 m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
1014 m_videobuffer.data[2] = (uint8_t*)malloc(16 + iChromaPixels);
1015 m_videobuffer.data[3] = NULL;
1016 m_videobuffer.format = RENDER_FMT_YUV420P;
1018 else if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420SemiPlanar
1019 || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_QCOM_FormatYUV420SemiPlanar
1020 || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar
1021 || color_format == CJNIMediaCodecInfoCodecCapabilities::OMX_QCOM_COLOR_FormatYVU420SemiPlanarInterlace)
1024 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420SemiPlanar");
1027 m_src_stride[0] = stride;
1028 m_src_offset[0] = crop_top * stride;
1029 m_src_offset[0]+= crop_left;
1032 m_src_stride[1] = stride;
1033 // skip over the Y plane
1034 m_src_offset[1] = slice_height * stride;
1035 m_src_offset[1]+= crop_top * stride;
1036 m_src_offset[1]+= crop_left;
1038 m_videobuffer.iLineSize[0] = width; // Y
1039 m_videobuffer.iLineSize[1] = width; // UV
1040 m_videobuffer.iLineSize[2] = 0;
1041 m_videobuffer.iLineSize[3] = 0;
1043 unsigned int iPixels = width * height;
1044 unsigned int iChromaPixels = iPixels;
1045 m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
1046 m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
1047 m_videobuffer.data[2] = NULL;
1048 m_videobuffer.data[3] = NULL;
1049 m_videobuffer.format = RENDER_FMT_NV12;
1053 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Fixme unknown color_format(%d)", color_format);
1058 // picture display width/height include the cropping.
1059 m_videobuffer.iDisplayWidth = crop_right + 1 - crop_left;
1060 m_videobuffer.iDisplayHeight = crop_bottom + 1 - crop_top;
1062 // clear any jni exceptions
1063 if (xbmc_jnienv()->ExceptionOccurred())
1064 xbmc_jnienv()->ExceptionClear();
1067 void CDVDVideoCodecAndroidMediaCodec::CallbackInitSurfaceTexture(void *userdata)
1069 CDVDVideoCodecAndroidMediaCodec *ctx = static_cast<CDVDVideoCodecAndroidMediaCodec*>(userdata);
1070 ctx->InitSurfaceTexture();
1073 void CDVDVideoCodecAndroidMediaCodec::InitSurfaceTexture(void)
1078 // We MUST create the GLES texture on the main thread
1079 // to match where the valid GLES context is located.
1080 // It would be nice to move this out of here, we would need
1081 // to create/fetch/create from g_RenderMananger. But g_RenderMananger
1082 // does not know we are using MediaCodec until Configure and we
1083 // we need m_surfaceTexture valid before then. Chicken, meet Egg.
1084 if (g_application.IsCurrentThread())
1086 // localize GLuint so we do not spew gles includes in our header
1089 glGenTextures(1, &texture_id);
1090 glBindTexture( GL_TEXTURE_EXTERNAL_OES, texture_id);
1091 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1092 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1093 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1094 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1095 glBindTexture( GL_TEXTURE_EXTERNAL_OES, 0);
1096 m_textureId = texture_id;
1098 m_surfaceTexture = boost::shared_ptr<CJNISurfaceTexture>(new CJNISurfaceTexture(m_textureId));
1099 // hook the surfaceTexture OnFrameAvailable callback
1100 m_frameAvailable = boost::shared_ptr<CDVDMediaCodecOnFrameAvailable>(new CDVDMediaCodecOnFrameAvailable(m_surfaceTexture));
1101 m_surface = new CJNISurface(*m_surfaceTexture);
1105 ThreadMessageCallback callbackData;
1106 callbackData.callback = &CallbackInitSurfaceTexture;
1107 callbackData.userptr = (void*)this;
1110 msg.dwMessage = TMSG_CALLBACK;
1111 msg.lpVoid = (void*)&callbackData;
1114 CApplicationMessenger::Get().SendMessage(msg, true);
1120 void CDVDVideoCodecAndroidMediaCodec::ReleaseSurfaceTexture(void)
1125 // it is safe to delete here even though these items
1126 // were created in the main thread instance
1127 SAFE_DELETE(m_surface);
1128 m_frameAvailable.reset();
1129 m_surfaceTexture.reset();
1131 if (m_textureId > 0)
1133 GLuint texture_id = m_textureId;
1134 glDeleteTextures(1, &texture_id);