2e7d7fb853ff7e52afb9319090c10d20badf7c74
[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   ConfigureMediaCodec();
430
431   // setup a YUV420P DVDVideoPicture buffer.
432   // first make sure all properties are reset.
433   memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
434
435   m_videobuffer.dts = DVD_NOPTS_VALUE;
436   m_videobuffer.pts = DVD_NOPTS_VALUE;
437   m_videobuffer.color_range  = 0;
438   m_videobuffer.color_matrix = 4;
439   m_videobuffer.iFlags  = DVP_FLAG_ALLOCATED;
440   m_videobuffer.iWidth  = m_hints.width;
441   m_videobuffer.iHeight = m_hints.height;
442   // these will get reset to crop values later
443   m_videobuffer.iDisplayWidth  = m_hints.width;
444   m_videobuffer.iDisplayHeight = m_hints.height;
445
446   CLog::Log(LOGINFO, "CDVDVideoCodecAndroidMediaCodec:: "
447     "Open Android MediaCodec %s", m_codecname.c_str());
448
449   m_opened = true;
450
451   return m_opened;
452 }
453
454 void CDVDVideoCodecAndroidMediaCodec::Dispose()
455 {
456   m_opened = false;
457
458   // release any retained demux packets
459   while (!m_demux.empty())
460   {
461     amc_demux &demux_pkt = m_demux.front();
462     free(demux_pkt.pData);
463     m_demux.pop();
464   }
465
466   // invalidate any inflight outputbuffers, make sure
467   // m_output is empty so we do not create new ones
468   m_input.clear();
469   m_output.clear();
470   FlushInternal();
471
472   // clear m_videobuffer bits
473   if (m_render_sw)
474   {
475     free(m_videobuffer.data[0]), m_videobuffer.data[0] = NULL;
476     free(m_videobuffer.data[1]), m_videobuffer.data[1] = NULL;
477     free(m_videobuffer.data[2]), m_videobuffer.data[2] = NULL;
478   }
479   m_videobuffer.iFlags = 0;
480   // m_videobuffer.mediacodec is unioned with m_videobuffer.data[0]
481   // so be very careful when and how you touch it.
482   m_videobuffer.mediacodec = NULL;
483
484   if (m_codec)
485   {
486     m_codec->stop();
487     m_codec->release();
488     m_codec.reset();
489   }
490   ReleaseSurfaceTexture();
491
492   SAFE_DELETE(m_bitstream);
493 }
494
495 int CDVDVideoCodecAndroidMediaCodec::Decode(uint8_t *pData, int iSize, double dts, double pts)
496 {
497   // Handle input, add demuxer packet to input queue, we must accept it or
498   // it will be discarded as DVDPlayerVideo has no concept of "try again".
499   // we must return VC_BUFFER or VC_PICTURE, default to VC_BUFFER.
500   int rtn = VC_BUFFER;
501
502   if (!m_opened)
503     return rtn;
504
505   if (m_hints.ptsinvalid)
506     pts = DVD_NOPTS_VALUE;
507
508   // must check for an output picture 1st,
509   // otherwise, mediacodec can stall on some devices.
510   if (GetOutputPicture() > 0)
511     rtn |= VC_PICTURE;
512
513   if (pData)
514   {
515     if (m_bitstream)
516     {
517       m_bitstream->Convert(pData, iSize);
518       iSize = m_bitstream->GetConvertSize();
519       pData = m_bitstream->GetConvertBuffer();
520     }
521
522     // queue demux pkt in case we cannot get an input buffer
523     amc_demux demux_pkt;
524     demux_pkt.dts = dts;
525     demux_pkt.pts = pts;
526     demux_pkt.iSize = iSize;
527     demux_pkt.pData = (uint8_t*)malloc(iSize);
528     memcpy(demux_pkt.pData, pData, iSize);
529     m_demux.push(demux_pkt);
530
531     // try to fetch an input buffer
532     int64_t timeout_us = 5000;
533     int index = m_codec->dequeueInputBuffer(timeout_us);
534     if (index >= 0)
535     {
536       // docs lie, getInputBuffers should be good after
537       // m_codec->start() but the internal refs are not
538       // setup until much later on some devices.
539       if (m_input.empty())
540         m_input = m_codec->getInputBuffers();
541
542       // we have an input buffer, fill it.
543       int size = m_input[index].capacity();
544       // fetch the front demux packet
545       amc_demux &demux_pkt = m_demux.front();
546       if (demux_pkt.iSize > size)
547       {
548         CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode, iSize(%d) > size(%d)", iSize, size);
549         demux_pkt.iSize = size;
550       }
551       // fetch a pointer to the ByteBuffer backing store
552       void *dst_ptr = xbmc_jnienv()->GetDirectBufferAddress(m_input[index].get_raw());
553       if (dst_ptr)
554         memcpy(dst_ptr, demux_pkt.pData, demux_pkt.iSize);
555
556       free(demux_pkt.pData);
557       m_demux.pop();
558
559       // Translate from dvdplayer dts/pts to MediaCodec pts,
560       // pts WILL get re-ordered by MediaCodec if needed.
561       // Do not try to pass pts as a unioned double/int64_t,
562       // some android devices will diddle with presentationTimeUs
563       // and you will get NaN back and DVDPlayerVideo will barf.
564       int64_t presentationTimeUs = AV_NOPTS_VALUE;
565       if (demux_pkt.pts != DVD_NOPTS_VALUE)
566         presentationTimeUs = demux_pkt.pts;
567       else if (demux_pkt.dts != DVD_NOPTS_VALUE)
568         presentationTimeUs = demux_pkt.dts;
569 /*
570       CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
571         "pts(%f), ipts(%lld), iSize(%d), GetDataSize(%d), loop_cnt(%d)",
572         presentationTimeUs, pts_dtoi(presentationTimeUs), iSize, GetDataSize(), loop_cnt);
573 */
574       int flags = 0;
575       int offset = 0;
576       m_codec->queueInputBuffer(index, offset, demux_pkt.iSize, presentationTimeUs, flags);
577       // clear any jni exceptions, jni gets upset if we do not.
578       if (xbmc_jnienv()->ExceptionOccurred())
579       {
580         CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode ExceptionOccurred");
581         xbmc_jnienv()->ExceptionClear();
582       }
583     }
584   }
585
586   return rtn;
587 }
588
589 void CDVDVideoCodecAndroidMediaCodec::Reset()
590 {
591   if (!m_opened)
592     return;
593
594   // dump any pending demux packets
595   while (!m_demux.empty())
596   {
597     amc_demux &demux_pkt = m_demux.front();
598     free(demux_pkt.pData);
599     m_demux.pop();
600   }
601
602   if (m_codec)
603   {
604     // flush all outputbuffers inflight, they will
605     // become invalid on m_codec->flush and generate
606     // a spew of java exceptions if used
607     FlushInternal();
608
609     // now we can flush the actual MediaCodec object
610     m_codec->flush();
611     if (xbmc_jnienv()->ExceptionOccurred())
612     {
613       CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Reset ExceptionOccurred");
614       xbmc_jnienv()->ExceptionClear();
615     }
616
617     // Invalidate our local DVDVideoPicture bits
618     m_videobuffer.pts = DVD_NOPTS_VALUE;
619     if (!m_render_sw)
620       m_videobuffer.mediacodec = NULL;
621   }
622 }
623
624 bool CDVDVideoCodecAndroidMediaCodec::GetPicture(DVDVideoPicture* pDvdVideoPicture)
625 {
626   if (!m_opened)
627     return false;
628
629   *pDvdVideoPicture = m_videobuffer;
630
631   // Invalidate our local DVDVideoPicture bits
632   m_videobuffer.pts = DVD_NOPTS_VALUE;
633   if (!m_render_sw)
634     m_videobuffer.mediacodec = NULL;
635
636   return true;
637 }
638
639 bool CDVDVideoCodecAndroidMediaCodec::ClearPicture(DVDVideoPicture* pDvdVideoPicture)
640 {
641   if (pDvdVideoPicture->format == RENDER_FMT_MEDIACODEC)
642     SAFE_RELEASE(pDvdVideoPicture->mediacodec);
643   memset(pDvdVideoPicture, 0x00, sizeof(DVDVideoPicture));
644
645   return true;
646 }
647
648 void CDVDVideoCodecAndroidMediaCodec::SetDropState(bool bDrop)
649 {
650   m_drop = bDrop;
651   if (m_drop)
652     m_videobuffer.iFlags |=  DVP_FLAG_DROPPED;
653   else
654     m_videobuffer.iFlags &= ~DVP_FLAG_DROPPED;
655 }
656
657 int CDVDVideoCodecAndroidMediaCodec::GetDataSize(void)
658 {
659   // just ignore internal buffering contribution.
660   return 0;
661 }
662
663 double CDVDVideoCodecAndroidMediaCodec::GetTimeSize(void)
664 {
665   // just ignore internal buffering contribution.
666   return 0.0;
667 }
668
669 unsigned CDVDVideoCodecAndroidMediaCodec::GetAllowedReferences()
670 {
671   return 3;
672 }
673
674 void CDVDVideoCodecAndroidMediaCodec::FlushInternal()
675 {
676   // invalidate any existing inflight buffers and create
677   // new ones to match the number of output buffers
678
679   if (m_render_sw)
680     return;
681
682   for (size_t i = 0; i < m_inflight.size(); i++)
683     m_inflight[i]->Validate(false);
684   m_inflight.clear();
685
686   for (size_t i = 0; i < m_output.size(); i++)
687   {
688     m_inflight.push_back(
689       new CDVDMediaCodecInfo(i, m_textureId, m_codec, m_surfaceTexture, m_frameAvailable)
690     );
691   }
692 }
693
694 void CDVDVideoCodecAndroidMediaCodec::ConfigureMediaCodec(void)
695 {
696   // setup a MediaFormat to match the video content,
697   // used by codec during configure
698   CJNIMediaFormat mediaformat = CJNIMediaFormat::createVideoFormat(
699     m_mime.c_str(), m_hints.width, m_hints.height);
700   // some android devices forget to default the demux input max size
701   mediaformat.setInteger(CJNIMediaFormat::KEY_MAX_INPUT_SIZE, 0);
702
703   // handle codec extradata
704   if (m_hints.extrasize)
705   {
706     size_t size = m_hints.extrasize;
707     void  *src_ptr = m_hints.extradata;
708     if (m_bitstream)
709     {
710       size = m_bitstream->GetExtraSize();
711       src_ptr = m_bitstream->GetExtraData();
712     }
713     // Allocate a byte buffer via allocateDirect in java instead of NewDirectByteBuffer,
714     // since the latter doesn't allocate storage of its own, and we don't know how long
715     // the codec uses the buffer.
716     CJNIByteBuffer bytebuffer = CJNIByteBuffer::allocateDirect(size);
717     void *dts_ptr = xbmc_jnienv()->GetDirectBufferAddress(bytebuffer.get_raw());
718     memcpy(dts_ptr, src_ptr, size);
719     // codec will automatically handle buffers as extradata
720     // using entries with keys "csd-0", "csd-1", etc.
721     mediaformat.setByteBuffer("csd-0", bytebuffer);
722   }
723
724   InitSurfaceTexture();
725
726   // configure and start the codec.
727   // use the MediaFormat that we have setup.
728   // use a null MediaCrypto, our content is not encrypted.
729   // use a null Surface, we will extract the video picture data manually.
730   int flags = 0;
731   CJNIMediaCrypto crypto(jni::jhobject(NULL));
732   // our jni gets upset if we do this a different
733   // way, do not mess with it.
734   if (m_render_sw)
735   {
736     CJNISurface surface(jni::jhobject(NULL));
737     m_codec->configure(mediaformat, surface, crypto, flags);
738   }
739   else
740   {
741     m_codec->configure(mediaformat, *m_surface, crypto, flags);
742   }
743
744   m_codec->start();
745
746   // always, check/clear jni exceptions.
747   if (xbmc_jnienv()->ExceptionOccurred())
748     xbmc_jnienv()->ExceptionClear();
749 }
750
751 int CDVDVideoCodecAndroidMediaCodec::GetOutputPicture(void)
752 {
753   int rtn = 0;
754
755   int64_t timeout_us = 5000;
756   CJNIMediaCodecBufferInfo bufferInfo;
757   int index = m_codec->dequeueOutputBuffer(bufferInfo, timeout_us);
758   if (index >= 0)
759   {
760     if (m_drop)
761     {
762       m_codec->releaseOutputBuffer(index, false);
763       if (xbmc_jnienv()->ExceptionOccurred())
764         xbmc_jnienv()->ExceptionClear();
765       return 0;
766     }
767
768     // some devices will return a valid index
769     // before signaling INFO_OUTPUT_BUFFERS_CHANGED which
770     // is used to setup m_output, D'uh. setup m_output here.
771     if (m_output.empty())
772     {
773       m_output = m_codec->getOutputBuffers();
774       FlushInternal();
775     }
776
777     int flags = bufferInfo.flags();
778     if (flags & CJNIMediaCodec::BUFFER_FLAG_SYNC_FRAME)
779       CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_SYNC_FRAME");
780
781     if (flags & CJNIMediaCodec::BUFFER_FLAG_CODEC_CONFIG)
782       CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_CODEC_CONFIG");
783
784     if (flags & CJNIMediaCodec::BUFFER_FLAG_END_OF_STREAM)
785     {
786       CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_END_OF_STREAM");
787       m_codec->releaseOutputBuffer(index, false);
788       if (xbmc_jnienv()->ExceptionOccurred())
789         xbmc_jnienv()->ExceptionClear();
790       return 0;
791     }
792
793     if (!m_render_sw)
794     {
795       m_videobuffer.mediacodec = m_inflight[index]->Retain();
796       m_videobuffer.mediacodec->Validate(true);
797     }
798     else
799     {
800       int size = bufferInfo.size();
801       int offset = bufferInfo.offset();
802
803       if (!m_output[index].isDirect())
804         CLog::Log(LOGWARNING, "CDVDVideoCodecAndroidMediaCodec:: m_output[index].isDirect == false");
805
806       if (size && m_output[index].capacity())
807       {
808         uint8_t *src_ptr = (uint8_t*)xbmc_jnienv()->GetDirectBufferAddress(m_output[index].get_raw());
809         src_ptr += offset;
810
811         int loop_end = 0;
812         if (m_videobuffer.format == RENDER_FMT_NV12)
813           loop_end = 2;
814         else if (m_videobuffer.format == RENDER_FMT_YUV420P)
815           loop_end = 3;
816
817         for (int i = 0; i < loop_end; i++)
818         {
819           uint8_t *src   = src_ptr + m_src_offset[i];
820           int src_stride = m_src_stride[i];
821           uint8_t *dst   = m_videobuffer.data[i];
822           int dst_stride = m_videobuffer.iLineSize[i];
823
824           int height = m_videobuffer.iHeight;
825           if (i > 0)
826             height = (m_videobuffer.iHeight + 1) / 2;
827
828           for (int j = 0; j < height; j++, src += src_stride, dst += dst_stride)
829             memcpy(dst, src, dst_stride);
830         }
831       }
832       m_codec->releaseOutputBuffer(index, false);
833     }
834
835     int64_t pts= bufferInfo.presentationTimeUs();
836     m_videobuffer.dts = DVD_NOPTS_VALUE;
837     m_videobuffer.pts = DVD_NOPTS_VALUE;
838     if (pts != AV_NOPTS_VALUE)
839       m_videobuffer.pts = pts;
840
841 /*
842     CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture "
843       "index(%d), pts(%f)", index, m_videobuffer.pts);
844 */
845     // always, check/clear jni exceptions.
846     if (xbmc_jnienv()->ExceptionOccurred())
847       xbmc_jnienv()->ExceptionClear();
848
849     rtn = 1;
850   }
851   else if (index == CJNIMediaCodec::INFO_OUTPUT_BUFFERS_CHANGED)
852   {
853     m_output = m_codec->getOutputBuffers();
854     FlushInternal();
855   }
856   else if (index == CJNIMediaCodec::INFO_OUTPUT_FORMAT_CHANGED)
857   {
858     OutputFormatChanged();
859   }
860   else if (index == CJNIMediaCodec::INFO_TRY_AGAIN_LATER)
861   {
862     // normal dequeueOutputBuffer timeout, ignore it.
863     rtn = -1;
864   }
865   else
866   {
867     // we should never get here
868     CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture unknown index(%d)", index);
869   }
870
871   return rtn;
872 }
873
874 void CDVDVideoCodecAndroidMediaCodec::OutputFormatChanged(void)
875 {
876   CJNIMediaFormat mediaformat = m_codec->getOutputFormat();
877
878   int width       = mediaformat.getInteger("width");
879   int height      = mediaformat.getInteger("height");
880   int stride      = mediaformat.getInteger("stride");
881   int slice_height= mediaformat.getInteger("slice-height");
882   int color_format= mediaformat.getInteger("color-format");
883   int crop_left   = mediaformat.getInteger("crop-left");
884   int crop_top    = mediaformat.getInteger("crop-top");
885   int crop_right  = mediaformat.getInteger("crop-right");
886   int crop_bottom = mediaformat.getInteger("crop-bottom");
887
888   CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
889     "width(%d), height(%d), stride(%d), slice-height(%d), color-format(%d)",
890     width, height, stride, slice_height, color_format);
891   CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
892     "crop-left(%d), crop-top(%d), crop-right(%d), crop-bottom(%d)",
893     crop_left, crop_top, crop_right, crop_bottom);
894
895   if (!m_render_sw)
896   {
897     CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Direct Surface Rendering");
898     m_videobuffer.format = RENDER_FMT_MEDIACODEC;
899   }
900   else
901   {
902     // Android device quirks and fixes
903     if (stride <= 0)
904         stride = width;
905     if (slice_height <= 0)
906     {
907       slice_height = height;
908       if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
909       {
910         // NVidia Tegra 3 on Nexus 7 does not set slice_heights
911         if (strstr(m_codecname.c_str(), "OMX.Nvidia.") != NULL)
912         {
913           slice_height = (((height) + 31) & ~31);
914           CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: NVidia Tegra 3 quirk, slice_height(%d)", slice_height);
915         }
916       }
917     }
918     if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar)
919     {
920       slice_height -= crop_top / 2;
921       // set crop top/left here, since the offset parameter already includes this.
922       // if we would ignore the offset parameter in the BufferInfo, we could just keep
923       // the original slice height and apply the top/left cropping instead.
924       crop_top = 0;
925       crop_left = 0;
926     }
927
928     // default picture format to none
929     for (int i = 0; i < 4; i++)
930       m_src_offset[i] = m_src_stride[i] = 0;
931     // delete any existing buffers
932     for (int i = 0; i < 4; i++)
933       free(m_videobuffer.data[i]);
934
935     // setup picture format and data offset vectors
936     if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
937     {
938       CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420Planar");
939
940       // Y plane
941       m_src_stride[0] = stride;
942       m_src_offset[0] = crop_top * stride;
943       m_src_offset[0]+= crop_left;
944
945       // U plane
946       m_src_stride[1] = (stride + 1) / 2;
947       //  skip over the Y plane
948       m_src_offset[1] = slice_height * stride;
949       //  crop_top/crop_left divided by two
950       //  because one byte of the U/V planes
951       //  corresponds to two pixels horizontally/vertically
952       m_src_offset[1]+= crop_top  / 2 * m_src_stride[1];
953       m_src_offset[1]+= crop_left / 2;
954
955       // V plane
956       m_src_stride[2] = (stride + 1) / 2;
957       //  skip over the Y plane
958       m_src_offset[2] = slice_height * stride;
959       //  skip over the U plane
960       m_src_offset[2]+= ((slice_height + 1) / 2) * ((stride + 1) / 2);
961       //  crop_top/crop_left divided by two
962       //  because one byte of the U/V planes
963       //  corresponds to two pixels horizontally/vertically
964       m_src_offset[2]+= crop_top  / 2 * m_src_stride[2];
965       m_src_offset[2]+= crop_left / 2;
966
967       m_videobuffer.iLineSize[0] =  width;         // Y
968       m_videobuffer.iLineSize[1] = (width + 1) /2; // U
969       m_videobuffer.iLineSize[2] = (width + 1) /2; // V
970       m_videobuffer.iLineSize[3] = 0;
971
972       unsigned int iPixels = width * height;
973       unsigned int iChromaPixels = iPixels/4;
974       m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
975       m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
976       m_videobuffer.data[2] = (uint8_t*)malloc(16 + iChromaPixels);
977       m_videobuffer.data[3] = NULL;
978       m_videobuffer.format  = RENDER_FMT_YUV420P;
979     }
980     else if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420SemiPlanar
981           || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_QCOM_FormatYUV420SemiPlanar
982           || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar
983           || color_format == CJNIMediaCodecInfoCodecCapabilities::OMX_QCOM_COLOR_FormatYVU420SemiPlanarInterlace)
984
985     {
986       CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420SemiPlanar");
987
988       // Y plane
989       m_src_stride[0] = stride;
990       m_src_offset[0] = crop_top * stride;
991       m_src_offset[0]+= crop_left;
992
993       // UV plane
994       m_src_stride[1] = stride;
995       //  skip over the Y plane
996       m_src_offset[1] = slice_height * stride;
997       m_src_offset[1]+= crop_top * stride;
998       m_src_offset[1]+= crop_left;
999
1000       m_videobuffer.iLineSize[0] = width;  // Y
1001       m_videobuffer.iLineSize[1] = width;  // UV
1002       m_videobuffer.iLineSize[2] = 0;
1003       m_videobuffer.iLineSize[3] = 0;
1004
1005       unsigned int iPixels = width * height;
1006       unsigned int iChromaPixels = iPixels;
1007       m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
1008       m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
1009       m_videobuffer.data[2] = NULL;
1010       m_videobuffer.data[3] = NULL;
1011       m_videobuffer.format  = RENDER_FMT_NV12;
1012     }
1013     else
1014     {
1015       CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Fixme unknown color_format(%d)", color_format);
1016       return;
1017     }
1018   }
1019
1020   // picture display width/height include the cropping.
1021   m_videobuffer.iDisplayWidth  = crop_right  + 1 - crop_left;
1022   m_videobuffer.iDisplayHeight = crop_bottom + 1 - crop_top;
1023
1024   // clear any jni exceptions
1025   if (xbmc_jnienv()->ExceptionOccurred())
1026     xbmc_jnienv()->ExceptionClear();
1027 }
1028
1029 void CDVDVideoCodecAndroidMediaCodec::CallbackInitSurfaceTexture(void *userdata)
1030 {
1031   CDVDVideoCodecAndroidMediaCodec *ctx = static_cast<CDVDVideoCodecAndroidMediaCodec*>(userdata);
1032   ctx->InitSurfaceTexture();
1033 }
1034
1035 void CDVDVideoCodecAndroidMediaCodec::InitSurfaceTexture(void)
1036 {
1037   if (m_render_sw)
1038     return;
1039
1040   // We MUST create the GLES texture on the main thread
1041   // to match where the valid GLES context is located.
1042   // It would be nice to move this out of here, we would need
1043   // to create/fetch/create from g_RenderMananger. But g_RenderMananger
1044   // does not know we are using MediaCodec until Configure and we
1045   // we need m_surfaceTexture valid before then. Chicken, meet Egg.
1046   if (g_application.IsCurrentThread())
1047   {
1048     // localize GLuint so we do not spew gles includes in our header
1049     GLuint texture_id;
1050
1051     glGenTextures(1, &texture_id);
1052     glBindTexture(  GL_TEXTURE_EXTERNAL_OES, texture_id);
1053     glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1054     glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1055     glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1056     glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1057     glBindTexture(  GL_TEXTURE_EXTERNAL_OES, 0);
1058     m_textureId = texture_id;
1059
1060     m_surfaceTexture = boost::shared_ptr<CJNISurfaceTexture>(new CJNISurfaceTexture(m_textureId));
1061     // hook the surfaceTexture OnFrameAvailable callback
1062     m_frameAvailable = boost::shared_ptr<CDVDMediaCodecOnFrameAvailable>(new CDVDMediaCodecOnFrameAvailable(m_surfaceTexture));
1063     m_surface = new CJNISurface(*m_surfaceTexture);
1064   }
1065   else
1066   {
1067     ThreadMessageCallback callbackData;
1068     callbackData.callback = &CallbackInitSurfaceTexture;
1069     callbackData.userptr  = (void*)this;
1070
1071     ThreadMessage msg;
1072     msg.dwMessage = TMSG_CALLBACK;
1073     msg.lpVoid = (void*)&callbackData;
1074
1075     // wait for it.
1076     CApplicationMessenger::Get().SendMessage(msg, true);
1077   }
1078
1079   return;
1080 }
1081
1082 void CDVDVideoCodecAndroidMediaCodec::ReleaseSurfaceTexture(void)
1083 {
1084   if (m_render_sw)
1085     return;
1086
1087   // it is safe to delete here even though these items
1088   // were created in the main thread instance
1089   SAFE_DELETE(m_surface);
1090   m_frameAvailable.reset();
1091   m_surfaceTexture.reset();
1092
1093   if (m_textureId > 0)
1094   {
1095     GLuint texture_id = m_textureId;
1096     glDeleteTextures(1, &texture_id);
1097     m_textureId = 0;
1098   }
1099 }