lavd/opengl_enc: factorize deinit_context function
[ffmpeg.git] / libavdevice / opengl_enc.c
index 32a77c3..ff9ffc0 100644 (file)
@@ -63,7 +63,7 @@
 #define APIENTRY
 #endif
 
-/* GL_RED_COMPONENT is used for plannar pixel types.
+/* FF_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_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
+#define FF_GL_RED_COMPONENT GL_RED
 #elif defined(GL_LUMINANCE)
-#define GL_RED_COMPONENT GL_LUMINANCE
+#define FF_GL_RED_COMPONENT GL_LUMINANCE
 #else
-#define GL_RED_COMPONENT 0x1903; //GL_RED
+#define FF_GL_RED_COMPONENT 0x1903; //GL_RED
 #endif
 
 /* Constants not defined for iOS */
@@ -188,6 +188,8 @@ typedef struct OpenGLContext {
 
     /* 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
@@ -225,21 +227,21 @@ static const struct OpenGLFormatDesc {
     GLenum format;
     GLenum type;
 } opengl_format_desc[] = {
-    { AV_PIX_FMT_YUV420P,    &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
-    { AV_PIX_FMT_YUV444P,    &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
-    { AV_PIX_FMT_YUV422P,    &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
-    { AV_PIX_FMT_YUV410P,    &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
-    { AV_PIX_FMT_YUV411P,    &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
-    { AV_PIX_FMT_YUV440P,    &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
-    { AV_PIX_FMT_YUV420P16,  &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
-    { AV_PIX_FMT_YUV422P16,  &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
-    { AV_PIX_FMT_YUV444P16,  &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
-    { AV_PIX_FMT_YUVA420P,   &FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR, GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
-    { AV_PIX_FMT_YUVA444P,   &FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR, GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
-    { AV_PIX_FMT_YUVA422P,   &FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR, GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
-    { AV_PIX_FMT_YUVA420P16, &FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR, GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
-    { AV_PIX_FMT_YUVA422P16, &FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR, GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
-    { AV_PIX_FMT_YUVA444P16, &FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR, GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
+    { AV_PIX_FMT_YUV420P,    &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  FF_GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
+    { AV_PIX_FMT_YUV444P,    &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  FF_GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
+    { AV_PIX_FMT_YUV422P,    &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  FF_GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
+    { AV_PIX_FMT_YUV410P,    &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  FF_GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
+    { AV_PIX_FMT_YUV411P,    &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  FF_GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
+    { AV_PIX_FMT_YUV440P,    &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  FF_GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
+    { AV_PIX_FMT_YUV420P16,  &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  FF_GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
+    { AV_PIX_FMT_YUV422P16,  &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  FF_GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
+    { AV_PIX_FMT_YUV444P16,  &FF_OPENGL_FRAGMENT_SHADER_YUV_PLANAR,  FF_GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
+    { AV_PIX_FMT_YUVA420P,   &FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR, FF_GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
+    { AV_PIX_FMT_YUVA444P,   &FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR, FF_GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
+    { AV_PIX_FMT_YUVA422P,   &FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR, FF_GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
+    { AV_PIX_FMT_YUVA420P16, &FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR, FF_GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
+    { AV_PIX_FMT_YUVA422P16, &FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR, FF_GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
+    { AV_PIX_FMT_YUVA444P16, &FF_OPENGL_FRAGMENT_SHADER_YUVA_PLANAR, FF_GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
     { AV_PIX_FMT_RGB24,      &FF_OPENGL_FRAGMENT_SHADER_RGB_PACKET,  GL_RGB, GL_UNSIGNED_BYTE },
     { AV_PIX_FMT_BGR24,      &FF_OPENGL_FRAGMENT_SHADER_RGB_PACKET,  GL_RGB, GL_UNSIGNED_BYTE },
     { AV_PIX_FMT_0RGB,       &FF_OPENGL_FRAGMENT_SHADER_RGB_PACKET,  GL_RGBA, GL_UNSIGNED_BYTE },
@@ -259,10 +261,10 @@ static const struct OpenGLFormatDesc {
     { AV_PIX_FMT_BGRA,       &FF_OPENGL_FRAGMENT_SHADER_RGBA_PACKET, GL_RGBA, GL_UNSIGNED_BYTE },
     { AV_PIX_FMT_RGBA64,     &FF_OPENGL_FRAGMENT_SHADER_RGBA_PACKET, GL_RGBA, GL_UNSIGNED_SHORT },
     { AV_PIX_FMT_BGRA64,     &FF_OPENGL_FRAGMENT_SHADER_RGBA_PACKET, GL_RGBA, GL_UNSIGNED_SHORT },
-    { AV_PIX_FMT_GBRP,       &FF_OPENGL_FRAGMENT_SHADER_RGB_PLANAR,  GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
-    { AV_PIX_FMT_GBRP16,     &FF_OPENGL_FRAGMENT_SHADER_RGB_PLANAR,  GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
-    { AV_PIX_FMT_GBRAP,      &FF_OPENGL_FRAGMENT_SHADER_RGBA_PLANAR, GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
-    { AV_PIX_FMT_GBRAP16,    &FF_OPENGL_FRAGMENT_SHADER_RGBA_PLANAR, GL_RED_COMPONENT, GL_UNSIGNED_SHORT },
+    { AV_PIX_FMT_GBRP,       &FF_OPENGL_FRAGMENT_SHADER_RGB_PLANAR,  FF_GL_RED_COMPONENT, GL_UNSIGNED_BYTE },
+    { 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_NONE,       NULL }
 };
 
@@ -270,6 +272,32 @@ 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 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;
@@ -349,8 +377,7 @@ static int opengl_sdl_process_events(AVFormatContext *h)
             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);
+                opengl_deinit_context(opengl);
             }
             if ((ret = opengl_sdl_recreate_window(opengl, event.resize.w, event.resize.h)) < 0)
                 return ret;
@@ -731,7 +758,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 +767,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 +784,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 +818,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;
 }
 
@@ -889,25 +915,59 @@ static int opengl_prepare(OpenGLContext *opengl)
     return AVERROR_EXTERNAL;
 }
 
-static av_cold int opengl_write_trailer(AVFormatContext *h)
+static int opengl_create_window(AVFormatContext *h)
 {
     OpenGLContext *opengl = h->priv_data;
+    int ret;
 
-    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 (!opengl->no_window) {
+#if HAVE_SDL
+        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 {
+        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");
+            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 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)
+    } 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;
+
+    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");
+
+    opengl_deinit_context(opengl);
+    opengl_release_window(h);
 
     return 0;
 }
@@ -991,25 +1051,8 @@ static av_cold int opengl_write_header(AVFormatContext *h)
     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;