droid: Add Android MediaCodec for DVDPlayer
authordavilla <davilla@4pi.com>
Fri, 9 Aug 2013 13:56:45 +0000 (09:56 -0400)
committerdavilla <davilla@4pi.com>
Wed, 2 Oct 2013 15:39:49 +0000 (11:39 -0400)
21 files changed:
language/English/strings.po
system/settings/android.xml
system/shaders/guishader_frag_rgba_oes.glsl [new file with mode: 0644]
system/shaders/guishader_vert.glsl
xbmc/android/jni/MediaCodecBufferInfo.cpp
xbmc/android/jni/SurfaceTexture.cpp
xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
xbmc/cores/VideoRenderers/LinuxRendererGLES.h
xbmc/cores/VideoRenderers/RenderFormats.h
xbmc/cores/VideoRenderers/RenderManager.cpp
xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h
xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp [new file with mode: 0644]
xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.h [new file with mode: 0644]
xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in
xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
xbmc/guilib/GUIShader.cpp
xbmc/guilib/GUIShader.h
xbmc/rendering/gles/RenderSystemGLES.cpp
xbmc/rendering/gles/RenderSystemGLES.h
xbmc/settings/Settings.cpp

index 0b6221a..24c4085 100755 (executable)
@@ -5801,7 +5801,11 @@ msgctxt "#13438"
 msgid "Allow hardware acceleration (amcodec)"
 msgstr ""
 
-#empty strings from id 13439 to 13499
+msgctxt "#13439"
+msgid "Allow hardware acceleration (MediaCodec)"
+msgstr ""
+
+#empty strings from id 13440 to 13499
 
 #: system/settings/settings.xml
 msgctxt "#13500"
@@ -14366,6 +14370,11 @@ msgctxt "#36543"
 msgid "Enable this to make dialogue louder compared to background sounds when downmixing multichannel audio"
 msgstr ""
 
+#: system/settings/settings.xml
+msgctxt "#36544"
+msgid "Enable hardware decoding of video files."
+msgstr ""
+
 #reserved strings 365XX
 
 #: xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNavigator.cpp
index 756d223..ce46a88 100644 (file)
       </group>
     </category>
   </section>
+  <section id="videos">
+    <category id="videoplayer">
+      <group id="2">
+        <setting id="videoplayer.usemediacodec" type="boolean" label="13439" help="36544">
+          <visible>HAS_MEDIACODEC</visible>
+          <level>2</level>
+          <default>true</default>
+        </setting>
+      </group>
+    </category>
+  </section>
 </settings>
diff --git a/system/shaders/guishader_frag_rgba_oes.glsl b/system/shaders/guishader_frag_rgba_oes.glsl
new file mode 100644 (file)
index 0000000..828d48b
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *      Copyright (C) 2010-2013 Team XBMC
+ *      http://xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#extension GL_OES_EGL_image_external : require
+
+precision mediump float;
+uniform samplerExternalOES m_samp0;
+varying vec4      m_cord0;
+
+// SM_TEXTURE_OES
+void main ()
+{
+  gl_FragColor.rgba = texture2D(m_samp0, m_cord0.xy).rgba;
+}
index 65ca101..46f3399 100644 (file)
@@ -27,12 +27,13 @@ varying vec4   m_cord1;
 varying lowp vec4 m_colour;
 uniform mat4   m_proj;
 uniform mat4   m_model;
+uniform mat4   m_coord0Matrix;
 
 void main ()
 {
   mat4 mvp    = m_proj * m_model;
   gl_Position = mvp * m_attrpos;
   m_colour    = m_attrcol;
-  m_cord0     = m_attrcord0;
+  m_cord0     = m_coord0Matrix * m_attrcord0;
   m_cord1     = m_attrcord1;
 }
index 80d99e1..e966e73 100644 (file)
@@ -29,7 +29,7 @@ using namespace jni;
 CJNIMediaCodecBufferInfo::CJNIMediaCodecBufferInfo() : CJNIBase("android/media/MediaCodec$BufferInfo")
 {
   m_object = new_object(GetClassName(), "<init>", "()V");
-  //m_object.setGlobal();
+  m_object.setGlobal();
 }
 
 void CJNIMediaCodecBufferInfo::set(int newOffset, int newSize, int64_t newTimeUs, int newFlags)
index 63ca382..df8d5da 100644 (file)
@@ -25,6 +25,8 @@
 
 #include "jutils/jutils-details.hpp"
 
+#include <algorithm>
+
 using namespace jni;
 
 //////////////////////////////////////////////////////////////////////////////////
@@ -40,11 +42,7 @@ CJNISurfaceTextureOnFrameAvailableListener::CJNISurfaceTextureOnFrameAvailableLi
 
   // Convert "the/class/name" to "the.class.name" as loadClass() expects it.
   std::string dotClassName = GetClassName();
-  for (std::string::iterator it = dotClassName.begin(); it != dotClassName.end(); ++it)
-  {
-    if (*it == '/')
-      *it = '.';
-  }
+  std::replace(dotClassName.begin(), dotClassName.end(), '/', '.');
   m_object = new_object(appInstance->getClassLoader().loadClass(dotClassName));
   m_object.setGlobal();
 
index ee437a8..4727ccb 100644 (file)
@@ -75,6 +75,10 @@ static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
 static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
 #endif
 
+#if defined(TARGET_ANDROID)
+#include "DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.h"
+#endif
+
 using namespace Shaders;
 
 CLinuxRendererGLES::YUVBUFFER::YUVBUFFER()
@@ -92,6 +96,9 @@ CLinuxRendererGLES::YUVBUFFER::YUVBUFFER()
   stf = NULL;
   eglimg = EGL_NO_IMAGE_KHR;
 #endif
+#if defined(TARGET_ANDROID)
+  mediacodec = NULL;
+#endif
 }
 
 CLinuxRendererGLES::YUVBUFFER::~YUVBUFFER()
@@ -265,7 +272,13 @@ int CLinuxRendererGLES::GetImage(YV12Image *image, int source, bool readonly)
   {
     return source;
   }
+
 #endif
+  if ( m_renderMethod & RENDER_MEDIACODEC )
+  {
+    return source;
+  }
+
 #ifdef HAVE_VIDEOTOOLBOXDECODER
   if (m_renderMethod & RENDER_CVREF )
   {
@@ -484,7 +497,7 @@ void CLinuxRendererGLES::RenderUpdate(bool clear, DWORD flags, DWORD alpha)
   int index = m_iYV12RenderBuffer;
   YUVBUFFER& buf =  m_buffers[index];
 
-  if (m_format != RENDER_FMT_OMXEGL && m_format != RENDER_FMT_EGLIMG)
+  if (m_format != RENDER_FMT_OMXEGL && m_format != RENDER_FMT_EGLIMG && m_format != RENDER_FMT_MEDIACODEC)
   {
     if (!buf.fields[FIELD_FULL][0].id) return;
   }
@@ -565,6 +578,9 @@ unsigned int CLinuxRendererGLES::PreInit()
 #ifdef HAS_LIBSTAGEFRIGHT
   m_formats.push_back(RENDER_FMT_EGLIMG);
 #endif
+#if defined(TARGET_ANDROID)
+  m_formats.push_back(RENDER_FMT_MEDIACODEC);
+#endif
 
   // setup the background colour
   m_clearColour = (float)(g_advancedSettings.m_videoBlackBarColour & 0xff) / 0xff;
@@ -670,6 +686,12 @@ void CLinuxRendererGLES::LoadShaders(int field)
         m_renderMethod = RENDER_EGLIMG;
         break;
       }
+      else if (m_format == RENDER_FMT_MEDIACODEC)
+      {
+        CLog::Log(LOGNOTICE, "GL: Using MediaCodec render method");
+        m_renderMethod = RENDER_MEDIACODEC;
+        break;
+      }
       else if (m_format == RENDER_FMT_BYPASS)
       {
         CLog::Log(LOGNOTICE, "GL: Using BYPASS render method");
@@ -750,6 +772,13 @@ void CLinuxRendererGLES::LoadShaders(int field)
     m_textureCreate = &CLinuxRendererGLES::CreateEGLIMGTexture;
     m_textureDelete = &CLinuxRendererGLES::DeleteEGLIMGTexture;
   }
+  else if (m_format == RENDER_FMT_MEDIACODEC)
+  {
+    m_textureUpload = &CLinuxRendererGLES::UploadSurfaceTexture;
+    m_textureCreate = &CLinuxRendererGLES::CreateSurfaceTexture;
+    m_textureDelete = &CLinuxRendererGLES::DeleteSurfaceTexture;
+  }
+
   else
   {
     // default to YV12 texture handlers
@@ -826,6 +855,11 @@ void CLinuxRendererGLES::ReleaseBuffer(int idx)
     CVBufferRelease(buf.cvBufferRef);
   buf.cvBufferRef = NULL;
 #endif
+#if defined(TARGET_ANDROID)
+  YUVBUFFER &buf = m_buffers[idx];
+
+  SAFE_RELEASE(m_buffers[idx].mediacodec);
+#endif
 }
 
 void CLinuxRendererGLES::Render(DWORD flags, int index)
