Commit ded6b3af authored by Lukasz Marek's avatar Lukasz Marek

lavd: add opengl device

It can render to OpenGL context provided by application or into SDL window
Signed-off-by: 's avatarLukasz Marek <lukasz.m.luki@gmail.com>
parent 102bd641
......@@ -21,6 +21,7 @@ version <next>
- framepack filter
- XYZ12 rawvideo support in NUT
- Exif metadata support in WebP decoder
- OpenGL device
version 2.1:
......
......@@ -251,6 +251,7 @@ External library support:
--enable-libzvbi enable teletext support via libzvbi [no]
--enable-openal enable OpenAL 1.1 capture support [no]
--enable-opencl enable OpenCL code
--enable-opengl enable OpenGL rendering [no]
--enable-openssl enable openssl [no]
--enable-x11grab enable X11 grabbing [no]
--disable-zlib disable zlib [autodetect]
......@@ -1311,6 +1312,7 @@ EXTERNAL_LIBRARY_LIST="
libzvbi
openal
opencl
opengl
openssl
x11grab
zlib
......@@ -1554,6 +1556,7 @@ HAVE_LIST="
dxva_h
ebp_available
ebx_available
ES2_gl_h
fast_64bit
fast_clz
fast_cmov
......@@ -1570,6 +1573,7 @@ HAVE_LIST="
getservbyport
gettimeofday
glob
glXGetProcAddress
gnu_as
gnu_windres
gsm_h
......@@ -1603,6 +1607,7 @@ HAVE_LIST="
mprotect
nanosleep
openjpeg_1_5_openjpeg_h
OpenGL_gl3_h
PeekNamedPipe
perl
pod2man
......@@ -1656,6 +1661,7 @@ HAVE_LIST="
vdpau_x11
vfp_args
VirtualAlloc
wglGetProcAddress
windows_h
winsock2_h
xform_asm
......@@ -2262,6 +2268,7 @@ libcdio_indev_deps="libcdio"
libdc1394_indev_deps="libdc1394"
libv4l2_indev_deps="libv4l2"
openal_indev_deps="openal"
opengl_outdev_deps="opengl"
oss_indev_deps_any="soundcard_h sys_soundcard_h"
oss_outdev_deps_any="soundcard_h sys_soundcard_h"
pulse_indev_deps="libpulse"
......@@ -4498,6 +4505,12 @@ enabled opencl && { check_lib2 OpenCL/cl.h clEnqueueNDRangeKernel -Wl
{ check_cpp_condition "OpenCL/cl.h" "defined(CL_VERSION_1_2)" ||
check_cpp_condition "CL/cl.h" "defined(CL_VERSION_1_2)" ||
die "ERROR: opencl must be installed and version must be 1.2 or compatible"; }
enabled opengl && { check_lib GL/glx.h glXGetProcAddress "-lGL" ||
check_lib2 windows.h wglGetProcAddress "-lopengl32 -lgdi32" ||
check_lib2 OpenGL/gl3.h glGetError "-Wl,-framework,OpenGL" ||
check_lib2 ES2/gl.h glGetError "-isysroot=${sysroot} -Wl,-framework,OpenGLES" ||
die "ERROR: opengl not found."
}
enabled openssl && { check_lib openssl/ssl.h SSL_library_init -lssl -lcrypto ||
check_lib openssl/ssl.h SSL_library_init -lssl32 -leay32 ||
check_lib openssl/ssl.h SSL_library_init -lssl -lcrypto -lws2_32 -lgdi32 ||
......
......@@ -149,6 +149,41 @@ ffmpeg -re -i INPUT -vcodec rawvideo -pix_fmt bgra -f fbdev /dev/fb0
See also @url{http://linux-fbdev.sourceforge.net/}, and fbset(1).
@section opengl
OpenGL output device.
To enable this output device you need to configure FFmpeg with @code{--enable-opengl}.
Device allows to render to OpenGL context.
Context may be provided by application or default SDL window is created.
When device renders to external context, application must implement handlers for following messages:
@code{AV_CTL_MESSAGE_CREATE_WINDOW_BUFFER} - create OpenGL context on current thread.
@code{AV_CTL_MESSAGE_PREPARE_WINDOW_BUFFER} - make OpenGL context current.
@code{AV_CTL_MESSAGE_DISPLAY_WINDOW_BUFFER} - swap buffers.
@code{AV_CTL_MESSAGE_DESTROY_WINDOW_BUFFER} - destroy OpenGL context.
Application is also required to inform a device about current resolution by sending @code{AV_DEVICE_WINDOW_RESIZED} message.
@subsection Options
@table @option
@item background
Set background color. Black is a default.
@item no_window
Disables default SDL window when set to non-zero value.
Application must provide OpenGL context and both @code{window_size_cb} and @code{window_swap_buffers_cb} callbacks when set.
@item window_title
Set the SDL window title, if not specified default to the filename specified for the output device.
Ignored when @option{no_window} is set.
@end table
@subsection Examples
Play a file on SDL window using OpenGL rendering:
@example
ffmpeg -i INPUT -f opengl "window title"
@end example
@section oss
OSS (Open Sound System) output device.
......
......@@ -29,6 +29,7 @@ OBJS-$(CONFIG_IEC61883_INDEV) += iec61883.o
OBJS-$(CONFIG_JACK_INDEV) += jack_audio.o timefilter.o
OBJS-$(CONFIG_LAVFI_INDEV) += lavfi.o
OBJS-$(CONFIG_OPENAL_INDEV) += openal-dec.o
OBJS-$(CONFIG_OPENGL_OUTDEV) += opengl_enc.o
OBJS-$(CONFIG_OSS_INDEV) += oss_audio.o
OBJS-$(CONFIG_OSS_OUTDEV) += oss_audio.o
OBJS-$(CONFIG_PULSE_INDEV) += pulse_audio_dec.o \
......
......@@ -56,6 +56,7 @@ void avdevice_register_all(void)
REGISTER_INDEV (JACK, jack);
REGISTER_INDEV (LAVFI, lavfi);
REGISTER_INDEV (OPENAL, openal);
REGISTER_OUTDEV (OPENGL, opengl);
REGISTER_INOUTDEV(OSS, oss);
REGISTER_INOUTDEV(PULSE, pulse);
REGISTER_OUTDEV (SDL, sdl);
......
/*
* Copyright (c) 2014 Lukasz Marek
*
* This file is part of FFmpeg.
*
* FFmpeg 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.
*
* FFmpeg 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 FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
//TODO: support for more formats
//TODO: support for more systems.
//TODO: implement X11, Windows, Mac OS native default window. SDL 1.2 doesn't allow to render to custom thread.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stddef.h>
#include "config.h"
#if HAVE_OPENGL_GL3_H
#include <OpenGL/gl3.h>
#elif HAVE_ES2_GL_H
#include <ES2/gl.h>
#else
#include <GL/gl.h>
#include <GL/glext.h>
#endif
#if HAVE_GLXGETPROCADDRESS
#include <GL/glx.h>
#endif
#if HAVE_WINDOWS_H
#include <windows.h>
#endif
#if HAVE_SDL
#include <SDL.h>
#endif
#include "libavutil/common.h"
#include "libavutil/pixdesc.h"
#include "libavutil/log.h"
#include "libavutil/opt.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
#include "opengl_enc_shaders.h"
#ifndef APIENTRY
#define APIENTRY
#endif
/* GL_RED_COMPONENT is used for plannar pixel types.
* Only red component is sampled in shaders.
* On some platforms GL_RED is not availabe and GL_LUMINANCE have to be used,
* but since OpenGL 3.0 GL_LUMINANCE is deprecated.
* GL_RED produces RGBA = value, 0, 0, 1.
* GL_LUMINANCE produces RGBA = value, value, value, 1.
* Note: GL_INTENSITY may also be used which produce RGBA = value, value, value, value. */
#if defined(GL_RED)
#define GL_RED_COMPONENT GL_RED
#elif defined(GL_LUMINANCE)
#define GL_RED_COMPONENT GL_LUMINANCE
#else
#define GL_RED_COMPONENT 0x1903; //GL_RED
#endif
/* Constants not defined for iOS */
#define FF_GL_UNSIGNED_BYTE_3_3_2 0x8032
#define FF_GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
#define FF_GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
/* MinGW exposes only OpenGL 1.1 API */
#define FF_GL_ARRAY_BUFFER 0x8892
#define FF_GL_ELEMENT_ARRAY_BUFFER 0x8893
#define FF_GL_STATIC_DRAW 0x88E4
#define FF_GL_FRAGMENT_SHADER 0x8B30
#define FF_GL_VERTEX_SHADER 0x8B31
#define FF_GL_COMPILE_STATUS 0x8B81
#define FF_GL_LINK_STATUS 0x8B82
#define FF_GL_INFO_LOG_LENGTH 0x8B84
typedef void (APIENTRY *FF_PFNGLACTIVETEXTUREPROC) (GLenum texture);
typedef void (APIENTRY *FF_PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
typedef void (APIENTRY *FF_PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
typedef void (APIENTRY *FF_PFNGLBUFFERDATAPROC) (GLenum target, ptrdiff_t size, const GLvoid *data, GLenum usage);
typedef void (APIENTRY *FF_PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
typedef GLint (APIENTRY *FF_PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const char *name);
typedef void (APIENTRY *FF_PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);
typedef void (APIENTRY *FF_PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, uintptr_t pointer);
typedef GLint (APIENTRY *FF_PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const char *name);
typedef void (APIENTRY *FF_PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0);
typedef void (APIENTRY *FF_PFNGLUNIFORM1IPROC) (GLint location, GLint v0);
typedef void (APIENTRY *FF_PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
typedef GLuint (APIENTRY *FF_PFNGLCREATEPROGRAMPROC) (void);
typedef void (APIENTRY *FF_PFNGLDELETEPROGRAMPROC) (GLuint program);
typedef void (APIENTRY *FF_PFNGLUSEPROGRAMPROC) (GLuint program);
typedef void (APIENTRY *FF_PFNGLLINKPROGRAMPROC) (GLuint program);
typedef void (APIENTRY *FF_PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params);
typedef void (APIENTRY *FF_PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, char *infoLog);
typedef void (APIENTRY *FF_PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);
typedef GLuint (APIENTRY *FF_PFNGLCREATESHADERPROC) (GLenum type);
typedef void (APIENTRY *FF_PFNGLDELETESHADERPROC) (GLuint shader);
typedef void (APIENTRY *FF_PFNGLCOMPILESHADERPROC) (GLuint shader);
typedef void (APIENTRY *FF_PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const char* *string, const GLint *length);
typedef void (APIENTRY *FF_PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params);
typedef void (APIENTRY *FF_PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, char *infoLog);
typedef struct FFOpenGLFunctions {
FF_PFNGLACTIVETEXTUREPROC glActiveTexture; //Require GL ARB multitexture
FF_PFNGLGENBUFFERSPROC glGenBuffers; //Require GL_ARB_vertex_buffer_object
FF_PFNGLDELETEBUFFERSPROC glDeleteBuffers; //Require GL_ARB_vertex_buffer_object
FF_PFNGLBUFFERDATAPROC glBufferData; //Require GL_ARB_vertex_buffer_object
FF_PFNGLBINDBUFFERPROC glBindBuffer; //Require GL_ARB_vertex_buffer_object
FF_PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation; //Require GL_ARB_vertex_shader
FF_PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; //Require GL_ARB_vertex_shader
FF_PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; //Require GL_ARB_vertex_shader
FF_PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; //Require GL_ARB_shader_objects
FF_PFNGLUNIFORM1FPROC glUniform1f; //Require GL_ARB_shader_objects
FF_PFNGLUNIFORM1IPROC glUniform1i; //Require GL_ARB_shader_objects
FF_PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; //Require GL_ARB_shader_objects
FF_PFNGLCREATEPROGRAMPROC glCreateProgram; //Require GL_ARB_shader_objects
FF_PFNGLDELETEPROGRAMPROC glDeleteProgram; //Require GL_ARB_shader_objects
FF_PFNGLUSEPROGRAMPROC glUseProgram; //Require GL_ARB_shader_objects
FF_PFNGLLINKPROGRAMPROC glLinkProgram; //Require GL_ARB_shader_objects
FF_PFNGLGETPROGRAMIVPROC glGetProgramiv; //Require GL_ARB_shader_objects
FF_PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; //Require GL_ARB_shader_objects
FF_PFNGLATTACHSHADERPROC glAttachShader; //Require GL_ARB_shader_objects
FF_PFNGLCREATESHADERPROC glCreateShader; //Require GL_ARB_shader_objects
FF_PFNGLDELETESHADERPROC glDeleteShader; //Require GL_ARB_shader_objects
FF_PFNGLCOMPILESHADERPROC glCompileShader; //Require GL_ARB_shader_objects
FF_PFNGLSHADERSOURCEPROC glShaderSource; //Require GL_ARB_shader_objects
FF_PFNGLGETSHADERIVPROC glGetShaderiv; //Require GL_ARB_shader_objects
FF_PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; //Require GL_ARB_shader_objects
} FFOpenGLFunctions;
#define OPENGL_ERROR_CHECK(ctx) \
{\
GLenum err_code; \
if ((err_code = glGetError()) != GL_NO_ERROR) { \
av_log(ctx, AV_LOG_ERROR, "OpenGL error occurred in '%s', line %d: %d\n", __FUNCTION__, __LINE__, err_code); \
goto fail; \
} \
}\
typedef struct OpenGLVertexInfo
{
float x, y, z; ///<Position
float s0, t0; ///<Texture coords
} OpenGLVertexInfo;
/* defines 2 triangles to display */
static GLushort g_index[6] =
{
0, 1, 2,
0, 3, 2,
};
typedef struct OpenGLContext {
AVClass *class; ///< class for private options
#if HAVE_SDL
SDL_Surface *surface;
#endif
FFOpenGLFunctions glprocs;
uint8_t background[4]; ///< Background color
int no_window; ///< 0 for create default window
char *window_title; ///< Title of the window
/* OpenGL implementation limits */
GLint max_texture_size; ///< Maximum texture size
GLint max_viewport_width; ///< Maximum viewport size
GLint max_viewport_height; ///< Maximum viewport size
int non_pow_2_textures; ///< 1 when non power of 2 textures are supported
/* Current OpenGL configuration */
GLuint program; ///< Shader program
GLuint texture_name[4]; ///< Textures' IDs
GLuint index_buffer; ///< Index buffer
GLuint vertex_buffer; ///< Vertex buffer
OpenGLVertexInfo vertex[4]; ///< VBO
GLint projection_matrix_location; ///< Uniforms' locations
GLint model_view_matrix_location;
GLint color_map_location;
GLint chroma_div_w_location;
GLint chroma_div_h_location;
GLint texture_location[4];
GLint position_attrib; ///< Attibutes' locations
GLint texture_coords_attrib;
GLfloat projection_matrix[16]; ///< Projection matrix
GLfloat model_view_matrix[16]; ///< Modev view matrix
GLfloat color_map[16]; ///< RGBA color map matrix
GLfloat chroma_div_w; ///< Chroma subsampling w ratio
GLfloat chroma_div_h; ///< Chroma subsampling h ratio
/* Stream information */
GLenum format;
GLenum type;
int width; ///< Stream width
int height; ///< Stream height
enum AVPixelFormat pix_fmt; ///< Stream pixel format
int picture_width; ///< Rendered width
int picture_height; ///< Rendered height
int window_width;
int window_height;
} OpenGLContext;
static av_cold int opengl_prepare_vertex(AVFormatContext *s);
static int opengl_draw(AVFormatContext *h, AVPacket *pkt, int repaint);
static av_cold int opengl_init_context(OpenGLContext *opengl);
static int opengl_resize(AVFormatContext *h, int width, int height)
{
int ret = 0;
OpenGLContext *opengl = h->priv_data;
opengl->window_width = width;
opengl->window_height = height;
/* max_viewport_width == 0 means write_header was not called yet. */
if (opengl->max_viewport_width) {
if (opengl->no_window &&
(ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_PREPARE_WINDOW_BUFFER, NULL , 0)) < 0) {
av_log(opengl, AV_LOG_ERROR, "Application failed to prepare window buffer.\n");
goto end;
}
if ((ret = opengl_prepare_vertex(h)) < 0)
goto end;
ret = opengl_draw(h, NULL, 1);
}
end:
return ret;
}
static int opengl_control_message(AVFormatContext *h, int type, void *data, size_t data_size)
{
OpenGLContext *opengl = h->priv_data;
switch(type) {
case AV_APP_TO_DEV_WINDOW_SIZE:
if (data) {
AVDeviceRect *message = data;
return opengl_resize(h, message->width, message->height);
}
return AVERROR(EINVAL);
case AV_APP_TO_DEV_WINDOW_REPAINT:
return opengl_resize(h, opengl->window_width, opengl->window_height);
}
return AVERROR(ENOSYS);
}
#if HAVE_SDL
static int opengl_sdl_recreate_window(OpenGLContext *opengl, int width, int height)
{
opengl->surface = SDL_SetVideoMode(width, height,
32, SDL_OPENGL | SDL_RESIZABLE);
if (!opengl->surface) {
av_log(opengl, AV_LOG_ERROR, "Unable to set video mode: %s\n", SDL_GetError());
return AVERROR_EXTERNAL;
}
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
return 0;
}
static int opengl_sdl_process_events(AVFormatContext *h)
{
int ret;
OpenGLContext *opengl = h->priv_data;
SDL_Event event;
SDL_PumpEvents();
while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_ALLEVENTS) > 0) {
switch (event.type) {
case SDL_QUIT:
return AVERROR(EIO);
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case SDLK_ESCAPE:
case SDLK_q:
return AVERROR(EIO);
}
return 0;
case SDL_VIDEORESIZE: {
char buffer[100];
int reinit;
AVDeviceRect message;
/* clean up old context because SDL_SetVideoMode may lose its state. */
SDL_VideoDriverName(buffer, sizeof(buffer));
reinit = !av_strncasecmp(buffer, "quartz", sizeof(buffer));
if (reinit) {
glDeleteTextures(4, opengl->texture_name);
opengl->glprocs.glDeleteBuffers(2, &opengl->index_buffer);
}
if ((ret = opengl_sdl_recreate_window(opengl, event.resize.w, event.resize.h)) < 0)
return ret;
if (reinit && (ret = opengl_init_context(opengl)) < 0)
return ret;
message.width = opengl->surface->w;
message.height = opengl->surface->h;
return opengl_control_message(h, AV_APP_TO_DEV_WINDOW_SIZE, &message, sizeof(AVDeviceRect));
}
}
}
return 0;
}
static int av_cold opengl_sdl_create_window(AVFormatContext *h)
{
int ret;
char buffer[100];
OpenGLContext *opengl = h->priv_data;
AVDeviceRect message;
if (SDL_Init(SDL_INIT_VIDEO)) {
av_log(opengl, AV_LOG_ERROR, "Unable to initialize SDL: %s\n", SDL_GetError());
return AVERROR_EXTERNAL;
}
if ((ret = opengl_sdl_recreate_window(opengl, opengl->width, opengl->height)) < 0)
return ret;
av_log(opengl, AV_LOG_INFO, "SDL driver: '%s'.\n", SDL_VideoDriverName(buffer, sizeof(buffer)));
message.width = opengl->surface->w;
message.height = opengl->surface->h;
opengl_control_message(h, AV_APP_TO_DEV_WINDOW_SIZE, &message, sizeof(AVDeviceRect));
return 0;
}
static int av_cold opengl_sdl_load_procedures(OpenGLContext *opengl)
{
FFOpenGLFunctions *procs = &opengl->glprocs;
#define LOAD_OPENGL_FUN(name, type) \
procs->name = (type)SDL_GL_GetProcAddress(#name); \
if (!procs->name) { \
av_log(opengl, AV_LOG_ERROR, "Cannot load OpenGL function: '%s'\n", #name); \
return AVERROR(ENOSYS); \
}
LOAD_OPENGL_FUN(glActiveTexture, FF_PFNGLACTIVETEXTUREPROC)
LOAD_OPENGL_FUN(glGenBuffers, FF_PFNGLGENBUFFERSPROC)
LOAD_OPENGL_FUN(glDeleteBuffers, FF_PFNGLDELETEBUFFERSPROC)
LOAD_OPENGL_FUN(glBufferData, FF_PFNGLBUFFERDATAPROC)
LOAD_OPENGL_FUN(glBindBuffer, FF_PFNGLBINDBUFFERPROC)
LOAD_OPENGL_FUN(glGetAttribLocation, FF_PFNGLGETATTRIBLOCATIONPROC)
LOAD_OPENGL_FUN(glGetUniformLocation, FF_PFNGLGETUNIFORMLOCATIONPROC)
LOAD_OPENGL_FUN(glUniform1f, FF_PFNGLUNIFORM1FPROC)
LOAD_OPENGL_FUN(glUniform1i, FF_PFNGLUNIFORM1IPROC)
LOAD_OPENGL_FUN(glUniformMatrix4fv, FF_PFNGLUNIFORMMATRIX4FVPROC)
LOAD_OPENGL_FUN(glCreateProgram, FF_PFNGLCREATEPROGRAMPROC)
LOAD_OPENGL_FUN(glDeleteProgram, FF_PFNGLDELETEPROGRAMPROC)
LOAD_OPENGL_FUN(glUseProgram, FF_PFNGLUSEPROGRAMPROC)
LOAD_OPENGL_FUN(glLinkProgram, FF_PFNGLLINKPROGRAMPROC)
LOAD_OPENGL_FUN(glGetProgramiv, FF_PFNGLGETPROGRAMIVPROC)
LOAD_OPENGL_FUN(glGetProgramInfoLog, FF_PFNGLGETPROGRAMINFOLOGPROC)
LOAD_OPENGL_FUN(glAttachShader, FF_PFNGLATTACHSHADERPROC)
LOAD_OPENGL_FUN(glCreateShader, FF_PFNGLCREATESHADERPROC)
LOAD_OPENGL_FUN(glDeleteShader, FF_PFNGLDELETESHADERPROC)
LOAD_OPENGL_FUN(glCompileShader, FF_PFNGLCOMPILESHADERPROC)
LOAD_OPENGL_FUN(glShaderSource, FF_PFNGLSHADERSOURCEPROC)
LOAD_OPENGL_FUN(glGetShaderiv, FF_PFNGLGETSHADERIVPROC)
LOAD_OPENGL_FUN(glGetShaderInfoLog, FF_PFNGLGETSHADERINFOLOGPROC)
LOAD_OPENGL_FUN(glEnableVertexAttribArray, FF_PFNGLENABLEVERTEXATTRIBARRAYPROC)
LOAD_OPENGL_FUN(glVertexAttribPointer, FF_PFNGLVERTEXATTRIBPOINTERPROC)
return 0;
#undef LOAD_OPENGL_FUN
}
#endif /* HAVE_SDL */
#if defined(__APPLE__)
static int av_cold opengl_load_procedures(OpenGLContext *opengl)
{
FFOpenGLFunctions *procs = &opengl->glprocs;
procs->glActiveTexture = glActiveTexture;
procs->glGenBuffers = glGenBuffers;
procs->glDeleteBuffers = glDeleteBuffers;
procs->glBufferData = glBufferData;
procs->glBindBuffer = glBindBuffer;
procs->glGetAttribLocation = glGetAttribLocation;
procs->glGetUniformLocation = glGetUniformLocation;
procs->glUniform1f = glUniform1f;
procs->glUniform1i = glUniform1i;
procs->glUniformMatrix4fv = glUniformMatrix4fv;
procs->glCreateProgram = glCreateProgram;
procs->glDeleteProgram = glDeleteProgram;
procs->glUseProgram = glUseProgram;
procs->glLinkProgram = glLinkProgram;
procs->glGetProgramiv = glGetProgramiv;
procs->glGetProgramInfoLog = glGetProgramInfoLog;
procs->glAttachShader = glAttachShader;
procs->glCreateShader = glCreateShader;
procs->glDeleteShader = glDeleteShader;
procs->glCompileShader = glCompileShader;
procs->glShaderSource = glShaderSource;
procs->glGetShaderiv = glGetShaderiv;
procs->glGetShaderInfoLog = glGetShaderInfoLog;
procs->glEnableVertexAttribArray = glEnableVertexAttribArray;
procs->glVertexAttribPointer = (FF_PFNGLVERTEXATTRIBPOINTERPROC) glVertexAttribPointer;
return 0;
}
#else
static int av_cold opengl_load_procedures(OpenGLContext *opengl)
{
FFOpenGLFunctions *procs = &opengl->glprocs;
#if HAVE_GLXGETPROCADDRESS
#define SelectedGetProcAddress glXGetProcAddress
#elif HAVE_WGLGETPROCADDRESS
#define SelectedGetProcAddress wglGetProcAddress
#endif
#define LOAD_OPENGL_FUN(name, type) \
procs->name = (type)SelectedGetProcAddress(#name); \
if (!procs->name) { \
av_log(opengl, AV_LOG_ERROR, "Cannot load OpenGL function: '%s'\n", #name); \
return AVERROR(ENOSYS); \
}
LOAD_OPENGL_FUN(glActiveTexture, FF_PFNGLACTIVETEXTUREPROC)
LOAD_OPENGL_FUN(glGenBuffers, FF_PFNGLGENBUFFERSPROC)
LOAD_OPENGL_FUN(glDeleteBuffers, FF_PFNGLDELETEBUFFERSPROC)
LOAD_OPENGL_FUN(glBufferData, FF_PFNGLBUFFERDATAPROC)
LOAD_OPENGL_FUN(glBindBuffer, FF_PFNGLBINDBUFFERPROC)
LOAD_OPENGL_FUN(glGetAttribLocation, FF_PFNGLGETATTRIBLOCATIONPROC)
LOAD_OPENGL_FUN(glGetUniformLocation, FF_PFNGLGETUNIFORMLOCATIONPROC)
LOAD_OPENGL_FUN(glUniform1f, FF_PFNGLUNIFORM1FPROC)
LOAD_OPENGL_FUN(glUniform1i, FF_PFNGLUNIFORM1IPROC)
LOAD_OPENGL_FUN(glUniformMatrix4fv, FF_PFNGLUNIFORMMATRIX4FVPROC)
LOAD_OPENGL_FUN(glCreateProgram, FF_PFNGLCREATEPROGRAMPROC)
LOAD_OPENGL_FUN(glDeleteProgram, FF_PFNGLDELETEPROGRAMPROC)
LOAD_OPENGL_FUN(glUseProgram, FF_PFNGLUSEPROGRAMPROC)
LOAD_OPENGL_FUN(glLinkProgram, FF_PFNGLLINKPROGRAMPROC)
LOAD_OPENGL_FUN(glGetProgramiv, FF_PFNGLGETPROGRAMIVPROC)
LOAD_OPENGL_FUN(glGetProgramInfoLog, FF_PFNGLGETPROGRAMINFOLOGPROC)
LOAD_OPENGL_FUN(glAttachShader, FF_PFNGLATTACHSHADERPROC)
LOAD_OPENGL_FUN(glCreateShader, FF_PFNGLCREATESHADERPROC)
LOAD_OPENGL_FUN(glDeleteShader, FF_PFNGLDELETESHADERPROC)
LOAD_OPENGL_FUN(glCompileShader, FF_PFNGLCOMPILESHADERPROC)
LOAD_OPENGL_FUN(glShaderSource, FF_PFNGLSHADERSOURCEPROC)
LOAD_OPENGL_FUN(glGetShaderiv, FF_PFNGLGETSHADERIVPROC)
LOAD_OPENGL_FUN(glGetShaderInfoLog, FF_PFNGLGETSHADERINFOLOGPROC)
LOAD_OPENGL_FUN(glEnableVertexAttribArray, FF_PFNGLENABLEVERTEXATTRIBARRAYPROC)
LOAD_OPENGL_FUN(glVertexAttribPointer, FF_PFNGLVERTEXATTRIBPOINTERPROC)
return 0;
#undef SelectedGetProcAddress
#undef LOAD_OPENGL_FUN
}
#endif
static av_always_inline void opengl_make_identity(float matrix[16])
{
memset(matrix, 0, 16 * sizeof(float));
matrix[0] = matrix[5] = matrix[10] = matrix[15] = 1.0f;
}
static av_always_inline void opengl_make_ortho(float matrix[16],
float left, float right,
float bottom, float top,
float nearZ, float farZ)
{
float ral = right + left;
float rsl = right - left;
float tab = top + bottom;
float tsb = top - bottom;
float fan = farZ + nearZ;
float fsn = farZ - nearZ;
memset(matrix, 0, 16 * sizeof(float));
matrix[0] = 2.0f / rsl;
matrix[5] = 2.0f / tsb;
matrix[10] = -2.0f / fsn;
matrix[12] = -ral / rsl;
matrix[13] = -tab / tsb;
matrix[14] = -fan / fsn;
matrix[15] = 1.0f;
}
static av_cold int opengl_read_limits(OpenGLContext *opengl)
{
static const struct{
const char *extention;
int major;
int minor;
} required_extensions[] = {
{ "GL_ARB_multitexture", 1, 3 },
{ "GL_ARB_vertex_buffer_object", 1, 5 }, //GLX_ARB_vertex_buffer_object
{ "GL_ARB_vertex_shader", 2, 0 },
{ "GL_ARB_fragment_shader", 2, 0 },
{ "GL_ARB_shader_objects", 2, 0 },
{ NULL, 0, 0 }
};
int i, major, minor;
const char *extensions, *version;
version = glGetString(GL_VERSION);
extensions = glGetString(GL_EXTENSIONS);
av_log(opengl, AV_LOG_DEBUG, "OpenGL version: %s\n", version);
sscanf(version, "%d.%d", &major, &minor);
for (i = 0; required_extensions[i].extention; i++) {
if (major < required_extensions[i].major &&
(major == required_extensions[i].major && minor < required_extensions[i].minor) &&
!strstr(extensions, required_extensions[i].extention)) {
av_log(opengl, AV_LOG_ERROR, "Required extension %s is not supported.\n",
required_extensions[i].extention);
av_log(opengl, AV_LOG_DEBUG, "Supported extensions are: %s\n", extensions);
return AVERROR(ENOSYS);
}
}
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &opengl->max_texture_size);
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &opengl->max_viewport_width);
opengl->non_pow_2_textures = major >= 2 || strstr(extensions, "GL_ARB_texture_non_power_of_two");
av_log(opengl, AV_LOG_DEBUG, "Non Power of 2 textures support: %s\n", opengl->non_pow_2_textures ? "Yes" : "No");
av_log(opengl, AV_LOG_DEBUG, "Max texture size: %dx%d\n", opengl->max_texture_size, opengl->max_texture_size);
av_log(opengl, AV_LOG_DEBUG, "Max viewport size: %dx%d\n",
opengl->max_viewport_width, opengl->max_viewport_height);
OPENGL_ERROR_CHECK(opengl);
return 0;
fail:
return AVERROR_EXTERNAL;
}
static av_always_inline const char * opengl_get_fragment_shader_code(enum AVPixelFormat format)
{
switch (format) {
case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUV444P:
case AV_PIX_FMT_YUV422P: case AV_PIX_FMT_YUV410P:
case AV_PIX_FMT_YUV411P: case AV_PIX_FMT_YUV440P:
case AV_PIX_FMT_YUV420P16: case AV_PIX_FMT_YUV422P16:
case AV_PIX_FMT_YUV444P16:
return FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR;
case AV_PIX_FMT_YUVA420P: case AV_PIX_FMT_YUVA444P:
case AV_PIX_FMT_YUVA422P:
case AV_PIX_FMT_YUVA420P16: case AV_PIX_FMT_YUVA422P16:
case AV_PIX_FMT_YUVA444P16:
return FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR;
case AV_PIX_FMT_RGB24: case AV_PIX_FMT_BGR24:
case AV_PIX_FMT_0RGB: case AV_PIX_FMT_RGB0:
case AV_PIX_FMT_0BGR: case AV_PIX_FMT_BGR0:
case AV_PIX_FMT_RGB565: case AV_PIX_FMT_BGR565:
case AV_PIX_FMT_RGB555: case AV_PIX_FMT_BGR555:
case AV_PIX_FMT_RGB8: case AV_PIX_FMT_BGR8:
case AV_PIX_FMT_RGB48:
return FF_OPENGL_FRAGMENT_SHADER_RGB_PACKET;
case AV_PIX_FMT_ARGB: case AV_PIX_FMT_RGBA:
case AV_PIX_FMT_ABGR: case AV_PIX_FMT_BGRA:
case AV_PIX_FMT_RGBA64: case AV_PIX_FMT_BGRA64:
return FF_OPENGL_FRAGMENT_SHADER_RGBA_PACKET;
case AV_PIX_FMT_GBRP: case AV_PIX_FMT_GBRP16:
return FF_OPENGL_FRAGMENT_SHADER_RGB_PLANAR;
case AV_PIX_FMT_GBRAP: case AV_PIX_FMT_GBRAP16:
return FF_OPENGL_FRAGMENT_SHADER_RGBA_PLANAR;
default:
break;
}
return NULL;
}
static av_always_inline int opengl_type_size(GLenum type)
{
switch(type) {
case GL_UNSIGNED_SHORT:
case FF_GL_UNSIGNED_SHORT_1_5_5_5_REV:
case GL_UNSIGNED_SHORT_5_6_5:
return 2;
case GL_UNSIGNED_BYTE:
case FF_GL_UNSIGNED_BYTE_3_3_2:
case FF_GL_UNSIGNED_BYTE_2_3_3_REV:
default:
break;
}
return 1;
}
static av_cold void opengl_get_texture_params(OpenGLContext *opengl)
{
switch(opengl->pix_fmt) {
case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUV444P:
case AV_PIX_FMT_YUV422P: case AV_PIX_FMT_YUV410P:
case AV_PIX_FMT_YUV411P: case AV_PIX_FMT_YUV440P:
case AV_PIX_FMT_YUVA420P: case AV_PIX_FMT_YUVA444P:
case AV_PIX_FMT_YUVA422P:
case AV_PIX_FMT_GBRP: case AV_PIX_FMT_GBRAP:
opengl->format = GL_RED_COMPONENT;
opengl->type = GL_UNSIGNED_BYTE;
break;
case AV_PIX_FMT_YUV420P16: case AV_PIX_FMT_YUV422P16:
case AV_PIX_FMT_YUV444P16:
case AV_PIX_FMT_YUVA420P16: case AV_PIX_FMT_YUVA422P16:
case AV_PIX_FMT_YUVA444P16:
case AV_PIX_FMT_GBRP16: case AV_PIX_FMT_GBRAP16:
opengl->format = GL_RED_COMPONENT;
opengl->type = GL_UNSIGNED_SHORT;
break;
case AV_PIX_FMT_RGB24: case AV_PIX_FMT_BGR24:
opengl->format = GL_RGB;
opengl->type = GL_UNSIGNED_BYTE;
break;
case AV_PIX_FMT_ARGB: case AV_PIX_FMT_RGBA:
case AV_PIX_FMT_ABGR: case AV_PIX_FMT_BGRA:
case AV_PIX_FMT_0RGB: case AV_PIX_FMT_RGB0:
case AV_PIX_FMT_0BGR: case AV_PIX_FMT_BGR0:
opengl->format = GL_RGBA;
opengl->type = GL_UNSIGNED_BYTE;
break;
case AV_PIX_FMT_RGB8:
opengl->format = GL_RGB;
opengl->type = FF_GL_UNSIGNED_BYTE_3_3_2;
break;
case AV_PIX_FMT_BGR8:
opengl->format = GL_RGB;
opengl->type = FF_GL_UNSIGNED_BYTE_2_3_3_REV;
break;
case AV_PIX_FMT_RGB555: case AV_PIX_FMT_BGR555:
opengl->format = GL_RGBA;
opengl->type = FF_GL_UNSIGNED_SHORT_1_5_5_5_REV;
break;
case AV_PIX_FMT_RGB565: case AV_PIX_FMT_BGR565:
opengl->format = GL_RGB;
opengl->type = GL_UNSIGNED_SHORT_5_6_5;
break;
case AV_PIX_FMT_RGB48:
opengl->format = GL_RGB;
opengl->type = GL_UNSIGNED_SHORT;
break;
case AV_PIX_FMT_RGBA64: case AV_PIX_FMT_BGRA64:
opengl->format = GL_RGBA;
opengl->type = GL_UNSIGNED_SHORT;
break;
}
}
static void opengl_compute_display_area(AVFormatContext *s)
{
AVRational sar, dar; /* sample and display aspect ratios */
OpenGLContext *opengl = s->priv_data;
AVStream *st = s->streams[0];
AVCodecContext *encctx = st->codec;
/* compute overlay width and height from the codec context information */
sar = st->sample_aspect_ratio.num ? st->sample_aspect_ratio : (AVRational){ 1, 1 };
dar = av_mul_q(sar, (AVRational){ encctx->width, encctx->height });
/* we suppose the screen has a 1/1 sample aspect ratio */
/* fit in the window */
if (av_cmp_q(dar, (AVRational){ opengl->window_width, opengl->window_height }) > 0) {
/* fit in width */
opengl->picture_width = opengl->window_width;
opengl->picture_height = av_rescale(opengl->picture_width, dar.den, dar.num);
} else {
/* fit in height */
opengl->picture_height = opengl->window_height;
opengl->picture_width = av_rescale(opengl->picture_height, dar.num, dar.den);
}
}
static av_cold void opengl_get_texture_size(OpenGLContext *opengl, int in_width, int in_height,
int *out_width, int *out_height)
{
if (opengl->non_pow_2_textures) {
*out_width = in_width;
*out_height = in_height;
} else {
int max = FFMIN(FFMAX(in_width, in_height), opengl->max_texture_size);
unsigned power_of_2 = 1;
while (power_of_2 < max)
power_of_2 *= 2;
*out_height = power_of_2;
*out_width = power_of_2;
av_log(opengl, AV_LOG_DEBUG, "Texture size calculated from %dx%d into %dx%d\n",
in_width, in_height, *out_width, *out_height);
}
}
static av_cold void opengl_fill_color_map(OpenGLContext *opengl)
{
const AVPixFmtDescriptor *desc;
int shift;
enum AVPixelFormat pix_fmt = opengl->pix_fmt;
/* We need order of components, not exact position, some minor HACKs here */
if (pix_fmt == AV_PIX_FMT_RGB565 || pix_fmt == AV_PIX_FMT_BGR555 ||
pix_fmt == AV_PIX_FMT_BGR8 || pix_fmt == AV_PIX_FMT_RGB8)
pix_fmt = AV_PIX_FMT_RGB24;
else if (pix_fmt == AV_PIX_FMT_BGR565 || pix_fmt == AV_PIX_FMT_RGB555)
pix_fmt = AV_PIX_FMT_BGR24;
desc = av_pix_fmt_desc_get(pix_fmt);
if (!(desc->flags & AV_PIX_FMT_FLAG_RGB))
return;
#define FILL_COMPONENT(i) { \
shift = desc->comp[i].depth_minus1 >> 3; \
opengl->color_map[(i << 2) + ((desc->comp[i].offset_plus1 - 1) >> shift)] = 1.0; \
}
memset(opengl->color_map, 0, sizeof(opengl->color_map));
FILL_COMPONENT(0);
FILL_COMPONENT(1);
FILL_COMPONENT(2);
if (desc->flags & AV_PIX_FMT_FLAG_ALPHA)
FILL_COMPONENT(3);
#undef FILL_COMPONENT
}
static av_cold GLuint opengl_load_shader(OpenGLContext *opengl, GLenum type, const char *source)
{
GLuint shader = opengl->glprocs.glCreateShader(type);
GLint result;
if (!shader) {
av_log(opengl, AV_LOG_ERROR, "glCreateShader() failed\n");
return 0;
}
opengl->glprocs.glShaderSource(shader, 1, &source, NULL);
opengl->glprocs.glCompileShader(shader);
opengl->glprocs.glGetShaderiv(shader, FF_GL_COMPILE_STATUS, &result);
if (!result) {
char *log;
opengl->glprocs.glGetShaderiv(shader, FF_GL_INFO_LOG_LENGTH, &result);
if (result) {
if ((log = av_malloc(result))) {
opengl->glprocs.glGetShaderInfoLog(shader, result, NULL, log);
av_log(opengl, AV_LOG_ERROR, "Compile error: %s\n", log);
av_free(log);
}
}
goto fail;
}
OPENGL_ERROR_CHECK(opengl);
return shader;
fail:
opengl->glprocs.glDeleteShader(shader);
return 0;
}
static av_cold int opengl_compile_shaders(OpenGLContext *opengl, enum AVPixelFormat pix_fmt)
{
GLuint vertex_shader = 0, fragment_shader = 0;
GLint result;
const char *fragment_shader_code = opengl_get_fragment_shader_code(pix_fmt);
if (!fragment_shader_code) {
av_log(opengl, AV_LOG_ERROR, "Provided pixel format '%s' is not supported\n",
av_get_pix_fmt_name(pix_fmt));
return AVERROR(EINVAL);
}
vertex_shader = opengl_load_shader(opengl, FF_GL_VERTEX_SHADER,
FF_OPENGL_VERTEX_SHADER);
if (!vertex_shader) {
av_log(opengl, AV_LOG_ERROR, "Vertex shader loading failed.\n");
goto fail;
}
fragment_shader = opengl_load_shader(opengl, FF_GL_FRAGMENT_SHADER,
fragment_shader_code);
if (!fragment_shader) {
av_log(opengl, AV_LOG_ERROR, "Fragment shader loading failed.\n");
goto fail;
}
opengl->program = opengl->glprocs.glCreateProgram();
if (!opengl->program)
goto fail;
opengl->glprocs.glAttachShader(opengl->program, vertex_shader);
opengl->glprocs.glAttachShader(opengl->program, fragment_shader);
opengl->glprocs.glLinkProgram(opengl->program);
opengl->glprocs.glGetProgramiv(opengl->program, FF_GL_LINK_STATUS, &result);
if (!result) {
char *log;
opengl->glprocs.glGetProgramiv(opengl->program, FF_GL_INFO_LOG_LENGTH, &result);
if (result) {
log = av_malloc(result);
if (!log)
goto fail;
opengl->glprocs.glGetProgramInfoLog(opengl->program, result, NULL, log);
av_log(opengl, AV_LOG_ERROR, "Link error: %s\n", log);
av_free(log);
}
goto fail;
}
opengl->position_attrib = opengl->glprocs.glGetAttribLocation(opengl->program, "a_position");
opengl->texture_coords_attrib = opengl->glprocs.glGetAttribLocation(opengl->program, "a_textureCoords");
opengl->projection_matrix_location = opengl->glprocs.glGetUniformLocation(opengl->program, "u_projectionMatrix");
opengl->model_view_matrix_location = opengl->glprocs.glGetUniformLocation(opengl->program, "u_modelViewMatrix");
opengl->color_map_location = opengl->glprocs.glGetUniformLocation(opengl->program, "u_colorMap");
opengl->texture_location[0] = opengl->glprocs.glGetUniformLocation(opengl->program, "u_texture0");
opengl->texture_location[1] = opengl->glprocs.glGetUniformLocation(opengl->program, "u_texture1");
opengl->texture_location[2] = opengl->glprocs.glGetUniformLocation(opengl->program, "u_texture2");
opengl->texture_location[3] = opengl->glprocs.glGetUniformLocation(opengl->program, "u_texture3");
opengl->chroma_div_w_location = opengl->glprocs.glGetUniformLocation(opengl->program, "u_chroma_div_w");
opengl->chroma_div_h_location = opengl->glprocs.glGetUniformLocation(opengl->program, "u_chroma_div_h");
OPENGL_ERROR_CHECK(opengl);
return 0;
fail:
opengl->glprocs.glDeleteShader(vertex_shader);
opengl->glprocs.glDeleteShader(fragment_shader);
opengl->glprocs.glDeleteProgram(opengl->program);
opengl->program = 0;
return AVERROR_EXTERNAL;
}
static av_cold int opengl_configure_texture(OpenGLContext *opengl, GLuint texture,
GLsizei width, GLsizei height)
{
if (texture) {
int new_width, new_height;
opengl_get_texture_size(opengl, width, height, &new_width, &new_height);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, opengl->format, new_width, new_height, 0,
opengl->format, opengl->type, NULL);
OPENGL_ERROR_CHECK(NULL);
}
return 0;
fail:
return AVERROR_EXTERNAL;
}
static av_cold int opengl_prepare_vertex(AVFormatContext *s)
{
OpenGLContext *opengl = s->priv_data;
int tex_w, tex_h;
if (opengl->window_width > opengl->max_viewport_width || opengl->window_height > opengl->max_viewport_height) {
opengl->window_width = FFMAX(opengl->window_width, opengl->max_viewport_width);
opengl->window_height = FFMAX(opengl->window_height, opengl->max_viewport_height);
av_log(opengl, AV_LOG_WARNING, "Too big viewport requested, limited to %dx%d", opengl->window_width, opengl->window_height);
}
glViewport(0, 0, opengl->window_width, opengl->window_height);
opengl_make_ortho(opengl->projection_matrix,
- (float)opengl->window_width / 2.0f, (float)opengl->window_width / 2.0f,
- (float)opengl->window_height / 2.0f, (float)opengl->window_height / 2.0f,
1.0f, -1.0f);
opengl_make_identity(opengl->model_view_matrix);
opengl_compute_display_area(s);
opengl->vertex[0].z = opengl->vertex[1].z = opengl->vertex[2].z = opengl->vertex[3].z = 0.0f;
opengl->vertex[0].x = opengl->vertex[1].x = - (float)opengl->picture_width / 2.0f;
opengl->vertex[2].x = opengl->vertex[3].x = (float)opengl->picture_width / 2.0f;
opengl->vertex[1].y = opengl->vertex[2].y = - (float)opengl->picture_height / 2.0f;
opengl->vertex[0].y = opengl->vertex[3].y = (float)opengl->picture_height / 2.0f;
opengl_get_texture_size(opengl, opengl->width, opengl->height, &tex_w, &tex_h);
opengl->vertex[0].s0 = 0.0f;
opengl->vertex[0].t0 = 0.0f;
opengl->vertex[1].s0 = 0.0f;
opengl->vertex[1].t0 = (float)opengl->height / (float)tex_h;
opengl->vertex[2].s0 = (float)opengl->width / (float)tex_w;
opengl->vertex[2].t0 = (float)opengl->height / (float)tex_h;
opengl->vertex[3].s0 = (float)opengl->width / (float)tex_w;
opengl->vertex[3].t0 = 0.0f;
opengl->glprocs.glBindBuffer(FF_GL_ARRAY_BUFFER, opengl->vertex_buffer);
opengl->glprocs.glBufferData(FF_GL_ARRAY_BUFFER, sizeof(opengl->vertex), opengl->vertex, FF_GL_STATIC_DRAW);
opengl->glprocs.glBindBuffer(FF_GL_ARRAY_BUFFER, 0);
OPENGL_ERROR_CHECK(opengl);
return 0;
fail:
return AVERROR_EXTERNAL;
}
static int opengl_prepare(OpenGLContext *opengl)
{
int i;
opengl->glprocs.glUseProgram(opengl->program);
opengl->glprocs.glUniformMatrix4fv(opengl->projection_matrix_location, 1, GL_FALSE, opengl->projection_matrix);
opengl->glprocs.glUniformMatrix4fv(opengl->model_view_matrix_location, 1, GL_FALSE, opengl->model_view_matrix);
for (i = 0; i < 4; i++)
if (opengl->texture_location[i] != -1) {
opengl->glprocs.glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, opengl->texture_name[i]);
opengl->glprocs.glUniform1i(opengl->texture_location[i], i);
}
if (opengl->color_map_location != -1)
opengl->glprocs.glUniformMatrix4fv(opengl->color_map_location, 1, GL_FALSE, opengl->color_map);
if (opengl->chroma_div_h_location != -1)
opengl->glprocs.glUniform1f(opengl->chroma_div_h_location, opengl->chroma_div_h);
if (opengl->chroma_div_w_location != -1)
opengl->glprocs.glUniform1f(opengl->chroma_div_w_location, opengl->chroma_div_w);
OPENGL_ERROR_CHECK(opengl);
return 0;
fail:
return AVERROR_EXTERNAL;
}
static av_cold int opengl_write_trailer(AVFormatContext *h)
{
OpenGLContext *opengl = h->priv_data;
if (opengl->no_window &&
avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_PREPARE_WINDOW_BUFFER, NULL , 0) < 0)
av_log(opengl, AV_LOG_ERROR, "Application failed to prepare window buffer.\n");
glDeleteTextures(4, opengl->texture_name);
if (opengl && opengl->glprocs.glDeleteBuffers)
opengl->glprocs.glDeleteBuffers(2, &opengl->index_buffer);
#if HAVE_SDL
if (!opengl->no_window)
SDL_Quit();
#endif
if (opengl->no_window &&
avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_DESTROY_WINDOW_BUFFER, NULL , 0) < 0)
av_log(opengl, AV_LOG_ERROR, "Application failed to release window buffer.\n");
return 0;
}
static av_cold int opengl_init_context(OpenGLContext *opengl)
{
int i, ret;
const AVPixFmtDescriptor *desc;
if ((ret = opengl_compile_shaders(opengl, opengl->pix_fmt)) < 0)
goto fail;
desc = av_pix_fmt_desc_get(opengl->pix_fmt);
av_assert0(desc->nb_components > 0 && desc->nb_components <= 4);
glGenTextures(desc->nb_components, opengl->texture_name);
opengl->glprocs.glGenBuffers(2, &opengl->index_buffer);
if (!opengl->index_buffer || !opengl->vertex_buffer) {
av_log(opengl, AV_LOG_ERROR, "Buffer generation failed.\n");
ret = AVERROR_EXTERNAL;
goto fail;
}
opengl_configure_texture(opengl, opengl->texture_name[0], opengl->width, opengl->height);
if (desc->nb_components > 1) {
int has_alpha = desc->flags & AV_PIX_FMT_FLAG_ALPHA;
int num_planes = desc->nb_components - (has_alpha ? 1 : 0);
if (opengl->non_pow_2_textures) {
opengl->chroma_div_w = 1.0f;
opengl->chroma_div_h = 1.0f;
} else {
opengl->chroma_div_w = 1 << desc->log2_chroma_w;
opengl->chroma_div_h = 1 << desc->log2_chroma_h;
}
for (i = 1; i < num_planes; i++)
if (opengl->non_pow_2_textures)
opengl_configure_texture(opengl, opengl->texture_name[i],
FF_CEIL_RSHIFT(opengl->width, desc->log2_chroma_w),
FF_CEIL_RSHIFT(opengl->height, desc->log2_chroma_h));
else
opengl_configure_texture(opengl, opengl->texture_name[i], opengl->width, opengl->height);
if (has_alpha)
opengl_configure_texture(opengl, opengl->texture_name[3], opengl->width, opengl->height);
}
opengl->glprocs.glBindBuffer(FF_GL_ELEMENT_ARRAY_BUFFER, opengl->index_buffer);
opengl->glprocs.glBufferData(FF_GL_ELEMENT_ARRAY_BUFFER, sizeof(g_index), g_index, FF_GL_STATIC_DRAW);
opengl->glprocs.glBindBuffer(FF_GL_ELEMENT_ARRAY_BUFFER, 0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor((float)opengl->background[0] / 255.0f, (float)opengl->background[1] / 255.0f,
(float)opengl->background[2] / 255.0f, 1.0f);
ret = AVERROR_EXTERNAL;
OPENGL_ERROR_CHECK(opengl);
return 0;
fail:
return ret;
}
static av_cold int opengl_write_header(AVFormatContext *h)
{
OpenGLContext *opengl = h->priv_data;
AVStream *st;
int ret;
if (h->nb_streams != 1 ||
h->streams[0]->codec->codec_type != AVMEDIA_TYPE_VIDEO ||
h->streams[0]->codec->codec_id != AV_CODEC_ID_RAWVIDEO) {
av_log(opengl, AV_LOG_ERROR, "Only a single video stream is supported.\n");
return AVERROR(EINVAL);
}
st = h->streams[0];
opengl->width = st->codec->width;
opengl->height = st->codec->height;
opengl->pix_fmt = st->codec->pix_fmt;
if (!opengl->window_title && !opengl->no_window)
opengl->window_title = av_strdup(h->filename);
if (!opengl->no_window) {
#if HAVE_SDL
if ((ret = opengl_sdl_create_window(h)) < 0)
goto fail;
#else
av_log(opengl, AV_LOG_ERROR, "FFmpeg is compiled without SDL. Cannot create default window.\n");
ret = AVERROR(ENOSYS);
goto fail;
#endif
} else {
if ((ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_CREATE_WINDOW_BUFFER, NULL , 0)) < 0) {
av_log(opengl, AV_LOG_ERROR, "Application failed to create window buffer.\n");
goto fail;
}
if ((ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_PREPARE_WINDOW_BUFFER, NULL , 0)) < 0) {
av_log(opengl, AV_LOG_ERROR, "Application failed to prepare window buffer.\n");
goto fail;
}
}
if ((ret = opengl_read_limits(opengl)) < 0)
goto fail;
if (opengl->width > opengl->max_texture_size || opengl->height > opengl->max_texture_size) {
av_log(opengl, AV_LOG_ERROR, "Too big picture %dx%d, max supported size is %dx%d\n",
opengl->width, opengl->height, opengl->max_texture_size, opengl->max_texture_size);
ret = AVERROR(EINVAL);
goto fail;
}
if (!opengl->no_window) {
#if HAVE_SDL
if ((ret = opengl_sdl_load_procedures(opengl)) < 0)
goto fail;
#endif
} else if ((ret = opengl_load_procedures(opengl)) < 0)
goto fail;
opengl_fill_color_map(opengl);
opengl_get_texture_params(opengl);
if ((ret = opengl_init_context(opengl)) < 0)
goto fail;
if ((ret = opengl_prepare_vertex(h)) < 0)
goto fail;
glClear(GL_COLOR_BUFFER_BIT);
#if HAVE_SDL
if (!opengl->no_window)
SDL_GL_SwapBuffers();
#endif
if (opengl->no_window &&
(ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_DISPLAY_WINDOW_BUFFER, NULL , 0)) < 0) {
av_log(opengl, AV_LOG_ERROR, "Application failed to display window buffer.\n");
goto fail;
}
ret = AVERROR_EXTERNAL;
OPENGL_ERROR_CHECK(opengl);
return 0;
fail:
opengl_write_trailer(h);
return ret;
}
static uint8_t* opengl_get_plane_pointer(OpenGLContext *opengl, AVPacket *pkt, int comp_index,
const AVPixFmtDescriptor *desc)
{
uint8_t *data = pkt->data;
int wordsize = opengl_type_size(opengl->type);
int width_chroma = FF_CEIL_RSHIFT(opengl->width, desc->log2_chroma_w);
int height_chroma = FF_CEIL_RSHIFT(opengl->height, desc->log2_chroma_h);
int plane = desc->comp[comp_index].plane;
switch(plane) {
case 0:
break;
case 1:
data += opengl->width * opengl->height * wordsize;
break;
case 2:
data += opengl->width * opengl->height * wordsize;
data += width_chroma * height_chroma * wordsize;
break;
case 3:
data += opengl->width * opengl->height * wordsize;
data += 2 * width_chroma * height_chroma * wordsize;
break;
default:
return NULL;
}
return data;
}
static int opengl_draw(AVFormatContext *h, AVPacket *pkt, int repaint)
{
OpenGLContext *opengl = h->priv_data;
enum AVPixelFormat pix_fmt = h->streams[0]->codec->pix_fmt;
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
int ret;
#if HAVE_SDL
if (!opengl->no_window && (ret = opengl_sdl_process_events(h)) < 0)
goto fail;
#endif
if (opengl->no_window &&
(ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_PREPARE_WINDOW_BUFFER, NULL , 0)) < 0) {
av_log(opengl, AV_LOG_ERROR, "Application failed to prepare window buffer.\n");
goto fail;
}
glClear(GL_COLOR_BUFFER_BIT);
if (!repaint) {
glBindTexture(GL_TEXTURE_2D, opengl->texture_name[0]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, opengl->width, opengl->height, opengl->format, opengl->type,
opengl_get_plane_pointer(opengl, pkt, 0, desc));
if (desc->flags & AV_PIX_FMT_FLAG_PLANAR) {
int width_chroma = FF_CEIL_RSHIFT(opengl->width, desc->log2_chroma_w);
int height_chroma = FF_CEIL_RSHIFT(opengl->height, desc->log2_chroma_h);
glBindTexture(GL_TEXTURE_2D, opengl->texture_name[1]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_chroma, height_chroma, opengl->format, opengl->type,
opengl_get_plane_pointer(opengl, pkt, 1, desc));
glBindTexture(GL_TEXTURE_2D, opengl->texture_name[2]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_chroma, height_chroma, opengl->format, opengl->type,
opengl_get_plane_pointer(opengl, pkt, 2, desc));
if (desc->flags & AV_PIX_FMT_FLAG_ALPHA) {
glBindTexture(GL_TEXTURE_2D, opengl->texture_name[3]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, opengl->width, opengl->height, opengl->format, opengl->type,
opengl_get_plane_pointer(opengl, pkt, 3, desc));
}
}
}
ret = AVERROR_EXTERNAL;
OPENGL_ERROR_CHECK(opengl);
if ((ret = opengl_prepare(opengl)) < 0)
goto fail;
opengl->glprocs.glBindBuffer(FF_GL_ARRAY_BUFFER, opengl->vertex_buffer);
opengl->glprocs.glBindBuffer(FF_GL_ELEMENT_ARRAY_BUFFER, opengl->index_buffer);
opengl->glprocs.glVertexAttribPointer(opengl->position_attrib, 3, GL_FLOAT, GL_FALSE, sizeof(OpenGLVertexInfo), 0);
opengl->glprocs.glEnableVertexAttribArray(opengl->position_attrib);
opengl->glprocs.glVertexAttribPointer(opengl->texture_coords_attrib, 2, GL_FLOAT, GL_FALSE, sizeof(OpenGLVertexInfo), 12);
opengl->glprocs.glEnableVertexAttribArray(opengl->texture_coords_attrib);
glDrawElements(GL_TRIANGLES, FF_ARRAY_ELEMS(g_index), GL_UNSIGNED_SHORT, 0);
ret = AVERROR_EXTERNAL;
OPENGL_ERROR_CHECK(opengl);
#if HAVE_SDL
if (!opengl->no_window)
SDL_GL_SwapBuffers();
#endif
if (opengl->no_window &&
(ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_DISPLAY_WINDOW_BUFFER, NULL , 0)) < 0) {
av_log(opengl, AV_LOG_ERROR, "Application failed to display window buffer.\n");
goto fail;
}
return 0;
fail:
return ret;
}
static int opengl_write_packet(AVFormatContext *h, AVPacket *pkt)
{
return opengl_draw(h, pkt, 0);
}
#define OFFSET(x) offsetof(OpenGLContext, x)
#define ENC AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
{ "background", "set background color", OFFSET(background), AV_OPT_TYPE_COLOR, {.str = "black"}, CHAR_MIN, CHAR_MAX, ENC },
{ "no_window", "disable default window", OFFSET(no_window), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, ENC },
{ "window_title", "set window title", OFFSET(window_title), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, ENC },
{ NULL }
};
static const AVClass opengl_class = {
.class_name = "opengl outdev",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
AVOutputFormat ff_opengl_muxer = {
.name = "opengl",
.long_name = NULL_IF_CONFIG_SMALL("OpenGL output"),
.priv_data_size = sizeof(OpenGLContext),
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_RAWVIDEO,
.write_header = opengl_write_header,
.write_packet = opengl_write_packet,
.write_trailer = opengl_write_trailer,
.control_message = opengl_control_message,
.flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS,
.priv_class = &opengl_class,
};
/*
* Copyright (c) 2014 Lukasz Marek
*
* This file is part of FFmpeg.
*
* FFmpeg 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.
*
* FFmpeg 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 FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef AVDEVICE_OPENGL_SHADERS_H
#define AVDEVICE_OPENGL_SHADERS_H
#include "libavutil/pixfmt.h"
const char *FF_OPENGL_VERTEX_SHADER =
"uniform mat4 u_projectionMatrix;"
"uniform mat4 u_modelViewMatrix;"
"attribute vec4 a_position;"
"attribute vec2 a_textureCoords;"
"varying vec2 texture_coordinate;"
"void main()"
"{"
"gl_Position = u_projectionMatrix * (a_position * u_modelViewMatrix);"
"texture_coordinate = a_textureCoords;"
"}";
/**
* Fragment shader for packet RGBA formats.
*/
const char *FF_OPENGL_FRAGMENT_SHADER_RGBA_PACKET =
#if defined(GL_ES_VERSION_2_0)
"precision mediump float;"
#endif
"uniform sampler2D u_texture0;"
"uniform mat4 u_colorMap;"
"varying vec2 texture_coordinate;"
"void main()"
"{"
"gl_FragColor = texture2D(u_texture0, texture_coordinate) * u_colorMap;"
"}";
/**
* Fragment shader for packet RGB formats.
*/
const char *FF_OPENGL_FRAGMENT_SHADER_RGB_PACKET =
#if defined(GL_ES_VERSION_2_0)
"precision mediump float;"
#endif
"uniform sampler2D u_texture0;"
"uniform mat4 u_colorMap;"
"varying vec2 texture_coordinate;"
"void main()"
"{"
"gl_FragColor = vec4((texture2D(u_texture0, texture_coordinate) * u_colorMap).rgb, 1.0);"
"}";
/**
* Fragment shader for planar RGBA formats.
*/
const char *FF_OPENGL_FRAGMENT_SHADER_RGBA_PLANAR =
#if defined(GL_ES_VERSION_2_0)
"precision mediump float;"
#endif
"uniform sampler2D u_texture0;"
"uniform sampler2D u_texture1;"
"uniform sampler2D u_texture2;"
"uniform sampler2D u_texture3;"
"varying vec2 texture_coordinate;"
"void main()"
"{"
"gl_FragColor = vec4(texture2D(u_texture0, texture_coordinate).r,"
"texture2D(u_texture1, texture_coordinate).r,"
"texture2D(u_texture2, texture_coordinate).r,"
"texture2D(u_texture3, texture_coordinate).r);"
"}";
/**
* Fragment shader for planar RGB formats.
*/
const char *FF_OPENGL_FRAGMENT_SHADER_RGB_PLANAR =
#if defined(GL_ES_VERSION_2_0)
"precision mediump float;"
#endif
"uniform sampler2D u_texture0;"
"uniform sampler2D u_texture1;"
"uniform sampler2D u_texture2;"
"varying vec2 texture_coordinate;"
"void main()"
"{"
"gl_FragColor = vec4(texture2D(u_texture0, texture_coordinate).r,"
"texture2D(u_texture1, texture_coordinate).r,"
"texture2D(u_texture2, texture_coordinate).r,"
"1.0);"
"}";
/**
* Fragment shader for planar YUV formats.
*/
const char *FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR =
#if defined(GL_ES_VERSION_2_0)
"precision mediump float;"
#endif
"uniform sampler2D u_texture0;"
"uniform sampler2D u_texture1;"
"uniform sampler2D u_texture2;"
"uniform float u_chroma_div_w;"
"uniform float u_chroma_div_h;"
"varying vec2 texture_coordinate;"
"void main()"
"{"
"vec3 yuv;"
"yuv.r = texture2D(u_texture0, texture_coordinate).r - 0.0625;"
"yuv.g = texture2D(u_texture1, vec2(texture_coordinate.x / u_chroma_div_w, texture_coordinate.y / u_chroma_div_h)).r - 0.5;"
"yuv.b = texture2D(u_texture2, vec2(texture_coordinate.x / u_chroma_div_w, texture_coordinate.y / u_chroma_div_h)).r - 0.5;"
"gl_FragColor = clamp(vec4(mat3(1.1643, 1.16430, 1.1643,"
"0.0, -0.39173, 2.0170,"
"1.5958, -0.81290, 0.0) * yuv, 1.0), 0.0, 1.0);"
"}";
/**
* Fragment shader for planar YUVA formats.
*/
const char *FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR =
#if defined(GL_ES_VERSION_2_0)
"precision mediump float;"
#endif
"uniform sampler2D u_texture0;"
"uniform sampler2D u_texture1;"
"uniform sampler2D u_texture2;"
"uniform sampler2D u_texture3;"
"uniform float u_chroma_div_w;"
"uniform float u_chroma_div_h;"
"varying vec2 texture_coordinate;"
"void main()"
"{"
"vec3 yuv;"
"yuv.r = texture2D(u_texture0, texture_coordinate).r - 0.0625;"
"yuv.g = texture2D(u_texture1, vec2(texture_coordinate.x / u_chroma_div_w, texture_coordinate.y / u_chroma_div_h)).r - 0.5;"
"yuv.b = texture2D(u_texture2, vec2(texture_coordinate.x / u_chroma_div_w, texture_coordinate.y / u_chroma_div_h)).r - 0.5;"
"gl_FragColor = clamp(vec4(mat3(1.1643, 1.16430, 1.1643,"
"0.0, -0.39173, 2.0170,"
"1.5958, -0.81290, 0.0) * yuv, texture2D(u_texture3, texture_coordinate).r), 0.0, 1.0);"
"}";
#endif /* AVDEVICE_OPENGL_SHADERS_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment