aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordavilla <davilla@4pi.com>2013-08-09 09:56:45 -0400
committerdavilla <davilla@4pi.com>2013-10-02 11:39:49 -0400
commit93ef38aa04a0e6dea6dbeac65ab6590524cc044e (patch)
tree213cc7b3c545e7f25dce0900cbd110e00c15aeb4
parente70222d518b65ad59e1d59a8c3b33992067bb5b3 (diff)
droid: Add Android MediaCodec for DVDPlayer
-rwxr-xr-xlanguage/English/strings.po11
-rw-r--r--system/settings/android.xml11
-rw-r--r--system/shaders/guishader_frag_rgba_oes.glsl31
-rw-r--r--system/shaders/guishader_vert.glsl3
-rw-r--r--xbmc/android/jni/MediaCodecBufferInfo.cpp2
-rw-r--r--xbmc/android/jni/SurfaceTexture.cpp8
-rw-r--r--xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp169
-rw-r--r--xbmc/cores/VideoRenderers/LinuxRendererGLES.h18
-rw-r--r--xbmc/cores/VideoRenderers/RenderFormats.h1
-rw-r--r--xbmc/cores/VideoRenderers/RenderManager.cpp5
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp16
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h6
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp1009
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.h142
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in4
-rw-r--r--xbmc/cores/dvdplayer/DVDPlayerVideo.cpp1
-rw-r--r--xbmc/guilib/GUIShader.cpp17
-rw-r--r--xbmc/guilib/GUIShader.h4
-rw-r--r--xbmc/rendering/gles/RenderSystemGLES.cpp9
-rw-r--r--xbmc/rendering/gles/RenderSystemGLES.h6
-rw-r--r--xbmc/settings/Settings.cpp7
21 files changed, 1464 insertions, 16 deletions
diff --git a/language/English/strings.po b/language/English/strings.po
index 0b6221a776..24c40852a6 100755
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -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
diff --git a/system/settings/android.xml b/system/settings/android.xml
index 756d223f94..ce46a88af4 100644
--- a/system/settings/android.xml
+++ b/system/settings/android.xml
@@ -27,4 +27,15 @@
</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
index 0000000000..828d48bef3
--- /dev/null
+++ b/system/shaders/guishader_frag_rgba_oes.glsl
@@ -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;
+}
diff --git a/system/shaders/guishader_vert.glsl b/system/shaders/guishader_vert.glsl
index 65ca101987..46f3399521 100644
--- a/system/shaders/guishader_vert.glsl
+++ b/system/shaders/guishader_vert.glsl
@@ -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;
}
diff --git a/xbmc/android/jni/MediaCodecBufferInfo.cpp b/xbmc/android/jni/MediaCodecBufferInfo.cpp
index 80d99e16f9..e966e7363b 100644
--- a/xbmc/android/jni/MediaCodecBufferInfo.cpp
+++ b/xbmc/android/jni/MediaCodecBufferInfo.cpp
@@ -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)
diff --git a/xbmc/android/jni/SurfaceTexture.cpp b/xbmc/android/jni/SurfaceTexture.cpp
index 63ca3821d0..df8d5daa90 100644
--- a/xbmc/android/jni/SurfaceTexture.cpp
+++ b/xbmc/android/jni/SurfaceTexture.cpp
@@ -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();
diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
index ee437a8996..4727ccb543 100644
--- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
+++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
@@ -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
diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h
index e15ec05155..2c72e151ef 100644
--- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h
+++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h
@@ -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];
};
diff --git a/xbmc/cores/VideoRenderers/RenderFormats.h b/xbmc/cores/VideoRenderers/RenderFormats.h
index 3b091947d3..f15e80d892 100644
--- a/xbmc/cores/VideoRenderers/RenderFormats.h
+++ b/xbmc/cores/VideoRenderers/RenderFormats.h
@@ -36,6 +36,7 @@ enum ERenderFormat {
RENDER_FMT_CVBREF,
RENDER_FMT_BYPASS,
RENDER_FMT_EGLIMG,
+ RENDER_FMT_MEDIACODEC,
};
#endif
diff --git a/xbmc/cores/VideoRenderers/RenderManager.cpp b/xbmc/cores/VideoRenderers/RenderManager.cpp
index fbc2aaec11..249222a06a 100644
--- a/xbmc/cores/VideoRenderers/RenderManager.cpp
+++ b/xbmc/cores/VideoRenderers/RenderManager.cpp
@@ -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);
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
index bc3072a2a0..183e2c5163 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
+++ b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp
@@ -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 )
{
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h
index 87edaa5353..baee6e0dad 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h
@@ -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
index 0000000000..2447bbbaf4
--- /dev/null
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.cpp
@@ -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
index 0000000000..7e2c069a1e
--- /dev/null
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecAndroidMediaCodec.h
@@ -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];
+};
diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in b/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in
index 043f570700..b0819a8475 100644
--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in
+++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in
@@ -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
diff --git a/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp b/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
index d35751cfe1..2321d2ca7e 100644
--- a/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
+++ b/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
@@ -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";
diff --git a/xbmc/guilib/GUIShader.cpp b/xbmc/guilib/GUIShader.cpp
index a3eb94ab81..11089b8bef 100644
--- a/xbmc/guilib/GUIShader.cpp
+++ b/xbmc/guilib/GUIShader.cpp
@@ -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 );
}
diff --git a/xbmc/guilib/GUIShader.h b/xbmc/guilib/GUIShader.h
index a55c246327..c7e95aa98a 100644
--- a/xbmc/guilib/GUIShader.h
+++ b/xbmc/guilib/GUIShader.h
@@ -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;
diff --git a/xbmc/rendering/gles/RenderSystemGLES.cpp b/xbmc/rendering/gles/RenderSystemGLES.cpp
index f43abf4ba4..46015bdd4e 100644
--- a/xbmc/rendering/gles/RenderSystemGLES.cpp
+++ b/xbmc/rendering/gles/RenderSystemGLES.cpp
@@ -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
diff --git a/xbmc/rendering/gles/RenderSystemGLES.h b/xbmc/rendering/gles/RenderSystemGLES.h
index dd4136632d..b0e4a19e90 100644
--- a/xbmc/rendering/gles/RenderSystemGLES.h
+++ b/xbmc/rendering/gles/RenderSystemGLES.h
@@ -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;
diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp
index be6990d564..da10a5dd50 100644
--- a/xbmc/settings/Settings.cpp
+++ b/xbmc/settings/Settings.cpp
@@ -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())