* Copyright (c) 2016 Bilibili
* copyright (c) 2016 Zhang Rui <bbcallen@gmail.com>
*
* This file is part of ijkPlayer.
*
* ijkPlayer is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* ijkPlayer 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with ijkPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <stdio.h>
#include <syslog.h>
#include "ijksdl_egl.h"
#include <stdlib.h>
#include <stdbool.h>
#include "ijksdl_gles2.h"
#include "ijksdl_log.h"
#include "ijksdl_vout.h"
#include "video/gles2/internal.h"
#include <syslog.h>
#ifdef __OHOS__
#include "native_window/external_window.h"
#endif
#define IJK_EGL_RENDER_BUFFER 0
typedef struct IJK_EGL_Opaque {
IJK_GLES2_Renderer * renderer;
} IJK_EGL_Opaque;
static EGLBoolean IJK_EGL_isValid(IJK_EGL* egl)
{
if (!egl) {
return EGL_FALSE;
}
if (!egl->window) {
}
if (!egl->display) {
}
if (!egl->surface) {
}
if (!egl->context) {
}
if (egl &&
egl->window &&
egl->display &&
egl->surface &&
egl->context) {
return EGL_TRUE;
}
return EGL_FALSE;
}
void IJK_EGL_terminate(IJK_EGL* egl)
{
LOGI("IJK_EGL_terminate");
if (!IJK_EGL_isValid(egl))
return;
if (egl->opaque)
IJK_GLES2_Renderer_freeP(&egl->opaque->renderer);
if (egl->display) {
eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (egl->context)
eglDestroyContext(egl->display, egl->context);
if (egl->surface)
eglDestroySurface(egl->display, egl->surface);
eglTerminate(egl->display);
eglReleaseThread();
}
egl->context = EGL_NO_CONTEXT;
egl->surface = EGL_NO_SURFACE;
egl->display = EGL_NO_DISPLAY;
LOGI("IJK_EGL_terminate end");
}
static int IJK_EGL_getSurfaceWidth(IJK_EGL* egl)
{
if(egl->display&&egl->surface){
}
EGLint width = 0;
if (!eglQuerySurface(egl->display, egl->surface, EGL_WIDTH, &width)) {
return 0;
}
return width;
}
static int IJK_EGL_getSurfaceHeight(IJK_EGL* egl)
{
EGLint height = 0;
if (!eglQuerySurface(egl->display, egl->surface, EGL_HEIGHT, &height)) {
ALOGE("[EGL] eglQuerySurface(EGL_HEIGHT) returned error %d", eglGetError());
return 0;
}
return height;
}
static EGLBoolean IJK_EGL_setSurfaceSize(IJK_EGL* egl, int width, int height)
{
LOGI("IJK_EGL_setSurfaceSize");
if (!IJK_EGL_isValid(egl))
return EGL_FALSE;
egl->width = IJK_EGL_getSurfaceWidth(egl);
egl->height = IJK_EGL_getSurfaceHeight(egl);
if (width != egl->width || height != egl->height) {
}
return EGL_TRUE;
}
static EGLBoolean IJK_EGL_makeCurrent(IJK_EGL* egl, EGLNativeWindowType window)
{
LOGI("IJK_EGL_makeCurrent");
if (window && window == egl->window &&
egl->display &&
egl->surface &&
egl->context) {
if (!eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context)) {
ALOGE("[EGL] elgMakeCurrent() failed (cached)\n");
return EGL_FALSE;
}
return EGL_TRUE;
}
IJK_EGL_terminate(egl);
egl->window = window;
if (!window)
return EGL_FALSE;
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
ALOGE("[EGL] eglGetDisplay failed\n");
return EGL_FALSE;
}
EGLint major, minor;
if (!eglInitialize(display, &major, &minor)) {
ALOGE("[EGL] eglInitialize failed\n");
return EGL_FALSE;
}
ALOGI("[EGL] eglInitialize %d.%d\n", (int)major, (int)minor);
static const EGLint configAttribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_NONE
};
static const EGLint contextAttribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLConfig config;
EGLint numConfig;
if (!eglChooseConfig(display, configAttribs, &config, 1, &numConfig)) {
ALOGE("[EGL] eglChooseConfig failed\n");
eglTerminate(display);
return EGL_FALSE;
}
#ifdef __ANDROID__
{
EGLint native_visual_id = 0;
if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &native_visual_id)) {
ALOGE("[EGL] eglGetConfigAttrib() returned error %d", eglGetError());
eglTerminate(display);
return EGL_FALSE;
}
int32_t width = ANativeWindow_getWidth(window);
int32_t height = ANativeWindow_getWidth(window);
ALOGI("[EGL] ANativeWindow_setBuffersGeometry(f=%d);", native_visual_id);
int ret = ANativeWindow_setBuffersGeometry(window, width, height, native_visual_id);
if (ret) {
ALOGE("[EGL] ANativeWindow_setBuffersGeometry(format) returned error %d", ret);
eglTerminate(display);
return EGL_FALSE;
}
}
#endif
#ifdef __OHOS__
{
EGLint native_visual_id = 0;
if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &native_visual_id)) {
ALOGE("[EGL] eglGetConfigAttrib() returned error %d", eglGetError());
eglTerminate(display);
return EGL_FALSE;
}
int32_t code = SET_FORMAT;
ALOGI("[EGL] OH_NativeWindow_NativeWindowHandleOpt(f=%d);", native_visual_id);
int ret = OH_NativeWindow_NativeWindowHandleOpt(window, code, native_visual_id);
if (ret) {
ALOGE("[EGL] OH_NativeWindow_NativeWindowHandleOpt(format) returned error %d", ret);
eglTerminate(display);
return EGL_FALSE;
}
}
#endif
EGLSurface surface = eglCreateWindowSurface(display, config, window, NULL);
if (surface == EGL_NO_SURFACE) {
ALOGE("[EGL] eglCreateWindowSurface failed\n");
eglTerminate(display);
return EGL_FALSE;
}
EGLSurface context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
if (context == EGL_NO_CONTEXT) {
ALOGE("[EGL] eglCreateContext failed\n");
eglDestroySurface(display, surface);
eglTerminate(display);
return EGL_FALSE;
}
if (!eglMakeCurrent(display, surface, surface, context)) {
ALOGE("[EGL] elgMakeCurrent() failed (new)\n");
eglDestroyContext(display, context);
eglDestroySurface(display, surface);
eglTerminate(display);
return EGL_FALSE;
}
#if 0
#if defined(__ANDROID__)
{
const char * extensions = (const char *)glGetString(GL_EXTENSIONS);
if (extensions) {
char * dup_extensions = strdup(extensions);
if (dup_extensions) {
char * brk = NULL;
char * ext = strtok_r(dup_extensions, " ", &brk);
while (ext) {
if (0 == strcmp(ext, "GL_EXT_texture_rg"))
egl->gles2_extensions[IJK_GLES2__GL_EXT_texture_rg] = 1;
ext = strtok_r(NULL, " ", &brk);
}
free(dup_extensions);
}
}
}
#elif defined(__APPLE__)
egl->gles2_extensions[IJK_GLES2__GL_EXT_texture_rg] = 1;
#endif
ALOGI("[EGL] GLES2 extensions begin:\n");
ALOGI("[EGL] GL_EXT_texture_rg: %d\n", egl->gles2_extensions[IJK_GLES2__GL_EXT_texture_rg]);
ALOGI("[EGL] GLES2 extensions end.\n");
#endif
IJK_GLES2_Renderer_setupGLES();
egl->context = context;
egl->surface = surface;
egl->display = display;
return EGL_TRUE;
}
static EGLBoolean IJK_EGL_prepareRenderer(IJK_EGL* egl, SDL_VoutOverlay * overlay)
{
LOGI("IJK_EGL_prepareRenderer");
assert(egl);
assert(egl->opaque);
IJK_EGL_Opaque * opaque = egl->opaque;
if (!IJK_GLES2_Renderer_isValid(opaque->renderer) ||
!IJK_GLES2_Renderer_isFormat(opaque->renderer, overlay->format)) {
IJK_GLES2_Renderer_reset(opaque->renderer);
IJK_GLES2_Renderer_freeP(&opaque->renderer);
opaque->renderer = IJK_GLES2_Renderer_create(overlay);
if (!opaque->renderer) {
return EGL_FALSE;
}
if (!IJK_GLES2_Renderer_use(opaque->renderer)) {
IJK_GLES2_Renderer_freeP(&opaque->renderer);
return EGL_FALSE;
}
}
if (!IJK_EGL_setSurfaceSize(egl, overlay->w, overlay->h)) {
char c[100];
sprintf(c, "IJK_EGL_display [EGL] IJK_EGL_setSurfaceSize(%d, %d) failed\n", overlay->w, overlay->h);
return EGL_FALSE;
}
glViewport(0, 0, egl->width, egl->height);
IJK_GLES2_checkError_TRACE("glViewport");
return EGL_TRUE;
}
static EGLBoolean IJK_EGL_display_internal(IJK_EGL* egl, EGLNativeWindowType window, SDL_VoutOverlay * overlay)
{
LOGI("IJK_EGL_display_internal");
IJK_EGL_Opaque * opaque = egl->opaque;
if (!IJK_EGL_prepareRenderer(egl, overlay)) {
ALOGE("[EGL] IJK_EGL_prepareRenderer failed\n");
return EGL_FALSE;
}
if (!IJK_GLES2_Renderer_renderOverlay(opaque->renderer, overlay)) {
ALOGE("[EGL] IJK_GLES2_render failed\n");
return EGL_FALSE;
}
eglSwapBuffers(egl->display, egl->surface);
LOGI("IJK_EGL_display_internal end");
return EGL_TRUE;
}
EGLBoolean IJK_EGL_display(IJK_EGL* egl, EGLNativeWindowType window, SDL_VoutOverlay * overlay)
{
EGLBoolean ret = EGL_FALSE;
if (!egl)
return EGL_FALSE;
IJK_EGL_Opaque * opaque = egl->opaque;
if (!opaque)
return EGL_FALSE;
if (!IJK_EGL_makeCurrent(egl, window))
return EGL_FALSE;
ret = IJK_EGL_display_internal(egl, window, overlay);
eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglReleaseThread();
return ret;
}
void IJK_EGL_releaseWindow(IJK_EGL* egl)
{
if (!egl || !egl->opaque || !egl->opaque->renderer)
return;
IJK_EGL_terminate(egl);
}
void IJK_EGL_free(IJK_EGL * egl)
{
if (!egl)
return;
IJK_EGL_terminate(egl);
memset(egl, 0, sizeof(IJK_EGL));
free(egl);
}
void IJK_EGL_freep(IJK_EGL ** egl)
{
if (!egl || !*egl)
return;
IJK_EGL_free(*egl);
*egl = NULL;
}
static SDL_Class g_class = {
.name = "EGL",
};
IJK_EGL * IJK_EGL_create()
{
IJK_EGL * egl = (IJK_EGL*)mallocz(sizeof(IJK_EGL));
if (!egl)
return NULL;
egl->opaque_class = &g_class;
egl->opaque = mallocz(sizeof(IJK_EGL_Opaque));
if (!egl->opaque) {
free(egl);
return NULL;
}
return egl;
}