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 m_bitstream = new CBitstreamConverter;
340 if (!m_bitstream->Open(m_hints.codec, (uint8_t*)m_hints.extradata, m_hints.extrasize, true))
342 SAFE_DELETE(m_bitstream);
346 case AV_CODEC_ID_VC1:
347 case AV_CODEC_ID_WMV3:
348 m_mime = "video/wvc1";
349 //m_mime = "video/wmv9";
350 m_formatname = "amc-vc1";
353 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Unknown hints.codec(%d)", hints.codec);
358 // CJNIMediaCodec::createDecoderByXXX doesn't handle errors nicely,
359 // it crashes if the codec isn't found. This is fixed in latest AOSP,
360 // but not in current 4.1 devices. So 1st search for a matching codec, then create it.
361 bool hasSupportedColorFormat = false;
362 int num_codecs = CJNIMediaCodecList::getCodecCount();
363 for (int i = 0; i < num_codecs; i++)
365 CJNIMediaCodecInfo codec_info = CJNIMediaCodecList::getCodecInfoAt(i);
366 if (codec_info.isEncoder())
368 m_codecname = codec_info.getName();
369 if (IsBlacklisted(m_codecname))
372 std::vector<std::string> types = codec_info.getSupportedTypes();
373 // return the 1st one we find, that one is typically 'the best'
374 for (size_t j = 0; j < types.size(); ++j)
376 if (types[j] == m_mime)
378 m_codec = boost::shared_ptr<CJNIMediaCodec>(new CJNIMediaCodec(CJNIMediaCodec::createByCodecName(m_codecname)));
380 CJNIMediaCodecInfoCodecCapabilities codec_caps = codec_info.getCapabilitiesForType(m_mime);
381 std::vector<int> color_formats = codec_caps.colorFormats();
383 // clear any jni exceptions, jni gets upset if we do not.
384 if (xbmc_jnienv()->ExceptionOccurred())
386 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open ExceptionOccurred");
387 xbmc_jnienv()->ExceptionClear();
391 hasSupportedColorFormat = false;
392 for (size_t k = 0; k < color_formats.size(); ++k)
394 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::Open "
395 "m_codecname(%s), colorFormat(%d)", m_codecname.c_str(), color_formats[k]);
396 if (IsSupportedColorFormat(color_formats[k]))
397 hasSupportedColorFormat = true;
407 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Failed to create Android MediaCodec");
408 SAFE_DELETE(m_bitstream);
412 // whitelist of devices that can surface render.
413 m_render_sw = !CanSurfaceRenderWhiteList(m_codecname);
416 if (!hasSupportedColorFormat)
418 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: No supported color format");
420 SAFE_DELETE(m_bitstream);
425 ConfigureMediaCodec();
427 // setup a YUV420P DVDVideoPicture buffer.
428 // first make sure all properties are reset.
429 memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
431 m_videobuffer.dts = DVD_NOPTS_VALUE;
432 m_videobuffer.pts = DVD_NOPTS_VALUE;
433 m_videobuffer.color_range = 0;
434 m_videobuffer.color_matrix = 4;
435 m_videobuffer.iFlags = DVP_FLAG_ALLOCATED;
436 m_videobuffer.iWidth = m_hints.width;
437 m_videobuffer.iHeight = m_hints.height;
438 // these will get reset to crop values later
439 m_videobuffer.iDisplayWidth = m_hints.width;
440 m_videobuffer.iDisplayHeight = m_hints.height;
442 CLog::Log(LOGINFO, "CDVDVideoCodecAndroidMediaCodec:: "
443 "Open Android MediaCodec %s", m_codecname.c_str());
450 void CDVDVideoCodecAndroidMediaCodec::Dispose()
454 // release any retained demux packets
455 while (!m_demux.empty())
457 amc_demux &demux_pkt = m_demux.front();
458 free(demux_pkt.pData);
462 // invalidate any inflight outputbuffers, make sure
463 // m_output is empty so we do not create new ones
468 // clear m_videobuffer bits
471 free(m_videobuffer.data[0]), m_videobuffer.data[0] = NULL;
472 free(m_videobuffer.data[1]), m_videobuffer.data[1] = NULL;
473 free(m_videobuffer.data[2]), m_videobuffer.data[2] = NULL;
475 m_videobuffer.iFlags = 0;
476 // m_videobuffer.mediacodec is unioned with m_videobuffer.data[0]
477 // so be very careful when and how you touch it.
478 m_videobuffer.mediacodec = NULL;
486 ReleaseSurfaceTexture();
488 SAFE_DELETE(m_bitstream);
491 int CDVDVideoCodecAndroidMediaCodec::Decode(uint8_t *pData, int iSize, double dts, double pts)
493 // Handle input, add demuxer packet to input queue, we must accept it or
494 // it will be discarded as DVDPlayerVideo has no concept of "try again".
495 // we must return VC_BUFFER or VC_PICTURE, default to VC_BUFFER.
501 if (m_hints.ptsinvalid)
502 pts = DVD_NOPTS_VALUE;
504 // must check for an output picture 1st,
505 // otherwise, mediacodec can stall on some devices.
506 if (GetOutputPicture() > 0)
513 m_bitstream->Convert(pData, iSize);
514 iSize = m_bitstream->GetConvertSize();
515 pData = m_bitstream->GetConvertBuffer();
518 // queue demux pkt in case we cannot get an input buffer
522 demux_pkt.iSize = iSize;
523 demux_pkt.pData = (uint8_t*)malloc(iSize);
524 memcpy(demux_pkt.pData, pData, iSize);
525 m_demux.push(demux_pkt);
527 // try to fetch an input buffer
528 int64_t timeout_us = 5000;
529 int index = m_codec->dequeueInputBuffer(timeout_us);
532 // docs lie, getInputBuffers should be good after
533 // m_codec->start() but the internal refs are not
534 // setup until much later on some devices.
536 m_input = m_codec->getInputBuffers();
538 // we have an input buffer, fill it.
539 int size = m_input[index].capacity();
540 // fetch the front demux packet
541 amc_demux &demux_pkt = m_demux.front();
542 if (demux_pkt.iSize > size)
544 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode, iSize(%d) > size(%d)", iSize, size);
545 demux_pkt.iSize = size;
547 // fetch a pointer to the ByteBuffer backing store
548 void *dst_ptr = xbmc_jnienv()->GetDirectBufferAddress(m_input[index].get_raw());
550 memcpy(dst_ptr, demux_pkt.pData, demux_pkt.iSize);
552 free(demux_pkt.pData);
555 // Translate from dvdplayer dts/pts to MediaCodec pts,
556 // pts WILL get re-ordered by MediaCodec if needed.
557 // Do not try to pass pts as a unioned double/int64_t,
558 // some android devices will diddle with presentationTimeUs
559 // and you will get NaN back and DVDPlayerVideo will barf.
560 int64_t presentationTimeUs = AV_NOPTS_VALUE;
561 if (demux_pkt.pts != DVD_NOPTS_VALUE)
562 presentationTimeUs = demux_pkt.pts;
563 else if (demux_pkt.dts != DVD_NOPTS_VALUE)
564 presentationTimeUs = demux_pkt.dts;
566 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
567 "pts(%f), ipts(%lld), iSize(%d), GetDataSize(%d), loop_cnt(%d)",
568 presentationTimeUs, pts_dtoi(presentationTimeUs), iSize, GetDataSize(), loop_cnt);
572 m_codec->queueInputBuffer(index, offset, demux_pkt.iSize, presentationTimeUs, flags);
573 // clear any jni exceptions, jni gets upset if we do not.
574 if (xbmc_jnienv()->ExceptionOccurred())
576 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode ExceptionOccurred");
577 xbmc_jnienv()->ExceptionClear();
585 void CDVDVideoCodecAndroidMediaCodec::Reset()
590 // dump any pending demux packets
591 while (!m_demux.empty())
593 amc_demux &demux_pkt = m_demux.front();
594 free(demux_pkt.pData);
600 // flush all outputbuffers inflight, they will
601 // become invalid on m_codec->flush and generate
602 // a spew of java exceptions if used
605 // now we can flush the actual MediaCodec object
607 if (xbmc_jnienv()->ExceptionOccurred())
609 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Reset ExceptionOccurred");
610 xbmc_jnienv()->ExceptionClear();
613 // Invalidate our local DVDVideoPicture bits
614 m_videobuffer.pts = DVD_NOPTS_VALUE;
616 m_videobuffer.mediacodec = NULL;
620 bool CDVDVideoCodecAndroidMediaCodec::GetPicture(DVDVideoPicture* pDvdVideoPicture)
625 *pDvdVideoPicture = m_videobuffer;
627 // Invalidate our local DVDVideoPicture bits
628 m_videobuffer.pts = DVD_NOPTS_VALUE;
630 m_videobuffer.mediacodec = NULL;
635 bool CDVDVideoCodecAndroidMediaCodec::ClearPicture(DVDVideoPicture* pDvdVideoPicture)
637 if (pDvdVideoPicture->format == RENDER_FMT_MEDIACODEC)
638 SAFE_RELEASE(pDvdVideoPicture->mediacodec);
639 memset(pDvdVideoPicture, 0x00, sizeof(DVDVideoPicture));
644 void CDVDVideoCodecAndroidMediaCodec::SetDropState(bool bDrop)
648 m_videobuffer.iFlags |= DVP_FLAG_DROPPED;
650 m_videobuffer.iFlags &= ~DVP_FLAG_DROPPED;
653 int CDVDVideoCodecAndroidMediaCodec::GetDataSize(void)
655 // just ignore internal buffering contribution.
659 double CDVDVideoCodecAndroidMediaCodec::GetTimeSize(void)
661 // just ignore internal buffering contribution.
665 unsigned CDVDVideoCodecAndroidMediaCodec::GetAllowedReferences()
670 void CDVDVideoCodecAndroidMediaCodec::FlushInternal()
672 // invalidate any existing inflight buffers and create
673 // new ones to match the number of output buffers
678 for (size_t i = 0; i < m_inflight.size(); i++)
679 m_inflight[i]->Validate(false);
682 for (size_t i = 0; i < m_output.size(); i++)
684 m_inflight.push_back(
685 new CDVDMediaCodecInfo(i, m_textureId, m_codec, m_surfaceTexture, m_frameAvailable)
690 void CDVDVideoCodecAndroidMediaCodec::ConfigureMediaCodec(void)
692 // setup a MediaFormat to match the video content,
693 // used by codec during configure
694 CJNIMediaFormat mediaformat = CJNIMediaFormat::createVideoFormat(
695 m_mime.c_str(), m_hints.width, m_hints.height);
696 // some android devices forget to default the demux input max size
697 mediaformat.setInteger(CJNIMediaFormat::KEY_MAX_INPUT_SIZE, 0);
699 // handle codec extradata
700 if (m_hints.extrasize)
702 size_t size = m_hints.extrasize;
703 void *src_ptr = m_hints.extradata;
706 size = m_bitstream->GetExtraSize();
707 src_ptr = m_bitstream->GetExtraData();
709 // Allocate a byte buffer via allocateDirect in java instead of NewDirectByteBuffer,
710 // since the latter doesn't allocate storage of its own, and we don't know how long
711 // the codec uses the buffer.
712 CJNIByteBuffer bytebuffer = CJNIByteBuffer::allocateDirect(size);
713 void *dts_ptr = xbmc_jnienv()->GetDirectBufferAddress(bytebuffer.get_raw());
714 memcpy(dts_ptr, src_ptr, size);
715 // codec will automatically handle buffers as extradata
716 // using entries with keys "csd-0", "csd-1", etc.
717 mediaformat.setByteBuffer("csd-0", bytebuffer);
720 InitSurfaceTexture();
722 // configure and start the codec.
723 // use the MediaFormat that we have setup.
724 // use a null MediaCrypto, our content is not encrypted.
725 // use a null Surface, we will extract the video picture data manually.
727 CJNIMediaCrypto crypto(jni::jhobject(NULL));
728 // our jni gets upset if we do this a different
729 // way, do not mess with it.
732 CJNISurface surface(jni::jhobject(NULL));
733 m_codec->configure(mediaformat, surface, crypto, flags);
737 m_codec->configure(mediaformat, *m_surface, crypto, flags);
742 // always, check/clear jni exceptions.
743 if (xbmc_jnienv()->ExceptionOccurred())
744 xbmc_jnienv()->ExceptionClear();
747 int CDVDVideoCodecAndroidMediaCodec::GetOutputPicture(void)
751 int64_t timeout_us = 5000;
752 CJNIMediaCodecBufferInfo bufferInfo;
753 int index = m_codec->dequeueOutputBuffer(bufferInfo, timeout_us);
758 m_codec->releaseOutputBuffer(index, false);
759 if (xbmc_jnienv()->ExceptionOccurred())
760 xbmc_jnienv()->ExceptionClear();
764 // some devices will return a valid index
765 // before signaling INFO_OUTPUT_BUFFERS_CHANGED which
766 // is used to setup m_output, D'uh. setup m_output here.
767 if (m_output.empty())
769 m_output = m_codec->getOutputBuffers();
773 int flags = bufferInfo.flags();
774 if (flags & CJNIMediaCodec::BUFFER_FLAG_SYNC_FRAME)
775 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_SYNC_FRAME");
777 if (flags & CJNIMediaCodec::BUFFER_FLAG_CODEC_CONFIG)
778 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_CODEC_CONFIG");
780 if (flags & CJNIMediaCodec::BUFFER_FLAG_END_OF_STREAM)
782 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_END_OF_STREAM");
783 m_codec->releaseOutputBuffer(index, false);
784 if (xbmc_jnienv()->ExceptionOccurred())
785 xbmc_jnienv()->ExceptionClear();
791 m_videobuffer.mediacodec = m_inflight[index]->Retain();
792 m_videobuffer.mediacodec->Validate(true);
796 int size = bufferInfo.size();
797 int offset = bufferInfo.offset();
799 if (!m_output[index].isDirect())
800 CLog::Log(LOGWARNING, "CDVDVideoCodecAndroidMediaCodec:: m_output[index].isDirect == false");
802 if (size && m_output[index].capacity())
804 uint8_t *src_ptr = (uint8_t*)xbmc_jnienv()->GetDirectBufferAddress(m_output[index].get_raw());
808 if (m_videobuffer.format == RENDER_FMT_NV12)
810 else if (m_videobuffer.format == RENDER_FMT_YUV420P)
813 for (int i = 0; i < loop_end; i++)
815 uint8_t *src = src_ptr + m_src_offset[i];
816 int src_stride = m_src_stride[i];
817 uint8_t *dst = m_videobuffer.data[i];
818 int dst_stride = m_videobuffer.iLineSize[i];
820 int height = m_videobuffer.iHeight;
822 height = (m_videobuffer.iHeight + 1) / 2;
824 for (int j = 0; j < height; j++, src += src_stride, dst += dst_stride)
825 memcpy(dst, src, dst_stride);
828 m_codec->releaseOutputBuffer(index, false);
831 int64_t pts= bufferInfo.presentationTimeUs();
832 m_videobuffer.dts = DVD_NOPTS_VALUE;
833 m_videobuffer.pts = DVD_NOPTS_VALUE;
834 if (pts != AV_NOPTS_VALUE)
835 m_videobuffer.pts = pts;
838 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture "
839 "index(%d), pts(%f)", index, m_videobuffer.pts);
841 // always, check/clear jni exceptions.
842 if (xbmc_jnienv()->ExceptionOccurred())
843 xbmc_jnienv()->ExceptionClear();
847 else if (index == CJNIMediaCodec::INFO_OUTPUT_BUFFERS_CHANGED)
849 m_output = m_codec->getOutputBuffers();
852 else if (index == CJNIMediaCodec::INFO_OUTPUT_FORMAT_CHANGED)
854 OutputFormatChanged();
856 else if (index == CJNIMediaCodec::INFO_TRY_AGAIN_LATER)
858 // normal dequeueOutputBuffer timeout, ignore it.
863 // we should never get here
864 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture unknown index(%d)", index);
870 void CDVDVideoCodecAndroidMediaCodec::OutputFormatChanged(void)
872 CJNIMediaFormat mediaformat = m_codec->getOutputFormat();
874 int width = mediaformat.getInteger("width");
875 int height = mediaformat.getInteger("height");
876 int stride = mediaformat.getInteger("stride");
877 int slice_height= mediaformat.getInteger("slice-height");
878 int color_format= mediaformat.getInteger("color-format");
879 int crop_left = mediaformat.getInteger("crop-left");
880 int crop_top = mediaformat.getInteger("crop-top");
881 int crop_right = mediaformat.getInteger("crop-right");
882 int crop_bottom = mediaformat.getInteger("crop-bottom");
884 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
885 "width(%d), height(%d), stride(%d), slice-height(%d), color-format(%d)",
886 width, height, stride, slice_height, color_format);
887 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
888 "crop-left(%d), crop-top(%d), crop-right(%d), crop-bottom(%d)",
889 crop_left, crop_top, crop_right, crop_bottom);
893 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Direct Surface Rendering");
894 m_videobuffer.format = RENDER_FMT_MEDIACODEC;
898 // Android device quirks and fixes
901 if (slice_height <= 0)
903 slice_height = height;
904 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
906 // NVidia Tegra 3 on Nexus 7 does not set slice_heights
907 if (strstr(m_codecname.c_str(), "OMX.Nvidia.") != NULL)
909 slice_height = (((height) + 31) & ~31);
910 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: NVidia Tegra 3 quirk, slice_height(%d)", slice_height);
914 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar)
916 slice_height -= crop_top / 2;
917 // set crop top/left here, since the offset parameter already includes this.
918 // if we would ignore the offset parameter in the BufferInfo, we could just keep
919 // the original slice height and apply the top/left cropping instead.
924 // default picture format to none
925 for (int i = 0; i < 4; i++)
926 m_src_offset[i] = m_src_stride[i] = 0;
927 // delete any existing buffers
928 for (int i = 0; i < 4; i++)
929 free(m_videobuffer.data[i]);
931 // setup picture format and data offset vectors
932 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
934 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420Planar");
937 m_src_stride[0] = stride;
938 m_src_offset[0] = crop_top * stride;
939 m_src_offset[0]+= crop_left;
942 m_src_stride[1] = (stride + 1) / 2;
943 // skip over the Y plane
944 m_src_offset[1] = slice_height * stride;
945 // crop_top/crop_left divided by two
946 // because one byte of the U/V planes
947 // corresponds to two pixels horizontally/vertically
948 m_src_offset[1]+= crop_top / 2 * m_src_stride[1];
949 m_src_offset[1]+= crop_left / 2;
952 m_src_stride[2] = (stride + 1) / 2;
953 // skip over the Y plane
954 m_src_offset[2] = slice_height * stride;
955 // skip over the U plane
956 m_src_offset[2]+= ((slice_height + 1) / 2) * ((stride + 1) / 2);
957 // crop_top/crop_left divided by two
958 // because one byte of the U/V planes
959 // corresponds to two pixels horizontally/vertically
960 m_src_offset[2]+= crop_top / 2 * m_src_stride[2];
961 m_src_offset[2]+= crop_left / 2;
963 m_videobuffer.iLineSize[0] = width; // Y
964 m_videobuffer.iLineSize[1] = (width + 1) /2; // U
965 m_videobuffer.iLineSize[2] = (width + 1) /2; // V
966 m_videobuffer.iLineSize[3] = 0;
968 unsigned int iPixels = width * height;
969 unsigned int iChromaPixels = iPixels/4;
970 m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
971 m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
972 m_videobuffer.data[2] = (uint8_t*)malloc(16 + iChromaPixels);
973 m_videobuffer.data[3] = NULL;
974 m_videobuffer.format = RENDER_FMT_YUV420P;
976 else if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420SemiPlanar
977 || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_QCOM_FormatYUV420SemiPlanar
978 || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar
979 || color_format == CJNIMediaCodecInfoCodecCapabilities::OMX_QCOM_COLOR_FormatYVU420SemiPlanarInterlace)
982 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420SemiPlanar");
985 m_src_stride[0] = stride;
986 m_src_offset[0] = crop_top * stride;
987 m_src_offset[0]+= crop_left;
990 m_src_stride[1] = stride;
991 // skip over the Y plane
992 m_src_offset[1] = slice_height * stride;
993 m_src_offset[1]+= crop_top * stride;
994 m_src_offset[1]+= crop_left;
996 m_videobuffer.iLineSize[0] = width; // Y
997 m_videobuffer.iLineSize[1] = width; // UV
998 m_videobuffer.iLineSize[2] = 0;
999 m_videobuffer.iLineSize[3] = 0;
1001 unsigned int iPixels = width * height;
1002 unsigned int iChromaPixels = iPixels;
1003 m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
1004 m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
1005 m_videobuffer.data[2] = NULL;
1006 m_videobuffer.data[3] = NULL;
1007 m_videobuffer.format = RENDER_FMT_NV12;
1011 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Fixme unknown color_format(%d)", color_format);
1016 // picture display width/height include the cropping.
1017 m_videobuffer.iDisplayWidth = crop_right + 1 - crop_left;
1018 m_videobuffer.iDisplayHeight = crop_bottom + 1 - crop_top;
1020 // clear any jni exceptions
1021 if (xbmc_jnienv()->ExceptionOccurred())
1022 xbmc_jnienv()->ExceptionClear();
1025 void CDVDVideoCodecAndroidMediaCodec::CallbackInitSurfaceTexture(void *userdata)
1027 CDVDVideoCodecAndroidMediaCodec *ctx = static_cast<CDVDVideoCodecAndroidMediaCodec*>(userdata);
1028 ctx->InitSurfaceTexture();
1031 void CDVDVideoCodecAndroidMediaCodec::InitSurfaceTexture(void)
1036 // We MUST create the GLES texture on the main thread
1037 // to match where the valid GLES context is located.
1038 // It would be nice to move this out of here, we would need
1039 // to create/fetch/create from g_RenderMananger. But g_RenderMananger
1040 // does not know we are using MediaCodec until Configure and we
1041 // we need m_surfaceTexture valid before then. Chicken, meet Egg.
1042 if (g_application.IsCurrentThread())
1044 // localize GLuint so we do not spew gles includes in our header
1047 glGenTextures(1, &texture_id);
1048 glBindTexture( GL_TEXTURE_EXTERNAL_OES, texture_id);
1049 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1050 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1051 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1052 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1053 glBindTexture( GL_TEXTURE_EXTERNAL_OES, 0);
1054 m_textureId = texture_id;
1056 m_surfaceTexture = boost::shared_ptr<CJNISurfaceTexture>(new CJNISurfaceTexture(m_textureId));
1057 // hook the surfaceTexture OnFrameAvailable callback
1058 m_frameAvailable = boost::shared_ptr<CDVDMediaCodecOnFrameAvailable>(new CDVDMediaCodecOnFrameAvailable(m_surfaceTexture));
1059 m_surface = new CJNISurface(*m_surfaceTexture);
1063 ThreadMessageCallback callbackData;
1064 callbackData.callback = &CallbackInitSurfaceTexture;
1065 callbackData.userptr = (void*)this;
1068 msg.dwMessage = TMSG_CALLBACK;
1069 msg.lpVoid = (void*)&callbackData;
1072 CApplicationMessenger::Get().SendMessage(msg, true);
1078 void CDVDVideoCodecAndroidMediaCodec::ReleaseSurfaceTexture(void)
1083 // it is safe to delete here even though these items
1084 // were created in the main thread instance
1085 SAFE_DELETE(m_surface);
1086 m_frameAvailable.reset();
1087 m_surfaceTexture.reset();
1089 if (m_textureId > 0)
1091 GLuint texture_id = m_textureId;
1092 glDeleteTextures(1, &texture_id);