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