avdevice/decklink_dec: Used av_parity instead of duplicated function
[ffmpeg.git] / libavdevice / opengl_enc.c
index f214585..bb6787c 100644 (file)
 
 #include "config.h"
 
+#if HAVE_WINDOWS_H
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
 #if HAVE_OPENGL_GL3_H
 #include <OpenGL/gl3.h>
 #elif HAVE_ES2_GL_H
 #if HAVE_GLXGETPROCADDRESS
 #include <GL/glx.h>
 #endif
-#if HAVE_WINDOWS_H
-#include <windows.h>
-#endif
 
-#if HAVE_SDL
+#if CONFIG_SDL2
 #include <SDL.h>
 #endif
 
@@ -56,6 +57,7 @@
 #include "libavutil/avassert.h"
 #include "libavutil/avstring.h"
 #include "libavformat/avformat.h"
+#include "libavformat/internal.h"
 #include "libavdevice/avdevice.h"
 #include "opengl_enc_shaders.h"
 
@@ -63,9 +65,9 @@
 #define APIENTRY
 #endif
 
-/* FF_GL_RED_COMPONENT is used for plannar pixel types.
+/* FF_GL_RED_COMPONENT is used for planar pixel types.
  * Only red component is sampled in shaders.
- * On some platforms GL_RED is not availabe and GL_LUMINANCE have to be used,
+ * On some platforms GL_RED is not available 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.
@@ -82,6 +84,7 @@
 #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
+#define FF_GL_UNPACK_ROW_LENGTH          0x0CF2
 
 /* MinGW exposes only OpenGL 1.1 API */
 #define FF_GL_ARRAY_BUFFER                0x8892
@@ -162,7 +165,7 @@ typedef struct OpenGLVertexInfo
 } OpenGLVertexInfo;
 
 /* defines 2 triangles to display */
