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[] = {
63 for (const char **ptr = cansurfacerender_decoders; *ptr; ptr++)
65 if (!strnicmp(*ptr, name.c_str(), strlen(*ptr)))
71 static bool IsBlacklisted(const std::string &name)
73 static const char *blacklisted_decoders[] = {
74 // No software decoders
78 for (const char **ptr = blacklisted_decoders; *ptr; ptr++)
80 if (!strnicmp(*ptr, name.c_str(), strlen(*ptr)))
86 static bool IsSupportedColorFormat(int color_format)
88 static const int supported_colorformats[] = {
89 CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar,
90 CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar,
91 CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420SemiPlanar,
92 CJNIMediaCodecInfoCodecCapabilities::COLOR_QCOM_FormatYUV420SemiPlanar,
93 CJNIMediaCodecInfoCodecCapabilities::OMX_QCOM_COLOR_FormatYVU420SemiPlanarInterlace,
96 for (const int *ptr = supported_colorformats; *ptr != -1; ptr++)
98 if (color_format == *ptr)
104 /*****************************************************************************/
105 /*****************************************************************************/
106 class CNULL_Listener : public CJNISurfaceTextureOnFrameAvailableListener
109 CNULL_Listener() : CJNISurfaceTextureOnFrameAvailableListener(jni::jhobject(NULL)) {};
112 virtual void OnFrameAvailable(CJNISurfaceTexture &surface) {};
115 class CDVDMediaCodecOnFrameAvailable : public CEvent, CJNISurfaceTextureOnFrameAvailableListener
118 CDVDMediaCodecOnFrameAvailable(boost::shared_ptr<CJNISurfaceTexture> &surfaceTexture)
119 : m_surfaceTexture(surfaceTexture)
121 m_surfaceTexture->setOnFrameAvailableListener(*this);
124 virtual ~CDVDMediaCodecOnFrameAvailable()
126 // unhook the callback
127 CNULL_Listener null_listener;
128 m_surfaceTexture->setOnFrameAvailableListener(null_listener);
132 virtual void OnFrameAvailable(CJNISurfaceTexture &surface)
138 boost::shared_ptr<CJNISurfaceTexture> m_surfaceTexture;
142 /*****************************************************************************/
143 /*****************************************************************************/
144 CDVDMediaCodecInfo::CDVDMediaCodecInfo(
146 , unsigned int texture
147 , boost::shared_ptr<CJNIMediaCodec> &codec
148 , boost::shared_ptr<CJNISurfaceTexture> &surfacetexture
149 , boost::shared_ptr<CDVDMediaCodecOnFrameAvailable> &frameready
157 , m_surfacetexture(surfacetexture)
158 , m_frameready(frameready)
161 assert(m_index >= 0);
162 assert(m_texture > 0);
163 assert(m_codec != NULL);
164 assert(m_surfacetexture != NULL);
165 assert(m_frameready != NULL);
168 CDVDMediaCodecInfo::~CDVDMediaCodecInfo()
173 CDVDMediaCodecInfo* CDVDMediaCodecInfo::Retain()
175 AtomicIncrement(&m_refs);
180 long CDVDMediaCodecInfo::Release()
182 long count = AtomicDecrement(&m_refs);
185 ReleaseOutputBuffer(false);
192 void CDVDMediaCodecInfo::Validate(bool state)
194 CSingleLock lock(m_section);
199 void CDVDMediaCodecInfo::ReleaseOutputBuffer(bool render)
201 CSingleLock lock(m_section);
206 // release OutputBuffer and render if indicated
207 // then wait for rendered frame to become avaliable.
210 m_frameready->Reset();
212 m_codec->releaseOutputBuffer(m_index, render);
214 if (xbmc_jnienv()->ExceptionOccurred())
216 CLog::Log(LOGERROR, "CDVDMediaCodecInfo::ReleaseOutputBuffer "
217 "ExceptionOccurred render(%d)", render);
218 xbmc_jnienv()->ExceptionDescribe();
219 xbmc_jnienv()->ExceptionClear();
223 int CDVDMediaCodecInfo::GetIndex() const
225 CSingleLock lock(m_section);
230 int CDVDMediaCodecInfo::GetTextureID() const
232 // since m_texture never changes,
233 // we do not need a m_section lock here.
237 void CDVDMediaCodecInfo::GetTransformMatrix(float *textureMatrix)
239 CSingleLock lock(m_section);
244 m_surfacetexture->getTransformMatrix(textureMatrix);
247 void CDVDMediaCodecInfo::UpdateTexImage()
249 CSingleLock lock(m_section);
254 // updateTexImage will check and spew any prior gl errors,
255 // clear them before we call updateTexImage.
258 // this is key, after calling releaseOutputBuffer, we must
259 // wait a little for MediaCodec to render to the surface.
260 // Then we can updateTexImage without delay. If we do not
261 // wait, then video playback gets jerky. To optomize this,
262 // we hook the SurfaceTexture OnFrameAvailable callback
263 // using CJNISurfaceTextureOnFrameAvailableListener and wait
264 // on a CEvent to fire. 20ms seems to be a good max fallback.
265 m_frameready->WaitMSec(20);
267 m_surfacetexture->updateTexImage();
268 if (xbmc_jnienv()->ExceptionOccurred())
270 CLog::Log(LOGERROR, "CDVDMediaCodecInfo::UpdateTexImage updateTexImage:ExceptionOccurred");
271 xbmc_jnienv()->ExceptionDescribe();
272 xbmc_jnienv()->ExceptionClear();
275 m_timestamp = m_surfacetexture->getTimestamp();
276 if (xbmc_jnienv()->ExceptionOccurred())
278 CLog::Log(LOGERROR, "CDVDMediaCodecInfo::UpdateTexImage getTimestamp:ExceptionOccurred");
279 xbmc_jnienv()->ExceptionDescribe();
280 xbmc_jnienv()->ExceptionClear();
284 /*****************************************************************************/
285 /*****************************************************************************/
286 CDVDVideoCodecAndroidMediaCodec::CDVDVideoCodecAndroidMediaCodec()
287 : m_formatname("mediacodec")
294 memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
297 CDVDVideoCodecAndroidMediaCodec::~CDVDVideoCodecAndroidMediaCodec()
302 bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
304 // check for 4.1 Jellybean and above.
305 if (CAndroidFeatures::GetVersion() < 16)
311 switch(m_hints.codec)
313 case AV_CODEC_ID_MPEG2VIDEO:
314 m_mime = "video/mpeg2";
315 m_formatname = "amc-mpeg2";
317 case AV_CODEC_ID_MPEG4:
318 m_mime = "video/mp4v-es";
319 m_formatname = "amc-mpeg4";
321 case AV_CODEC_ID_H263:
322 m_mime = "video/3gpp";
323 m_formatname = "amc-h263";
325 case AV_CODEC_ID_VP3:
326 case AV_CODEC_ID_VP6:
327 case AV_CODEC_ID_VP6F:
328 case AV_CODEC_ID_VP8:
329 //m_mime = "video/x-vp6";
330 //m_mime = "video/x-vp7";
331 m_mime = "video/x-vnd.on2.vp8";
332 m_formatname = "amc-vpX";
334 case AV_CODEC_ID_AVS:
335 case AV_CODEC_ID_CAVS:
336 case AV_CODEC_ID_H264:
337 m_mime = "video/avc";
338 m_formatname = "amc-h264";
339 // check for h264-avcC and convert to h264-annex-b
340 if (m_hints.extradata && *(uint8_t*)m_hints.extradata == 1)
342 m_bitstream = new CBitstreamConverter;
343 if (!m_bitstream->Open(m_hints.codec, (uint8_t*)m_hints.extradata, m_hints.extrasize, true))
345 SAFE_DELETE(m_bitstream);
350 case AV_CODEC_ID_VC1:
351 case AV_CODEC_ID_WMV3:
352 m_mime = "video/wvc1";
353 //m_mime = "video/wmv9";
354 m_formatname = "amc-vc1";
357 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Unknown hints.codec(%d)", hints.codec);
362 // CJNIMediaCodec::createDecoderByXXX doesn't handle errors nicely,
363 // it crashes if the codec isn't found. This is fixed in latest AOSP,
364 // but not in current 4.1 devices. So 1st search for a matching codec, then create it.
365 bool hasSupportedColorFormat = false;
366 int num_codecs = CJNIMediaCodecList::getCodecCount();
367 for (int i = 0; i < num_codecs; i++)
369 CJNIMediaCodecInfo codec_info = CJNIMediaCodecList::getCodecInfoAt(i);
370 if (codec_info.isEncoder())
372 m_codecname = codec_info.getName();
373 if (IsBlacklisted(m_codecname))
376 std::vector<std::string> types = codec_info.getSupportedTypes();
377 // return the 1st one we find, that one is typically 'the best'
378 for (size_t j = 0; j < types.size(); ++j)
380 if (types[j] == m_mime)
382 m_codec = boost::shared_ptr<CJNIMediaCodec>(new CJNIMediaCodec(CJNIMediaCodec::createByCodecName(m_codecname)));
384 CJNIMediaCodecInfoCodecCapabilities codec_caps = codec_info.getCapabilitiesForType(m_mime);
385 std::vector<int> color_formats = codec_caps.colorFormats();
387 // clear any jni exceptions, jni gets upset if we do not.
388 if (xbmc_jnienv()->ExceptionOccurred())
390 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open ExceptionOccurred");
391 xbmc_jnienv()->ExceptionClear();
395 hasSupportedColorFormat = false;
396 for (size_t k = 0; k < color_formats.size(); ++k)
398 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::Open "
399 "m_codecname(%s), colorFormat(%d)", m_codecname.c_str(), color_formats[k]);
400 if (IsSupportedColorFormat(color_formats[k]))
401 hasSupportedColorFormat = true;
411 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Failed to create Android MediaCodec");
412 SAFE_DELETE(m_bitstream);
416 // whitelist of devices that can surface render.
417 m_render_sw = !CanSurfaceRenderWhiteList(m_codecname);
420 if (!hasSupportedColorFormat)
422 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: No supported color format");
424 SAFE_DELETE(m_bitstream);
429 if (!ConfigureMediaCodec())
432 SAFE_DELETE(m_bitstream);
436 // setup a YUV420P DVDVideoPicture buffer.
437 // first make sure all properties are reset.
438 memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
440 m_videobuffer.dts = DVD_NOPTS_VALUE;
441 m_videobuffer.pts = DVD_NOPTS_VALUE;
442 m_videobuffer.color_range = 0;
443 m_videobuffer.color_matrix = 4;
444 m_videobuffer.iFlags = DVP_FLAG_ALLOCATED;
445 m_videobuffer.iWidth = m_hints.width;
446 m_videobuffer.iHeight = m_hints.height;
447 // these will get reset to crop values later
448 m_videobuffer.iDisplayWidth = m_hints.width;
449 m_videobuffer.iDisplayHeight = m_hints.height;
451 CLog::Log(LOGINFO, "CDVDVideoCodecAndroidMediaCodec:: "
452 "Open Android MediaCodec %s", m_codecname.c_str());
459 void CDVDVideoCodecAndroidMediaCodec::Dispose()
463 // release any retained demux packets
464 while (!m_demux.empty())
466 amc_demux &demux_pkt = m_demux.front();
467 free(demux_pkt.pData);
471 // invalidate any inflight outputbuffers, make sure
472 // m_output is empty so we do not create new ones
477 // clear m_videobuffer bits
480 free(m_videobuffer.data[0]), m_videobuffer.data[0] = NULL;
481 free(m_videobuffer.data[1]), m_videobuffer.data[1] = NULL;
482 free(m_videobuffer.data[2]), m_videobuffer.data[2] = NULL;
484 m_videobuffer.iFlags = 0;
485 // m_videobuffer.mediacodec is unioned with m_videobuffer.data[0]
486 // so be very careful when and how you touch it.
487 m_videobuffer.mediacodec = NULL;
495 ReleaseSurfaceTexture();
497 SAFE_DELETE(m_bitstream);
500 int CDVDVideoCodecAndroidMediaCodec::Decode(uint8_t *pData, int iSize, double dts, double pts)
502 // Handle input, add demuxer packet to input queue, we must accept it or
503 // it will be discarded as DVDPlayerVideo has no concept of "try again".
504 // we must return VC_BUFFER or VC_PICTURE, default to VC_BUFFER.
510 if (m_hints.ptsinvalid)
511 pts = DVD_NOPTS_VALUE;
513 // must check for an output picture 1st,
514 // otherwise, mediacodec can stall on some devices.
515 if (GetOutputPicture() > 0)
522 m_bitstream->Convert(pData, iSize);
523 iSize = m_bitstream->GetConvertSize();
524 pData = m_bitstream->GetConvertBuffer();
527 // queue demux pkt in case we cannot get an input buffer
531 demux_pkt.iSize = iSize;
532 demux_pkt.pData = (uint8_t*)malloc(iSize);
533 memcpy(demux_pkt.pData, pData, iSize);
534 m_demux.push(demux_pkt);
536 // try to fetch an input buffer
537 int64_t timeout_us = 5000;
538 int index = m_codec->dequeueInputBuffer(timeout_us);
541 // docs lie, getInputBuffers should be good after
542 // m_codec->start() but the internal refs are not
543 // setup until much later on some devices.
545 m_input = m_codec->getInputBuffers();
547 // we have an input buffer, fill it.
548 int size = m_input[index].capacity();
549 // fetch the front demux packet
550 amc_demux &demux_pkt = m_demux.front();
551 if (demux_pkt.iSize > size)
553 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode, iSize(%d) > size(%d)", iSize, size);
554 demux_pkt.iSize = size;
556 // fetch a pointer to the ByteBuffer backing store
557 void *dst_ptr = xbmc_jnienv()->GetDirectBufferAddress(m_input[index].get_raw());
559 memcpy(dst_ptr, demux_pkt.pData, demux_pkt.iSize);
561 free(demux_pkt.pData);
564 // Translate from dvdplayer dts/pts to MediaCodec pts,
565 // pts WILL get re-ordered by MediaCodec if needed.
566 // Do not try to pass pts as a unioned double/int64_t,
567 // some android devices will diddle with presentationTimeUs
568 // and you will get NaN back and DVDPlayerVideo will barf.
569 int64_t presentationTimeUs = AV_NOPTS_VALUE;
570 if (demux_pkt.pts != DVD_NOPTS_VALUE)
571 presentationTimeUs = demux_pkt.pts;
572 else if (demux_pkt.dts != DVD_NOPTS_VALUE)
573 presentationTimeUs = demux_pkt.dts;
575 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
576 "pts(%f), ipts(%lld), iSize(%d), GetDataSize(%d), loop_cnt(%d)",
577 presentationTimeUs, pts_dtoi(presentationTimeUs), iSize, GetDataSize(), loop_cnt);
581 m_codec->queueInputBuffer(index, offset, demux_pkt.iSize, presentationTimeUs, flags);
582 // clear any jni exceptions, jni gets upset if we do not.
583 if (xbmc_jnienv()->ExceptionOccurred())
585 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode ExceptionOccurred");
586 xbmc_jnienv()->ExceptionClear();
594 void CDVDVideoCodecAndroidMediaCodec::Reset()
599 // dump any pending demux packets
600 while (!m_demux.empty())
602 amc_demux &demux_pkt = m_demux.front();
603 free(demux_pkt.pData);
609 // flush all outputbuffers inflight, they will
610 // become invalid on m_codec->flush and generate
611 // a spew of java exceptions if used
614 // now we can flush the actual MediaCodec object
616 if (xbmc_jnienv()->ExceptionOccurred())
618 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Reset ExceptionOccurred");
619 xbmc_jnienv()->ExceptionClear();
622 // Invalidate our local DVDVideoPicture bits
623 m_videobuffer.pts = DVD_NOPTS_VALUE;
625 m_videobuffer.mediacodec = NULL;
629 bool CDVDVideoCodecAndroidMediaCodec::GetPicture(DVDVideoPicture* pDvdVideoPicture)
634 *pDvdVideoPicture = m_videobuffer;
636 // Invalidate our local DVDVideoPicture bits
637 m_videobuffer.pts = DVD_NOPTS_VALUE;
639 m_videobuffer.mediacodec = NULL;
644 bool CDVDVideoCodecAndroidMediaCodec::ClearPicture(DVDVideoPicture* pDvdVideoPicture)
646 if (pDvdVideoPicture->format == RENDER_FMT_MEDIACODEC)
647 SAFE_RELEASE(pDvdVideoPicture->mediacodec);
648 memset(pDvdVideoPicture, 0x00, sizeof(DVDVideoPicture));
653 void CDVDVideoCodecAndroidMediaCodec::SetDropState(bool bDrop)
657 m_videobuffer.iFlags |= DVP_FLAG_DROPPED;
659 m_videobuffer.iFlags &= ~DVP_FLAG_DROPPED;
662 int CDVDVideoCodecAndroidMediaCodec::GetDataSize(void)
664 // just ignore internal buffering contribution.
668 double CDVDVideoCodecAndroidMediaCodec::GetTimeSize(void)
670 // just ignore internal buffering contribution.
674 unsigned CDVDVideoCodecAndroidMediaCodec::GetAllowedReferences()
679 void CDVDVideoCodecAndroidMediaCodec::FlushInternal()
681 // invalidate any existing inflight buffers and create
682 // new ones to match the number of output buffers
687 for (size_t i = 0; i < m_inflight.size(); i++)
688 m_inflight[i]->Validate(false);
691 for (size_t i = 0; i < m_output.size(); i++)
693 m_inflight.push_back(
694 new CDVDMediaCodecInfo(i, m_textureId, m_codec, m_surfaceTexture, m_frameAvailable)
699 bool CDVDVideoCodecAndroidMediaCodec::ConfigureMediaCodec(void)
701 // setup a MediaFormat to match the video content,
702 // used by codec during configure
703 CJNIMediaFormat mediaformat = CJNIMediaFormat::createVideoFormat(
704 m_mime.c_str(), m_hints.width, m_hints.height);
705 // some android devices forget to default the demux input max size
706 mediaformat.setInteger(CJNIMediaFormat::KEY_MAX_INPUT_SIZE, 0);
708 // handle codec extradata
709 if (m_hints.extrasize)
711 size_t size = m_hints.extrasize;
712 void *src_ptr = m_hints.extradata;
715 size = m_bitstream->GetExtraSize();
716 src_ptr = m_bitstream->GetExtraData();
718 // Allocate a byte buffer via allocateDirect in java instead of NewDirectByteBuffer,
719 // since the latter doesn't allocate storage of its own, and we don't know how long
720 // the codec uses the buffer.
721 CJNIByteBuffer bytebuffer = CJNIByteBuffer::allocateDirect(size);
722 void *dts_ptr = xbmc_jnienv()->GetDirectBufferAddress(bytebuffer.get_raw());
723 memcpy(dts_ptr, src_ptr, size);
724 // codec will automatically handle buffers as extradata
725 // using entries with keys "csd-0", "csd-1", etc.
726 mediaformat.setByteBuffer("csd-0", bytebuffer);
729 InitSurfaceTexture();
731 // configure and start the codec.
732 // use the MediaFormat that we have setup.
733 // use a null MediaCrypto, our content is not encrypted.
734 // use a null Surface, we will extract the video picture data manually.
736 CJNIMediaCrypto crypto(jni::jhobject(NULL));
737 // our jni gets upset if we do this a different
738 // way, do not mess with it.
741 CJNISurface surface(jni::jhobject(NULL));
742 m_codec->configure(mediaformat, surface, crypto, flags);
746 m_codec->configure(mediaformat, *m_surface, crypto, flags);
748 // always, check/clear jni exceptions.
749 if (xbmc_jnienv()->ExceptionOccurred())
751 xbmc_jnienv()->ExceptionClear();
757 // always, check/clear jni exceptions.
758 if (xbmc_jnienv()->ExceptionOccurred())
760 xbmc_jnienv()->ExceptionClear();
767 int CDVDVideoCodecAndroidMediaCodec::GetOutputPicture(void)
771 int64_t timeout_us = 5000;
772 CJNIMediaCodecBufferInfo bufferInfo;
773 int index = m_codec->dequeueOutputBuffer(bufferInfo, timeout_us);
778 m_codec->releaseOutputBuffer(index, false);
779 if (xbmc_jnienv()->ExceptionOccurred())
780 xbmc_jnienv()->ExceptionClear();
784 // some devices will return a valid index
785 // before signaling INFO_OUTPUT_BUFFERS_CHANGED which
786 // is used to setup m_output, D'uh. setup m_output here.
787 if (m_output.empty())
789 m_output = m_codec->getOutputBuffers();
793 int flags = bufferInfo.flags();
794 if (flags & CJNIMediaCodec::BUFFER_FLAG_SYNC_FRAME)
795 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_SYNC_FRAME");
797 if (flags & CJNIMediaCodec::BUFFER_FLAG_CODEC_CONFIG)
798 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_CODEC_CONFIG");
800 if (flags & CJNIMediaCodec::BUFFER_FLAG_END_OF_STREAM)
802 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_END_OF_STREAM");
803 m_codec->releaseOutputBuffer(index, false);
804 if (xbmc_jnienv()->ExceptionOccurred())
805 xbmc_jnienv()->ExceptionClear();
811 m_videobuffer.mediacodec = m_inflight[index]->Retain();
812 m_videobuffer.mediacodec->Validate(true);
816 int size = bufferInfo.size();
817 int offset = bufferInfo.offset();
819 if (!m_output[index].isDirect())
820 CLog::Log(LOGWARNING, "CDVDVideoCodecAndroidMediaCodec:: m_output[index].isDirect == false");
822 if (size && m_output[index].capacity())
824 uint8_t *src_ptr = (uint8_t*)xbmc_jnienv()->GetDirectBufferAddress(m_output[index].get_raw());
828 if (m_videobuffer.format == RENDER_FMT_NV12)
830 else if (m_videobuffer.format == RENDER_FMT_YUV420P)
833 for (int i = 0; i < loop_end; i++)
835 uint8_t *src = src_ptr + m_src_offset[i];
836 int src_stride = m_src_stride[i];
837 uint8_t *dst = m_videobuffer.data[i];
838 int dst_stride = m_videobuffer.iLineSize[i];
840 int height = m_videobuffer.iHeight;
842 height = (m_videobuffer.iHeight + 1) / 2;
844 for (int j = 0; j < height; j++, src += src_stride, dst += dst_stride)
845 memcpy(dst, src, dst_stride);
848 m_codec->releaseOutputBuffer(index, false);
851 int64_t pts= bufferInfo.presentationTimeUs();
852 m_videobuffer.dts = DVD_NOPTS_VALUE;
853 m_videobuffer.pts = DVD_NOPTS_VALUE;
854 if (pts != AV_NOPTS_VALUE)
855 m_videobuffer.pts = pts;
858 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture "
859 "index(%d), pts(%f)", index, m_videobuffer.pts);
861 // always, check/clear jni exceptions.
862 if (xbmc_jnienv()->ExceptionOccurred())
863 xbmc_jnienv()->ExceptionClear();
867 else if (index == CJNIMediaCodec::INFO_OUTPUT_BUFFERS_CHANGED)
869 m_output = m_codec->getOutputBuffers();
872 else if (index == CJNIMediaCodec::INFO_OUTPUT_FORMAT_CHANGED)
874 OutputFormatChanged();
876 else if (index == CJNIMediaCodec::INFO_TRY_AGAIN_LATER)
878 // normal dequeueOutputBuffer timeout, ignore it.
883 // we should never get here
884 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture unknown index(%d)", index);
890 void CDVDVideoCodecAndroidMediaCodec::OutputFormatChanged(void)
892 CJNIMediaFormat mediaformat = m_codec->getOutputFormat();
894 int width = mediaformat.getInteger("width");
895 int height = mediaformat.getInteger("height");
896 int stride = mediaformat.getInteger("stride");
897 int slice_height= mediaformat.getInteger("slice-height");
898 int color_format= mediaformat.getInteger("color-format");
899 int crop_left = mediaformat.getInteger("crop-left");
900 int crop_top = mediaformat.getInteger("crop-top");
901 int crop_right = mediaformat.getInteger("crop-right");
902 int crop_bottom = mediaformat.getInteger("crop-bottom");
904 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
905 "width(%d), height(%d), stride(%d), slice-height(%d), color-format(%d)",
906 width, height, stride, slice_height, color_format);
907 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
908 "crop-left(%d), crop-top(%d), crop-right(%d), crop-bottom(%d)",
909 crop_left, crop_top, crop_right, crop_bottom);
913 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Direct Surface Rendering");
914 m_videobuffer.format = RENDER_FMT_MEDIACODEC;
918 // Android device quirks and fixes
921 if (slice_height <= 0)
923 slice_height = height;
924 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
926 // NVidia Tegra 3 on Nexus 7 does not set slice_heights
927 if (strstr(m_codecname.c_str(), "OMX.Nvidia.") != NULL)
929 slice_height = (((height) + 31) & ~31);
930 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: NVidia Tegra 3 quirk, slice_height(%d)", slice_height);
934 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar)
936 slice_height -= crop_top / 2;
937 // set crop top/left here, since the offset parameter already includes this.
938 // if we would ignore the offset parameter in the BufferInfo, we could just keep
939 // the original slice height and apply the top/left cropping instead.
944 // default picture format to none
945 for (int i = 0; i < 4; i++)
946 m_src_offset[i] = m_src_stride[i] = 0;
947 // delete any existing buffers
948 for (int i = 0; i < 4; i++)
949 free(m_videobuffer.data[i]);
951 // setup picture format and data offset vectors
952 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
954 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420Planar");
957 m_src_stride[0] = stride;
958 m_src_offset[0] = crop_top * stride;
959 m_src_offset[0]+= crop_left;
962 m_src_stride[1] = (stride + 1) / 2;
963 // skip over the Y plane
964 m_src_offset[1] = slice_height * stride;
965 // crop_top/crop_left divided by two
966 // because one byte of the U/V planes
967 // corresponds to two pixels horizontally/vertically
968 m_src_offset[1]+= crop_top / 2 * m_src_stride[1];
969 m_src_offset[1]+= crop_left / 2;
972 m_src_stride[2] = (stride + 1) / 2;
973 // skip over the Y plane
974 m_src_offset[2] = slice_height * stride;
975 // skip over the U plane
976 m_src_offset[2]+= ((slice_height + 1) / 2) * ((stride + 1) / 2);
977 // crop_top/crop_left divided by two
978 // because one byte of the U/V planes
979 // corresponds to two pixels horizontally/vertically
980 m_src_offset[2]+= crop_top / 2 * m_src_stride[2];
981 m_src_offset[2]+= crop_left / 2;
983 m_videobuffer.iLineSize[0] = width; // Y
984 m_videobuffer.iLineSize[1] = (width + 1) /2; // U
985 m_videobuffer.iLineSize[2] = (width + 1) /2; // V
986 m_videobuffer.iLineSize[3] = 0;
988 unsigned int iPixels = width * height;
989 unsigned int iChromaPixels = iPixels/4;
990 m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
991 m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
992 m_videobuffer.data[2] = (uint8_t*)malloc(16 + iChromaPixels);
993 m_videobuffer.data[3] = NULL;
994 m_videobuffer.format = RENDER_FMT_YUV420P;
996 else if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420SemiPlanar
997 || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_QCOM_FormatYUV420SemiPlanar
998 || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar
999 || color_format == CJNIMediaCodecInfoCodecCapabilities::OMX_QCOM_COLOR_FormatYVU420SemiPlanarInterlace)
1002 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420SemiPlanar");
1005 m_src_stride[0] = stride;
1006 m_src_offset[0] = crop_top * stride;
1007 m_src_offset[0]+= crop_left;
1010 m_src_stride[1] = stride;
1011 // skip over the Y plane
1012 m_src_offset[1] = slice_height * stride;
1013 m_src_offset[1]+= crop_top * stride;
1014 m_src_offset[1]+= crop_left;
1016 m_videobuffer.iLineSize[0] = width; // Y
1017 m_videobuffer.iLineSize[1] = width; // UV
1018 m_videobuffer.iLineSize[2] = 0;
1019 m_videobuffer.iLineSize[3] = 0;
1021 unsigned int iPixels = width * height;
1022 unsigned int iChromaPixels = iPixels;
1023 m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
1024 m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
1025 m_videobuffer.data[2] = NULL;
1026 m_videobuffer.data[3] = NULL;
1027 m_videobuffer.format = RENDER_FMT_NV12;
1031 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Fixme unknown color_format(%d)", color_format);
1036 // picture display width/height include the cropping.
1037 m_videobuffer.iDisplayWidth = crop_right + 1 - crop_left;
1038 m_videobuffer.iDisplayHeight = crop_bottom + 1 - crop_top;
1040 // clear any jni exceptions
1041 if (xbmc_jnienv()->ExceptionOccurred())
1042 xbmc_jnienv()->ExceptionClear();
1045 void CDVDVideoCodecAndroidMediaCodec::CallbackInitSurfaceTexture(void *userdata)
1047 CDVDVideoCodecAndroidMediaCodec *ctx = static_cast<CDVDVideoCodecAndroidMediaCodec*>(userdata);
1048 ctx->InitSurfaceTexture();
1051 void CDVDVideoCodecAndroidMediaCodec::InitSurfaceTexture(void)
1056 // We MUST create the GLES texture on the main thread
1057 // to match where the valid GLES context is located.
1058 // It would be nice to move this out of here, we would need
1059 // to create/fetch/create from g_RenderMananger. But g_RenderMananger
1060 // does not know we are using MediaCodec until Configure and we
1061 // we need m_surfaceTexture valid before then. Chicken, meet Egg.
1062 if (g_application.IsCurrentThread())
1064 // localize GLuint so we do not spew gles includes in our header
1067 glGenTextures(1, &texture_id);
1068 glBindTexture( GL_TEXTURE_EXTERNAL_OES, texture_id);
1069 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1070 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1071 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1072 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1073 glBindTexture( GL_TEXTURE_EXTERNAL_OES, 0);
1074 m_textureId = texture_id;
1076 m_surfaceTexture = boost::shared_ptr<CJNISurfaceTexture>(new CJNISurfaceTexture(m_textureId));
1077 // hook the surfaceTexture OnFrameAvailable callback
1078 m_frameAvailable = boost::shared_ptr<CDVDMediaCodecOnFrameAvailable>(new CDVDMediaCodecOnFrameAvailable(m_surfaceTexture));
1079 m_surface = new CJNISurface(*m_surfaceTexture);
1083 ThreadMessageCallback callbackData;
1084 callbackData.callback = &CallbackInitSurfaceTexture;
1085 callbackData.userptr = (void*)this;
1088 msg.dwMessage = TMSG_CALLBACK;
1089 msg.lpVoid = (void*)&callbackData;
1092 CApplicationMessenger::Get().SendMessage(msg, true);
1098 void CDVDVideoCodecAndroidMediaCodec::ReleaseSurfaceTexture(void)
1103 // it is safe to delete here even though these items
1104 // were created in the main thread instance
1105 SAFE_DELETE(m_surface);
1106 m_frameAvailable.reset();
1107 m_surfaceTexture.reset();
1109 if (m_textureId > 0)
1111 GLuint texture_id = m_textureId;
1112 glDeleteTextures(1, &texture_id);