@@ -883,6 +917,10 @@ void CLinuxRendererGLES::Render(DWORD flags, int index)
     RenderCoreVideoRef(index, m_currentField);
     VerifyGLState();
   }
+  else if (m_renderMethod & RENDER_MEDIACODEC)
+  {
+    RenderMediaCodec(index, m_currentField);
+  }
   else
   {
     RenderSoftware(index, m_currentField);
@@ -1372,6 +1410,84 @@ void CLinuxRendererGLES::RenderEglImage(int index, int field)
 #endif
 }
 
+void CLinuxRendererGLES::RenderMediaCodec(int index, int field)
+{
+#if defined(TARGET_ANDROID)
+  #ifdef DEBUG_VERBOSE
+    unsigned int time = XbmcThreads::SystemClockMillis();
+  #endif
+
+  YUVPLANE &plane = m_buffers[index].fields[0][0];
+
+  glDisable(GL_DEPTH_TEST);
+
+  glActiveTexture(GL_TEXTURE0);
+  glBindTexture(GL_TEXTURE_EXTERNAL_OES, plane.id);
+
+  g_Windowing.EnableGUIShader(SM_TEXTURE_RGBA_OES);
+
+  glUniformMatrix4fv(g_Windowing.GUIShaderGetCoord0Matrix(), 1, GL_FALSE, m_textureMatrix);
+
+  GLubyte idx[4] = {0, 1, 3, 2};        //determines order of triangle strip
+  GLfloat ver[4][4];
+  GLfloat tex[4][4];
+
+  GLint   posLoc = g_Windowing.GUIShaderGetPos();
+  GLint   texLoc = g_Windowing.GUIShaderGetCoord0();
+
+
+  glVertexAttribPointer(posLoc, 4, GL_FLOAT, 0, 0, ver);
+  glVertexAttribPointer(texLoc, 4, GL_FLOAT, 0, 0, tex);
+
+  glEnableVertexAttribArray(posLoc);
+  glEnableVertexAttribArray(texLoc);
+
+  // Set vertex coordinates
+  for(int i = 0; i < 4; i++)
+  {
+    ver[i][0] = m_rotatedDestCoords[i].x;
+    ver[i][1] = m_rotatedDestCoords[i].y;
+    ver[i][2] = 0.0f;        // set z to 0
+    ver[i][3] = 1.0f;
+  }
+
+  // Set texture coordinates (MediaCodec is flipped in y)
+  tex[0][0] = tex[3][0] = 0.0f;
+  tex[0][1] = tex[1][1] = 1.0f;
+  tex[1][0] = tex[2][0] = 1.0f;
+  tex[2][1] = tex[3][1] = 0.0f;
+
+  for(int i = 0; i < 4; i++)
+  {
+    tex[i][2] = 0.0f;
+    tex[i][3] = 1.0f;
+  }
+
+  glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx);
+
+  glDisableVertexAttribArray(posLoc);
+  glDisableVertexAttribArray(texLoc);
+
+  const float identity[16] = {
+      1.0f, 0.0f, 0.0f, 0.0f,
+      0.0f, 1.0f, 0.0f, 0.0f,
+      0.0f, 0.0f, 1.0f, 0.0f,
+      0.0f, 0.0f, 0.0f, 1.0f
+  };
+  glUniformMatrix4fv(g_Windowing.GUIShaderGetCoord0Matrix(),  1, GL_FALSE, identity);
+
+  g_Windowing.DisableGUIShader();
+  VerifyGLState();
+
+  glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
+  VerifyGLState();
+
+  #ifdef DEBUG_VERBOSE
+    CLog::Log(LOGDEBUG, "RenderMediaCodecImage %d: tm:%d", index, XbmcThreads::SystemClockMillis() - time);
+  #endif
+#endif
+}
+
 void CLinuxRendererGLES::RenderCoreVideoRef(int index, int field)
 {
 #ifdef HAVE_VIDEOTOOLBOXDECODER
@@ -2006,6 +2122,34 @@ bool CLinuxRendererGLES::CreateEGLIMGTexture(int index)
   return true;
 }
 
+//********************************************************************************************************
+// SurfaceTexture creation, deletion, copying + clearing
+//********************************************************************************************************
+void CLinuxRendererGLES::UploadSurfaceTexture(int index)
+{
+#if defined(TARGET_ANDROID)
+  if (m_buffers[index].mediacodec)
+  {
+    m_buffers[index].fields[0][0].id = m_buffers[index].mediacodec->GetTextureID();
+    m_buffers[index].mediacodec->ReleaseOutputBuffer(true);
+    m_buffers[index].mediacodec->UpdateTexImage();
+    m_buffers[index].mediacodec->GetTransformMatrix(m_textureMatrix);
+    SAFE_RELEASE(m_buffers[index].mediacodec);
+  }
+
+#endif
+}
+void CLinuxRendererGLES::DeleteSurfaceTexture(int index)
+{
+#if defined(TARGET_ANDROID)
+  SAFE_RELEASE(m_buffers[index].mediacodec);
+#endif
+}
+bool CLinuxRendererGLES::CreateSurfaceTexture(int index)
+{
+  return true;
+}
+
 void CLinuxRendererGLES::SetTextureFilter(GLenum method)
 {
   for (int i = 0 ; i<m_NumYV12Buffers ; i++)
@@ -2123,6 +2267,9 @@ bool CLinuxRendererGLES::Supports(EINTERLACEMETHOD method)
   if(m_renderMethod & RENDER_EGLIMG)
     return false;
 
+  if(m_renderMethod & RENDER_MEDIACODEC)
+    return false;
+
   if(m_renderMethod & RENDER_CVREF)
     return false;
 
@@ -2188,7 +2335,8 @@ unsigned int CLinuxRendererGLES::GetProcessorSize()
 {
   if(m_format == RENDER_FMT_OMXEGL
   || m_format == RENDER_FMT_CVBREF
-  || m_format == RENDER_FMT_EGLIMG)
+  || m_format == RENDER_FMT_EGLIMG
+  || m_format == RENDER_FMT_MEDIACODEC)
     return 1;
   else
     return 0;
@@ -2233,5 +2381,22 @@ void CLinuxRendererGLES::AddProcessor(CStageFrightVideo* stf, EGLImageKHR eglimg
 }
 #endif
 
+#if defined(TARGET_ANDROID)
+void CLinuxRendererGLES::AddProcessor(CDVDMediaCodecInfo *mediacodec, int index)
+{
+#ifdef DEBUG_VERBOSE
+  unsigned int time = XbmcThreads::SystemClockMillis();
+#endif
+
+  YUVBUFFER &buf = m_buffers[index];
+  if (mediacodec)
+    buf.mediacodec = mediacodec->Retain();
+
+#ifdef DEBUG_VERBOSE
+  CLog::Log(LOGDEBUG, "AddProcessor %d: img:%d: tm:%d\n", index, buf.mediacodec->GetTexture(), XbmcThreads::SystemClockMillis() - time);
+#endif
+}
+#endif
+
 #endif
 
index e15ec05..2c72e15 100644 (file)
@@ -41,6 +41,7 @@ namespace Shaders { class BaseYUV2RGBShader; }
 namespace Shaders { class BaseVideoFilterShader; }
 class COpenMaxVideo;
 class CStageFrightVideo;
+class CDVDMediaCodecInfo;
 typedef std::vector<int>     Features;
 
 
@@ -87,7 +88,8 @@ enum RenderMethod
   RENDER_OMXEGL = 0x040,
   RENDER_CVREF  = 0x080,
   RENDER_BYPASS = 0x100,
-  RENDER_EGLIMG = 0x200
+  RENDER_EGLIMG = 0x200,
+  RENDER_MEDIACODEC = 0x400
 };
 
 enum RenderQuality
@@ -167,6 +169,10 @@ public:
 #ifdef HAS_LIBSTAGEFRIGHT
   virtual void         AddProcessor(CStageFrightVideo* stf, EGLImageKHR eglimg, int index);
 #endif
+#if defined(TARGET_ANDROID)
+  // mediaCodec
+  virtual void         AddProcessor(CDVDMediaCodecInfo *mediacodec, int index);
+#endif
 
 protected:
   virtual void Render(DWORD flags, int index);
@@ -198,6 +204,10 @@ protected:
   void DeleteEGLIMGTexture(int index);
   bool CreateEGLIMGTexture(int index);
 
+  void UploadSurfaceTexture(int index);
+  void DeleteSurfaceTexture(int index);
+  bool CreateSurfaceTexture(int index);
+
   void CalculateTextureSourceRects(int source, int num_planes);
 
   // renderers
