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[] = {
62 for (const char **ptr = cansurfacerender_decoders; *ptr; ptr++)
64 if (!strncmp(*ptr, name.c_str(), strlen(*ptr)))
70 static bool IsBlacklisted(const std::string &name)
72 static const char *blacklisted_decoders[] = {
73 // No software decoders
77 for (const char **ptr = blacklisted_decoders; *ptr; ptr++)
79 if (!strncmp(*ptr, name.c_str(), strlen(*ptr)))
85 /*****************************************************************************/
86 /*****************************************************************************/
87 class CNULL_Listener : public CJNISurfaceTextureOnFrameAvailableListener
90 CNULL_Listener() : CJNISurfaceTextureOnFrameAvailableListener(jni::jhobject(NULL)) {};
93 virtual void OnFrameAvailable(CJNISurfaceTexture &surface) {};
96 class CDVDMediaCodecOnFrameAvailable : public CEvent, CJNISurfaceTextureOnFrameAvailableListener
99 CDVDMediaCodecOnFrameAvailable(boost::shared_ptr<CJNISurfaceTexture> &surfaceTexture)
100 : m_surfaceTexture(surfaceTexture)
102 m_surfaceTexture->setOnFrameAvailableListener(*this);
105 virtual ~CDVDMediaCodecOnFrameAvailable()
107 // unhook the callback
108 CNULL_Listener null_listener;
109 m_surfaceTexture->setOnFrameAvailableListener(null_listener);
113 virtual void OnFrameAvailable(CJNISurfaceTexture &surface)
119 boost::shared_ptr<CJNISurfaceTexture> m_surfaceTexture;
123 /*****************************************************************************/
124 /*****************************************************************************/
125 CDVDMediaCodecInfo::CDVDMediaCodecInfo(
127 , unsigned int texture
128 , boost::shared_ptr<CJNIMediaCodec> &codec
129 , boost::shared_ptr<CJNISurfaceTexture> &surfacetexture
130 , boost::shared_ptr<CDVDMediaCodecOnFrameAvailable> &frameready
138 , m_surfacetexture(surfacetexture)
139 , m_frameready(frameready)
142 assert(m_index >= 0);
143 assert(m_texture > 0);
144 assert(m_codec != NULL);
145 assert(m_surfacetexture != NULL);
146 assert(m_frameready != NULL);
149 CDVDMediaCodecInfo::~CDVDMediaCodecInfo()
154 CDVDMediaCodecInfo* CDVDMediaCodecInfo::Retain()
156 AtomicIncrement(&m_refs);
161 long CDVDMediaCodecInfo::Release()
163 long count = AtomicDecrement(&m_refs);
166 ReleaseOutputBuffer(false);
173 void CDVDMediaCodecInfo::Validate(bool state)
175 CSingleLock lock(m_section);
180 void CDVDMediaCodecInfo::ReleaseOutputBuffer(bool render)
182 CSingleLock lock(m_section);
187 // release OutputBuffer and render if indicated
188 // then wait for rendered frame to become avaliable.
191 m_frameready->Reset();
193 m_codec->releaseOutputBuffer(m_index, render);
195 if (xbmc_jnienv()->ExceptionOccurred())
197 CLog::Log(LOGERROR, "CDVDMediaCodecInfo::ReleaseOutputBuffer "
198 "ExceptionOccurred render(%d)", render);
199 xbmc_jnienv()->ExceptionDescribe();
200 xbmc_jnienv()->ExceptionClear();
204 int CDVDMediaCodecInfo::GetIndex() const
206 CSingleLock lock(m_section);
211 int CDVDMediaCodecInfo::GetTextureID() const
213 // since m_texture never changes,
214 // we do not need a m_section lock here.
218 void CDVDMediaCodecInfo::GetTransformMatrix(float *textureMatrix)
220 CSingleLock lock(m_section);
225 m_surfacetexture->getTransformMatrix(textureMatrix);
228 void CDVDMediaCodecInfo::UpdateTexImage()
230 CSingleLock lock(m_section);
235 // updateTexImage will check and spew any prior gl errors,
236 // clear them before we call updateTexImage.
239 // this is key, after calling releaseOutputBuffer, we must
240 // wait a little for MediaCodec to render to the surface.
241 // Then we can updateTexImage without delay. If we do not
242 // wait, then video playback gets jerky. To optomize this,
243 // we hook the SurfaceTexture OnFrameAvailable callback
244 // using CJNISurfaceTextureOnFrameAvailableListener and wait
245 // on a CEvent to fire. 20ms seems to be a good max fallback.
246 m_frameready->WaitMSec(20);
248 m_surfacetexture->updateTexImage();
249 if (xbmc_jnienv()->ExceptionOccurred())
251 CLog::Log(LOGERROR, "CDVDMediaCodecInfo::UpdateTexImage updateTexImage:ExceptionOccurred");
252 xbmc_jnienv()->ExceptionDescribe();
253 xbmc_jnienv()->ExceptionClear();
256 m_timestamp = m_surfacetexture->getTimestamp();
257 if (xbmc_jnienv()->ExceptionOccurred())
259 CLog::Log(LOGERROR, "CDVDMediaCodecInfo::UpdateTexImage getTimestamp:ExceptionOccurred");
260 xbmc_jnienv()->ExceptionDescribe();
261 xbmc_jnienv()->ExceptionClear();
265 /*****************************************************************************/
266 /*****************************************************************************/
267 CDVDVideoCodecAndroidMediaCodec::CDVDVideoCodecAndroidMediaCodec()
268 : m_formatname("mediacodec")
275 memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
278 CDVDVideoCodecAndroidMediaCodec::~CDVDVideoCodecAndroidMediaCodec()
283 bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
285 // check for 4.1 Jellybean and above.
286 if (CAndroidFeatures::GetVersion() < 16)
292 switch(m_hints.codec)
294 case AV_CODEC_ID_MPEG2VIDEO:
295 m_mime = "video/mpeg2";
296 m_formatname = "amc-mpeg2";
298 case AV_CODEC_ID_MPEG4:
299 m_mime = "video/mp4v-es";
300 m_formatname = "amc-mpeg4";
302 case AV_CODEC_ID_H263:
303 m_mime = "video/3gpp";
304 m_formatname = "amc-h263";
306 case AV_CODEC_ID_VP3:
307 case AV_CODEC_ID_VP6:
308 case AV_CODEC_ID_VP6F:
309 case AV_CODEC_ID_VP8:
310 //m_mime = "video/x-vp6";
311 //m_mime = "video/x-vp7";
312 m_mime = "video/x-vnd.on2.vp8";
313 m_formatname = "amc-vpX";
315 case AV_CODEC_ID_AVS:
316 case AV_CODEC_ID_CAVS:
317 case AV_CODEC_ID_H264:
318 m_mime = "video/avc";
319 m_formatname = "amc-h264";
320 m_bitstream = new CBitstreamConverter;
321 if (!m_bitstream->Open(m_hints.codec, (uint8_t*)m_hints.extradata, m_hints.extrasize, true))
323 SAFE_DELETE(m_bitstream);
327 case AV_CODEC_ID_VC1:
328 case AV_CODEC_ID_WMV3:
329 m_mime = "video/wvc1";
330 //m_mime = "video/wmv9";
331 m_formatname = "amc-vc1";
334 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Unknown hints.codec(%d)", hints.codec);
339 // CJNIMediaCodec::createDecoderByXXX doesn't handle errors nicely,
340 // it crashes if the codec isn't found. This is fixed in latest AOSP,
341 // but not in current 4.1 devices. So 1st search for a matching codec, then create it.
342 int num_codecs = CJNIMediaCodecList::getCodecCount();
343 for (int i = 0; i < num_codecs; i++)
345 CJNIMediaCodecInfo codec_info = CJNIMediaCodecList::getCodecInfoAt(i);
346 if (codec_info.isEncoder())
348 m_codecname = codec_info.getName();
349 if (IsBlacklisted(m_codecname))
352 std::vector<std::string> types = codec_info.getSupportedTypes();
353 // return the 1st one we find, that one is typically 'the best'
354 for (size_t j = 0; j < types.size(); ++j)
356 if (types[j] == m_mime)
358 m_codec = boost::shared_ptr<CJNIMediaCodec>(new CJNIMediaCodec(CJNIMediaCodec::createByCodecName(m_codecname)));
360 CJNIMediaCodecInfoCodecCapabilities codec_caps = codec_info.getCapabilitiesForType(m_mime);
361 std::vector<int> color_formats = codec_caps.colorFormats();
362 for (size_t k = 0; k < color_formats.size(); ++k)
364 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::Open "
365 "m_codecname(%s), colorFormat(%d)", m_codecname.c_str(), color_formats[k]);
368 // clear any jni exceptions, jni gets upset if we do not.
369 if (xbmc_jnienv()->ExceptionOccurred())
371 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open ExceptionOccurred");
372 xbmc_jnienv()->ExceptionClear();
384 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Failed to create Android MediaCodec");
385 SAFE_DELETE(m_bitstream);
389 // whitelist of devices that can surface render.
390 m_render_sw = !CanSurfaceRenderWhiteList(m_codecname);
392 ConfigureMediaCodec();
394 // setup a YUV420P DVDVideoPicture buffer.
395 // first make sure all properties are reset.
396 memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
398 m_videobuffer.dts = DVD_NOPTS_VALUE;
399 m_videobuffer.pts = DVD_NOPTS_VALUE;
400 m_videobuffer.color_range = 0;
401 m_videobuffer.color_matrix = 4;
402 m_videobuffer.iFlags = DVP_FLAG_ALLOCATED;
403 m_videobuffer.iWidth = m_hints.width;
404 m_videobuffer.iHeight = m_hints.height;
405 // these will get reset to crop values later
406 m_videobuffer.iDisplayWidth = m_hints.width;
407 m_videobuffer.iDisplayHeight = m_hints.height;
409 CLog::Log(LOGINFO, "CDVDVideoCodecAndroidMediaCodec:: "
410 "Open Android MediaCodec %s", m_codecname.c_str());
417 void CDVDVideoCodecAndroidMediaCodec::Dispose()
421 // release any retained demux packets
422 while (!m_demux.empty())
424 amc_demux &demux_pkt = m_demux.front();
425 free(demux_pkt.pData);
429 // invalidate any inflight outputbuffers, make sure
430 // m_output is empty so we do not create new ones
435 // clear m_videobuffer bits
438 free(m_videobuffer.data[0]), m_videobuffer.data[0] = NULL;
439 free(m_videobuffer.data[1]), m_videobuffer.data[1] = NULL;
440 free(m_videobuffer.data[2]), m_videobuffer.data[2] = NULL;
442 m_videobuffer.iFlags = 0;
443 // m_videobuffer.mediacodec is unioned with m_videobuffer.data[0]
444 // so be very careful when and how you touch it.
445 m_videobuffer.mediacodec = NULL;
453 ReleaseSurfaceTexture();
455 SAFE_DELETE(m_bitstream);
458 int CDVDVideoCodecAndroidMediaCodec::Decode(uint8_t *pData, int iSize, double dts, double pts)
460 // Handle input, add demuxer packet to input queue, we must accept it or
461 // it will be discarded as DVDPlayerVideo has no concept of "try again".
462 // we must return VC_BUFFER or VC_PICTURE, default to VC_BUFFER.
468 if (m_hints.ptsinvalid)
469 pts = DVD_NOPTS_VALUE;
471 // must check for an output picture 1st,
472 // otherwise, mediacodec can stall on some devices.
473 if (GetOutputPicture() > 0)
480 m_bitstream->Convert(pData, iSize);
481 iSize = m_bitstream->GetConvertSize();
482 pData = m_bitstream->GetConvertBuffer();
485 // queue demux pkt in case we cannot get an input buffer
489 demux_pkt.iSize = iSize;
490 demux_pkt.pData = (uint8_t*)malloc(iSize);
491 memcpy(demux_pkt.pData, pData, iSize);
492 m_demux.push(demux_pkt);
494 // try to fetch an input buffer
495 int64_t timeout_us = 5000;
496 int index = m_codec->dequeueInputBuffer(timeout_us);
499 // docs lie, getInputBuffers should be good after
500 // m_codec->start() but the internal refs are not
501 // setup until much later on some devices.
503 m_input = m_codec->getInputBuffers();
505 // we have an input buffer, fill it.
506 int size = m_input[index].capacity();
507 // fetch the front demux packet
508 amc_demux &demux_pkt = m_demux.front();
509 if (demux_pkt.iSize > size)
511 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode, iSize(%d) > size(%d)", iSize, size);
512 demux_pkt.iSize = size;
514 // fetch a pointer to the ByteBuffer backing store
515 void *dst_ptr = xbmc_jnienv()->GetDirectBufferAddress(m_input[index].get_raw());
517 memcpy(dst_ptr, demux_pkt.pData, demux_pkt.iSize);
519 free(demux_pkt.pData);
522 // Translate from dvdplayer dts/pts to MediaCodec pts,
523 // pts WILL get re-ordered by MediaCodec if needed.
524 // Do not try to pass pts as a unioned double/int64_t,
525 // some android devices will diddle with presentationTimeUs
526 // and you will get NaN back and DVDPlayerVideo will barf.
527 int64_t presentationTimeUs = AV_NOPTS_VALUE;
528 if (demux_pkt.pts != DVD_NOPTS_VALUE)
529 presentationTimeUs = demux_pkt.pts;
530 else if (demux_pkt.dts != DVD_NOPTS_VALUE)
531 presentationTimeUs = demux_pkt.dts;
533 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
534 "pts(%f), ipts(%lld), iSize(%d), GetDataSize(%d), loop_cnt(%d)",
535 presentationTimeUs, pts_dtoi(presentationTimeUs), iSize, GetDataSize(), loop_cnt);
539 m_codec->queueInputBuffer(index, offset, demux_pkt.iSize, presentationTimeUs, flags);
540 // clear any jni exceptions, jni gets upset if we do not.
541 if (xbmc_jnienv()->ExceptionOccurred())
543 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode ExceptionOccurred");
544 xbmc_jnienv()->ExceptionClear();
552 void CDVDVideoCodecAndroidMediaCodec::Reset()
557 // dump any pending demux packets
558 while (!m_demux.empty())
560 amc_demux &demux_pkt = m_demux.front();
561 free(demux_pkt.pData);
567 // flush all outputbuffers inflight, they will
568 // become invalid on m_codec->flush and generate
569 // a spew of java exceptions if used
572 // now we can flush the actual MediaCodec object
574 if (xbmc_jnienv()->ExceptionOccurred())
576 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Reset ExceptionOccurred");
577 xbmc_jnienv()->ExceptionClear();
580 // Invalidate our local DVDVideoPicture bits
581 m_videobuffer.pts = DVD_NOPTS_VALUE;
583 m_videobuffer.mediacodec = NULL;
587 bool CDVDVideoCodecAndroidMediaCodec::GetPicture(DVDVideoPicture* pDvdVideoPicture)
592 *pDvdVideoPicture = m_videobuffer;
594 // Invalidate our local DVDVideoPicture bits
595 m_videobuffer.pts = DVD_NOPTS_VALUE;
597 m_videobuffer.mediacodec = NULL;
602 bool CDVDVideoCodecAndroidMediaCodec::ClearPicture(DVDVideoPicture* pDvdVideoPicture)
604 if (pDvdVideoPicture->format == RENDER_FMT_MEDIACODEC)
605 SAFE_RELEASE(pDvdVideoPicture->mediacodec);
606 memset(pDvdVideoPicture, 0x00, sizeof(DVDVideoPicture));
611 void CDVDVideoCodecAndroidMediaCodec::SetDropState(bool bDrop)
615 m_videobuffer.iFlags |= DVP_FLAG_DROPPED;
617 m_videobuffer.iFlags &= ~DVP_FLAG_DROPPED;
620 int CDVDVideoCodecAndroidMediaCodec::GetDataSize(void)
622 // just ignore internal buffering contribution.
626 double CDVDVideoCodecAndroidMediaCodec::GetTimeSize(void)
628 // just ignore internal buffering contribution.
632 unsigned CDVDVideoCodecAndroidMediaCodec::GetAllowedReferences()
637 void CDVDVideoCodecAndroidMediaCodec::FlushInternal()
639 // invalidate any existing inflight buffers and create
640 // new ones to match the number of output buffers
645 for (size_t i = 0; i < m_inflight.size(); i++)
646 m_inflight[i]->Validate(false);
649 for (size_t i = 0; i < m_output.size(); i++)
651 m_inflight.push_back(
652 new CDVDMediaCodecInfo(i, m_textureId, m_codec, m_surfaceTexture, m_frameAvailable)
657 void CDVDVideoCodecAndroidMediaCodec::ConfigureMediaCodec(void)
659 // setup a MediaFormat to match the video content,
660 // used by codec during configure
661 CJNIMediaFormat mediaformat = CJNIMediaFormat::createVideoFormat(
662 m_mime.c_str(), m_hints.width, m_hints.height);
663 // some android devices forget to default the demux input max size
664 mediaformat.setInteger(CJNIMediaFormat::KEY_MAX_INPUT_SIZE, 0);
666 // handle codec extradata
667 if (m_hints.extrasize)
669 size_t size = m_hints.extrasize;
670 void *src_ptr = m_hints.extradata;
673 size = m_bitstream->GetExtraSize();
674 src_ptr = m_bitstream->GetExtraData();
676 // Allocate a byte buffer via allocateDirect in java instead of NewDirectByteBuffer,
677 // since the latter doesn't allocate storage of its own, and we don't know how long
678 // the codec uses the buffer.
679 CJNIByteBuffer bytebuffer = CJNIByteBuffer::allocateDirect(size);
680 void *dts_ptr = xbmc_jnienv()->GetDirectBufferAddress(bytebuffer.get_raw());
681 memcpy(dts_ptr, src_ptr, size);
682 // codec will automatically handle buffers as extradata
683 // using entries with keys "csd-0", "csd-1", etc.
684 mediaformat.setByteBuffer("csd-0", bytebuffer);
687 InitSurfaceTexture();
689 // configure and start the codec.
690 // use the MediaFormat that we have setup.
691 // use a null MediaCrypto, our content is not encrypted.
692 // use a null Surface, we will extract the video picture data manually.
694 CJNIMediaCrypto crypto(jni::jhobject(NULL));
695 // our jni gets upset if we do this a different
696 // way, do not mess with it.
699 CJNISurface surface(jni::jhobject(NULL));
700 m_codec->configure(mediaformat, surface, crypto, flags);
704 m_codec->configure(mediaformat, *m_surface, crypto, flags);
709 // always, check/clear jni exceptions.
710 if (xbmc_jnienv()->ExceptionOccurred())
711 xbmc_jnienv()->ExceptionClear();
714 int CDVDVideoCodecAndroidMediaCodec::GetOutputPicture(void)
718 int64_t timeout_us = 5000;
719 CJNIMediaCodecBufferInfo bufferInfo;
720 int index = m_codec->dequeueOutputBuffer(bufferInfo, timeout_us);
725 m_codec->releaseOutputBuffer(index, false);
726 if (xbmc_jnienv()->ExceptionOccurred())
727 xbmc_jnienv()->ExceptionClear();
731 // some devices will return a valid index
732 // before signaling INFO_OUTPUT_BUFFERS_CHANGED which
733 // is used to setup m_output, D'uh. setup m_output here.
734 if (m_output.empty())
736 m_output = m_codec->getOutputBuffers();
740 int flags = bufferInfo.flags();
741 if (flags & CJNIMediaCodec::BUFFER_FLAG_SYNC_FRAME)
742 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_SYNC_FRAME");
744 if (flags & CJNIMediaCodec::BUFFER_FLAG_CODEC_CONFIG)
745 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_CODEC_CONFIG");
747 if (flags & CJNIMediaCodec::BUFFER_FLAG_END_OF_STREAM)
749 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_END_OF_STREAM");
750 m_codec->releaseOutputBuffer(index, false);
751 if (xbmc_jnienv()->ExceptionOccurred())
752 xbmc_jnienv()->ExceptionClear();
758 m_videobuffer.mediacodec = m_inflight[index]->Retain();
759 m_videobuffer.mediacodec->Validate(true);
763 int size = bufferInfo.size();
764 int offset = bufferInfo.offset();
766 if (!m_output[index].isDirect())
767 CLog::Log(LOGWARNING, "CDVDVideoCodecAndroidMediaCodec:: m_output[index].isDirect == false");
769 if (size && m_output[index].capacity())
771 uint8_t *src_ptr = (uint8_t*)xbmc_jnienv()->GetDirectBufferAddress(m_output[index].get_raw());
775 if (m_videobuffer.format == RENDER_FMT_NV12)
777 else if (m_videobuffer.format == RENDER_FMT_YUV420P)
780 for (int i = 0; i < loop_end; i++)
782 uint8_t *src = src_ptr + m_src_offset[i];
783 int src_stride = m_src_stride[i];
784 uint8_t *dst = m_videobuffer.data[i];
785 int dst_stride = m_videobuffer.iLineSize[i];
787 int height = m_videobuffer.iHeight;
789 height = (m_videobuffer.iHeight + 1) / 2;
791 for (int j = 0; j < height; j++, src += src_stride, dst += dst_stride)
792 memcpy(dst, src, dst_stride);
795 m_codec->releaseOutputBuffer(index, false);
798 int64_t pts= bufferInfo.presentationTimeUs();
799 m_videobuffer.dts = DVD_NOPTS_VALUE;
800 m_videobuffer.pts = DVD_NOPTS_VALUE;
801 if (pts != AV_NOPTS_VALUE)
802 m_videobuffer.pts = pts;
805 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture "
806 "index(%d), pts(%f)", index, m_videobuffer.pts);
808 // always, check/clear jni exceptions.
809 if (xbmc_jnienv()->ExceptionOccurred())
810 xbmc_jnienv()->ExceptionClear();
814 else if (index == CJNIMediaCodec::INFO_OUTPUT_BUFFERS_CHANGED)
816 m_output = m_codec->getOutputBuffers();
819 else if (index == CJNIMediaCodec::INFO_OUTPUT_FORMAT_CHANGED)
821 OutputFormatChanged();
823 else if (index == CJNIMediaCodec::INFO_TRY_AGAIN_LATER)
825 // normal dequeueOutputBuffer timeout, ignore it.
830 // we should never get here
831 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture unknown index(%d)", index);
837 void CDVDVideoCodecAndroidMediaCodec::OutputFormatChanged(void)
839 CJNIMediaFormat mediaformat = m_codec->getOutputFormat();
841 int width = mediaformat.getInteger("width");
842 int height = mediaformat.getInteger("height");
843 int stride = mediaformat.getInteger("stride");
844 int slice_height= mediaformat.getInteger("slice-height");
845 int color_format= mediaformat.getInteger("color-format");
846 int crop_left = mediaformat.getInteger("crop-left");
847 int crop_top = mediaformat.getInteger("crop-top");
848 int crop_right = mediaformat.getInteger("crop-right");
849 int crop_bottom = mediaformat.getInteger("crop-bottom");
851 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
852 "width(%d), height(%d), stride(%d), slice-height(%d), color-format(%d)",
853 width, height, stride, slice_height, color_format);
854 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
855 "crop-left(%d), crop-top(%d), crop-right(%d), crop-bottom(%d)",
856 crop_left, crop_top, crop_right, crop_bottom);
860 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Direct Surface Rendering");
861 m_videobuffer.format = RENDER_FMT_MEDIACODEC;
865 // Android device quirks and fixes
868 if (slice_height <= 0)
870 slice_height = height;
871 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
873 // NVidia Tegra 3 on Nexus 7 does not set slice_heights
874 if (strstr(m_codecname.c_str(), "OMX.Nvidia.") != NULL)
876 slice_height = (((height) + 31) & ~31);
877 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: NVidia Tegra 3 quirk, slice_height(%d)", slice_height);
881 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar)
883 slice_height -= crop_top / 2;
884 // set crop top/left here, since the offset parameter already includes this.
885 // if we would ignore the offset parameter in the BufferInfo, we could just keep
886 // the original slice height and apply the top/left cropping instead.
891 // default picture format to none
892 for (int i = 0; i < 4; i++)
893 m_src_offset[i] = m_src_stride[i] = 0;
894 // delete any existing buffers
895 for (int i = 0; i < 4; i++)
896 free(m_videobuffer.data[i]);
898 // setup picture format and data offset vectors
899 if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
901 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420Planar");
904 m_src_stride[0] = stride;
905 m_src_offset[0] = crop_top * stride;
906 m_src_offset[0]+= crop_left;
909 m_src_stride[1] = (stride + 1) / 2;
910 // skip over the Y plane
911 m_src_offset[1] = slice_height * stride;
912 // crop_top/crop_left divided by two
913 // because one byte of the U/V planes
914 // corresponds to two pixels horizontally/vertically
915 m_src_offset[1]+= crop_top / 2 * m_src_stride[1];
916 m_src_offset[1]+= crop_left / 2;
919 m_src_stride[2] = (stride + 1) / 2;
920 // skip over the Y plane
921 m_src_offset[2] = slice_height * stride;
922 // skip over the U plane
923 m_src_offset[2]+= ((slice_height + 1) / 2) * ((stride + 1) / 2);
924 // crop_top/crop_left divided by two
925 // because one byte of the U/V planes
926 // corresponds to two pixels horizontally/vertically
927 m_src_offset[2]+= crop_top / 2 * m_src_stride[2];
928 m_src_offset[2]+= crop_left / 2;
930 m_videobuffer.iLineSize[0] = width; // Y
931 m_videobuffer.iLineSize[1] = (width + 1) /2; // U
932 m_videobuffer.iLineSize[2] = (width + 1) /2; // V
933 m_videobuffer.iLineSize[3] = 0;
935 unsigned int iPixels = width * height;
936 unsigned int iChromaPixels = iPixels/4;
937 m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
938 m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
939 m_videobuffer.data[2] = (uint8_t*)malloc(16 + iChromaPixels);
940 m_videobuffer.data[3] = NULL;
941 m_videobuffer.format = RENDER_FMT_YUV420P;
943 else if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420SemiPlanar
944 || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_QCOM_FormatYUV420SemiPlanar
945 || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar
946 || color_format == CJNIMediaCodecInfoCodecCapabilities::OMX_QCOM_COLOR_FormatYVU420SemiPlanarInterlace)
949 CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420SemiPlanar");
952 m_src_stride[0] = stride;
953 m_src_offset[0] = crop_top * stride;
954 m_src_offset[0]+= crop_left;
957 m_src_stride[1] = stride;
958 // skip over the Y plane
959 m_src_offset[1] = slice_height * stride;
960 m_src_offset[1]+= crop_top * stride;
961 m_src_offset[1]+= crop_left;
963 m_videobuffer.iLineSize[0] = width; // Y
964 m_videobuffer.iLineSize[1] = width; // UV
965 m_videobuffer.iLineSize[2] = 0;
966 m_videobuffer.iLineSize[3] = 0;
968 unsigned int iPixels = width * height;
969 unsigned int iChromaPixels = iPixels;
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] = NULL;
973 m_videobuffer.data[3] = NULL;
974 m_videobuffer.format = RENDER_FMT_NV12;
978 CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Fixme unknown color_format(%d)", color_format);
983 // picture display width/height include the cropping.
984 m_videobuffer.iDisplayWidth = crop_right + 1 - crop_left;
985 m_videobuffer.iDisplayHeight = crop_bottom + 1 - crop_top;
987 // clear any jni exceptions
988 if (xbmc_jnienv()->ExceptionOccurred())
989 xbmc_jnienv()->ExceptionClear();
992 void CDVDVideoCodecAndroidMediaCodec::CallbackInitSurfaceTexture(void *userdata)
994 CDVDVideoCodecAndroidMediaCodec *ctx = static_cast<CDVDVideoCodecAndroidMediaCodec*>(userdata);
995 ctx->InitSurfaceTexture();
998 void CDVDVideoCodecAndroidMediaCodec::InitSurfaceTexture(void)
1003 // We MUST create the GLES texture on the main thread
1004 // to match where the valid GLES context is located.
1005 // It would be nice to move this out of here, we would need
1006 // to create/fetch/create from g_RenderMananger. But g_RenderMananger
1007 // does not know we are using MediaCodec until Configure and we
1008 // we need m_surfaceTexture valid before then. Chicken, meet Egg.
1009 if (g_application.IsCurrentThread())
1011 // localize GLuint so we do not spew gles includes in our header
1014 glGenTextures(1, &texture_id);
1015 glBindTexture( GL_TEXTURE_EXTERNAL_OES, texture_id);
1016 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1017 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1018 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1019 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1020 glBindTexture( GL_TEXTURE_EXTERNAL_OES, 0);
1021 m_textureId = texture_id;
1023 m_surfaceTexture = boost::shared_ptr<CJNISurfaceTexture>(new CJNISurfaceTexture(m_textureId));
1024 // hook the surfaceTexture OnFrameAvailable callback
1025 m_frameAvailable = boost::shared_ptr<CDVDMediaCodecOnFrameAvailable>(new CDVDMediaCodecOnFrameAvailable(m_surfaceTexture));
1026 m_surface = new CJNISurface(*m_surfaceTexture);
1030 ThreadMessageCallback callbackData;
1031 callbackData.callback = &CallbackInitSurfaceTexture;
1032 callbackData.userptr = (void*)this;
1035 msg.dwMessage = TMSG_CALLBACK;
1036 msg.lpVoid = (void*)&callbackData;
1039 CApplicationMessenger::Get().SendMessage(msg, true);
1045 void CDVDVideoCodecAndroidMediaCodec::ReleaseSurfaceTexture(void)
1050 // it is safe to delete here even though these items
1051 // were created in the main thread instance
1052 SAFE_DELETE(m_surface);
1053 m_frameAvailable.reset();
1054 m_surfaceTexture.reset();
1056 if (m_textureId > 0)
1058 GLuint texture_id = m_textureId;
1059 glDeleteTextures(1, &texture_id);