2 * Copyright (C) 2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
20 /***************************************************************************/
22 //#define DEBUG_VERBOSE 1
24 #include "StageFrightVideoPrivate.h"
27 #include <EGL/eglext.h>
28 #include <GLES2/gl2.h>
29 #include <GLES2/gl2ext.h>
30 #include "windowing/egl/EGLWrapper.h"
31 #include "Application.h"
32 #include "ApplicationMessenger.h"
33 #include "windowing/WindowingFactory.h"
34 #include "settings/AdvancedSettings.h"
35 #include "utils/log.h"
36 #include "threads/Thread.h"
38 #include "android/jni/Surface.h"
39 #include "android/jni/SurfaceTexture.h"
41 #define CLASSNAME "CStageFrightVideoPrivate"
44 #define CheckEglError(x) while((glerror = eglGetError()) != EGL_SUCCESS) CLog::Log(LOGERROR, "EGL error in %s: %x",x, glerror);
45 #define CheckGlError(x) while((glerror = glGetError()) != GL_NO_ERROR) CLog::Log(LOGERROR, "GL error in %s: %x",x, glerror);
47 // EGL extension functions
48 static PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
49 static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
50 static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
52 int NP2( unsigned x ) {
62 CStageFrightVideoPrivate::CStageFrightVideoPrivate()
63 : decode_thread(NULL), source(NULL)
64 , eglDisplay(EGL_NO_DISPLAY), eglSurface(EGL_NO_SURFACE), eglContext(EGL_NO_CONTEXT)
65 , eglInitialized(false)
67 , quirks(QuirkNone), cur_frame(NULL), prev_frame(NULL)
68 , width(-1), height(-1)
69 , texwidth(-1), texheight(-1)
70 , client(NULL), decoder(NULL), decoder_component(NULL)
71 , drop_state(false), resetting(false)
73 if (!eglCreateImageKHR)
74 eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC) CEGLWrapper::GetProcAddress("eglCreateImageKHR");
75 if (!eglDestroyImageKHR)
76 eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC) CEGLWrapper::GetProcAddress("eglDestroyImageKHR");
77 if (!glEGLImageTargetTexture2DOES)
78 glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) CEGLWrapper::GetProcAddress("glEGLImageTargetTexture2DOES");
80 for (int i=0; i<INBUFCOUNT; ++i)
84 void CStageFrightVideoPrivate::signalBufferReturned(MediaBuffer *buffer)
88 MediaBuffer* CStageFrightVideoPrivate::getBuffer(size_t size)
91 for (; i<INBUFCOUNT; ++i)
92 if (inbuf[i]->refcount() == 0 && inbuf[i]->size() >= size)
97 for (; i<INBUFCOUNT; ++i)
98 if (inbuf[i]->refcount() == 0)
102 inbuf[i]->setObserver(NULL);
104 inbuf[i] = new MediaBuffer(size);
105 inbuf[i]->setObserver(this);
113 bool CStageFrightVideoPrivate::inputBufferAvailable()
115 for (int i=0; i<INBUFCOUNT; ++i)
116 if (inbuf[i]->refcount() == 0)
122 stSlot* CStageFrightVideoPrivate::getSlot(EGLImageKHR eglimg)
124 for (int i=0; i<NUMFBOTEX; ++i)
125 if (texslots[i].eglimg == eglimg)
126 return &(texslots[i]);
131 stSlot* CStageFrightVideoPrivate::getFreeSlot()
133 for (int i=0; i<NUMFBOTEX; ++i)
134 if (texslots[i].use_cnt == 0)
135 return &(texslots[i]);
140 void CStageFrightVideoPrivate::loadOESShader(GLenum shaderType, const char* pSource, GLuint* outShader)
142 #if defined(DEBUG_VERBOSE)
143 CLog::Log(LOGDEBUG, ">>loadOESShader\n");
146 GLuint shader = glCreateShader(shaderType);
147 CheckGlError("loadOESShader");
149 glShaderSource(shader, 1, &pSource, NULL);
150 glCompileShader(shader);
152 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
155 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
157 char* buf = (char*) malloc(infoLen);
159 glGetShaderInfoLog(shader, infoLen, NULL, buf);
160 printf("Shader compile log:\n%s\n", buf);
164 char* buf = (char*) malloc(0x1000);
166 glGetShaderInfoLog(shader, 0x1000, NULL, buf);
167 printf("Shader compile log:\n%s\n", buf);
171 glDeleteShader(shader);
178 void CStageFrightVideoPrivate::createOESProgram(const char* pVertexSource, const char* pFragmentSource, GLuint* outPgm)
180 #if defined(DEBUG_VERBOSE)
181 CLog::Log(LOGDEBUG, ">>createOESProgram\n");
183 GLuint vertexShader, fragmentShader;
185 loadOESShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader);
188 loadOESShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader);
191 GLuint program = glCreateProgram();
193 glAttachShader(program, vertexShader);
194 glAttachShader(program, fragmentShader);
195 glLinkProgram(program);
196 GLint linkStatus = GL_FALSE;
197 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
198 if (linkStatus != GL_TRUE) {
200 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
202 char* buf = (char*) malloc(bufLength);
204 glGetProgramInfoLog(program, bufLength, NULL, buf);
205 printf("Program link log:\n%s\n", buf);
209 glDeleteProgram(program);
213 glDeleteShader(vertexShader);
214 glDeleteShader(fragmentShader);
218 void CStageFrightVideoPrivate::OES_shader_setUp()
222 "attribute vec4 vPosition;\n"
223 "varying vec2 texCoords;\n"
224 "uniform mat4 texMatrix;\n"
226 " vec2 vTexCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n"
227 " texCoords = (texMatrix * vec4(vTexCoords, 0.0, 1.0)).xy;\n"
228 " gl_Position = vPosition;\n"
232 "#extension GL_OES_EGL_image_external : require\n"
233 "precision mediump float;\n"
234 "uniform samplerExternalOES texSampler;\n"
235 "varying vec2 texCoords;\n"
237 " gl_FragColor = texture2D(texSampler, texCoords);\n"
241 #if defined(DEBUG_VERBOSE)
242 CLog::Log(LOGDEBUG, ">>OES_shader_setUp\n");
244 CheckGlError("OES_shader_setUp");
245 createOESProgram(vsrc, fsrc, &mPgm);
248 mPositionHandle = glGetAttribLocation(mPgm, "vPosition");
249 mTexSamplerHandle = glGetUniformLocation(mPgm, "texSampler");
250 mTexMatrixHandle = glGetUniformLocation(mPgm, "texMatrix");
253 void CStageFrightVideoPrivate::InitializeEGL(int w, int h)
255 #if defined(DEBUG_VERBOSE)
256 CLog::Log(LOGDEBUG, "%s: >>> InitializeEGL: w:%d; h:%d\n", CLASSNAME, w, h);
260 if (!m_g_Windowing->IsExtSupported("GL_TEXTURE_NPOT"))
262 texwidth = NP2(texwidth);
263 texheight = NP2(texheight);
266 eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
267 if (eglDisplay == EGL_NO_DISPLAY)
268 CLog::Log(LOGERROR, "%s: InitializeEGL: no display\n", CLASSNAME);
269 eglBindAPI(EGL_OPENGL_ES_API);
270 EGLint contextAttributes[] = {
271 EGL_CONTEXT_CLIENT_VERSION, 2,
274 eglContext = eglCreateContext(eglDisplay, m_g_Windowing->GetEGLConfig(), EGL_NO_CONTEXT, contextAttributes);
275 EGLint pbufferAttribs[] = {
277 EGL_HEIGHT, texheight,
280 eglSurface = eglCreatePbufferSurface(eglDisplay, m_g_Windowing->GetEGLConfig(), pbufferAttribs);
281 eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
282 CheckGlError("stf init");
284 static const EGLint imageAttributes[] = {
285 EGL_IMAGE_PRESERVED_KHR, EGL_FALSE,
286 EGL_GL_TEXTURE_LEVEL_KHR, 0,
290 for (int i=0; i<NUMFBOTEX; ++i)
292 glGenTextures(1, &(texslots[i].texid));
293 glBindTexture(GL_TEXTURE_2D, texslots[i].texid);
295 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texwidth, texheight, 0,
296 GL_RGBA, GL_UNSIGNED_BYTE, 0);
298 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
299 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
300 // This is necessary for non-power-of-two textures
301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
302 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
304 texslots[i].eglimg = eglCreateImageKHR(eglDisplay, eglContext, EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(texslots[i].texid),imageAttributes);
305 texslots[i].use_cnt = 0;
307 glBindTexture(GL_TEXTURE_2D, 0);
312 eglInitialized = true;
313 #if defined(DEBUG_VERBOSE)
314 CLog::Log(LOGDEBUG, "%s: <<< InitializeEGL: w:%d; h:%d\n", CLASSNAME, texwidth, texheight);
318 void CStageFrightVideoPrivate::ReleaseEGL()
320 #if defined(DEBUG_VERBOSE)
321 CLog::Log(LOGDEBUG, "%s: >>> UninitializeEGL\n", CLASSNAME);
324 for (int i=0; i<NUMFBOTEX; ++i)
326 glDeleteTextures(1, &(texslots[i].texid));
327 eglDestroyImageKHR(eglDisplay, texslots[i].eglimg);
330 if (eglContext != EGL_NO_CONTEXT)
331 eglDestroyContext(eglDisplay, eglContext);
332 eglContext = EGL_NO_CONTEXT;
334 if (eglSurface != EGL_NO_SURFACE)
335 eglDestroySurface(eglDisplay, eglSurface);
336 eglSurface = EGL_NO_SURFACE;
338 eglInitialized = false;
341 void CStageFrightVideoPrivate::CallbackInitSurfaceTexture(void *userdata)
343 CStageFrightVideoPrivate *ctx = static_cast<CStageFrightVideoPrivate*>(userdata);
344 ctx->InitSurfaceTexture();
347 bool CStageFrightVideoPrivate::InitSurfaceTexture()
349 #if defined(DEBUG_VERBOSE)
350 CLog::Log(LOGDEBUG, "%s: >>> InitSurfaceTexture\n", CLASSNAME);
352 if (mVideoNativeWindow != NULL)
355 //FIXME: Playing back-to-back vids induces a bug when properly generating textures between runs.
356 // Symptoms are upside down vid, "updateTexImage: error binding external texture" in log, and crash
358 // Workaround is to always use the same, arbitrary chosen, texture ids.
359 mVideoTextureId = 0xbaad;
361 glBindTexture( GL_TEXTURE_EXTERNAL_OES, mVideoTextureId);
362 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
363 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
364 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
365 glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
366 glBindTexture( GL_TEXTURE_EXTERNAL_OES, 0);
368 mSurfTexture = new CJNISurfaceTexture(mVideoTextureId);
369 mSurface = new CJNISurface(*mSurfTexture);
371 JNIEnv* env = xbmc_jnienv();
372 mVideoNativeWindow = ANativeWindow_fromSurface(env, mSurface->get_raw());
373 native_window_api_connect(mVideoNativeWindow.get(), NATIVE_WINDOW_API_MEDIA);
375 #if defined(DEBUG_VERBOSE)
376 CLog::Log(LOGDEBUG, "%s: <<< InitSurfaceTexture texid(%d) natwin(%p)\n", CLASSNAME, mVideoTextureId, mVideoNativeWindow.get());
382 void CStageFrightVideoPrivate::ReleaseSurfaceTexture()
384 #if defined(DEBUG_VERBOSE)
385 CLog::Log(LOGDEBUG, "%s: >>> ReleaseSurfaceTexture\n", CLASSNAME);
387 if (mVideoNativeWindow == NULL)
390 native_window_api_disconnect(mVideoNativeWindow.get(), NATIVE_WINDOW_API_MEDIA);
391 ANativeWindow_release(mVideoNativeWindow.get());
392 mVideoNativeWindow.clear();
395 mSurfTexture->release();
400 #if defined(DEBUG_VERBOSE)
401 CLog::Log(LOGDEBUG, "%s: <<< ReleaseSurfaceTexture\n", CLASSNAME);
405 void CStageFrightVideoPrivate::UpdateSurfaceTexture()
407 mSurfTexture->updateTexImage();
410 void CStageFrightVideoPrivate::GetSurfaceTextureTransformMatrix(float* transformMatrix)
412 mSurfTexture->getTransformMatrix(transformMatrix);