-static GLushort g_index[6] =
+static const GLushort g_index[6] =
 {
     0, 1, 2,
     0, 3, 2,
@@ -171,11 +174,13 @@ static GLushort g_index[6] =
 typedef struct OpenGLContext {
     AVClass *class;                    ///< class for private options
 
-#if HAVE_SDL
-    SDL_Surface *surface;
+#if CONFIG_SDL2
+    SDL_Window *window;
+    SDL_GLContext glcontext;
 #endif
     FFOpenGLFunctions glprocs;
 
+    int inited;                        ///< Set to 1 when write_header was successfully called.
     uint8_t background[4];             ///< Background color
     int no_window;                     ///< 0 for create default window
     char *window_title;                ///< Title of the window
@@ -185,9 +190,12 @@ typedef struct OpenGLContext {
     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
+    int unpack_subimage;               ///< 1 when GL_EXT_unpack_subimage is available
 
     /* Current OpenGL configuration */
     GLuint program;                    ///< Shader program
+    GLuint vertex_shader;              ///< Vertex shader
+    GLuint fragment_shader;            ///< Fragment shader for current pix_pmt
     GLuint texture_name[4];            ///< Textures' IDs
     GLuint index_buffer;               ///< Index buffer
     GLuint vertex_buffer;              ///< Vertex buffer
@@ -253,6 +261,7 @@ static const struct OpenGLFormatDesc {
     { AV_PIX_FMT_RGB8,       &FF_OPENGL_FRAGMENT_SHADER_RGB_PACKET,  GL_RGB, FF_GL_UNSIGNED_BYTE_3_3_2 },
     { AV_PIX_FMT_BGR8,       &FF_OPENGL_FRAGMENT_SHADER_RGB_PACKET,  GL_RGB, FF_GL_UNSIGNED_BYTE_2_3_3_REV },
     { AV_PIX_FMT_RGB48,      &FF_OPENGL_FRAGMENT_SHADER_RGB_PACKET,  GL_RGB, GL_UNSIGNED_SHORT },
+    { AV_PIX_FMT_BGR48,      &FF_OPENGL_FRAGMENT_SHADER_RGB_PACKET,  GL_RGB, GL_UNSIGNED_SHORT },
     { AV_PIX_FMT_ARGB,       &FF_OPENGL_FRAGMENT_SHADER_RGBA_PACKET, GL_RGBA, GL_UNSIGNED_BYTE },
     { AV_PIX_FMT_RGBA,       &FF_OPENGL_FRAGMENT_SHADER_RGBA_PACKET, GL_RGBA, GL_UNSIGNED_BYTE },
     { AV_PIX_FMT_ABGR,       &FF_OPENGL_FRAGMENT_SHADER_RGBA_PACKET, GL_RGBA, GL_UNSIGNED_BYTE },
@@ -263,21 +272,48 @@ static const struct OpenGLFormatDesc {
     { AV_PIX_FMT_GBRP16,     &FF_OPENGL_FRAGMENT_SHADER_RGB_PLANAR,  FF_GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
     { AV_PIX_FMT_GBRAP,      &FF_OPENGL_FRAGMENT_SHADER_RGBA_PLANAR, FF_GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
     { AV_PIX_FMT_GBRAP16,    &FF_OPENGL_FRAGMENT_SHADER_RGBA_PLANAR, FF_GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
+    { AV_PIX_FMT_GRAY8,      &FF_OPENGL_FRAGMENT_SHADER_GRAY,        FF_GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
+    { AV_PIX_FMT_GRAY16,     &FF_OPENGL_FRAGMENT_SHADER_GRAY,        FF_GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
     { AV_PIX_FMT_NONE,       NULL }
 };
 
 static av_cold int opengl_prepare_vertex(AVFormatContext *s);
-static int opengl_draw(AVFormatContext *h, AVPacket *pkt, int repaint);
+static int opengl_draw(AVFormatContext *h, void *intput, int repaint, int is_pkt);
 static av_cold int opengl_init_context(OpenGLContext *opengl);
 
+static av_cold void opengl_deinit_context(OpenGLContext *opengl)
+{
+    glDeleteTextures(4, opengl->texture_name);
+    opengl->texture_name[0] = opengl->texture_name[1] =
+    opengl->texture_name[2] = opengl->texture_name[3] = 0;
+    if (opengl->glprocs.glUseProgram)
+        opengl->glprocs.glUseProgram(0);
+    if (opengl->glprocs.glDeleteProgram) {
+        opengl->glprocs.glDeleteProgram(opengl->program);
+        opengl->program = 0;
+    }
+    if (opengl->glprocs.glDeleteShader) {
+        opengl->glprocs.glDeleteShader(opengl->vertex_shader);
+        opengl->glprocs.glDeleteShader(opengl->fragment_shader);
+        opengl->vertex_shader = opengl->fragment_shader = 0;
+    }
+    if (opengl->glprocs.glBindBuffer) {
+        opengl->glprocs.glBindBuffer(FF_GL_ARRAY_BUFFER, 0);
+        opengl->glprocs.glBindBuffer(FF_GL_ELEMENT_ARRAY_BUFFER, 0);
+    }
+    if (opengl->glprocs.glDeleteBuffers) {
+        opengl->glprocs.glDeleteBuffers(2, &opengl->index_buffer);
+        opengl->vertex_buffer = opengl->index_buffer = 0;
+    }
+}
+
 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->inited) {
         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");
@@ -285,7 +321,7 @@ static int opengl_resize(AVFormatContext *h, int width, int height)
         }
         if ((ret = opengl_prepare_vertex(h)) < 0)
             goto end;
-        ret = opengl_draw(h, NULL, 1);
+        ret = opengl_draw(h, NULL, 1, 0);
     }
   end:
     return ret;
@@ -307,30 +343,14 @@ static int opengl_control_message(AVFormatContext *h, int type, void *data, size
     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;
-}
-
+#if CONFIG_SDL2
 static int opengl_sdl_process_events(AVFormatContext *h)
 {
-    int ret;
     OpenGLContext *opengl = h->priv_data;
+    AVDeviceRect message;
     SDL_Event event;
     SDL_PumpEvents();
-    while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_ALLEVENTS) > 0) {
+    while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) > 0) {
         switch (event.type) {
         case SDL_QUIT:
             return AVERROR(EIO);
@@ -341,24 +361,14 @@ static int opengl_sdl_process_events(AVFormatContext *h)
                 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));
+        case SDL_WINDOWEVENT:
+            switch(event.window.event) {
+            case SDL_WINDOWEVENT_RESIZED:
+            case SDL_WINDOWEVENT_SIZE_CHANGED:
+                SDL_GL_GetDrawableSize(opengl->window, &message.width, &message.height);
+                return opengl_control_message(h, AV_APP_TO_DEV_WINDOW_SIZE, &message, sizeof(AVDeviceRect));
+            default:
+                break;
             }
         }
     }
@@ -367,21 +377,34 @@ static int opengl_sdl_process_events(AVFormatContext *h)
 
 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;
+    opengl->window = SDL_CreateWindow(opengl->window_title,
+                                      SDL_WINDOWPOS_UNDEFINED,
+                                      SDL_WINDOWPOS_UNDEFINED,
+                                      opengl->window_width, opengl->window_height,
+                                      SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
+    if (!opengl->window) {
+        av_log(opengl, AV_LOG_ERROR, "Unable to create default window: %s\n", SDL_GetError());
+        return AVERROR_EXTERNAL;
+    }
+    opengl->glcontext = SDL_GL_CreateContext(opengl->window);
+    if (!opengl->glcontext) {
+        av_log(opengl, AV_LOG_ERROR, "Unable to create OpenGL context on default window: %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);
+    av_log(opengl, AV_LOG_INFO, "SDL driver: '%s'.\n", SDL_GetCurrentVideoDriver());
+    SDL_GL_GetDrawableSize(opengl->window, &message.width, &message.height);
+    return opengl_control_message(h, AV_APP_TO_DEV_WINDOW_SIZE, &message, sizeof(AVDeviceRect));
 }
 
 static int av_cold opengl_sdl_load_procedures(OpenGLContext *opengl)
@@ -425,13 +448,18 @@ static int av_cold opengl_sdl_load_procedures(OpenGLContext *opengl)
 
 #undef LOAD_OPENGL_FUN
 }
-#endif /* HAVE_SDL */
+#endif /* CONFIG_SDL2 */
 
 #if defined(__APPLE__)
 static int av_cold opengl_load_procedures(OpenGLContext *opengl)
 {
     FFOpenGLFunctions *procs = &opengl->glprocs;
 
+#if CONFIG_SDL2
+    if (!opengl->no_window)
+        return opengl_sdl_load_procedures(opengl);
+#endif
+
     procs->glActiveTexture = glActiveTexture;
     procs->glGenBuffers = glGenBuffers;
     procs->glDeleteBuffers = glDeleteBuffers;
@@ -477,6 +505,11 @@ static int av_cold opengl_load_procedures(OpenGLContext *opengl)
         return AVERROR(ENOSYS); \
     }
 
+#if CONFIG_SDL2
+    if (!opengl->no_window)
+        return opengl_sdl_load_procedures(opengl);
+#endif
+
     LOAD_OPENGL_FUN(glActiveTexture, FF_PFNGLACTIVETEXTUREPROC)
     LOAD_OPENGL_FUN(glGenBuffers, FF_PFNGLGENBUFFERSPROC)
     LOAD_OPENGL_FUN(glDeleteBuffers, FF_PFNGLDELETEBUFFERSPROC)
@@ -510,16 +543,14 @@ static int av_cold opengl_load_procedures(OpenGLContext *opengl)
 }
 #endif
 
-static av_always_inline void opengl_make_identity(float matrix[16])
+static 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)
+static 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;
@@ -541,7 +572,7 @@ static av_always_inline void opengl_make_ortho(float matrix[16],
 static av_cold int opengl_read_limits(OpenGLContext *opengl)
 {
     static const struct{
-        const char *extention;
+        const char *extension;
         int major;
         int minor;
     } required_extensions[] = {
@@ -561,12 +592,12 @@ static av_cold int opengl_read_limits(OpenGLContext *opengl)
     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++) {
+    for (i = 0; required_extensions[i].extension; i++) {
         if (major < required_extensions[i].major &&
             (major == required_extensions[i].major && minor < required_extensions[i].minor) &&
-            !strstr(extensions, required_extensions[i].extention)) {
+            !strstr(extensions, required_extensions[i].extension)) {
             av_log(opengl, AV_LOG_ERROR, "Required extension %s is not supported.\n",
-                   required_extensions[i].extention);
+                   required_extensions[i].extension);
             av_log(opengl, AV_LOG_DEBUG, "Supported extensions are: %s\n", extensions);
             return AVERROR(ENOSYS);
         }
@@ -574,8 +605,14 @@ static av_cold int opengl_read_limits(OpenGLContext *opengl)
     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");
+#if defined(GL_ES_VERSION_2_0)
+    opengl->unpack_subimage = !!strstr(extensions, "GL_EXT_unpack_subimage");
+#else
+    opengl->unpack_subimage = 1;
+#endif
 
     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, "Unpack Subimage extension support: %s\n", opengl->unpack_subimage ? "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);
@@ -596,7 +633,7 @@ static const char* opengl_get_fragment_shader_code(enum AVPixelFormat format)
     return NULL;
 }
 
-static av_always_inline int opengl_type_size(GLenum type)
+static int opengl_type_size(GLenum type)
 {
     switch(type) {
     case GL_UNSIGNED_SHORT:
@@ -629,11 +666,11 @@ 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;
+    AVCodecParameters *par = st->codecpar;
 
     /* 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 });
+    dar = av_mul_q(sar, (AVRational){ par->width, par->height });
 
     /* we suppose the screen has a 1/1 sample aspect ratio */
     /* fit in the window */
@@ -684,8 +721,8 @@ static av_cold void opengl_fill_color_map(OpenGLContext *opengl)
         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; \
+        shift = (desc->comp[i].depth - 1) >> 3; \
+        opengl->color_map[(i << 2) + (desc->comp[i].offset >> shift)] = 1.0; \
     }
 
     memset(opengl->color_map, 0, sizeof(opengl->color_map));
@@ -731,7 +768,6 @@ static av_cold GLuint opengl_load_shader(OpenGLContext *opengl, GLenum type, con
 
 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);
 
@@ -741,15 +777,15 @@ static av_cold int opengl_compile_shaders(OpenGLContext *opengl, enum AVPixelFor
         return AVERROR(EINVAL);
     }
 
-    vertex_shader = opengl_load_shader(opengl, FF_GL_VERTEX_SHADER,
-                                       FF_OPENGL_VERTEX_SHADER);
-    if (!vertex_shader) {
+    opengl->vertex_shader = opengl_load_shader(opengl, FF_GL_VERTEX_SHADER,
+                                               FF_OPENGL_VERTEX_SHADER);
+    if (!opengl->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) {
+    opengl->fragment_shader = opengl_load_shader(opengl, FF_GL_FRAGMENT_SHADER,
+                                                 fragment_shader_code);
+    if (!opengl->fragment_shader) {
         av_log(opengl, AV_LOG_ERROR, "Fragment shader loading failed.\n");
         goto fail;
     }
@@ -758,8 +794,8 @@ static av_cold int opengl_compile_shaders(OpenGLContext *opengl, enum AVPixelFor
     if (!opengl->program)
         goto fail;
 
-    opengl->glprocs.glAttachShader(opengl->program, vertex_shader);
-    opengl->glprocs.glAttachShader(opengl->program, fragment_shader);
+    opengl->glprocs.glAttachShader(opengl->program, opengl->vertex_shader);
+    opengl->glprocs.glAttachShader(opengl->program, opengl->fragment_shader);
     opengl->glprocs.glLinkProgram(opengl->program);
 
     opengl->glprocs.glGetProgramiv(opengl->program, FF_GL_LINK_STATUS, &result);
@@ -792,10 +828,10 @@ static av_cold int opengl_compile_shaders(OpenGLContext *opengl, enum AVPixelFor
     OPENGL_ERROR_CHECK(opengl);
     return 0;
   fail:
-    opengl->glprocs.glDeleteShader(vertex_shader);
-    opengl->glprocs.glDeleteShader(fragment_shader);
+    opengl->glprocs.glDeleteShader(opengl->vertex_shader);
+    opengl->glprocs.glDeleteShader(opengl->fragment_shader);
     opengl->glprocs.glDeleteProgram(opengl->program);
-    opengl->program = 0;
+    opengl->fragment_shader = opengl->vertex_shader = opengl->program = 0;
     return AVERROR_EXTERNAL;
 }
 
@@ -825,8 +861,8 @@ static av_cold int opengl_prepare_vertex(AVFormatContext *s)
     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);
+        opengl->window_width = FFMIN(opengl->window_width, opengl->max_viewport_width);
+        opengl->window_height = FFMIN(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);
@@ -889,6 +925,56 @@ static int opengl_prepare(OpenGLContext *opengl)
     return AVERROR_EXTERNAL;
 }
 
+static int opengl_create_window(AVFormatContext *h)
+{
+    OpenGLContext *opengl = h->priv_data;
+    int ret;
+
+    if (!opengl->no_window) {
+#if CONFIG_SDL2
+        if ((ret = opengl_sdl_create_window(h)) < 0) {
+            av_log(opengl, AV_LOG_ERROR, "Cannot create default SDL window.\n");
+            return ret;
+        }
+#else
+        av_log(opengl, AV_LOG_ERROR, "FFmpeg is compiled without SDL. Cannot create default window.\n");
+        return AVERROR(ENOSYS);
+#endif
+    } else {
+        AVDeviceRect message;
+        message.x = message.y = 0;
+        message.width = opengl->window_width;
+        message.height = opengl->window_height;
+        if ((ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_CREATE_WINDOW_BUFFER,
+                                                       &message , sizeof(message))) < 0) {
+            av_log(opengl, AV_LOG_ERROR, "Application failed to create window buffer.\n");
+            return ret;
+        }
+        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");
+            return ret;
+        }
+    }
+    return 0;
+}
+
+static int opengl_release_window(AVFormatContext *h)
+{
+    int ret;
+    OpenGLContext *opengl = h->priv_data;
+    if (!opengl->no_window) {
+#if CONFIG_SDL2
+        SDL_GL_DeleteContext(opengl->glcontext);
+        SDL_DestroyWindow(opengl->window);
+        SDL_Quit();
+#endif
+    } else if ((ret = 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 ret;
+    }
+    return 0;
+}
+
 static av_cold int opengl_write_trailer(AVFormatContext *h)
 {
     OpenGLContext *opengl = h->priv_data;
@@ -897,17 +983,8 @@ static av_cold int opengl_write_trailer(AVFormatContext *h)
         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");
+    opengl_deinit_context(opengl);
+    opengl_release_window(h);
 
     return 0;
 }
@@ -945,8 +1022,8 @@ static av_cold int opengl_init_context(OpenGLContext *opengl)
         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));
+                        AV_CEIL_RSHIFT(opengl->width, desc->log2_chroma_w),
+                        AV_CEIL_RSHIFT(opengl->height, desc->log2_chroma_h));
             else
                 opengl_configure_texture(opengl, opengl->texture_name[i], opengl->width, opengl->height);
         if (has_alpha)
@@ -978,38 +1055,25 @@ static av_cold int opengl_write_header(AVFormatContext *h)
     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) {
+        h->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO ||
+        h->streams[0]->codecpar->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;
+    opengl->width = st->codecpar->width;
+    opengl->height = st->codecpar->height;
+    opengl->pix_fmt = st->codecpar->format;
+    if (!opengl->window_width)
+        opengl->window_width = opengl->width;
+    if (!opengl->window_height)
+        opengl->window_height = opengl->height;
 
     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);
+    if ((ret = opengl_create_window(h)))
         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;
@@ -1021,12 +1085,7 @@ static av_cold int opengl_write_header(AVFormatContext *h)
         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)
