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