@@ -207,6 +217,7 @@ protected:
   void RenderOpenMax(int index, int field);       // OpenMAX rgb texture
   void RenderEglImage(int index, int field);       // Android OES texture
   void RenderCoreVideoRef(int index, int field);  // CoreVideo reference
+  void RenderMediaCodec(int index, int field);    // MediaCodec reference
 
   CFrameBufferObject m_fbo;
 
@@ -266,6 +277,10 @@ protected:
     CStageFrightVideo* stf;
     EGLImageKHR eglimg;
 #endif
+#if defined(TARGET_ANDROID)
+    // mediacodec
+    CDVDMediaCodecInfo *mediacodec;
+#endif
   };
 
   typedef YUVBUFFER          YUVBUFFERS[NUM_BUFFERS];
@@ -296,6 +311,7 @@ protected:
   struct SwsContext *m_sw_context;
   BYTE       *m_rgbBuffer;  // if software scale is used, this will hold the result image
   unsigned int m_rgbBufferSize;
+  float        m_textureMatrix[16];
 };
 
 
index 3b09194..f15e80d 100644 (file)
@@ -36,6 +36,7 @@ enum ERenderFormat {
   RENDER_FMT_CVBREF,
   RENDER_FMT_BYPASS,
   RENDER_FMT_EGLIMG,
+  RENDER_FMT_MEDIACODEC,
 };
 
 #endif
index fbc2aae..249222a 100644 (file)
@@ -26,6 +26,7 @@
 #include "threads/CriticalSection.h"
 #include "video/VideoReferenceClock.h"
 #include "utils/MathUtils.h"
+#include "threads/Atomics.h"
 #include "threads/SingleLock.h"
 #include "utils/log.h"
 #include "utils/TimeUtils.h"
@@ -937,6 +938,10 @@ int CXBMCRenderManager::AddVideoPicture(DVDVideoPicture& pic)
   else if(pic.format == RENDER_FMT_EGLIMG)
     m_pRenderer->AddProcessor(pic.stf, pic.eglimg, index);
 #endif
+#if defined(TARGET_ANDROID)
+  else if(pic.format == RENDER_FMT_MEDIACODEC)
+    m_pRenderer->AddProcessor(pic.mediacodec, index);
+#endif
 
   m_pRenderer->ReleaseImage(index, false);
 
index bc3072a..183e2c5 100644 (file)
@@ -43,6 +43,9 @@
 #include "utils/AMLUtils.h"
 #include "Video/DVDVideoCodecAmlogic.h"
 #endif
+#if defined(TARGET_ANDROID)
+#include "Video/DVDVideoCodecAndroidMediaCodec.h"
+#endif
 #include "Audio/DVDAudioCodecFFmpeg.h"
 #include "Audio/DVDAudioCodecLibMad.h"
 #include "Audio/DVDAudioCodecPcm.h"
