Merge pull request #4099 from vkosh/configure-git
[vuplus_xbmc] / xbmc / cores / dvdplayer / DVDCodecs / Video / DVDVideoCodecAndroidMediaCodec.cpp
1 /*
2  *      Copyright (C) 2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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
19  *
20  */
21
22 // http://developer.android.com/reference/android/media/MediaCodec.html
23 //
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.
27 //
28
29 #include "DVDVideoCodecAndroidMediaCodec.h"
30
31 #include "Application.h"
32 #include "ApplicationMessenger.h"
33 #include "DVDClock.h"
34 #include "threads/Atomics.h"
35 #include "utils/BitstreamConverter.h"
36 #include "utils/CPUInfo.h"
37 #include "utils/log.h"
38
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"
48
49 #include <GLES2/gl2.h>
50 #include <GLES2/gl2ext.h>
51
52 static bool CanSurfaceRenderWhiteList(const std::string &name)
53 {
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[] = {
58     "OMX.Nvidia",
59     "OMX.rk",
60     "OMX.qcom",
61     "OMX.Intel",
62     NULL
63   };
64   for (const char **ptr = cansurfacerender_decoders; *ptr; ptr++)
65   {
66     if (!strnicmp(*ptr, name.c_str(), strlen(*ptr)))
67       return true;
68   }
69   return false;
70 }
71
72 static bool IsBlacklisted(const std::string &name)
73 {
74   static const char *blacklisted_decoders[] = {
75     // No software decoders
76     "OMX.google",
77     NULL
78   };
79   for (const char **ptr = blacklisted_decoders; *ptr; ptr++)
80   {
81     if (!strnicmp(*ptr, name.c_str(), strlen(*ptr)))
82       return true;
83   }
84   return false;
85 }
86
87 static bool IsSupportedColorFormat(int color_format)
88 {
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,
95     -1
96   };
97   for (const int *ptr = supported_colorformats; *ptr != -1; ptr++)
98   {
99     if (color_format == *ptr)
100       return true;
101   }
102   return false;
103 }
104
105 /*****************************************************************************/
106 /*****************************************************************************/
107 class CNULL_Listener : public CJNISurfaceTextureOnFrameAvailableListener
108 {
109 public:
110   CNULL_Listener() : CJNISurfaceTextureOnFrameAvailableListener(jni::jhobject(NULL)) {};
111
112 protected:
113   virtual void OnFrameAvailable(CJNISurfaceTexture &surface) {};
114 };
115
116 class CDVDMediaCodecOnFrameAvailable : public CEvent, CJNISurfaceTextureOnFrameAvailableListener
117 {
118 public:
119   CDVDMediaCodecOnFrameAvailable(boost::shared_ptr<CJNISurfaceTexture> &surfaceTexture)
120   : m_surfaceTexture(surfaceTexture)
121   {
122     m_surfaceTexture->setOnFrameAvailableListener(*this);
123   }
124
125   virtual ~CDVDMediaCodecOnFrameAvailable()
126   {
127     // unhook the callback
128     CNULL_Listener null_listener;
129     m_surfaceTexture->setOnFrameAvailableListener(null_listener);
130   }
131
132 protected:
133   virtual void OnFrameAvailable(CJNISurfaceTexture &surface)
134   {
135     Set();
136   }
137
138 private:
139   boost::shared_ptr<CJNISurfaceTexture> m_surfaceTexture;
140
141 };
142
143 /*****************************************************************************/
144 /*****************************************************************************/
145 CDVDMediaCodecInfo::CDVDMediaCodecInfo(
146     int index
147   , unsigned int texture
148   , boost::shared_ptr<CJNIMediaCodec> &codec
149   , boost::shared_ptr<CJNISurfaceTexture> &surfacetexture
150   , boost::shared_ptr<CDVDMediaCodecOnFrameAvailable> &frameready
151 )
152 : m_refs(1)
153 , m_valid(true)
154 , m_index(index)
155 , m_texture(texture)
156 , m_timestamp(0)
157 , m_codec(codec)
158 , m_surfacetexture(surfacetexture)
159 , m_frameready(frameready)
160 {
161   // paranoid checks
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);
167 }
168
169 CDVDMediaCodecInfo::~CDVDMediaCodecInfo()
170 {
171   assert(m_refs == 0);
172 }
173
174 CDVDMediaCodecInfo* CDVDMediaCodecInfo::Retain()
175 {
176   AtomicIncrement(&m_refs);
177
178   return this;
179 }
180
181 long CDVDMediaCodecInfo::Release()
182 {
183   long count = AtomicDecrement(&m_refs);
184   if (count == 0)
185   {
186     ReleaseOutputBuffer(false);
187     delete this;
188   }
189
190   return count;
191 }
192
193 void CDVDMediaCodecInfo::Validate(bool state)
194 {
195   CSingleLock lock(m_section);
196
197   m_valid = state;
198 }
199
200 void CDVDMediaCodecInfo::ReleaseOutputBuffer(bool render)
201 {
202   CSingleLock lock(m_section);
203
204   if (!m_valid)
205     return;
206
207   // release OutputBuffer and render if indicated
208   // then wait for rendered frame to become avaliable.
209
210   if (render)
211     m_frameready->Reset();
212
213   m_codec->releaseOutputBuffer(m_index, render);
214
215   if (xbmc_jnienv()->ExceptionOccurred())
216   {
217     CLog::Log(LOGERROR, "CDVDMediaCodecInfo::ReleaseOutputBuffer "
218       "ExceptionOccurred render(%d)", render);
219     xbmc_jnienv()->ExceptionDescribe();
220     xbmc_jnienv()->ExceptionClear();
221   }
222 }
223
224 int CDVDMediaCodecInfo::GetIndex() const
225 {
226   CSingleLock lock(m_section);
227
228   return m_index;
229 }
230
231 int CDVDMediaCodecInfo::GetTextureID() const
232 {
233   // since m_texture never changes,
234   // we do not need a m_section lock here.
235   return m_texture;
236 }
237
238 void CDVDMediaCodecInfo::GetTransformMatrix(float *textureMatrix)
239 {
240   CSingleLock lock(m_section);
241
242   if (!m_valid)
243     return;
244
245   m_surfacetexture->getTransformMatrix(textureMatrix);
246 }
247
248 void CDVDMediaCodecInfo::UpdateTexImage()
249 {
250   CSingleLock lock(m_section);
251
252   if (!m_valid)
253     return;
254
255   // updateTexImage will check and spew any prior gl errors,
256   // clear them before we call updateTexImage.
257   glGetError();
258
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);
267
268   m_surfacetexture->updateTexImage();
269   if (xbmc_jnienv()->ExceptionOccurred())
270   {
271     CLog::Log(LOGERROR, "CDVDMediaCodecInfo::UpdateTexImage updateTexImage:ExceptionOccurred");
272     xbmc_jnienv()->ExceptionDescribe();
273     xbmc_jnienv()->ExceptionClear();
274   }
275
276   m_timestamp = m_surfacetexture->getTimestamp();
277   if (xbmc_jnienv()->ExceptionOccurred())
278   {
279     CLog::Log(LOGERROR, "CDVDMediaCodecInfo::UpdateTexImage getTimestamp:ExceptionOccurred");
280     xbmc_jnienv()->ExceptionDescribe();
281     xbmc_jnienv()->ExceptionClear();
282   }
283 }
284
285 /*****************************************************************************/
286 /*****************************************************************************/
287 CDVDVideoCodecAndroidMediaCodec::CDVDVideoCodecAndroidMediaCodec()
288 : m_formatname("mediacodec")
289 , m_opened(false)
290 , m_surface(NULL)
291 , m_textureId(0)
292 , m_bitstream(NULL)
293 , m_render_sw(false)
294 {
295   memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
296 }
297
298 CDVDVideoCodecAndroidMediaCodec::~CDVDVideoCodecAndroidMediaCodec()
299 {
300   Dispose();
301 }
302
303 bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
304 {
305   // check for 4.1 Jellybean and above.
306   if (CAndroidFeatures::GetVersion() < 16)
307     return false;
308
309   m_drop = false;
310   m_hints = hints;
311
312   switch(m_hints.codec)
313   {
314     case AV_CODEC_ID_MPEG2VIDEO:
315       m_mime = "video/mpeg2";
316       m_formatname = "amc-mpeg2";
317       break;
318     case AV_CODEC_ID_MPEG4:
319       m_mime = "video/mp4v-es";
320       m_formatname = "amc-mpeg4";
321       break;
322     case AV_CODEC_ID_H263:
323       m_mime = "video/3gpp";
324       m_formatname = "amc-h263";
325       break;
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";
334       break;
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)
342       {
343         m_bitstream = new CBitstreamConverter;
344         if (!m_bitstream->Open(m_hints.codec, (uint8_t*)m_hints.extradata, m_hints.extrasize, true))
345         {
346           SAFE_DELETE(m_bitstream);
347           return false;
348         }
349       }
350       break;
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";
356       break;
357     default:
358       CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Unknown hints.codec(%d)", hints.codec);
359       return false;
360       break;
361   }
362
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++)
369   {
370     CJNIMediaCodecInfo codec_info = CJNIMediaCodecList::getCodecInfoAt(i);
371     if (codec_info.isEncoder())
372       continue;
373     m_codecname = codec_info.getName();
374     if (IsBlacklisted(m_codecname))
375       continue;
376
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)
380     {
381       if (types[j] == m_mime)
382       {
383         m_codec = boost::shared_ptr<CJNIMediaCodec>(new CJNIMediaCodec(CJNIMediaCodec::createByCodecName(m_codecname)));
384
385         CJNIMediaCodecInfoCodecCapabilities codec_caps = codec_info.getCapabilitiesForType(m_mime);
386         std::vector<int> color_formats = codec_caps.colorFormats();
387
388         // clear any jni exceptions, jni gets upset if we do not.
389         if (xbmc_jnienv()->ExceptionOccurred())
390         {
391           CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open ExceptionOccurred");
392           xbmc_jnienv()->ExceptionClear();
393           m_codec.reset();
394           continue;
395         }
396         hasSupportedColorFormat = false;
397         for (size_t k = 0; k < color_formats.size(); ++k)
398         {
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;
403         }
404         break;
405       }
406     }
407     if (m_codec)
408       break;
409   }
410   if (!m_codec)
411   {
412     CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Failed to create Android MediaCodec");
413     SAFE_DELETE(m_bitstream);
414     return false;
415   }
416
417   // whitelist of devices that can surface render.
418   m_render_sw = !CanSurfaceRenderWhiteList(m_codecname);
419   if (m_render_sw)
420   {
421     if (!hasSupportedColorFormat)
422     {
423       CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: No supported color format");
424       m_codec.reset();
425       SAFE_DELETE(m_bitstream);
426       return false;
427     }
428   }
429
430   if (!ConfigureMediaCodec())
431   {
432     m_codec.reset();
433     SAFE_DELETE(m_bitstream);
434     return false;
435   }
436
437   // setup a YUV420P DVDVideoPicture buffer.
438   // first make sure all properties are reset.
439   memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
440
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;
451
452   CLog::Log(LOGINFO, "CDVDVideoCodecAndroidMediaCodec:: "
453     "Open Android MediaCodec %s", m_codecname.c_str());
454
455   m_opened = true;
456
457   return m_opened;
458 }
459
460 void CDVDVideoCodecAndroidMediaCodec::Dispose()
461 {
462   m_opened = false;
463
464   // release any retained demux packets
465   while (!m_demux.empty())
466   {
467     amc_demux &demux_pkt = m_demux.front();
468     free(demux_pkt.pData);
469     m_demux.pop();
470   }
471
472   // invalidate any inflight outputbuffers, make sure
473   // m_output is empty so we do not create new ones
474   m_input.clear();
475   m_output.clear();
476   FlushInternal();
477
478   // clear m_videobuffer bits
479   if (m_render_sw)
480   {
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;
484   }
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;
489
490   if (m_codec)
491   {
492     m_codec->stop();
493     m_codec->release();
494     m_codec.reset();
495   }
496   ReleaseSurfaceTexture();
497
498   SAFE_DELETE(m_bitstream);
499 }
500
501 int CDVDVideoCodecAndroidMediaCodec::Decode(uint8_t *pData, int iSize, double dts, double pts)
502 {
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.
506   int rtn = VC_BUFFER;
507
508   if (!m_opened)
509     return rtn;
510
511   if (m_hints.ptsinvalid)
512     pts = DVD_NOPTS_VALUE;
513
514   // must check for an output picture 1st,
515   // otherwise, mediacodec can stall on some devices.
516   if (GetOutputPicture() > 0)
517     rtn |= VC_PICTURE;
518
519   if (pData)
520   {
521     if (m_bitstream)
522     {
523       m_bitstream->Convert(pData, iSize);
524       iSize = m_bitstream->GetConvertSize();
525       pData = m_bitstream->GetConvertBuffer();
526     }
527
528     // queue demux pkt in case we cannot get an input buffer
529     amc_demux demux_pkt;
530     demux_pkt.dts = dts;
531     demux_pkt.pts = pts;
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);
536
537     // try to fetch an input buffer
538     int64_t timeout_us = 5000;
539     int index = m_codec->dequeueInputBuffer(timeout_us);
540     if (index >= 0)
541     {
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.
545       if (m_input.empty())
546         m_input = m_codec->getInputBuffers();
547
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)
553       {
554         CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode, iSize(%d) > size(%d)", iSize, size);
555         demux_pkt.iSize = size;
556       }
557       // fetch a pointer to the ByteBuffer backing store
558       void *dst_ptr = xbmc_jnienv()->GetDirectBufferAddress(m_input[index].get_raw());
559       if (dst_ptr)
560         memcpy(dst_ptr, demux_pkt.pData, demux_pkt.iSize);
561
562       free(demux_pkt.pData);
563       m_demux.pop();
564
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;
575 /*
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);
579 */
580       int flags = 0;
581       int offset = 0;
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())
585       {
586         CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode ExceptionOccurred");
587         xbmc_jnienv()->ExceptionClear();
588       }
589     }
590   }
591
592   return rtn;
593 }
594
595 void CDVDVideoCodecAndroidMediaCodec::Reset()
596 {
597   if (!m_opened)
598     return;
599
600   // dump any pending demux packets
601   while (!m_demux.empty())
602   {
603     amc_demux &demux_pkt = m_demux.front();
604     free(demux_pkt.pData);
605     m_demux.pop();
606   }
607
608   if (m_codec)
609   {
610     // flush all outputbuffers inflight, they will
611     // become invalid on m_codec->flush and generate
612     // a spew of java exceptions if used
613     FlushInternal();
614
615     // now we can flush the actual MediaCodec object
616     m_codec->flush();
617     if (xbmc_jnienv()->ExceptionOccurred())
618     {
619       CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Reset ExceptionOccurred");
620       xbmc_jnienv()->ExceptionClear();
621     }
622
623     // Invalidate our local DVDVideoPicture bits
624     m_videobuffer.pts = DVD_NOPTS_VALUE;
625     if (!m_render_sw)
626       m_videobuffer.mediacodec = NULL;
627   }
628 }
629
630 bool CDVDVideoCodecAndroidMediaCodec::GetPicture(DVDVideoPicture* pDvdVideoPicture)
631 {
632   if (!m_opened)
633     return false;
634
635   *pDvdVideoPicture = m_videobuffer;
636
637   // Invalidate our local DVDVideoPicture bits
638   m_videobuffer.pts = DVD_NOPTS_VALUE;
639   if (!m_render_sw)
640     m_videobuffer.mediacodec = NULL;
641
642   return true;
643 }
644
645 bool CDVDVideoCodecAndroidMediaCodec::ClearPicture(DVDVideoPicture* pDvdVideoPicture)
646 {
647   if (pDvdVideoPicture->format == RENDER_FMT_MEDIACODEC)
648     SAFE_RELEASE(pDvdVideoPicture->mediacodec);
649   memset(pDvdVideoPicture, 0x00, sizeof(DVDVideoPicture));
650
651   return true;
652 }
653
654 void CDVDVideoCodecAndroidMediaCodec::SetDropState(bool bDrop)
655 {
656   m_drop = bDrop;
657   if (m_drop)
658     m_videobuffer.iFlags |=  DVP_FLAG_DROPPED;
659   else
660     m_videobuffer.iFlags &= ~DVP_FLAG_DROPPED;
661 }
662
663 int CDVDVideoCodecAndroidMediaCodec::GetDataSize(void)
664 {
665   // just ignore internal buffering contribution.
666   return 0;
667 }
668
669 double CDVDVideoCodecAndroidMediaCodec::GetTimeSize(void)
670 {
671   // just ignore internal buffering contribution.
672   return 0.0;
673 }
674
675 unsigned CDVDVideoCodecAndroidMediaCodec::GetAllowedReferences()
676 {
677   return 3;
678 }
679
680 void CDVDVideoCodecAndroidMediaCodec::FlushInternal()
681 {
682   // invalidate any existing inflight buffers and create
683   // new ones to match the number of output buffers
684
685   if (m_render_sw)
686     return;
687
688   for (size_t i = 0; i < m_inflight.size(); i++)
689     m_inflight[i]->Validate(false);
690   m_inflight.clear();
691
692   for (size_t i = 0; i < m_output.size(); i++)
693   {
694     m_inflight.push_back(
695       new CDVDMediaCodecInfo(i, m_textureId, m_codec, m_surfaceTexture, m_frameAvailable)
696     );
697   }
698 }
699
700 bool CDVDVideoCodecAndroidMediaCodec::ConfigureMediaCodec(void)
701 {
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);
708
709   // handle codec extradata
710   if (m_hints.extrasize)
711   {
712     size_t size = m_hints.extrasize;
713     void  *src_ptr = m_hints.extradata;
714     if (m_bitstream)
715     {
716       size = m_bitstream->GetExtraSize();
717       src_ptr = m_bitstream->GetExtraData();
718     }
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);
728   }
729
730   InitSurfaceTexture();
731
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.
736   int flags = 0;
737   CJNIMediaCrypto crypto(jni::jhobject(NULL));
738   // our jni gets upset if we do this a different
739   // way, do not mess with it.
740   if (m_render_sw)
741   {
742     CJNISurface surface(jni::jhobject(NULL));
743     m_codec->configure(mediaformat, surface, crypto, flags);
744   }
745   else
746   {
747     m_codec->configure(mediaformat, *m_surface, crypto, flags);
748   }
749   // always, check/clear jni exceptions.
750   if (xbmc_jnienv()->ExceptionOccurred())
751   {
752     xbmc_jnienv()->ExceptionClear();
753     return false;
754   }
755
756
757   m_codec->start();
758   // always, check/clear jni exceptions.
759   if (xbmc_jnienv()->ExceptionOccurred())
760   {
761     xbmc_jnienv()->ExceptionClear();
762     return false;
763   }
764
765   return true;
766 }
767
768 int CDVDVideoCodecAndroidMediaCodec::GetOutputPicture(void)
769 {
770   int rtn = 0;
771
772   int64_t timeout_us = 5000;
773   CJNIMediaCodecBufferInfo bufferInfo;
774   int index = m_codec->dequeueOutputBuffer(bufferInfo, timeout_us);
775   if (index >= 0)
776   {
777     if (m_drop)
778     {
779       m_codec->releaseOutputBuffer(index, false);
780       if (xbmc_jnienv()->ExceptionOccurred())
781         xbmc_jnienv()->ExceptionClear();
782       return 0;
783     }
784
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())
789     {
790       m_output = m_codec->getOutputBuffers();
791       FlushInternal();
792     }
793
794     int flags = bufferInfo.flags();
795     if (flags & CJNIMediaCodec::BUFFER_FLAG_SYNC_FRAME)
796       CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_SYNC_FRAME");
797
798     if (flags & CJNIMediaCodec::BUFFER_FLAG_CODEC_CONFIG)
799       CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_CODEC_CONFIG");
800
801     if (flags & CJNIMediaCodec::BUFFER_FLAG_END_OF_STREAM)
802     {
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();
807       return 0;
808     }
809
810     if (!m_render_sw)
811     {
812       m_videobuffer.mediacodec = m_inflight[index]->Retain();
813       m_videobuffer.mediacodec->Validate(true);
814     }
815     else
816     {
817       int size = bufferInfo.size();
818       int offset = bufferInfo.offset();
819
820       if (!m_output[index].isDirect())
821         CLog::Log(LOGWARNING, "CDVDVideoCodecAndroidMediaCodec:: m_output[index].isDirect == false");
822
823       if (size && m_output[index].capacity())
824       {
825         uint8_t *src_ptr = (uint8_t*)xbmc_jnienv()->GetDirectBufferAddress(m_output[index].get_raw());
826         src_ptr += offset;
827
828         int loop_end = 0;
829         if (m_videobuffer.format == RENDER_FMT_NV12)
830           loop_end = 2;
831         else if (m_videobuffer.format == RENDER_FMT_YUV420P)
832           loop_end = 3;
833
834         for (int i = 0; i < loop_end; i++)
835         {
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];
840
841           int height = m_videobuffer.iHeight;
842           if (i > 0)
843             height = (m_videobuffer.iHeight + 1) / 2;
844
845           if (src_stride == dst_stride)
846             memcpy(dst, src, dst_stride * height);
847           else
848             for (int j = 0; j < height; j++, src += src_stride, dst += dst_stride)
849               memcpy(dst, src, dst_stride);
850         }
851       }
852       m_codec->releaseOutputBuffer(index, false);
853     }
854
855     int64_t pts= bufferInfo.presentationTimeUs();
856     m_videobuffer.dts = DVD_NOPTS_VALUE;
857     m_videobuffer.pts = DVD_NOPTS_VALUE;
858     if (pts != AV_NOPTS_VALUE)
859       m_videobuffer.pts = pts;
860
861 /*
862     CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture "
863       "index(%d), pts(%f)", index, m_videobuffer.pts);
864 */
865     // always, check/clear jni exceptions.
866     if (xbmc_jnienv()->ExceptionOccurred())
867       xbmc_jnienv()->ExceptionClear();
868
869     rtn = 1;
870   }
871   else if (index == CJNIMediaCodec::INFO_OUTPUT_BUFFERS_CHANGED)
872   {
873     m_output = m_codec->getOutputBuffers();
874     FlushInternal();
875   }
876   else if (index == CJNIMediaCodec::INFO_OUTPUT_FORMAT_CHANGED)
877   {
878     OutputFormatChanged();
879   }
880   else if (index == CJNIMediaCodec::INFO_TRY_AGAIN_LATER)
881   {
882     // normal dequeueOutputBuffer timeout, ignore it.
883     rtn = -1;
884   }
885   else
886   {
887     // we should never get here
888     CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture unknown index(%d)", index);
889   }
890
891   return rtn;
892 }
893
894 void CDVDVideoCodecAndroidMediaCodec::OutputFormatChanged(void)
895 {
896   CJNIMediaFormat mediaformat = m_codec->getOutputFormat();
897
898   int width       = mediaformat.getInteger("width");
899   int height      = mediaformat.getInteger("height");
900   int stride      = mediaformat.getInteger("stride");
901   int slice_height= mediaformat.getInteger("slice-height");
902   int color_format= mediaformat.getInteger("color-format");
903   int crop_left   = mediaformat.getInteger("crop-left");
904   int crop_top    = mediaformat.getInteger("crop-top");
905   int crop_right  = mediaformat.getInteger("crop-right");
906   int crop_bottom = mediaformat.getInteger("crop-bottom");
907
908   CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
909     "width(%d), height(%d), stride(%d), slice-height(%d), color-format(%d)",
910     width, height, stride, slice_height, color_format);
911   CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
912     "crop-left(%d), crop-top(%d), crop-right(%d), crop-bottom(%d)",
913     crop_left, crop_top, crop_right, crop_bottom);
914
915   if (!m_render_sw)
916   {
917     CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Direct Surface Rendering");
918     m_videobuffer.format = RENDER_FMT_MEDIACODEC;
919   }
920   else
921   {
922     // Android device quirks and fixes
923
924     // Samsung Quirk: ignore width/height/stride/slice: http://code.google.com/p/android/issues/detail?id=37768#c3
925     if (strstr(m_codecname.c_str(), "OMX.SEC.avc.dec") != NULL || strstr(m_codecname.c_str(), "OMX.SEC.avcdec") != NULL)
926     {
927       width = stride = m_hints.width;
928       height = slice_height = m_hints.height;
929     }
930     if (stride <= width)
931         stride = width;
932     if (slice_height <= height)
933     {
934       slice_height = height;
935       if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
936       {
937         // NVidia Tegra 3 on Nexus 7 does not set slice_heights
938         if (strstr(m_codecname.c_str(), "OMX.Nvidia.") != NULL)
939         {
940           slice_height = (((height) + 15) & ~15);
941           CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: NVidia Tegra 3 quirk, slice_height(%d)", slice_height);
942         }
943       }
944     }
945     if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar)
946     {
947       slice_height -= crop_top / 2;
948       // set crop top/left here, since the offset parameter already includes this.
949       // if we would ignore the offset parameter in the BufferInfo, we could just keep
950       // the original slice height and apply the top/left cropping instead.
951       crop_top = 0;
952       crop_left = 0;
953     }
954
955     // default picture format to none
956     for (int i = 0; i < 4; i++)
957       m_src_offset[i] = m_src_stride[i] = 0;
958     // delete any existing buffers
959     for (int i = 0; i < 4; i++)
960       free(m_videobuffer.data[i]);
961
962     // setup picture format and data offset vectors
963     if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
964     {
965       CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420Planar");
966
967       // Y plane
968       m_src_stride[0] = stride;
969       m_src_offset[0] = crop_top * stride;
970       m_src_offset[0]+= crop_left;
971
972       // U plane
973       m_src_stride[1] = (stride + 1) / 2;
974       //  skip over the Y plane
975       m_src_offset[1] = slice_height * stride;
976       //  crop_top/crop_left divided by two
977       //  because one byte of the U/V planes
978       //  corresponds to two pixels horizontally/vertically
979       m_src_offset[1]+= crop_top  / 2 * m_src_stride[1];
980       m_src_offset[1]+= crop_left / 2;
981
982       // V plane
983       m_src_stride[2] = (stride + 1) / 2;
984       //  skip over the Y plane
985       m_src_offset[2] = slice_height * stride;
986       //  skip over the U plane
987       m_src_offset[2]+= ((slice_height + 1) / 2) * ((stride + 1) / 2);
988       //  crop_top/crop_left divided by two
989       //  because one byte of the U/V planes
990       //  corresponds to two pixels horizontally/vertically
991       m_src_offset[2]+= crop_top  / 2 * m_src_stride[2];
992       m_src_offset[2]+= crop_left / 2;
993
994       m_videobuffer.iLineSize[0] =  width;         // Y
995       m_videobuffer.iLineSize[1] = (width + 1) /2; // U
996       m_videobuffer.iLineSize[2] = (width + 1) /2; // V
997       m_videobuffer.iLineSize[3] = 0;
998
999       unsigned int iPixels = width * height;
1000       unsigned int iChromaPixels = iPixels/4;
1001       m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
1002       m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
1003       m_videobuffer.data[2] = (uint8_t*)malloc(16 + iChromaPixels);
1004       m_videobuffer.data[3] = NULL;
1005       m_videobuffer.format  = RENDER_FMT_YUV420P;
1006     }
1007     else if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420SemiPlanar
1008           || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_QCOM_FormatYUV420SemiPlanar
1009           || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar
1010           || color_format == CJNIMediaCodecInfoCodecCapabilities::OMX_QCOM_COLOR_FormatYVU420SemiPlanarInterlace)
1011
1012     {
1013       CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420SemiPlanar");
1014
1015       // Y plane
1016       m_src_stride[0] = stride;
1017       m_src_offset[0] = crop_top * stride;
1018       m_src_offset[0]+= crop_left;
1019
1020       // UV plane
1021       m_src_stride[1] = stride;
1022       //  skip over the Y plane
1023       m_src_offset[1] = slice_height * stride;
1024       m_src_offset[1]+= crop_top * stride;
1025       m_src_offset[1]+= crop_left;
1026
1027       m_videobuffer.iLineSize[0] = width;  // Y
1028       m_videobuffer.iLineSize[1] = width;  // UV
1029       m_videobuffer.iLineSize[2] = 0;
1030       m_videobuffer.iLineSize[3] = 0;
1031
1032       unsigned int iPixels = width * height;
1033       unsigned int iChromaPixels = iPixels;
1034       m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
1035       m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
1036       m_videobuffer.data[2] = NULL;
1037       m_videobuffer.data[3] = NULL;
1038       m_videobuffer.format  = RENDER_FMT_NV12;
1039     }
1040     else
1041     {
1042       CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Fixme unknown color_format(%d)", color_format);
1043       return;
1044     }
1045   }
1046
1047   // picture display width/height include the cropping.
1048   m_videobuffer.iDisplayWidth  = crop_right  + 1 - crop_left;
1049   m_videobuffer.iDisplayHeight = crop_bottom + 1 - crop_top;
1050
1051   // clear any jni exceptions
1052   if (xbmc_jnienv()->ExceptionOccurred())
1053     xbmc_jnienv()->ExceptionClear();
1054 }
1055
1056 void CDVDVideoCodecAndroidMediaCodec::CallbackInitSurfaceTexture(void *userdata)
1057 {
1058   CDVDVideoCodecAndroidMediaCodec *ctx = static_cast<CDVDVideoCodecAndroidMediaCodec*>(userdata);
1059   ctx->InitSurfaceTexture();
1060 }
1061
1062 void CDVDVideoCodecAndroidMediaCodec::InitSurfaceTexture(void)
1063 {
1064   if (m_render_sw)
1065     return;
1066
1067   // We MUST create the GLES texture on the main thread
1068   // to match where the valid GLES context is located.
1069   // It would be nice to move this out of here, we would need
1070   // to create/fetch/create from g_RenderMananger. But g_RenderMananger
1071   // does not know we are using MediaCodec until Configure and we
1072   // we need m_surfaceTexture valid before then. Chicken, meet Egg.
1073   if (g_application.IsCurrentThread())
1074   {
1075     // localize GLuint so we do not spew gles includes in our header
1076     GLuint texture_id;
1077
1078     glGenTextures(1, &texture_id);
1079     glBindTexture(  GL_TEXTURE_EXTERNAL_OES, texture_id);
1080     glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1081     glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1082     glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1083     glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1084     glBindTexture(  GL_TEXTURE_EXTERNAL_OES, 0);
1085     m_textureId = texture_id;
1086
1087     m_surfaceTexture = boost::shared_ptr<CJNISurfaceTexture>(new CJNISurfaceTexture(m_textureId));
1088     // hook the surfaceTexture OnFrameAvailable callback
1089     m_frameAvailable = boost::shared_ptr<CDVDMediaCodecOnFrameAvailable>(new CDVDMediaCodecOnFrameAvailable(m_surfaceTexture));
1090     m_surface = new CJNISurface(*m_surfaceTexture);
1091   }
1092   else
1093   {
1094     ThreadMessageCallback callbackData;
1095     callbackData.callback = &CallbackInitSurfaceTexture;
1096     callbackData.userptr  = (void*)this;
1097
1098     ThreadMessage msg;
1099     msg.dwMessage = TMSG_CALLBACK;
1100     msg.lpVoid = (void*)&callbackData;
1101
1102     // wait for it.
1103     CApplicationMessenger::Get().SendMessage(msg, true);
1104   }
1105
1106   return;
1107 }
1108
1109 void CDVDVideoCodecAndroidMediaCodec::ReleaseSurfaceTexture(void)
1110 {
1111   if (m_render_sw)
1112     return;
1113
1114   // it is safe to delete here even though these items
1115   // were created in the main thread instance
1116   SAFE_DELETE(m_surface);
1117   m_frameAvailable.reset();
1118   m_surfaceTexture.reset();
1119
1120   if (m_textureId > 0)
1121   {
1122     GLuint texture_id = m_textureId;
1123     glDeleteTextures(1, &texture_id);
1124     m_textureId = 0;
1125   }
1126 }