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[] = {
64 for (const char **ptr = cansurfacerender_decoders; *ptr; ptr++)
66 if (!strnicmp(*ptr, name.c_str(), strlen(*ptr)))
72 static bool IsBlacklisted(const std::string &name)
74 static const char *blacklisted_decoders[] = {
75 // No software decoders
79 for (const char **ptr = blacklisted_decoders; *ptr; ptr++)
81 if (!strnicmp(*ptr, name.c_str(), strlen(*ptr)))
87 static bool IsSupportedColorFormat(int color_format)
89 static const int supported_colorformats[] = {
90 CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar,
91 CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar,
92 CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420SemiPlanar,
93 CJNIMediaCodecInfoCodecCapabilities::COLOR_QCOM_FormatYUV420SemiPlanar,
94 CJNIMediaCodecInfoCodecCapabilities::OMX_QCOM_COLOR_FormatYVU420SemiPlanarInterlace,
97 for (const int *ptr = supported_colorformats; *ptr != -1; ptr++)
99 if (color_format == *ptr)
105 /*****************************************************************************/
106 /*****************************************************************************/
107 class CNULL_Listener : public CJNISurfaceTextureOnFrameAvailableListener
110 CNULL_Listener() : CJNISurfaceTextureOnFrameAvailableListener(jni::jhobject(NULL)) {};
113 virtual void OnFrameAvailable(CJNISurfaceTexture &surface) {};
116 class CDVDMediaCodecOnFrameAvailable : public CEvent, CJNISurfaceTextureOnFrameAvailableListener
119 CDVDMediaCodecOnFrameAvailable(boost::shared_ptr<CJNISurfaceTexture> &surfaceTexture)
120 : m_surfaceTexture(surfaceTexture)
122 m_surfaceTexture->setOnFrameAvailableListener(*this);
125 virtual ~CDVDMediaCodecOnFrameAvailable()
127 // unhook the callback
128 CNULL_Listener null_listener;
129 m_surfaceTexture->setOnFrameAvailableListener(null_listener);
133 virtual void OnFrameAvailable(CJNISurfaceTexture &surface)
139 boost::shared_ptr<CJNISurfaceTexture> m_surfaceTexture;
143 /*****************************************************************************/
144 /*****************************************************************************/
145 CDVDMediaCodecInfo::CDVDMediaCodecInfo(
147 , unsigned int texture
148 , boost::shared_ptr<CJNIMediaCodec> &codec
149 , boost::shared_ptr<CJNISurfaceTexture> &surfacetexture
150 , boost::shared_ptr<CDVDMediaCodecOnFrameAvailable> &frameready
158 , m_surfacetexture(surfacetexture)
159 , m_frameready(frameready)
162 assert(m_index >= 0);
163 assert(m_texture > 0);
164 assert(m_codec != NULL);
165 assert(m_surfacetexture != NULL);
166 assert(m_frameready != NULL);
169 CDVDMediaCodecInfo::~CDVDMediaCodecInfo()
174 CDVDMediaCodecInfo* CDVDMediaCodecInfo::Retain()
176 AtomicIncrement(&m_refs);
181 long CDVDMediaCodecInfo::Release()
183 long count = AtomicDecrement(&m_refs);
186 ReleaseOutputBuffer(false);
193 void CDVDMediaCodecInfo::Validate(bool state)
195 CSingleLock lock(m_section);
200 void CDVDMediaCodecInfo::ReleaseOutputBuffer(bool render)
202 CSingleLock lock(m_section);
207 // release OutputBuffer and render if indicated
208 // then wait for rendered frame to become avaliable.
211 m_frameready->Reset();
213 m_codec->releaseOutputBuffer(m_index, render);
215 if (xbmc_jnienv()->ExceptionOccurred())
217 CLog::Log(LOGERROR, "CDVDMediaCodecInfo::ReleaseOutputBuffer "
218 "ExceptionOccurred render(%d)", render);
219 xbmc_jnienv()->ExceptionDescribe();
220 xbmc_jnienv()->ExceptionClear();
224 int CDVDMediaCodecInfo::GetIndex() const
226 CSingleLock lock(m_section);
231 int CDVDMediaCodecInfo::GetTextureID() const
233 // since m_texture never changes,
234 // we do not need a m_section lock here.
238 void CDVDMediaCodecInfo::GetTransformMatrix(float *textureMatrix)
240 CSingleLock lock(m_section);
245 m_surfacetexture->getTransformMatrix(textureMatrix);
248 void CDVDMediaCodecInfo::UpdateTexImage()
250 CSingleLock lock(m_section);
255 // updateTexImage will check and spew any prior gl errors,
256 // clear them before we call updateTexImage.
259 // this is key, after calling releaseOutputBuffer, we must
260 // wait a little for MediaCodec to render to the surface.
261 // Then we can updateTexImage without delay. If we do not
262 // wait, then video playback gets jerky. To optomize this,
263 // we hook the SurfaceTexture OnFrameAvailable callback
264 // using CJNISurfaceTextureOnFrameAvailableListener and wait
265 // on a CEvent to fire. 20ms seems to be a good max fallback.
266 m_frameready->WaitMSec(20);
268 m_surfacetexture->updateTexImage();
269 if (xbmc_jnienv()->ExceptionOccurred())
271 CLog::Log(LOGERROR, "CDVDMediaCodecInfo::UpdateTexImage updateTexImage:ExceptionOccurred");
272 xbmc_jnienv()->ExceptionDescribe();
273 xbmc_jnienv()->ExceptionClear();
276 m_timestamp = m_surfacetexture->getTimestamp();
277 if (xbmc_jnienv()->ExceptionOccurred())
279 CLog::Log(LOGERROR, "CDVDMediaCodecInfo::UpdateTexImage getTimestamp:ExceptionOccurred");
280 xbmc_jnienv()->ExceptionDescribe();
281 xbmc_jnienv()->ExceptionClear();
285 /*****************************************************************************/
286 /*****************************************************************************/
287 CDVDVideoCodecAndroidMediaCodec::CDVDVideoCodecAndroidMediaCodec()
288 : m_formatname("mediacodec")
295 memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
298 CDVDVideoCodecAndroidMediaCodec::~CDVDVideoCodecAndroidMediaCodec()
303 bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
305 // check for 4.1 Jellybean and above.
306 if (CAndroidFeatures::GetVersion() < 16)
312 switch(m_hints.codec)
314 case AV_CODEC_ID_MPEG2VIDEO:
315 m_mime = "video/mpeg2";
316 m_formatname = "amc-mpeg2";
318 case AV_CODEC_ID_MPEG4:
319 m_mime = "video/mp4v-es";
320 m_formatname = "amc-mpeg4";
322 case AV_CODEC_ID_H263:
323 m_mime = "video/3gpp";
324 m_formatname = "amc-h263";
326 case AV_CODEC_ID_VP3:
327 case AV_CODEC_ID_VP6:
328 case AV_CODEC_ID_VP6F:
329 case AV_CODEC_ID_VP8:
330 //m_mime = "video/x-vp6";
331 //m_mime = "video/x-vp7";
332 m_mime = "video/x-vnd.on2.vp8";
333 m_formatname = "amc-vpX";
335 case AV_CODEC_ID_AVS:
336 case AV_CODEC_ID_CAVS:
337 case AV_CODEC_ID_H264:
338 m_mime = "video/avc";
339 m_formatname = "amc-h264";
340 // check for h264-avcC and convert to h264-annex-b
341 if (m_hints.extradata && *(uint8_t*)m_hints.extradata == 1)
343 m_bitstream = new CBitstreamConverter;
344 if (!m_bitstream->Open(m_hints.codec, (uint8_t*)m_hints.extradata, m_hints.extrasize, true))
346 SAFE_DELETE(m_bitstream);
351 case AV_CODEC_ID_VC1:
352 case AV_CODEC_ID_WMV3:
353 m_mime = "video/wvc1";
354 //m_mime = "video/wmv9";
355 m_formatname = "amc-vc1";
358 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Unknown hints.codec(%d)", hints.codec);
363 // CJNIMediaCodec::createDecoderByXXX doesn't handle errors nicely,
364 // it crashes if the codec isn't found. This is fixed in latest AOSP,
365 // but not in current 4.1 devices. So 1st search for a matching codec, then create it.
366 bool hasSupportedColorFormat = false;
367 int num_codecs = CJNIMediaCodecList::getCodecCount();
368 for (int i = 0; i < num_codecs; i++)
370 CJNIMediaCodecInfo codec_info = CJNIMediaCodecList::getCodecInfoAt(i);
371 if (codec_info.isEncoder())
373 m_codecname = codec_info.getName();
374 if (IsBlacklisted(m_codecname))
377 std::vector<std::string> types = codec_info.getSupportedTypes();
378 // return the 1st one we find, that one is typically 'the best'
379 for (size_t j = 0; j < types.size(); ++j)
381 if (types[j] == m_mime)
383 m_codec = boost::shared_ptr<CJNIMediaCodec>(new CJNIMediaCodec(CJNIMediaCodec::createByCodecName(m_codecname)));
385 CJNIMediaCodecInfoCodecCapabilities codec_caps = codec_info.getCapabilitiesForType(m_mime);
386 std::vector<int> color_formats = codec_caps.colorFormats();
388 // clear any jni exceptions, jni gets upset if we do not.
389 if (xbmc_jnienv()->ExceptionOccurred())
391 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open ExceptionOccurred");
392 xbmc_jnienv()->ExceptionClear();
396 hasSupportedColorFormat = false;
397 for (size_t k = 0; k < color_formats.size(); ++k)
399 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::Open "
400 "m_codecname(%s), colorFormat(%d)", m_codecname.c_str(), color_formats[k]);
401 if (IsSupportedColorFormat(color_formats[k]))
402 hasSupportedColorFormat = true;
412 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Failed to create Android MediaCodec");
413 SAFE_DELETE(m_bitstream);
417 // whitelist of devices that can surface render.
418 m_render_sw = !CanSurfaceRenderWhiteList(m_codecname);
421 if (!hasSupportedColorFormat)
423 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: No supported color format");
425 SAFE_DELETE(m_bitstream);
430 if (!ConfigureMediaCodec())
433 SAFE_DELETE(m_bitstream);
437 // setup a YUV420P DVDVideoPicture buffer.
438 // first make sure all properties are reset.
439 memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
441 m_videobuffer.dts = DVD_NOPTS_VALUE;
442 m_videobuffer.pts = DVD_NOPTS_VALUE;
443 m_videobuffer.color_range = 0;
444 m_videobuffer.color_matrix = 4;
445 m_videobuffer.iFlags = DVP_FLAG_ALLOCATED;
446 m_videobuffer.iWidth = m_hints.width;
447 m_videobuffer.iHeight = m_hints.height;
448 // these will get reset to crop values later
449 m_videobuffer.iDisplayWidth = m_hints.width;
450 m_videobuffer.iDisplayHeight = m_hints.height;
452 CLog::Log(LOGINFO, "CDVDVideoCodecAndroidMediaCodec:: "
453 "Open Android MediaCodec %s", m_codecname.c_str());
460 void CDVDVideoCodecAndroidMediaCodec::Dispose()
464 // release any retained demux packets
465 while (!m_demux.empty())
467 amc_demux &demux_pkt = m_demux.front();
468 free(demux_pkt.pData);
472 // invalidate any inflight outputbuffers, make sure
473 // m_output is empty so we do not create new ones
478 // clear m_videobuffer bits
481 free(m_videobuffer.data[0]), m_videobuffer.data[0] = NULL;
482 free(m_videobuffer.data[1]), m_videobuffer.data[1] = NULL;
483 free(m_videobuffer.data[2]), m_videobuffer.data[2] = NULL;
485 m_videobuffer.iFlags = 0;
486 // m_videobuffer.mediacodec is unioned with m_videobuffer.data[0]
487 // so be very careful when and how you touch it.
488 m_videobuffer.mediacodec = NULL;
496 ReleaseSurfaceTexture();
498 SAFE_DELETE(m_bitstream);
501 int CDVDVideoCodecAndroidMediaCodec::Decode(uint8_t *pData, int iSize, double dts, double pts)
503 // Handle input, add demuxer packet to input queue, we must accept it or
504 // it will be discarded as DVDPlayerVideo has no concept of "try again".
505 // we must return VC_BUFFER or VC_PICTURE, default to VC_BUFFER.
511 if (m_hints.ptsinvalid)
512 pts = DVD_NOPTS_VALUE;
514 // must check for an output picture 1st,
515 // otherwise, mediacodec can stall on some devices.
516 if (GetOutputPicture() > 0)
523 m_bitstream->Convert(pData, iSize);
524 iSize = m_bitstream->GetConvertSize();
525 pData = m_bitstream->GetConvertBuffer();
528 // queue demux pkt in case we cannot get an input buffer
532 demux_pkt.iSize = iSize;
533 demux_pkt.pData = (uint8_t*)malloc(iSize);
534 memcpy(demux_pkt.pData, pData, iSize);
535 m_demux.push(demux_pkt);
537 // try to fetch an input buffer
538 int64_t timeout_us = 5000;
539 int index = m_codec->dequeueInputBuffer(timeout_us);
542 // docs lie, getInputBuffers should be good after
543 // m_codec->start() but the internal refs are not
544 // setup until much later on some devices.
546 m_input = m_codec->getInputBuffers();
548 // we have an input buffer, fill it.
549 int size = m_input[index].capacity();
550 // fetch the front demux packet
551 amc_demux &demux_pkt = m_demux.front();
552 if (demux_pkt.iSize > size)
554 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode, iSize(%d) > size(%d)", iSize, size);
555 demux_pkt.iSize = size;
557 // fetch a pointer to the ByteBuffer backing store
558 void *dst_ptr = xbmc_jnienv()->GetDirectBufferAddress(m_input[index].get_raw());
560 memcpy(dst_ptr, demux_pkt.pData, demux_pkt.iSize);
562 free(demux_pkt.pData);
565 // Translate from dvdplayer dts/pts to MediaCodec pts,
566 // pts WILL get re-ordered by MediaCodec if needed.
567 // Do not try to pass pts as a unioned double/int64_t,
568 // some android devices will diddle with presentationTimeUs
569 // and you will get NaN back and DVDPlayerVideo will barf.
570 int64_t presentationTimeUs = AV_NOPTS_VALUE;
571 if (demux_pkt.pts != DVD_NOPTS_VALUE)
572 presentationTimeUs = demux_pkt.pts;
573 else if (demux_pkt.dts != DVD_NOPTS_VALUE)
574 presentationTimeUs = demux_pkt.dts;
576 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
577 "pts(%f), ipts(%lld), iSize(%d), GetDataSize(%d), loop_cnt(%d)",
578 presentationTimeUs, pts_dtoi(presentationTimeUs), iSize, GetDataSize(), loop_cnt);
582 m_codec->queueInputBuffer(index, offset, demux_pkt.iSize, presentationTimeUs, flags);
583 // clear any jni exceptions, jni gets upset if we do not.
584 if (xbmc_jnienv()->ExceptionOccurred())
586 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode ExceptionOccurred");
587 xbmc_jnienv()->ExceptionClear();
595 void CDVDVideoCodecAndroidMediaCodec::Reset()
600 // dump any pending demux packets
601 while (!m_demux.empty())
603 amc_demux &demux_pkt = m_demux.front();
604 free(demux_pkt.pData);
610 // flush all outputbuffers inflight, they will
611 // become invalid on m_codec->flush and generate
612 // a spew of java exceptions if used
615 // now we can flush the actual MediaCodec object
617 if (xbmc_jnienv()->ExceptionOccurred())
619 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Reset ExceptionOccurred");
620 xbmc_jnienv()->ExceptionClear();
623 // Invalidate our local DVDVideoPicture bits
624 m_videobuffer.pts = DVD_NOPTS_VALUE;
626 m_videobuffer.mediacodec = NULL;
630 bool CDVDVideoCodecAndroidMediaCodec::GetPicture(DVDVideoPicture* pDvdVideoPicture)
635 *pDvdVideoPicture = m_videobuffer;
637 // Invalidate our local DVDVideoPicture bits
638 m_videobuffer.pts = DVD_NOPTS_VALUE;
640 m_videobuffer.mediacodec = NULL;
645 bool CDVDVideoCodecAndroidMediaCodec::ClearPicture(DVDVideoPicture* pDvdVideoPicture)
647 if (pDvdVideoPicture->format == RENDER_FMT_MEDIACODEC)
648 SAFE_RELEASE(pDvdVideoPicture->mediacodec);
649 memset(pDvdVideoPicture, 0x00, sizeof(DVDVideoPicture));
654 void CDVDVideoCodecAndroidMediaCodec::SetDropState(bool bDrop)
658 m_videobuffer.iFlags |= DVP_FLAG_DROPPED;
660 m_videobuffer.iFlags &= ~DVP_FLAG_DROPPED;
663 int CDVDVideoCodecAndroidMediaCodec::GetDataSize(void)
665 // just ignore internal buffering contribution.
669 double CDVDVideoCodecAndroidMediaCodec::GetTimeSize(void)
671 // just ignore internal buffering contribution.
675 unsigned CDVDVideoCodecAndroidMediaCodec::GetAllowedReferences()
680 void CDVDVideoCodecAndroidMediaCodec::FlushInternal()
682 // invalidate any existing inflight buffers and create
683 // new ones to match the number of output buffers
688 for (size_t i = 0; i < m_inflight.size(); i++)
689 m_inflight[i]->Validate(false);
692 for (size_t i = 0; i < m_output.size(); i++)
694 m_inflight.push_back(
695 new CDVDMediaCodecInfo(i, m_textureId, m_codec, m_surfaceTexture, m_frameAvailable)
700 bool CDVDVideoCodecAndroidMediaCodec::ConfigureMediaCodec(void)
702 // setup a MediaFormat to match the video content,
703 // used by codec during configure
704 CJNIMediaFormat mediaformat = CJNIMediaFormat::createVideoFormat(
705 m_mime.c_str(), m_hints.width, m_hints.height);
706 // some android devices forget to default the demux input max size
707 mediaformat.setInteger(CJNIMediaFormat::KEY_MAX_INPUT_SIZE, 0);
709 // handle codec extradata
710 if (m_hints.extrasize)
712 size_t size = m_hints.extrasize;
713 void *src_ptr = m_hints.extradata;
716 size = m_bitstream->GetExtraSize();
717 src_ptr = m_bitstream->GetExtraData();
719 // Allocate a byte buffer via allocateDirect in java instead of NewDirectByteBuffer,
720 // since the latter doesn't allocate storage of its own, and we don't know how long
721 // the codec uses the buffer.
722 CJNIByteBuffer bytebuffer = CJNIByteBuffer::allocateDirect(size);
723 void *dts_ptr = xbmc_jnienv()->GetDirectBufferAddress(bytebuffer.get_raw());
724 memcpy(dts_ptr, src_ptr, size);
725 // codec will automatically handle buffers as extradata
726 // using entries with keys "csd-0", "csd-1", etc.
727 mediaformat.setByteBuffer("csd-0", bytebuffer);
730 InitSurfaceTexture();
732 // configure and start the codec.
733 // use the MediaFormat that we have setup.
734 // use a null MediaCrypto, our content is not encrypted.
735 // use a null Surface, we will extract the video picture data manually.
737 CJNIMediaCrypto crypto(jni::jhobject(NULL));
738 // our jni gets upset if we do this a different
739 // way, do not mess with it.
742 CJNISurface surface(jni::jhobject(NULL));
743 m_codec->configure(mediaformat, surface, crypto, flags);
747 m_codec->configure(mediaformat, *m_surface, crypto, flags);
749 // always, check/clear jni exceptions.
750 if (xbmc_jnienv()->ExceptionOccurred())
752 xbmc_jnienv()->ExceptionClear();
758 // always, check/clear jni exceptions.
759 if (xbmc_jnienv()->ExceptionOccurred())
761 xbmc_jnienv()->ExceptionClear();
768 int CDVDVideoCodecAndroidMediaCodec::GetOutputPicture(void)
772 int64_t timeout_us = 5000;
773 CJNIMediaCodecBufferInfo bufferInfo;
774 int index = m_codec->dequeueOutputBuffer(bufferInfo, timeout_us);
779 m_codec->releaseOutputBuffer(index, false);
780 if (xbmc_jnienv()->ExceptionOccurred())
781 xbmc_jnienv()->ExceptionClear();
785 // some devices will return a valid index
786 // before signaling INFO_OUTPUT_BUFFERS_CHANGED which
787 // is used to setup m_output, D'uh. setup m_output here.
788 if (m_output.empty())
790 m_output = m_codec->getOutputBuffers();
794 int flags = bufferInfo.flags();
795 if (flags & CJNIMediaCodec::BUFFER_FLAG_SYNC_FRAME)
796 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_SYNC_FRAME");
798 if (flags & CJNIMediaCodec::BUFFER_FLAG_CODEC_CONFIG)
799 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_CODEC_CONFIG");
801 if (flags & CJNIMediaCodec::BUFFER_FLAG_END_OF_STREAM)
803 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_END_OF_STREAM");
804 m_codec->releaseOutputBuffer(index, false);
805 if (xbmc_jnienv()->ExceptionOccurred())
806 xbmc_jnienv()->ExceptionClear();
812 m_videobuffer.mediacodec = m_inflight[index]->Retain();
813 m_videobuffer.mediacodec->Validate(true);
817 int size = bufferInfo.size();
818 int offset = bufferInfo.offset();
820 if (!m_output[index].isDirect())
821 CLog::Log(LOGWARNING, "CDVDVideoCodecAndroidMediaCodec:: m_output[index].isDirect == false");
823 if (size && m_output[index].capacity())
825 uint8_t *src_ptr = (uint8_t*)xbmc_jnienv()->GetDirectBufferAddress(m_output[index].get_raw());
829 if (m_videobuffer.format == RENDER_FMT_NV12)
831 else if (m_videobuffer.format == RENDER_FMT_YUV420P)
834 for (int i = 0; i < loop_end; i++)
836 uint8_t *src = src_ptr + m_src_offset[i];
837 int src_stride = m_src_stride[i];
838 uint8_t *dst = m_videobuffer.data[i];
839 int dst_stride = m_videobuffer.iLineSize[i];
841 int height = m_videobuffer.iHeight;
843 height = (m_videobuffer.iHeight + 1) / 2;
845 for (int j = 0; j < height; j++, src += src_stride, dst += dst_stride)
846 memcpy(dst, src, dst_stride);
849 m_codec->releaseOutputBuffer(index, false);
852 int64_t pts= bufferInfo.presentationTimeUs();
853 m_videobuffer.dts = DVD_NOPTS_VALUE;
854 m_videobuffer.pts = DVD_NOPTS_VALUE;
855 if (pts != AV_NOPTS_VALUE)
856 m_videobuffer.pts = pts;
859 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture "
860 "index(%d), pts(%f)", index, m_videobuffer.pts);
862 // always, check/clear jni exceptions.
863 if (xbmc_jnienv()->ExceptionOccurred())
864 xbmc_jnienv()->ExceptionClear();
868 else if (index == CJNIMediaCodec::INFO_OUTPUT_BUFFERS_CHANGED)
870 m_output = m_codec->getOutputBuffers();
873 else if (index == CJNIMediaCodec::INFO_OUTPUT_FORMAT_CHANGED)
875 OutputFormatChanged();
877 else if (index == CJNIMediaCodec::INFO_TRY_AGAIN_LATER)
879 // normal dequeueOutputBuffer timeout, ignore it.
884 // we should never get here
885 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture unknown index(%d)", index);
891 void CDVDVideoCodecAndroidMediaCodec::OutputFormatChanged(void)
893 CJNIMediaFormat mediaformat = m_codec->getOutputFormat();
895 int width = mediaformat.getInteger("width");
896 int height = mediaformat.getInteger("height");
897 int stride = mediaformat.getInteger("stride");
898 int slice_height= mediaformat.getInteger("slice-height");
899 int color_format= mediaformat.getInteger("color-format");
900 int crop_left = mediaformat.getInteger("crop-left");
901 int crop_top = mediaformat.getInteger("crop-top");
902 int crop_right = mediaformat.getInteger("crop-right");
903 int crop_bottom = mediaformat.getInteger("crop-bottom");
905 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
906 "width(%d), height(%d), stride(%d), slice-height(%d), color-format(%d)",
907 width, height, stride, slice_height, color_format);
908 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
909 "crop-left(%d), crop-top(%d), crop-right(%d), crop-bottom(%d)",
910 crop_left, crop_top, crop_right, crop_bottom);
914 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Direct Surface Rendering");
915 m_videobuffer.format = RENDER_FMT_MEDIACODEC;
919 // Android device quirks and fixes
922 if (slice_height <= height)
924 slice_height = height;
925 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
927 // NVidia Tegra 3 on Nexus 7 does not set slice_heights
928 if (strstr(m_codecname.c_str(), "OMX.Nvidia.") != NULL)
930 slice_height = (((height) + 31) & ~31);
931 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: NVidia Tegra 3 quirk, slice_height(%d)", slice_height);
935 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar)
937 slice_height -= crop_top / 2;
938 // set crop top/left here, since the offset parameter already includes this.
939 // if we would ignore the offset parameter in the BufferInfo, we could just keep
940 // the original slice height and apply the top/left cropping instead.
945 // default picture format to none
946 for (int i = 0; i < 4; i++)
947 m_src_offset[i] = m_src_stride[i] = 0;
948 // delete any existing buffers
949 for (int i = 0; i < 4; i++)
950 free(m_videobuffer.data[i]);
952 // setup picture format and data offset vectors
953 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
955 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420Planar");
958 m_src_stride[0] = stride;
959 m_src_offset[0] = crop_top * stride;
960 m_src_offset[0]+= crop_left;
963 m_src_stride[1] = (stride + 1) / 2;
964 // skip over the Y plane
965 m_src_offset[1] = slice_height * stride;
966 // crop_top/crop_left divided by two
967 // because one byte of the U/V planes
968 // corresponds to two pixels horizontally/vertically
969 m_src_offset[1]+= crop_top / 2 * m_src_stride[1];
970 m_src_offset[1]+= crop_left / 2;
973 m_src_stride[2] = (stride + 1) / 2;
974 // skip over the Y plane
975 m_src_offset[2] = slice_height * stride;
976 // skip over the U plane
977 m_src_offset[2]+= ((slice_height + 1) / 2) * ((stride + 1) / 2);
978 // crop_top/crop_left divided by two
979 // because one byte of the U/V planes
980 // corresponds to two pixels horizontally/vertically
981 m_src_offset[2]+= crop_top / 2 * m_src_stride[2];
982 m_src_offset[2]+= crop_left / 2;
984 m_videobuffer.iLineSize[0] = width; // Y
985 m_videobuffer.iLineSize[1] = (width + 1) /2; // U
986 m_videobuffer.iLineSize[2] = (width + 1) /2; // V
987 m_videobuffer.iLineSize[3] = 0;
989 unsigned int iPixels = width * height;
990 unsigned int iChromaPixels = iPixels/4;
991 m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
992 m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
993 m_videobuffer.data[2] = (uint8_t*)malloc(16 + iChromaPixels);
994 m_videobuffer.data[3] = NULL;
995 m_videobuffer.format = RENDER_FMT_YUV420P;
997 else if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420SemiPlanar
998 || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_QCOM_FormatYUV420SemiPlanar
999 || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar
1000 || color_format == CJNIMediaCodecInfoCodecCapabilities::OMX_QCOM_COLOR_FormatYVU420SemiPlanarInterlace)
1003 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420SemiPlanar");
1006 m_src_stride[0] = stride;
1007 m_src_offset[0] = crop_top * stride;
1008 m_src_offset[0]+= crop_left;
1011 m_src_stride[1] = stride;
1012 // skip over the Y plane
1013 m_src_offset[1] = slice_height * stride;
1014 m_src_offset[1]+= crop_top * stride;
1015 m_src_offset[1]+= crop_left;
1017 m_videobuffer.iLineSize[0] = width; // Y
1018 m_videobuffer.iLineSize[1] = width; // UV
1019 m_videobuffer.iLineSize[2] = 0;
1020 m_videobuffer.iLineSize[3] = 0;
1022 unsigned int iPixels = width * height;
1023 unsigned int iChromaPixels = iPixels;
1024 m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
1025 m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
1026 m_videobuffer.data[2] = NULL;
1027 m_videobuffer.data[3] = NULL;
1028 m_videobuffer.format = RENDER_FMT_NV12;
1032 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Fixme unknown color_format(%d)", color_format);
1037 // picture display width/height include the cropping.
1038 m_videobuffer.iDisplayWidth = crop_right + 1 - crop_left;
1039 m_videobuffer.iDisplayHeight = crop_bottom + 1 - crop_top;
1041 // clear any jni exceptions
1042 if (xbmc_jnienv()->ExceptionOccurred())
1043 xbmc_jnienv()->ExceptionClear();
1046 void CDVDVideoCodecAndroidMediaCodec::CallbackInitSurfaceTexture(void *userdata)
1048 CDVDVideoCodecAndroidMediaCodec *ctx = static_cast<CDVDVideoCodecAndroidMediaCodec*>(userdata);
1049 ctx->InitSurfaceTexture();
1052 void CDVDVideoCodecAndroidMediaCodec::InitSurfaceTexture(void)
1057 // We MUST create the GLES texture on the main thread
1058 // to match where the valid GLES context is located.
1059 // It would be nice to move this out of here, we would need
1060 // to create/fetch/create from g_RenderMananger. But g_RenderMananger
1061 // does not know we are using MediaCodec until Configure and we
1062 // we need m_surfaceTexture valid before then. Chicken, meet Egg.
1063 if (g_application.IsCurrentThread())
1065 // localize GLuint so we do not spew gles includes in our header
1068 glGenTextures(1, &texture_id);
1069 glBindTexture( GL_TEXTURE_EXTERNAL_OES, texture_id);
1070 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1071 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1072 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1073 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1074 glBindTexture( GL_TEXTURE_EXTERNAL_OES, 0);
1075 m_textureId = texture_id;
1077 m_surfaceTexture = boost::shared_ptr<CJNISurfaceTexture>(new CJNISurfaceTexture(m_textureId));
1078 // hook the surfaceTexture OnFrameAvailable callback
1079 m_frameAvailable = boost::shared_ptr<CDVDMediaCodecOnFrameAvailable>(new CDVDMediaCodecOnFrameAvailable(m_surfaceTexture));
1080 m_surface = new CJNISurface(*m_surfaceTexture);
1084 ThreadMessageCallback callbackData;
1085 callbackData.callback = &CallbackInitSurfaceTexture;
1086 callbackData.userptr = (void*)this;
1089 msg.dwMessage = TMSG_CALLBACK;
1090 msg.lpVoid = (void*)&callbackData;
1093 CApplicationMessenger::Get().SendMessage(msg, true);
1099 void CDVDVideoCodecAndroidMediaCodec::ReleaseSurfaceTexture(void)
1104 // it is safe to delete here even though these items
1105 // were created in the main thread instance
1106 SAFE_DELETE(m_surface);
1107 m_frameAvailable.reset();
1108 m_surfaceTexture.reset();
1110 if (m_textureId > 0)
1112 GLuint texture_id = m_textureId;
1113 glDeleteTextures(1, &texture_id);