Commit c94ed2a7 authored by Michael Niedermayer's avatar Michael Niedermayer

Merge remote-tracking branch 'lukaszmluki/master'

* lukaszmluki/master:
  lavd: add opengl device
  lavd: add avdevice_dev_to_app_control_message API
  lavd: add avdevice_app_to_dev_control_message API
Merged-by: 's avatarMichael Niedermayer <michaelni@gmx.at>
parents 673ce8e4 ded6b3af
...@@ -21,6 +21,7 @@ version <next> ...@@ -21,6 +21,7 @@ version <next>
- framepack filter - framepack filter
- XYZ12 rawvideo support in NUT - XYZ12 rawvideo support in NUT
- Exif metadata support in WebP decoder - Exif metadata support in WebP decoder
- OpenGL device
version 2.1: version 2.1:
......
...@@ -251,6 +251,7 @@ External library support: ...@@ -251,6 +251,7 @@ External library support:
--enable-libzvbi enable teletext support via libzvbi [no] --enable-libzvbi enable teletext support via libzvbi [no]
--enable-openal enable OpenAL 1.1 capture support [no] --enable-openal enable OpenAL 1.1 capture support [no]
--enable-opencl enable OpenCL code --enable-opencl enable OpenCL code
--enable-opengl enable OpenGL rendering [no]
--enable-openssl enable openssl [no] --enable-openssl enable openssl [no]
--enable-x11grab enable X11 grabbing [no] --enable-x11grab enable X11 grabbing [no]
--disable-zlib disable zlib [autodetect] --disable-zlib disable zlib [autodetect]
...@@ -1311,6 +1312,7 @@ EXTERNAL_LIBRARY_LIST=" ...@@ -1311,6 +1312,7 @@ EXTERNAL_LIBRARY_LIST="
libzvbi libzvbi
openal openal
opencl opencl
opengl
openssl openssl
x11grab x11grab
zlib zlib
...@@ -1554,6 +1556,7 @@ HAVE_LIST=" ...@@ -1554,6 +1556,7 @@ HAVE_LIST="
dxva_h dxva_h
ebp_available ebp_available
ebx_available ebx_available
ES2_gl_h
fast_64bit fast_64bit
fast_clz fast_clz
fast_cmov fast_cmov
...@@ -1570,6 +1573,7 @@ HAVE_LIST=" ...@@ -1570,6 +1573,7 @@ HAVE_LIST="
getservbyport getservbyport
gettimeofday gettimeofday
glob glob
glXGetProcAddress
gnu_as gnu_as
gnu_windres gnu_windres
gsm_h gsm_h
...@@ -1603,6 +1607,7 @@ HAVE_LIST=" ...@@ -1603,6 +1607,7 @@ HAVE_LIST="
mprotect mprotect
nanosleep nanosleep
openjpeg_1_5_openjpeg_h openjpeg_1_5_openjpeg_h
OpenGL_gl3_h
PeekNamedPipe PeekNamedPipe
perl perl
pod2man pod2man
...@@ -1656,6 +1661,7 @@ HAVE_LIST=" ...@@ -1656,6 +1661,7 @@ HAVE_LIST="
vdpau_x11 vdpau_x11
vfp_args vfp_args
VirtualAlloc VirtualAlloc
wglGetProcAddress
windows_h windows_h
winsock2_h winsock2_h
xform_asm xform_asm
...@@ -2262,6 +2268,7 @@ libcdio_indev_deps="libcdio" ...@@ -2262,6 +2268,7 @@ libcdio_indev_deps="libcdio"
libdc1394_indev_deps="libdc1394" libdc1394_indev_deps="libdc1394"
libv4l2_indev_deps="libv4l2" libv4l2_indev_deps="libv4l2"
openal_indev_deps="openal" openal_indev_deps="openal"
opengl_outdev_deps="opengl"
oss_indev_deps_any="soundcard_h sys_soundcard_h" oss_indev_deps_any="soundcard_h sys_soundcard_h"
oss_outdev_deps_any="soundcard_h sys_soundcard_h" oss_outdev_deps_any="soundcard_h sys_soundcard_h"
pulse_indev_deps="libpulse" pulse_indev_deps="libpulse"
...@@ -4498,6 +4505,12 @@ enabled opencl && { check_lib2 OpenCL/cl.h clEnqueueNDRangeKernel -Wl ...@@ -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 "OpenCL/cl.h" "defined(CL_VERSION_1_2)" ||
check_cpp_condition "CL/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"; } 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 || 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 -lssl32 -leay32 ||
check_lib openssl/ssl.h SSL_library_init -lssl -lcrypto -lws2_32 -lgdi32 || 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 ...@@ -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). 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 @section oss
OSS (Open Sound System) output device. OSS (Open Sound System) output device.
......
...@@ -29,6 +29,7 @@ OBJS-$(CONFIG_IEC61883_INDEV) += iec61883.o ...@@ -29,6 +29,7 @@ OBJS-$(CONFIG_IEC61883_INDEV) += iec61883.o
OBJS-$(CONFIG_JACK_INDEV) += jack_audio.o timefilter.o OBJS-$(CONFIG_JACK_INDEV) += jack_audio.o timefilter.o
OBJS-$(CONFIG_LAVFI_INDEV) += lavfi.o OBJS-$(CONFIG_LAVFI_INDEV) += lavfi.o
OBJS-$(CONFIG_OPENAL_INDEV) += openal-dec.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_INDEV) += oss_audio.o
OBJS-$(CONFIG_OSS_OUTDEV) += oss_audio.o OBJS-$(CONFIG_OSS_OUTDEV) += oss_audio.o
OBJS-$(CONFIG_PULSE_INDEV) += pulse_audio_dec.o \ OBJS-$(CONFIG_PULSE_INDEV) += pulse_audio_dec.o \
......
...@@ -56,6 +56,7 @@ void avdevice_register_all(void) ...@@ -56,6 +56,7 @@ void avdevice_register_all(void)
REGISTER_INDEV (JACK, jack); REGISTER_INDEV (JACK, jack);
REGISTER_INDEV (LAVFI, lavfi); REGISTER_INDEV (LAVFI, lavfi);
REGISTER_INDEV (OPENAL, openal); REGISTER_INDEV (OPENAL, openal);
REGISTER_OUTDEV (OPENGL, opengl);
REGISTER_INOUTDEV(OSS, oss); REGISTER_INOUTDEV(OSS, oss);
REGISTER_INOUTDEV(PULSE, pulse); REGISTER_INOUTDEV(PULSE, pulse);
REGISTER_OUTDEV (SDL, sdl); REGISTER_OUTDEV (SDL, sdl);
......
...@@ -36,3 +36,19 @@ const char * avdevice_license(void) ...@@ -36,3 +36,19 @@ const char * avdevice_license(void)
#define LICENSE_PREFIX "libavdevice license: " #define LICENSE_PREFIX "libavdevice license: "
return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1;
} }
int avdevice_app_to_dev_control_message(struct AVFormatContext *s, enum AVAppToDevMessageType type,
void *data, size_t data_size)
{
if (!s->oformat || !s->oformat->control_message)
return AVERROR(ENOSYS);
return s->oformat->control_message(s, type, data, data_size);
}
int avdevice_dev_to_app_control_message(struct AVFormatContext *s, enum AVDevToAppMessageType type,
void *data, size_t data_size)
{
if (!s->control_message_cb)
return AVERROR(ENOSYS);
return s->control_message_cb(s, type, data, data_size);
}
...@@ -66,4 +66,124 @@ const char *avdevice_license(void); ...@@ -66,4 +66,124 @@ const char *avdevice_license(void);
*/ */
void avdevice_register_all(void); void avdevice_register_all(void);
typedef struct AVDeviceRect {
int x; /**< x coordinate of top left corner */
int y; /**< y coordinate of top left corner */
int width; /**< width */
int height; /**< height */
} AVDeviceRect;
/**
* Message types used by avdevice_app_to_dev_control_message().
*/
enum AVAppToDevMessageType {
/**
* Dummy message.
*/
AV_APP_TO_DEV_NONE = MKBETAG('N','O','N','E'),
/**
* Window size change message.
*
* Message is sent to the device every time the application changes the size
* of the window device renders to.
* Message should also be sent right after window is created.
*
* data: AVDeviceRect: new window size.
*/
AV_APP_TO_DEV_WINDOW_SIZE = MKBETAG('G','E','O','M'),
/**
* Repaint request message.
*
* Message is sent to the device when window have to be rapainted.
*
* data: AVDeviceRect: area required to be repainted.
* NULL: whole area is required to be repainted.
*/
AV_APP_TO_DEV_WINDOW_REPAINT = MKBETAG('R','E','P','A')
};
/**
* Message types used by avdevice_dev_to_app_control_message().
*/
enum AVDevToAppMessageType {
/**
* Dummy message.
*/
AV_DEV_TO_APP_NONE = MKBETAG('N','O','N','E'),
/**
* Create window buffer message.
*
* Device requests to create a window buffer. Exact meaning is device-
* and application-dependent. Message is sent before rendering first
* frame and all one-shot initializations should be done here.
*
* data: NULL.
*/
AV_DEV_TO_APP_CREATE_WINDOW_BUFFER = MKBETAG('B','C','R','E'),
/**
* Prepare window buffer message.
*
* Device requests to prepare a window buffer for rendering.
* Exact meaning is device- and application-dependent.
* Message is sent before rendering of each frame.
*
* data: NULL.
*/
AV_DEV_TO_APP_PREPARE_WINDOW_BUFFER = MKBETAG('B','P','R','E'),
/**
* Display window buffer message.
*
* Device requests to display a window buffer.
* Message is sent when new frame is ready to be displyed.
* Usually buffers need to be swapped in handler of this message.
*
* data: NULL.
*/
AV_DEV_TO_APP_DISPLAY_WINDOW_BUFFER = MKBETAG('B','D','I','S'),
/**
* Destroy window buffer message.
*
* Device requests to destroy a window buffer.
* Message is sent when device is about to be destroyed and window
* buffer is not required anymore.
*
* data: NULL.
*/
AV_DEV_TO_APP_DESTROY_WINDOW_BUFFER = MKBETAG('B','D','E','S')
};
/**
* Send control message from application to device.
*
* @param s device context.
* @param type message type.
* @param data message data. Exact type depends on message type.
* @param data_size size of message data.
* @return >= 0 on success, negative on error.
* AVERROR(ENOSYS) when device doesn't implement handler of the message.
*/
int avdevice_app_to_dev_control_message(struct AVFormatContext *s,
enum AVAppToDevMessageType type,
void *data, size_t data_size);
/**
* Send control message from device to application.
*
* @param s device context.
* @param type message type.
* @param data message data. Can be NULL.
* @param data_size size of message data.
* @return >= 0 on success, negative on error.
* AVERROR(ENOSYS) when application doesn't implement handler of the message.
*/
int avdevice_dev_to_app_control_message(struct AVFormatContext *s,
enum AVDevToAppMessageType type,
void *data, size_t data_size);
#endif /* AVDEVICE_AVDEVICE_H */ #endif /* AVDEVICE_AVDEVICE_H */
/*
* 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 */
...@@ -28,8 +28,8 @@ ...@@ -28,8 +28,8 @@
#include "libavutil/version.h" #include "libavutil/version.h"
#define LIBAVDEVICE_VERSION_MAJOR 55 #define LIBAVDEVICE_VERSION_MAJOR 55
#define LIBAVDEVICE_VERSION_MINOR 5 #define LIBAVDEVICE_VERSION_MINOR 7
#define LIBAVDEVICE_VERSION_MICRO 102 #define LIBAVDEVICE_VERSION_MICRO 100
#define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \ #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
LIBAVDEVICE_VERSION_MINOR, \ LIBAVDEVICE_VERSION_MINOR, \
......
...@@ -453,6 +453,11 @@ typedef struct AVOutputFormat { ...@@ -453,6 +453,11 @@ typedef struct AVOutputFormat {
void (*get_output_timestamp)(struct AVFormatContext *s, int stream, void (*get_output_timestamp)(struct AVFormatContext *s, int stream,
int64_t *dts, int64_t *wall); int64_t *dts, int64_t *wall);
/**
* Allows sending messages from application to device.
*/
int (*control_message)(struct AVFormatContext *s, int type,
void *data, size_t data_size);
} AVOutputFormat; } AVOutputFormat;
/** /**
* @} * @}
...@@ -947,6 +952,13 @@ typedef struct AVChapter { ...@@ -947,6 +952,13 @@ typedef struct AVChapter {
} AVChapter; } AVChapter;
/**
* Callback used by devices to communicate with application.
*/
typedef int (*av_format_control_message)(struct AVFormatContext *s, int type,
void *data, size_t data_size);
/** /**
* The duration of a video can be estimated through various ways, and this enum can be used * The duration of a video can be estimated through various ways, and this enum can be used
* to know how the duration was estimated. * to know how the duration was estimated.
...@@ -1355,6 +1367,19 @@ typedef struct AVFormatContext { ...@@ -1355,6 +1367,19 @@ typedef struct AVFormatContext {
* Muxing: Set by user via av_format_set_metadata_header_padding. * Muxing: Set by user via av_format_set_metadata_header_padding.
*/ */
int metadata_header_padding; int metadata_header_padding;
/**
* User data.
* This is a place for some private data of the user.
* Mostly usable with control_message_cb or any future callbacks in device's context.
*/
void *opaque;
/**
* Callback used by devices to communicate with application.
*/
av_format_control_message control_message_cb;
} AVFormatContext; } AVFormatContext;
int av_format_get_probe_score(const AVFormatContext *s); int av_format_get_probe_score(const AVFormatContext *s);
...@@ -1366,6 +1391,10 @@ AVCodec * av_format_get_subtitle_codec(const AVFormatContext *s); ...@@ -1366,6 +1391,10 @@ AVCodec * av_format_get_subtitle_codec(const AVFormatContext *s);
void av_format_set_subtitle_codec(AVFormatContext *s, AVCodec *c); void av_format_set_subtitle_codec(AVFormatContext *s, AVCodec *c);
int av_format_get_metadata_header_padding(const AVFormatContext *s); int av_format_get_metadata_header_padding(const AVFormatContext *s);
void av_format_set_metadata_header_padding(AVFormatContext *s, int c); void av_format_set_metadata_header_padding(AVFormatContext *s, int c);
void * av_format_get_opaque(const AVFormatContext *s);
void av_format_set_opaque(AVFormatContext *s, void *opaque);
av_format_control_message av_format_get_control_message_cb(const AVFormatContext *s);
void av_format_set_control_message_cb(AVFormatContext *s, av_format_control_message callback);
/** /**
* Returns the method used to set ctx->duration. * Returns the method used to set ctx->duration.
......
...@@ -107,6 +107,8 @@ MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, video_codec) ...@@ -107,6 +107,8 @@ MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, video_codec)
MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, audio_codec) MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, audio_codec)
MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, subtitle_codec) MAKE_ACCESSORS(AVFormatContext, format, AVCodec *, subtitle_codec)
MAKE_ACCESSORS(AVFormatContext, format, int, metadata_header_padding) MAKE_ACCESSORS(AVFormatContext, format, int, metadata_header_padding)
MAKE_ACCESSORS(AVFormatContext, format, void *, opaque)
MAKE_ACCESSORS(AVFormatContext, format, av_format_control_message, control_message_cb)
static AVCodec *find_decoder(AVFormatContext *s, AVStream *st, enum AVCodecID codec_id) static AVCodec *find_decoder(AVFormatContext *s, AVStream *st, enum AVCodecID codec_id)
{ {
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
#include "libavutil/version.h" #include "libavutil/version.h"
#define LIBAVFORMAT_VERSION_MAJOR 55 #define LIBAVFORMAT_VERSION_MAJOR 55
#define LIBAVFORMAT_VERSION_MINOR 26 #define LIBAVFORMAT_VERSION_MINOR 28
#define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_MICRO 100
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
......
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