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