+    if ((ret = opengl_load_procedures(opengl)) < 0)
         goto fail;
 
     opengl_fill_color_map(opengl);
@@ -1040,9 +1099,9 @@ static av_cold int opengl_write_header(AVFormatContext *h)
 
     glClear(GL_COLOR_BUFFER_BIT);
 
-#if HAVE_SDL
+#if CONFIG_SDL2
     if (!opengl->no_window)
-        SDL_GL_SwapBuffers();
+        SDL_GL_SwapWindow(opengl->window);
 #endif
     if (opengl->no_window &&
         (ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_DISPLAY_WINDOW_BUFFER, NULL , 0)) < 0) {
@@ -1052,6 +1111,8 @@ static av_cold int opengl_write_header(AVFormatContext *h)
 
     ret = AVERROR_EXTERNAL;
     OPENGL_ERROR_CHECK(opengl);
+
+    opengl->inited = 1;
     return 0;
 
   fail:
@@ -1064,8 +1125,8 @@ static uint8_t* opengl_get_plane_pointer(OpenGLContext *opengl, AVPacket *pkt, i
 {
     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 width_chroma = AV_CEIL_RSHIFT(opengl->width, desc->log2_chroma_w);
+    int height_chroma = AV_CEIL_RSHIFT(opengl->height, desc->log2_chroma_h);
     int plane = desc->comp[comp_index].plane;
 
     switch(plane) {
@@ -1088,14 +1149,52 @@ static uint8_t* opengl_get_plane_pointer(OpenGLContext *opengl, AVPacket *pkt, i
     return data;
 }
 
-static int opengl_draw(AVFormatContext *h, AVPacket *pkt, int repaint)
+#define LOAD_TEXTURE_DATA(comp_index, sub)                                                  \
+{                                                                                           \
+    int width = sub ? AV_CEIL_RSHIFT(opengl->width, desc->log2_chroma_w) : opengl->width;   \
+    int height = sub ? AV_CEIL_RSHIFT(opengl->height, desc->log2_chroma_h): opengl->height; \
+    uint8_t *data;                                                                          \
+    int plane = desc->comp[comp_index].plane;                                               \
+                                                                                            \
+    glBindTexture(GL_TEXTURE_2D, opengl->texture_name[comp_index]);                         \
+    if (!is_pkt) {                                                                          \
+        GLint length = ((AVFrame *)input)->linesize[plane];                                 \
+        int bytes_per_pixel = opengl_type_size(opengl->type);                               \
+        if (!(desc->flags & AV_PIX_FMT_FLAG_PLANAR))                                        \
+            bytes_per_pixel *= desc->nb_components;                                         \
+        data = ((AVFrame *)input)->data[plane];                                             \
+        if (!(length % bytes_per_pixel) &&                                                  \
+            (opengl->unpack_subimage || ((length / bytes_per_pixel) == width))) {           \
+            length /= bytes_per_pixel;                                                      \
+            if (length != width)                                                            \
+                glPixelStorei(FF_GL_UNPACK_ROW_LENGTH, length);                             \
+            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height,                          \
+                            opengl->format, opengl->type, data);                            \
+            if (length != width)                                                            \
+                glPixelStorei(FF_GL_UNPACK_ROW_LENGTH, 0);                                  \
+        } else {                                                                            \
+            int h;                                                                          \
+            for (h = 0; h < height; h++) {                                                  \
+                glTexSubImage2D(GL_TEXTURE_2D, 0, 0, h, width, 1,                           \
+                                opengl->format, opengl->type, data);                        \
+                data += length;                                                             \
+            }                                                                               \
+        }                                                                                   \
+    } else {                                                                                \
+        data = opengl_get_plane_pointer(opengl, input, comp_index, desc);                   \
+        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height,                              \
+                        opengl->format, opengl->type, data);                                \
+    }                                                                                       \
+}
+
+static int opengl_draw(AVFormatContext *h, void *input, int repaint, int is_pkt)
 {
     OpenGLContext *opengl = h->priv_data;
-    enum AVPixelFormat pix_fmt = h->streams[0]->codec->pix_fmt;
+    enum AVPixelFormat pix_fmt = h->streams[0]->codecpar->format;
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
     int ret;
 
-#if HAVE_SDL
+#if CONFIG_SDL2
     if (!opengl->no_window && (ret = opengl_sdl_process_events(h)) < 0)
         goto fail;
 #endif
@@ -1108,23 +1207,14 @@ static int opengl_draw(AVFormatContext *h, AVPacket *pkt, int repaint)
     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 (is_pkt)
+            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+        LOAD_TEXTURE_DATA(0, 0)
         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));
-            }
+            LOAD_TEXTURE_DATA(1, 1)
+            LOAD_TEXTURE_DATA(2, 1)
+            if (desc->flags & AV_PIX_FMT_FLAG_ALPHA)
+                LOAD_TEXTURE_DATA(3, 0)
         }
     }
     ret = AVERROR_EXTERNAL;