@@ -159,6 +162,11 @@ CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec(CDVDStreamInfo &hint, unsigne
 #else
   hwSupport += "AMCodec:no ";
 #endif
+#if defined(TARGET_ANDROID)
+  hwSupport += "MediaCodec:yes ";
+#else
+  hwSupport += "MediaCodec:no ";
+#endif
 #if defined(HAVE_LIBOPENMAX) && defined(TARGET_POSIX)
   hwSupport += "OpenMax:yes ";
 #elif defined(TARGET_POSIX)
@@ -259,6 +267,14 @@ CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec(CDVDStreamInfo &hint, unsigne
   }
 #endif
 
+#if defined(TARGET_ANDROID)
+  if (!hint.software && CSettings::Get().GetBool("videoplayer.usemediacodec"))
+  {
+    CLog::Log(LOGINFO, "MediaCodec Video Decoder...");
+    if ( (pCodec = OpenCodec(new CDVDVideoCodecAndroidMediaCodec(), hint, options)) ) return pCodec;
+  }
+#endif
+
 #if defined(HAVE_LIBOPENMAX)
   if (CSettings::Get().GetBool("videoplayer.useomx") && !hint.software )
   {
index 87edaa5..baee6e0 100644 (file)
@@ -39,8 +39,10 @@ class COpenMax;
 class COpenMaxVideo;
 struct OpenMaxVideoBuffer;
 class CStageFrightVideo;
+class CDVDMediaCodecInfo;
 typedef void* EGLImageKHR;
 
+
 // should be entirely filled by all codecs
 struct DVDVideoPicture
 {
@@ -76,6 +78,10 @@ struct DVDVideoPicture
       CStageFrightVideo* stf;
       EGLImageKHR eglimg;
     };
+
+    struct {
+      CDVDMediaCodecInfo *mediacodec;
+    };
   };
 
   unsigned int iFlags;
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp
new file mode 100644 (file)
index 0000000..2447bbb
--- /dev/null
@@ -0,0 +1,1009 @@
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// http://developer.android.com/reference/android/media/MediaCodec.html
+//
+// Android MediaCodec class can be used to access low-level media codec,
+// i.e. encoder/decoder components. (android.media.MediaCodec). Requires
+// SDK16+ which is 4.1 Jellybean and above.
+//
+
+#include "DVDVideoCodecAndroidMediaCodec.h"
+
+#include "Application.h"
+#include "ApplicationMessenger.h"
+#include "DVDClock.h"
+#include "threads/Atomics.h"
+#include "utils/BitstreamConverter.h"
+#include "utils/CPUInfo.h"
+#include "utils/log.h"
+
+#include "android/jni/ByteBuffer.h"
+#include "android/jni/MediaCodec.h"
+#include "android/jni/MediaCrypto.h"
+#include "android/jni/MediaFormat.h"
+#include "android/jni/MediaCodecList.h"
+#include "android/jni/MediaCodecInfo.h"
+#include "android/jni/Surface.h"
+#include "android/jni/SurfaceTexture.h"
+#include "android/activity/AndroidFeatures.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+/*****************************************************************************/
+/*****************************************************************************/
+class CNULL_Listener : public CJNISurfaceTextureOnFrameAvailableListener
+{
+public:
+  CNULL_Listener() : CJNISurfaceTextureOnFrameAvailableListener(jni::jhobject(NULL)) {};
+
+protected:
+  virtual void OnFrameAvailable(CJNISurfaceTexture &surface) {};
+};
+
+class CDVDMediaCodecOnFrameAvailable : public CEvent, CJNISurfaceTextureOnFrameAvailableListener
+{
+public:
+  CDVDMediaCodecOnFrameAvailable(boost::shared_ptr<CJNISurfaceTexture> &surfaceTexture)
+  : m_surfaceTexture(surfaceTexture)
+  {
+    m_surfaceTexture->setOnFrameAvailableListener(*this);
+  }
+
+  virtual ~CDVDMediaCodecOnFrameAvailable()
+  {
+    // unhook the callback
+    CNULL_Listener null_listener;
+    m_surfaceTexture->setOnFrameAvailableListener(null_listener);
+  }
+
+protected:
+  virtual void OnFrameAvailable(CJNISurfaceTexture &surface)
+  {
+    Set();
+  }
+
+private:
+  boost::shared_ptr<CJNISurfaceTexture> m_surfaceTexture;
+
+};
+
+/*****************************************************************************/
+/*****************************************************************************/
+CDVDMediaCodecInfo::CDVDMediaCodecInfo(
+    int index
+  , unsigned int texture
+  , boost::shared_ptr<CJNIMediaCodec> &codec
+  , boost::shared_ptr<CJNISurfaceTexture> &surfacetexture
+  , boost::shared_ptr<CDVDMediaCodecOnFrameAvailable> &frameready
+)
+: m_refs(1)
+, m_valid(true)
+, m_index(index)
+, m_texture(texture)
+, m_timestamp(0)
+, m_codec(codec)
+, m_surfacetexture(surfacetexture)
+, m_frameready(frameready)
+{
+  // paranoid checks
+  assert(m_index >= 0);
+  assert(m_texture > 0);
+  assert(m_codec != NULL);
+  assert(m_surfacetexture != NULL);
+  assert(m_frameready != NULL);
+}
+
+CDVDMediaCodecInfo::~CDVDMediaCodecInfo()
+{
+  assert(m_refs == 0);
+}
+
+CDVDMediaCodecInfo* CDVDMediaCodecInfo::Retain()
+{
+  AtomicIncrement(&m_refs);
+
+  return this;
+}
+
+long CDVDMediaCodecInfo::Release()
+{
+  long count = AtomicDecrement(&m_refs);
+  if (count == 0)
+  {
+    ReleaseOutputBuffer(false);
+    delete this;
+  }
+
+  return count;
+}
+
+void CDVDMediaCodecInfo::Validate(bool state)
+{
+  CSingleLock lock(m_section);
+
+  m_valid = state;
+}
+
+void CDVDMediaCodecInfo::ReleaseOutputBuffer(bool render)
+{
+  CSingleLock lock(m_section);
+
+  if (!m_valid)
+    return;
+
+  // release OutputBuffer and render if indicated
+  // then wait for rendered frame to become avaliable.
+
+  if (render)
+    m_frameready->Reset();
+
+  m_codec->releaseOutputBuffer(m_index, render);
+  if (xbmc_jnienv()->ExceptionOccurred())
+  {
+    CLog::Log(LOGERROR, "CDVDMediaCodecInfo::ReleaseOutputBuffer "
+      "ExceptionOccurred render(%d)", render);
+    xbmc_jnienv()->ExceptionDescribe();
+    xbmc_jnienv()->ExceptionClear();
+  }
+
+  // this is key, after calling releaseOutputBuffer, we must
+  // wait a little for MediaCodec to render to the surface.
+  // Then we can updateTexImage without delay. If we do not
+  // wait, then video playback gets jerky. To optomize this,
+  // we hook the SurfaceTexture OnFrameAvailable callback
+  // using CJNISurfaceTextureOnFrameAvailableListener and wait
+  // on a CEvent to fire. 20ms seems to be a good max fallback.
+  if (render)
+    m_frameready->WaitMSec(20);
+}
+
+int CDVDMediaCodecInfo::GetTextureID() const
+{
+  // since m_texture never changes,
+  // we do not need a m_section lock here.
+  return m_texture;
+}
+
+void CDVDMediaCodecInfo::GetTransformMatrix(float *textureMatrix)
+{
+  CSingleLock lock(m_section);
+
+  if (!m_valid)
+    return;
+
+  m_surfacetexture->getTransformMatrix(textureMatrix);
+}
+
+void CDVDMediaCodecInfo::UpdateTexImage()
+{
+  CSingleLock lock(m_section);
+
+  if (!m_valid)
+    return;
+
+  m_surfacetexture->updateTexImage();
+  if (xbmc_jnienv()->ExceptionOccurred())
+  {
+    CLog::Log(LOGERROR, "CDVDMediaCodecInfo::UpdateTexImage updateTexImage:ExceptionOccurred");
+    xbmc_jnienv()->ExceptionDescribe();
+    xbmc_jnienv()->ExceptionClear();
+  }
+
+  m_timestamp = m_surfacetexture->getTimestamp();
+  if (xbmc_jnienv()->ExceptionOccurred())
+  {
+    CLog::Log(LOGERROR, "CDVDMediaCodecInfo::UpdateTexImage getTimestamp:ExceptionOccurred");
+    xbmc_jnienv()->ExceptionDescribe();
+    xbmc_jnienv()->ExceptionClear();
+  }
+}
+
+/*****************************************************************************/
+/*****************************************************************************/
+CDVDVideoCodecAndroidMediaCodec::CDVDVideoCodecAndroidMediaCodec()
+: m_formatname("mediacodec")
+, m_opened(false)
+, m_surface(NULL)
+, m_textureId(0)
+, m_bitstream(NULL)
+, m_render_sw(false)
+{
+  memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
+}
+
+CDVDVideoCodecAndroidMediaCodec::~CDVDVideoCodecAndroidMediaCodec()
+{
+  Dispose();
+}
+
+bool CDVDVideoCodecAndroidMediaCodec::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
+{
+  // check for 4.1 Jellybean and above.
+  if (CAndroidFeatures::GetVersion() < 16)
+    return false;
+
+  m_drop = false;
+  m_hints = hints;
+
+  switch(m_hints.codec)
+  {
+    case AV_CODEC_ID_MPEG2VIDEO:
+      m_mime = "video/mpeg2";
+      m_formatname = "amc-mpeg2";
+      break;
+    case AV_CODEC_ID_MPEG4:
+      m_mime = "video/mp4v-es";
+      m_formatname = "amc-mpeg4";
+      break;
+    case AV_CODEC_ID_H263:
+      m_mime = "video/3gpp";
+      m_formatname = "amc-h263";
+      break;
+    case AV_CODEC_ID_VP3:
+    case AV_CODEC_ID_VP6:
+    case AV_CODEC_ID_VP6F:
+    case AV_CODEC_ID_VP8:
+      //m_mime = "video/x-vp6";
+      //m_mime = "video/x-vp7";
+      m_mime = "video/x-vnd.on2.vp8";
+      m_formatname = "amc-vpX";
+      break;
+    case AV_CODEC_ID_AVS:
+    case AV_CODEC_ID_CAVS:
+    case AV_CODEC_ID_H264:
+      m_mime = "video/avc";
+      m_formatname = "amc-h264";
+      m_bitstream = new CBitstreamConverter;
+      if (!m_bitstream->Open(m_hints.codec, (uint8_t*)m_hints.extradata, m_hints.extrasize, true))
+      {
+        SAFE_DELETE(m_bitstream);
+        return false;
+      }
+      break;
+    case AV_CODEC_ID_VC1:
+    case AV_CODEC_ID_WMV3:
+      m_mime = "video/wvc1";
+      //m_mime = "video/wmv9";
+      m_formatname = "amc-vc1";
+      break;
+    default:
+      CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Unknown hints.codec(%d)", hints.codec);
+      return false;
+      break;
+  }
+
+  // odroid platform throws trying to updateTexImage with a 'error creating EGLImage' and
+  // 'unsupported native buffer format (0x13)', sw render them until we figure out why.
+  if (!m_render_sw)
+    m_render_sw = g_cpuInfo.getCPUHardware().find("ODROID") != std::string::npos;
+
+
+  // CJNIMediaCodec::createDecoderByXXX doesn't handle errors nicely,
+  // it crashes if the codec isn't found. This is fixed in latest AOSP,
+  // but not in current 4.1 devices. So 1st search for a matching codec, then create it.
+  int num_codecs = CJNIMediaCodecList::getCodecCount();
+  for (int i = 0; i < num_codecs; i++)
+  {
+    CJNIMediaCodecInfo codec_info = CJNIMediaCodecList::getCodecInfoAt(i);
+    if (codec_info.isEncoder())
+      continue;
+
+    std::vector<std::string> types = codec_info.getSupportedTypes();
+    // return the 1st one we find, that one is typically 'the best'
+    for (size_t j = 0; j < types.size(); ++j)
+    {
+      if (types[j] == m_mime)
+      {
+        m_codecname = codec_info.getName();
+        m_codec = boost::shared_ptr<CJNIMediaCodec>(new CJNIMediaCodec(CJNIMediaCodec::createByCodecName(m_codecname)));
+
+        // clear any jni exceptions, jni gets upset if we do not.
+        if (xbmc_jnienv()->ExceptionOccurred())
+        {
+          CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Open ExceptionOccurred");
+          xbmc_jnienv()->ExceptionClear();
+          m_codec.reset();
+          continue;
+        }
+        break;
+      }
+    }
+    if (m_codec)
+      break;
+  }
+  if (!m_codec)
+  {
+    CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Failed to create Android MediaCodec");
+    SAFE_DELETE(m_bitstream);
+    return false;
+  }
+
+  ConfigureMediaCodec();
+
+  // setup a YUV420P DVDVideoPicture buffer.
+  // first make sure all properties are reset.
+  memset(&m_videobuffer, 0x00, sizeof(DVDVideoPicture));
+
+  m_videobuffer.dts = DVD_NOPTS_VALUE;
+  m_videobuffer.pts = DVD_NOPTS_VALUE;
+  m_videobuffer.color_range  = 0;
+  m_videobuffer.color_matrix = 4;
+  m_videobuffer.iFlags  = DVP_FLAG_ALLOCATED;
+  m_videobuffer.iWidth  = m_hints.width;
+  m_videobuffer.iHeight = m_hints.height;
+  // these will get reset to crop values later
+  m_videobuffer.iDisplayWidth  = m_hints.width;
+  m_videobuffer.iDisplayHeight = m_hints.height;
+
+  CLog::Log(LOGINFO, "CDVDVideoCodecAndroidMediaCodec:: "
+    "Open Android MediaCodec %s", m_codecname.c_str());
+
+  m_opened = true;
+
+  return m_opened;
+}
+
+void CDVDVideoCodecAndroidMediaCodec::Dispose()
+{
+  m_opened = false;
+
+  // release any retained demux packets
+  while (!m_demux.empty())
+  {
+    amc_demux &demux_pkt = m_demux.front();
+    free(demux_pkt.pData);
+    m_demux.pop();
+  }
+
+  // invalidate any inflight outputbuffers, make sure
+  // m_output is empty so we do not create new ones
+  m_input.clear();
+  m_output.clear();
+  FlushInternal();
+
+  // clear m_videobuffer bits
+  if (m_render_sw)
+  {
+    free(m_videobuffer.data[0]), m_videobuffer.data[0] = NULL;
+    free(m_videobuffer.data[1]), m_videobuffer.data[1] = NULL;
+    free(m_videobuffer.data[2]), m_videobuffer.data[2] = NULL;
+  }
+  m_videobuffer.iFlags = 0;
+  // m_videobuffer.mediacodec is unioned with m_videobuffer.data[0]
+  // so be very careful when and how you touch it.
+  m_videobuffer.mediacodec = NULL;
+
+  if (m_codec)
+  {
+    m_codec->stop();
+    m_codec->release();
+    m_codec.reset();
+  }
+  ReleaseSurfaceTexture();
+
+  SAFE_DELETE(m_bitstream);
+}
+
+int CDVDVideoCodecAndroidMediaCodec::Decode(uint8_t *pData, int iSize, double dts, double pts)
+{
+  // Handle input, add demuxer packet to input queue, we must accept it or
+  // it will be discarded as DVDPlayerVideo has no concept of "try again".
+  // we must return VC_BUFFER or VC_PICTURE, default to VC_BUFFER.
+  int rtn = VC_BUFFER;
+
+  if (!m_opened)
+    return rtn;
+
+  if (m_hints.ptsinvalid)
+    pts = DVD_NOPTS_VALUE;
+
+  // must check for an output picture 1st,
+  // otherwise, mediacodec can stall on some devices.
+  if (GetOutputPicture() > 0)
+    rtn |= VC_PICTURE;
+
+  if (pData)
+  {
+    if (m_bitstream)
+    {
+      m_bitstream->Convert(pData, iSize);
+      iSize = m_bitstream->GetConvertSize();
+      pData = m_bitstream->GetConvertBuffer();
+    }
+
+    // queue demux pkt in case we cannot get an input buffer
+    amc_demux demux_pkt;
+    demux_pkt.dts = dts;
+    demux_pkt.pts = pts;
+    demux_pkt.iSize = iSize;
+    demux_pkt.pData = (uint8_t*)malloc(iSize);
+    memcpy(demux_pkt.pData, pData, iSize);
+    m_demux.push(demux_pkt);
+
+    // try to fetch an input buffer
+    int64_t timeout_us = 5000;
+    int index = m_codec->dequeueInputBuffer(timeout_us);
+    if (index >= 0)
+    {
+      // docs lie, getInputBuffers should be good after
+      // m_codec->start() but the internal refs are not
+      // setup until much later on some devices.
+      if (m_input.empty())
+        m_input = m_codec->getInputBuffers();
+
+      // we have an input buffer, fill it.
+      int size = m_input[index].capacity();
+      // fetch the front demux packet
+      amc_demux &demux_pkt = m_demux.front();
+      if (demux_pkt.iSize > size)
+      {
+        CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode, iSize(%d) > size(%d)", iSize, size);
+        demux_pkt.iSize = size;
+      }
+      // fetch a pointer to the ByteBuffer backing store
+      void *dst_ptr = xbmc_jnienv()->GetDirectBufferAddress(m_input[index].get_raw());
+      if (dst_ptr)
+        memcpy(dst_ptr, demux_pkt.pData, demux_pkt.iSize);
+
+      free(demux_pkt.pData);
+      m_demux.pop();
+
+      // Translate from dvdplayer dts/pts to MediaCodec pts,
+      // pts WILL get re-ordered by MediaCodec if needed.
+      // Do not try to pass pts as a unioned double/int64_t,
+      // some android devices will diddle with presentationTimeUs
+      // and you will get NaN back and DVDPlayerVideo will barf.
+      int64_t presentationTimeUs = AV_NOPTS_VALUE;
+      if (demux_pkt.pts != DVD_NOPTS_VALUE)
+        presentationTimeUs = demux_pkt.pts;
+      else if (demux_pkt.dts != DVD_NOPTS_VALUE)
+        presentationTimeUs = demux_pkt.dts;
+/*
+      CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
+        "pts(%f), ipts(%lld), iSize(%d), GetDataSize(%d), loop_cnt(%d)",
+        presentationTimeUs, pts_dtoi(presentationTimeUs), iSize, GetDataSize(), loop_cnt);
+*/
+      int flags = 0;
+      int offset = 0;
+      m_codec->queueInputBuffer(index, offset, demux_pkt.iSize, presentationTimeUs, flags);
+      // clear any jni exceptions, jni gets upset if we do not.
+      if (xbmc_jnienv()->ExceptionOccurred())
+      {
+        CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Decode ExceptionOccurred");
+        xbmc_jnienv()->ExceptionClear();
+      }
+    }
+  }
+
+  return rtn;
+}
+
+void CDVDVideoCodecAndroidMediaCodec::Reset()
+{
+  if (!m_opened)
+    return;
+
+  // dump any pending demux packets
+  while (!m_demux.empty())
+  {
+    amc_demux &demux_pkt = m_demux.front();
+    free(demux_pkt.pData);
+    m_demux.pop();
+  }
+
+  if (m_codec)
+  {
+    // flush all outputbuffers inflight, they will
+    // become invalid on m_codec->flush and generate
+    // a spew of java exceptions if used
+    FlushInternal();
+
+    // now we can flush the actual MediaCodec object
+    m_codec->flush();
+    if (xbmc_jnienv()->ExceptionOccurred())
+    {
+      CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::Reset ExceptionOccurred");
+      xbmc_jnienv()->ExceptionClear();
+    }
+
+    // Invalidate our local DVDVideoPicture bits
+    m_videobuffer.pts = DVD_NOPTS_VALUE;
+    if (!m_render_sw)
+      m_videobuffer.mediacodec = NULL;
+  }
+}
+
+bool CDVDVideoCodecAndroidMediaCodec::GetPicture(DVDVideoPicture* pDvdVideoPicture)
+{
+  if (!m_opened)
+    return false;
+
+  *pDvdVideoPicture = m_videobuffer;
+
+  // Invalidate our local DVDVideoPicture bits
+  m_videobuffer.pts = DVD_NOPTS_VALUE;
+  if (!m_render_sw)
+    m_videobuffer.mediacodec = NULL;
+
+  return true;
+}
+
+bool CDVDVideoCodecAndroidMediaCodec::ClearPicture(DVDVideoPicture* pDvdVideoPicture)
+{
+  if (pDvdVideoPicture->format == RENDER_FMT_MEDIACODEC)
+    SAFE_RELEASE(pDvdVideoPicture->mediacodec);
+  memset(pDvdVideoPicture, 0x00, sizeof(DVDVideoPicture));
+
+  return true;
+}
+
+void CDVDVideoCodecAndroidMediaCodec::SetDropState(bool bDrop)
+{
+  m_drop = bDrop;
+  if (m_drop)
+    m_videobuffer.iFlags |=  DVP_FLAG_DROPPED;
+  else
+    m_videobuffer.iFlags &= ~DVP_FLAG_DROPPED;
+}
+
+int CDVDVideoCodecAndroidMediaCodec::GetDataSize(void)
+{
+  // just ignore internal buffering contribution.
+  return 0;
+}
+
+double CDVDVideoCodecAndroidMediaCodec::GetTimeSize(void)
+{
+  // just ignore internal buffering contribution.
+  return 0.0;
+}
+
+unsigned CDVDVideoCodecAndroidMediaCodec::GetAllowedReferences()
+{
+  return 3;
+}
+
+void CDVDVideoCodecAndroidMediaCodec::FlushInternal()
+{
+  // invalidate any existing inflight buffers and create
+  // new ones to match the number of output buffers
+
+  if (m_render_sw)
+    return;
+
+  for (size_t i = 0; i < m_inflight.size(); i++)
+    m_inflight[i]->Validate(false);
+  m_inflight.clear();
+
+  for (size_t i = 0; i < m_output.size(); i++)
+  {
+    m_inflight.push_back(
+      new CDVDMediaCodecInfo(i, m_textureId, m_codec, m_surfaceTexture, m_frameAvailable)
+    );
+  }
+}
+
+void CDVDVideoCodecAndroidMediaCodec::ConfigureMediaCodec(void)
+{
+  // setup a MediaFormat to match the video content,
+  // used by codec during configure
+  CJNIMediaFormat mediaformat = CJNIMediaFormat::createVideoFormat(
+    m_mime.c_str(), m_hints.width, m_hints.height);
+  // some android devices forget to default the demux input max size
+  mediaformat.setInteger(CJNIMediaFormat::KEY_MAX_INPUT_SIZE, 0);
+
+  // handle codec extradata
+  if (m_hints.extrasize)
+  {
+    size_t size = m_hints.extrasize;
+    void  *src_ptr = m_hints.extradata;
+    if (m_bitstream)
+    {
+      size = m_bitstream->GetExtraSize();
+      src_ptr = m_bitstream->GetExtraData();
+    }
+    // Allocate a byte buffer via allocateDirect in java instead of NewDirectByteBuffer,
+    // since the latter doesn't allocate storage of its own, and we don't know how long
+    // the codec uses the buffer.
+    CJNIByteBuffer bytebuffer = CJNIByteBuffer::allocateDirect(size);
+    void *dts_ptr = xbmc_jnienv()->GetDirectBufferAddress(bytebuffer.get_raw());
+    memcpy(dts_ptr, src_ptr, size);
+    // codec will automatically handle buffers as extradata
+    // using entries with keys "csd-0", "csd-1", etc.
+    mediaformat.setByteBuffer("csd-0", bytebuffer);
+  }
+
+  InitSurfaceTexture();
+
+  // configure and start the codec.
+  // use the MediaFormat that we have setup.
+  // use a null MediaCrypto, our content is not encrypted.
+  // use a null Surface, we will extract the video picture data manually.
+  int flags = 0;
+  CJNIMediaCrypto crypto(jni::jhobject(NULL));
+  // our jni gets upset if we do this a different
+  // way, do not mess with it.
+  if (m_render_sw)
+  {
+    CJNISurface surface(jni::jhobject(NULL));
+    m_codec->configure(mediaformat, surface, crypto, flags);
+  }
+  else
+  {
+    m_codec->configure(mediaformat, *m_surface, crypto, flags);
+  }
+
+  m_codec->start();
+
+  // always, check/clear jni exceptions.
+  if (xbmc_jnienv()->ExceptionOccurred())
+    xbmc_jnienv()->ExceptionClear();
+}
+
+int CDVDVideoCodecAndroidMediaCodec::GetOutputPicture(void)
+{
+  int rtn = 0;
+
+  int64_t timeout_us = 5000;
+  CJNIMediaCodecBufferInfo bufferInfo;
+  int index = m_codec->dequeueOutputBuffer(bufferInfo, timeout_us);
+  if (index >= 0)
+  {
+    if (m_drop)
+    {
+      m_codec->releaseOutputBuffer(index, false);
+      if (xbmc_jnienv()->ExceptionOccurred())
+        xbmc_jnienv()->ExceptionClear();
+      return 0;
+    }
+
+    // some devices will return a valid index
+    // before signaling INFO_OUTPUT_BUFFERS_CHANGED which
+    // is used to setup m_output, D'uh. setup m_output here.
+    if (m_output.empty())
+    {
+      m_output = m_codec->getOutputBuffers();
+      FlushInternal();
+    }
+
+    int flags = bufferInfo.flags();
+    if (flags & CJNIMediaCodec::BUFFER_FLAG_SYNC_FRAME)
+      CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_SYNC_FRAME");
+
+    if (flags & CJNIMediaCodec::BUFFER_FLAG_CODEC_CONFIG)
+      CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_CODEC_CONFIG");
+
+    if (flags & CJNIMediaCodec::BUFFER_FLAG_END_OF_STREAM)
+    {
+      CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: BUFFER_FLAG_END_OF_STREAM");
+      m_codec->releaseOutputBuffer(index, false);
+      if (xbmc_jnienv()->ExceptionOccurred())
+        xbmc_jnienv()->ExceptionClear();
+      return 0;
+    }
+
+    if (!m_render_sw)
+    {
+      m_videobuffer.mediacodec = m_inflight[index]->Retain();
+      m_videobuffer.mediacodec->Validate(true);
+    }
+    else
+    {
+      int size = bufferInfo.size();
+      int offset = bufferInfo.offset();
+
+      if (!m_output[index].isDirect())
+        CLog::Log(LOGWARNING, "CDVDVideoCodecAndroidMediaCodec:: m_output[index].isDirect == false");
+
+      if (size && m_output[index].capacity())
+      {
+        uint8_t *src_ptr = (uint8_t*)xbmc_jnienv()->GetDirectBufferAddress(m_output[index].get_raw());
+        src_ptr += offset;
+
+        int loop_end;
+        if (m_videobuffer.format == RENDER_FMT_YUV420P)
+          loop_end = 3;
+        else if (m_videobuffer.format == RENDER_FMT_NV12)
+          loop_end = 2;
+
+        for (int i = 0; i < loop_end; i++)
+        {
+          uint8_t *src   = src_ptr + m_src_offset[i];
+          int src_stride = m_src_stride[i];
+          uint8_t *dst   = m_videobuffer.data[i];
+          int dst_stride = m_videobuffer.iLineSize[i];
+
+          int height = m_videobuffer.iHeight;
+          if (i > 0)
+            height = (m_videobuffer.iHeight + 1) / 2;
+
+          for (int j = 0; j < height; j++, src += src_stride, dst += dst_stride)
+            memcpy(dst, src, dst_stride);
+        }
+      }
+      m_codec->releaseOutputBuffer(index, false);
+    }
+
+    int64_t pts= bufferInfo.presentationTimeUs();
+    m_videobuffer.dts = DVD_NOPTS_VALUE;
+    m_videobuffer.pts = DVD_NOPTS_VALUE;
+    if (pts != AV_NOPTS_VALUE)
+      m_videobuffer.pts = pts;
+
+/*
+    CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture "
+      "index(%d), pts(%f)", index, m_videobuffer.pts);
+*/
+    // always, check/clear jni exceptions.
+    if (xbmc_jnienv()->ExceptionOccurred())
+      xbmc_jnienv()->ExceptionClear();
+
+    rtn = 1;
+  }
+  else if (index == CJNIMediaCodec::INFO_OUTPUT_BUFFERS_CHANGED)
+  {
+    m_output = m_codec->getOutputBuffers();
+    FlushInternal();
+  }
+  else if (index == CJNIMediaCodec::INFO_OUTPUT_FORMAT_CHANGED)
+  {
+    OutputFormatChanged();
+  }
+  else if (index == CJNIMediaCodec::INFO_TRY_AGAIN_LATER)
+  {
+    // normal dequeueOutputBuffer timeout, ignore it.
+    rtn = -1;
+  }
+  else
+  {
+    // we should never get here
+    CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec::GetOutputPicture unknown index(%d)", index);
+  }
+
+  return rtn;
+}
+
+void CDVDVideoCodecAndroidMediaCodec::OutputFormatChanged(void)
+{
+  CJNIMediaFormat mediaformat = m_codec->getOutputFormat();
+
+  int width       = mediaformat.getInteger("width");
+  int height      = mediaformat.getInteger("height");
+  int stride      = mediaformat.getInteger("stride");
+  int slice_height= mediaformat.getInteger("slice-height");
+  int color_format= mediaformat.getInteger("color-format");
+  int crop_left   = mediaformat.getInteger("crop-left");
+  int crop_top    = mediaformat.getInteger("crop-top");
+  int crop_right  = mediaformat.getInteger("crop-right");
+  int crop_bottom = mediaformat.getInteger("crop-bottom");
+
+  CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
+    "width(%d), height(%d), stride(%d), slice-height(%d), color-format(%d)",
+    width, height, stride, slice_height, color_format);
+  CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: "
+    "crop-left(%d), crop-top(%d), crop-right(%d), crop-bottom(%d)",
+    crop_left, crop_top, crop_right, crop_bottom);
+
+  if (!m_render_sw)
+  {
+    CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: Direct Surface Rendering");
+    m_videobuffer.format = RENDER_FMT_MEDIACODEC;
+  }
+  else
+  {
+    // Android device quirks and fixes
+    if (stride <= 0)
+        stride = width;
+    if (slice_height <= 0)
+    {
+      slice_height = height;
+      if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
+      {
+        // NVidia Tegra 3 on Nexus 7 does not set slice_heights
+        if (strstr(m_codecname.c_str(), "OMX.Nvidia.") != NULL)
+        {
+          slice_height = (((height) + 31) & ~31);
+          CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: NVidia Tegra 3 quirk, slice_height(%d)", slice_height);
+        }
+      }
+    }
+    if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar)
+    {
+      slice_height -= crop_top / 2;
+      // set crop top/left here, since the offset parameter already includes this.
+      // if we would ignore the offset parameter in the BufferInfo, we could just keep
+      // the original slice height and apply the top/left cropping instead.
+      crop_top = 0;
+      crop_left = 0;
+    }
+
+    // default picture format to none
+    for (int i = 0; i < 4; i++)
+      m_src_offset[i] = m_src_stride[i] = 0;
+    // delete any existing buffers
+    for (int i = 0; i < 4; i++)
+      free(m_videobuffer.data[i]);
+
+    // setup picture format and data offset vectors
+    if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420Planar)
+    {
+      CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420Planar");
+
+      // Y plane
+      m_src_stride[0] = stride;
+      m_src_offset[0] = crop_top * stride;
+      m_src_offset[0]+= crop_left;
+
+      // U plane
+      m_src_stride[1] = (stride + 1) / 2;
+      //  skip over the Y plane
+      m_src_offset[1] = slice_height * stride;
+      //  crop_top/crop_left divided by two
+      //  because one byte of the U/V planes
+      //  corresponds to two pixels horizontally/vertically
+      m_src_offset[1]+= crop_top  / 2 * m_src_stride[1];
+      m_src_offset[1]+= crop_left / 2;
+
+      // V plane
+      m_src_stride[2] = (stride + 1) / 2;
+      //  skip over the Y plane
+      m_src_offset[2] = slice_height * stride;
+      //  skip over the U plane
+      m_src_offset[2]+= ((slice_height + 1) / 2) * ((stride + 1) / 2);
+      //  crop_top/crop_left divided by two
+      //  because one byte of the U/V planes
+      //  corresponds to two pixels horizontally/vertically
+      m_src_offset[2]+= crop_top  / 2 * m_src_stride[2];
+      m_src_offset[2]+= crop_left / 2;
+
+      m_videobuffer.iLineSize[0] =  width;         // Y
+      m_videobuffer.iLineSize[1] = (width + 1) /2; // U
+      m_videobuffer.iLineSize[2] = (width + 1) /2; // V
+      m_videobuffer.iLineSize[3] = 0;
+
+      unsigned int iPixels = width * height;
+      unsigned int iChromaPixels = iPixels/4;
+      m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
+      m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
+      m_videobuffer.data[2] = (uint8_t*)malloc(16 + iChromaPixels);
+      m_videobuffer.data[3] = NULL;
+      m_videobuffer.format  = RENDER_FMT_YUV420P;
+    }
+    else if (color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_FormatYUV420SemiPlanar
+          || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_QCOM_FormatYUV420SemiPlanar
+          || color_format == CJNIMediaCodecInfoCodecCapabilities::COLOR_TI_FormatYUV420PackedSemiPlanar)
+    {
+      CLog::Log(LOGDEBUG, "CDVDVideoCodecAndroidMediaCodec:: COLOR_FormatYUV420SemiPlanar");
+
+      // Y plane
+      m_src_stride[0] = stride;
+      m_src_offset[0] = crop_top * stride;
+      m_src_offset[0]+= crop_left;
+
+      // UV plane
+      m_src_stride[1] = stride;
+      //  skip over the Y plane
+      m_src_offset[1] = slice_height * stride;
+      m_src_offset[1]+= crop_top * stride;
+      m_src_offset[1]+= crop_left;
+
+      m_videobuffer.iLineSize[0] = width;  // Y
+      m_videobuffer.iLineSize[1] = width;  // UV
+      m_videobuffer.iLineSize[2] = 0;
+      m_videobuffer.iLineSize[3] = 0;
+
+      unsigned int iPixels = width * height;
+      unsigned int iChromaPixels = iPixels;
+      m_videobuffer.data[0] = (uint8_t*)malloc(16 + iPixels);
+      m_videobuffer.data[1] = (uint8_t*)malloc(16 + iChromaPixels);
+      m_videobuffer.data[2] = NULL;
+      m_videobuffer.data[3] = NULL;
+      m_videobuffer.format  = RENDER_FMT_NV12;
+    }
+    else
+    {
+      CLog::Log(LOGERROR, "CDVDVideoCodecAndroidMediaCodec:: Fixme unknown color_format(%d)", color_format);
+      return;
+    }
+  }
+
+  // picture display width/height include the cropping.
+  m_videobuffer.iDisplayWidth  = crop_right  + 1 - crop_left;
+  m_videobuffer.iDisplayHeight = crop_bottom + 1 - crop_top;
+
+  // clear any jni exceptions
+  if (xbmc_jnienv()->ExceptionOccurred())
+    xbmc_jnienv()->ExceptionClear();
+}
+
+void CDVDVideoCodecAndroidMediaCodec::CallbackInitSurfaceTexture(void *userdata)
+{
+  CDVDVideoCodecAndroidMediaCodec *ctx = static_cast<CDVDVideoCodecAndroidMediaCodec*>(userdata);
+  ctx->InitSurfaceTexture();
+}
+
+void CDVDVideoCodecAndroidMediaCodec::InitSurfaceTexture(void)
+{
+  if (m_render_sw)
+    return;
+
+  // We MUST create the GLES texture on the main thread
+  // to match where the valid GLES context is located.
+  // It would be nice to move this out of here, we would need
+  // to create/fetch/create from g_RenderMananger. But g_RenderMananger
+  // does not know we are using MediaCodec until Configure and we
+  // we need m_surfaceTexture valid before then. Chicken, meet Egg.
+  if (g_application.IsCurrentThread())
+  {
+    // localize GLuint so we do not spew gles includes in our header
+    GLuint texture_id;
+
+    glGenTextures(1, &texture_id);
+    glBindTexture(  GL_TEXTURE_EXTERNAL_OES, texture_id);
+    glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glBindTexture(  GL_TEXTURE_EXTERNAL_OES, 0);
+    m_textureId = texture_id;
+
+    m_surfaceTexture = boost::shared_ptr<CJNISurfaceTexture>(new CJNISurfaceTexture(m_textureId));
+    // hook the surfaceTexture OnFrameAvailable callback
+    m_frameAvailable = boost::shared_ptr<CDVDMediaCodecOnFrameAvailable>(new CDVDMediaCodecOnFrameAvailable(m_surfaceTexture));
+    m_surface = new CJNISurface(*m_surfaceTexture);
+  }
+  else
+  {
+    ThreadMessageCallback callbackData;
+    callbackData.callback = &CallbackInitSurfaceTexture;
+    callbackData.userptr  = (void*)this;
+
+    ThreadMessage msg;
+    msg.dwMessage = TMSG_CALLBACK;
+    msg.lpVoid = (void*)&callbackData;
+
+    // wait for it.
+    CApplicationMessenger::Get().SendMessage(msg, true);
+  }
+
+  return;
+}
+
+void CDVDVideoCodecAndroidMediaCodec::ReleaseSurfaceTexture(void)
+{
+  if (m_render_sw)
+    return;
+
+  // it is safe to delete here even though these items
+  // were created in the main thread instance
+  SAFE_DELETE(m_surface);
+  m_frameAvailable.reset();
+  m_surfaceTexture.reset();
+
+  if (m_textureId > 0)
+  {
+    GLuint texture_id = m_textureId;
+    glDeleteTextures(1, &texture_id);
+    m_textureId = 0;
+  }
+}
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.h
new file mode 100644 (file)
index 0000000..7e2c069
--- /dev/null
@@ -0,0 +1,142 @@
+#pragma once
+/*
+ *      Copyright (C) 2013 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <queue>
+#include <vector>
+#include <boost/shared_ptr.hpp>
+
+#include "DVDVideoCodec.h"
+#include "DVDStreamInfo.h"
+#include "threads/Thread.h"
+#include "threads/SingleLock.h"
+
+class CJNISurface;
+class CJNISurfaceTexture;
+class CJNIMediaCodec;
+class CDVDMediaCodecOnFrameAvailable;
+class CJNIByteBuffer;
+class CBitstreamConverter;
+
+typedef struct amc_demux {
+  uint8_t  *pData;
+  int       iSize;
+  double    dts;
+  double    pts;
+} amc_demux;
+
+class CDVDMediaCodecInfo
+{
+public:
+  CDVDMediaCodecInfo( int index,
+                      unsigned int texture,
+                      boost::shared_ptr<CJNIMediaCodec> &codec,
+                      boost::shared_ptr<CJNISurfaceTexture> &surfacetexture,
+                      boost::shared_ptr<CDVDMediaCodecOnFrameAvailable> &frameready);
+
+  // reference counting
+  CDVDMediaCodecInfo* Retain();
+  long                Release();
+
+  // meat and potatos
+  void                Validate(bool state);
+  // MediaCodec related
+  void                ReleaseOutputBuffer(bool render);
+  // SurfaceTexture released
+  int                 GetTextureID() const;
+  void                GetTransformMatrix(float *textureMatrix);
+  void                UpdateTexImage();
+
+private:
+  // private because we are reference counted
+  virtual            ~CDVDMediaCodecInfo();
+
+  long                m_refs;
+  bool                m_valid;
+  int                 m_index;
+  unsigned int        m_texture;
+  int64_t             m_timestamp;
+  CCriticalSection    m_section;
+  // shared_ptr bits, shared between
+  // CDVDVideoCodecAndroidMediaCodec and LinuxRenderGLES.
+  boost::shared_ptr<CJNIMediaCodec> m_codec;
+  boost::shared_ptr<CJNISurfaceTexture> m_surfacetexture;
+  boost::shared_ptr<CDVDMediaCodecOnFrameAvailable> m_frameready;
+};
+
+class CDVDVideoCodecAndroidMediaCodec : public CDVDVideoCodec
+{
+public:
+  CDVDVideoCodecAndroidMediaCodec();
+  virtual ~CDVDVideoCodecAndroidMediaCodec();
+
+  // required overrides
+  virtual bool    Open(CDVDStreamInfo &hints, CDVDCodecOptions &options);
+  virtual void    Dispose();
+  virtual int     Decode(uint8_t *pData, int iSize, double dts, double pts);
+  virtual void    Reset();
+  virtual bool    GetPicture(DVDVideoPicture *pDvdVideoPicture);
+  virtual bool    ClearPicture(DVDVideoPicture* pDvdVideoPicture);
+  virtual void    SetDropState(bool bDrop);
+  virtual int     GetDataSize(void);
+  virtual double  GetTimeSize(void);
+  virtual const char* GetName(void) { return m_formatname; }
+  virtual unsigned GetAllowedReferences();
+
+protected:
+  void            FlushInternal(void);
+  void            ConfigureMediaCodec(void);
+  int             GetOutputPicture(void);
+  void            OutputFormatChanged(void);
+
+  // surface handling functions
+  static void     CallbackInitSurfaceTexture(void*);
+  void            InitSurfaceTexture(void);
+  void            ReleaseSurfaceTexture(void);
+
+  CDVDStreamInfo  m_hints;
+  std::string     m_mime;
+  std::string     m_codecname;
+  const char     *m_formatname;
+  bool            m_opened;
+  bool            m_drop;
+
+  CJNISurface    *m_surface;
+  unsigned int    m_textureId;
+  // we need these as shared_ptr because CDVDVideoCodecAndroidMediaCodec
+  // will get deleted before CLinuxRendererGLES is shut down and
+  // CLinuxRendererGLES refs them via CDVDMediaCodecInfo.
+  boost::shared_ptr<CJNIMediaCodec> m_codec;
+  boost::shared_ptr<CJNISurfaceTexture> m_surfaceTexture;
+  boost::shared_ptr<CDVDMediaCodecOnFrameAvailable> m_frameAvailable;
+
+  std::queue<amc_demux> m_demux;
+  std::vector<CJNIByteBuffer> m_input;
+  std::vector<CJNIByteBuffer> m_output;
+  std::vector<CDVDMediaCodecInfo*> m_inflight;
+
+  CBitstreamConverter *m_bitstream;
+  DVDVideoPicture m_videobuffer;
+
+  bool            m_render_sw;
+  int             m_src_offset[4];
+  int             m_src_stride[4];
+};
index 043f570..b0819a8 100644 (file)
@@ -41,6 +41,10 @@ INCLUDES += -I$(prefix)/include/amcodec
 INCLUDES += -I$(prefix)/include/amplayer
 endif
 
+ifeq (@USE_ANDROID@,1)
+SRCS += DVDVideoCodecAndroidMediaCodec.cpp
+endif
+
 LIB=Video.a
 
 include @abs_top_srcdir@/Makefile.include
index d35751c..2321d2c 100644 (file)
@@ -999,6 +999,7 @@ static std::string GetRenderFormatName(ERenderFormat format)
     case RENDER_FMT_CVBREF:    return "BGRA";
     case RENDER_FMT_EGLIMG:    return "EGLIMG";
     case RENDER_FMT_BYPASS:    return "BYPASS";
+    case RENDER_FMT_MEDIACODEC:return "MEDIACODEC";
     case RENDER_FMT_NONE:      return "NONE";
   }
   return "UNKNOWN";
index a3eb94a..11089b8 100644 (file)
@@ -39,6 +39,7 @@ CGUIShader::CGUIShader( const char *shader ) : CGLSLShaderProgram("guishader_ver
   m_hCord0  = 0;
   m_hCord1  = 0;
   m_hUniCol = 0;
+  m_hCoord0Matrix = 0;
 
   m_proj   = NULL;
   m_model  = NULL;
@@ -54,8 +55,11 @@ void CGUIShader::OnCompiledAndLinked()
   m_hUniCol   = glGetUniformLocation(ProgramHandle(), "m_unicol");
 
   // Variables passed directly to the Vertex shader
-  m_hProj   = glGetUniformLocation(ProgramHandle(), "m_proj");
-  m_hModel  = glGetUniformLocation(ProgramHandle(), "m_model");
+  m_hProj  = glGetUniformLocation(ProgramHandle(), "m_proj");
+  m_hModel = glGetUniformLocation(ProgramHandle(), "m_model");
+  m_hCoord0Matrix = glGetUniformLocation(ProgramHandle(), "m_coord0Matrix");
+
+  // Vertex attributes
   m_hPos    = glGetAttribLocation(ProgramHandle(),  "m_attrpos");
   m_hCol    = glGetAttribLocation(ProgramHandle(),  "m_attrcol");
   m_hCord0  = glGetAttribLocation(ProgramHandle(),  "m_attrcord0");
@@ -66,6 +70,15 @@ void CGUIShader::OnCompiledAndLinked()
   glUniform1i(m_hTex0, 0);
   glUniform1i(m_hTex1, 1);
   glUniform4f(m_hUniCol, 1.0, 1.0, 1.0, 1.0);
+
+  const float identity[16] = {
+    1.0f, 0.0f, 0.0f, 0.0f,
+    0.0f, 1.0f, 0.0f, 0.0f,
+    0.0f, 0.0f, 1.0f, 0.0f,
+    0.0f, 0.0f, 0.0f, 1.0f
+  };
+  glUniformMatrix4fv(m_hCoord0Matrix,  1, GL_FALSE, identity);
+
   glUseProgram( 0 );
 }
 
index a55c246..c7e95aa 100644 (file)
@@ -40,7 +40,8 @@ public:
   GLint GetCord0Loc() { return m_hCord0; }
   GLint GetCord1Loc() { return m_hCord1; }
   GLint GetUniColLoc() { return m_hUniCol; }
-  
+  GLint GetCoord0MatrixLoc() { return m_hCoord0Matrix; }
+
 protected:
   GLint m_hTex0;
   GLint m_hTex1;
@@ -51,6 +52,7 @@ protected:
   GLint m_hCol;
   GLint m_hCord0;
   GLint m_hCord1;
+  GLint m_hCoord0Matrix;
 
   GLfloat *m_proj;
   GLfloat *m_model;
index f43abf4..46015bd 100644 (file)
@@ -41,6 +41,7 @@ static const char* ShaderNames[SM_ESHADERCOUNT] =
      "guishader_frag_texture_noblend.glsl",
      "guishader_frag_multi_blendcolor.glsl",
      "guishader_frag_rgba.glsl",
+     "guishader_frag_rgba_oes.glsl",
      "guishader_frag_rgba_blendcolor.glsl"
     };
 
@@ -640,4 +641,12 @@ GLint CRenderSystemGLES::GUIShaderGetUniCol()
   return -1;
 }
 
+GLint CRenderSystemGLES::GUIShaderGetCoord0Matrix()
+{
+  if (m_pGUIshader[m_method])
+    return m_pGUIshader[m_method]->GetCoord0MatrixLoc();
+
+  return -1;
+}
+
 #endif
index dd41366..b0e4a19 100644 (file)
@@ -37,6 +37,7 @@ enum ESHADERMETHOD
   SM_TEXTURE_NOBLEND,
   SM_MULTI_BLENDCOLOR,
   SM_TEXTURE_RGBA,
+  SM_TEXTURE_RGBA_OES,
   SM_TEXTURE_RGBA_BLENDCOLOR,
   SM_ESHADERCOUNT
 };
@@ -76,7 +77,7 @@ public:
   virtual bool TestRender();
 
   virtual void Project(float &x, float &y, float &z);
-  
+
   void InitialiseGUIShader();
   void EnableGUIShader(ESHADERMETHOD method);
   void DisableGUIShader();
@@ -86,12 +87,13 @@ public:
   GLint GUIShaderGetCoord0();
   GLint GUIShaderGetCoord1();
   GLint GUIShaderGetUniCol();
+  GLint GUIShaderGetCoord0Matrix();
 
 protected:
   virtual void SetVSyncImpl(bool enable) = 0;
   virtual bool PresentRenderImpl(const CDirtyRegionList &dirty) = 0;
   void CalculateMaxTexturesize();
-  
+
   int        m_iVSyncMode;
   int        m_iVSyncErrors;
   int64_t    m_iSwapStamp;
index be6990d..da10a5d 100644 (file)
@@ -82,6 +82,9 @@
 #include "utils/XBMCTinyXML.h"
 #include "view/ViewStateSettings.h"
 #include "windowing/WindowingFactory.h"
+#if defined(TARGET_ANDROID)
+#include "android/activity/AndroidFeatures.h"
+#endif
 
 #if defined(HAS_LIBAMCODEC)
 #include "utils/AMLUtils.h"
@@ -759,6 +762,10 @@ void CSettings::InitializeConditions()
 #ifdef HAVE_LIBVDPAU
   m_settingsManager->AddCondition("have_libvdpau");
 #endif
+#ifdef TARGET_ANDROID
+  if (CAndroidFeatures::GetVersion() > 15)
+    m_settingsManager->AddCondition("has_mediacodec");
+#endif
 #ifdef HAVE_VIDEOTOOLBOXDECODER
   m_settingsManager->AddCondition("have_videotoolboxdecoder");
   if (g_sysinfo.HasVideoToolBoxDecoder())