@@ -1145,9 +1235,9 @@ static int opengl_draw(AVFormatContext *h, AVPacket *pkt, int repaint)
     ret = AVERROR_EXTERNAL;
     OPENGL_ERROR_CHECK(opengl);
 
-#if HAVE_SDL
+#if CONFIG_SDL2
     if (!opengl->no_window)
-        SDL_GL_SwapBuffers();
+        SDL_GL_SwapWindow(opengl->window);
 #endif
     if (opengl->no_window &&
         (ret = avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_DISPLAY_WINDOW_BUFFER, NULL , 0)) < 0) {
@@ -1162,7 +1252,15 @@ static int opengl_draw(AVFormatContext *h, AVPacket *pkt, int repaint)
 
 static int opengl_write_packet(AVFormatContext *h, AVPacket *pkt)
 {
-    return opengl_draw(h, pkt, 0);
+    return opengl_draw(h, pkt, 0, 1);
+}
+
+static int opengl_write_frame(AVFormatContext *h, int stream_index,
+                              AVFrame **frame, unsigned flags)
+{
+    if ((flags & AV_WRITE_UNCODED_FRAME_QUERY))
+        return 0;
+    return opengl_draw(h, *frame, 0, 0);
 }
 
 #define OFFSET(x) offsetof(OpenGLContext, x)
@@ -1171,6 +1269,7 @@ 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 },
+    { "window_size",  "set window size",        OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, ENC },
     { NULL }
 };
 
@@ -1179,6 +1278,7 @@ static const AVClass opengl_class = {
     .item_name  = av_default_item_name,
     .option     = options,
     .version    = LIBAVUTIL_VERSION_INT,
+    .category   = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT,
 };
 
 AVOutputFormat ff_opengl_muxer = {
@@ -1189,6 +1289,7 @@ AVOutputFormat ff_opengl_muxer = {
     .video_codec    = AV_CODEC_ID_RAWVIDEO,
     .write_header   = opengl_write_header,
     .write_packet   = opengl_write_packet,
+    .write_uncoded_frame = opengl_write_frame,
     .write_trailer  = opengl_write_trailer,
     .control_message = opengl_control_message,
     .flags          = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS,