From 0971be62956b6d39bf3d6b063d269dfc6276d21f Mon Sep 17 00:00:00 2001 From: Christophe Thommeret Date: Fri, 13 Jul 2012 22:06:02 +0300 Subject: OpenGL 2 video out plugin --- src/video_out/video_out_opengl2.c | 1682 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1682 insertions(+) create mode 100644 src/video_out/video_out_opengl2.c diff --git a/src/video_out/video_out_opengl2.c b/src/video_out/video_out_opengl2.c new file mode 100644 index 000000000..9ae0c5710 --- /dev/null +++ b/src/video_out/video_out_opengl2.c @@ -0,0 +1,1682 @@ +/* + * kate: space-indent on; indent-width 2; mixedindent off; indent-mode cstyle; remove-trailing-space on; + * Copyright (C) 20012 the xine project + * Copyright (C) 20012 Christophe Thommeret + * + * This file is part of xine, a free video player. + * + * xine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * xine 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * + * video_out_opengl2.c, a video output plugin using opengl 2.0 + * + * + */ + +/* #define LOG */ +#define LOG_MODULE "video_out_opengl2" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "yuv2rgb.h" + +#ifdef HAVE_FFMPEG_AVUTIL_H +# include +#else +# include +#endif + +#define GL_GLEXT_PROTOTYPES +#include +#include +#include + + + +typedef int (*GLXSWAPINTERVALSGI) ( int ); + +typedef struct { + vo_frame_t vo_frame; + int width, height, format, flags; + double ratio; +} opengl2_frame_t; + + + +typedef struct { + uint8_t *ovl_rgba; + int ovl_w, ovl_h; + int ovl_x, ovl_y; + + GLuint tex; + int tex_w, tex_h; + + int unscaled; + int vid_scale; +} opengl2_overlay_t; + + + +typedef struct { + int compiled; + GLuint shader; + GLuint program; +} opengl2_program_t; + + + +typedef struct { + GLuint y, u, v; + GLuint yuv; + int width; + int height; +} opengl2_yuvtex_t; + + + +typedef struct { + + vo_driver_t vo_driver; + vo_scale_t sc; + + Display *display; + int screen; + Drawable drawable; + GLXContext context; + + int texture_float; + opengl2_program_t yuv420_program; + opengl2_program_t yuv422_program; + opengl2_yuvtex_t yuvtex; + GLuint videoPBO; + GLuint fbo; + GLuint videoTex, videoTex2; + GLXSWAPINTERVALSGI mglXSwapInterval; + + int ovl_changed; + int ovl_vid_scale; + int num_ovls; + opengl2_overlay_t overlays[XINE_VORAW_MAX_OVL]; + yuv2rgb_factory_t *yuv2rgb_factory; + yuv2rgb_t *ovl_yuv2rgb; + + opengl2_program_t sharpness_program; + float csc_matrix[3 * 4]; + float *color_standard; + int update_csc; + int saturation; + int contrast; + int brightness; + int hue; + int sharpness; + + opengl2_program_t bicubic_pass1_program; + opengl2_program_t bicubic_pass2_program; + GLuint bicubic_lut_texture; + GLuint bicubic_pass1_texture; + int bicubic_pass1_texture_width; + int bicubic_pass1_texture_height; + GLuint bicubic_fbo; + int scale_bicubic; + + pthread_mutex_t drawable_lock; + uint32_t display_width; + uint32_t display_height; + + config_values_t *config; + + uint32_t capabilities; + xine_t *xine; + + int zoom_x; + int zoom_y; +} opengl2_driver_t; + + + +typedef struct { + video_driver_class_t driver_class; + GLXContext ctx; + xine_t *xine; +} opengl2_class_t; + + + +static const char *bicubic_pass1_frag= +"#extension GL_ARB_texture_rectangle : enable\n" +"uniform sampler2DRect tex, lut;\n" +"uniform float spline;\n" +"void main() {\n" +" vec2 coord = gl_TexCoord[0].xy;\n" +" vec2 TexCoord = vec2( floor( coord.x - 0.5 ) + 0.5, coord.y );\n" +" vec4 sum = vec4( 0.0 );\n" +" float coefsum = 0.0;\n" +" mat4 wlut;\n" +" wlut[0] = texture2DRect( lut, vec2( abs( coord.x - TexCoord.x ) * 1000.0, spline ) );\n" +" for( int x = -1; x <= 2; x++ ) {\n" +" vec4 col = texture2DRect( tex, TexCoord + vec2( float( x ), 0.0) );\n" +" sum += col * wlut[0][x+1];\n" +" coefsum += wlut[0][x+1];\n" +" }\n" +" gl_FragColor = sum / coefsum;\n" +"}\n"; + + + +static const char *bicubic_pass2_frag= +"#extension GL_ARB_texture_rectangle : enable\n" +"uniform sampler2DRect tex, lut;\n" +"uniform float spline;\n" +"void main() {\n" +" vec2 coord = gl_TexCoord[0].xy;\n" +" vec2 TexCoord = vec2( coord.x, floor( coord.y - 0.5 ) + 0.5 );\n" +" vec4 sum = vec4( 0.0 );\n" +" float coefsum = 0.0;\n" +" mat4 wlut;\n" +" wlut[0] = texture2DRect( lut, vec2( abs( coord.y - TexCoord.y ) * 1000.0, spline ) );\n" +" for( int y = -1; y <= 2; y++ ) {\n" +" vec4 col = texture2DRect( tex, TexCoord + vec2( 0.0, float( y ) ) );\n" +" sum += col * wlut[0][y+1];\n" +" coefsum += wlut[0][y+1];\n" +" }\n" +" gl_FragColor = sum / coefsum;\n" +"}\n"; + + + +#define PI 3.14159265359 +#define LUTWIDTH 1000 +#define N_SPLINES 2 +#define CATMULLROM_SPLINE 0 +#define COS_SPLINE 1 + +static float compute_cos_spline( float x ) +{ + if ( x < 0.0 ) + x = -x; + return 0.5 * cos( PI * x / 2.0 ) + 0.5; +} + +static float compute_catmullrom_spline( float x ) +{ + if ( x < 0.0 ) + x = -x; + if ( x < 1.0 ) + return ((9.0 * (x * x * x)) - (15.0 * (x * x)) + 6.0) / 6.0; + if ( x <= 2.0 ) + return ((-3.0 * (x * x * x)) + (15.0 * (x * x)) - (24.0 * x) + 12.0) / 6.0; + return 0.0; +} + +static int create_lut_texture( opengl2_driver_t *that ) +{ + int i = 0; + float *lut = calloc( sizeof(float) * LUTWIDTH * 4 * N_SPLINES, 1 ); + float t; + while ( i < LUTWIDTH ) { + t = (float)i / (float)LUTWIDTH; + + lut[i * 4] = compute_catmullrom_spline( t + 1.0 ); + lut[(i * 4) + 1] = compute_catmullrom_spline( t ); + lut[(i * 4) + 2] = compute_catmullrom_spline( t - 1.0 ); + lut[(i * 4) + 3] = compute_catmullrom_spline( t - 2.0 ); + + lut[(i * 4) + (LUTWIDTH * 4)] = compute_cos_spline( t + 1.0 ); + lut[(i * 4) + (LUTWIDTH * 4) + 1] = compute_cos_spline( t ); + lut[(i * 4) + (LUTWIDTH * 4) + 2] = compute_cos_spline( t - 1.0 ); + lut[(i * 4) + (LUTWIDTH * 4) + 3] = compute_cos_spline( t - 2.0 ); + + ++i; + } + + that->bicubic_lut_texture = 0; + glGenTextures( 1, &that->bicubic_lut_texture ); + if ( !that->bicubic_lut_texture ) + return 0; + + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, that->bicubic_lut_texture ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA32F, LUTWIDTH, N_SPLINES, 0, GL_RGBA, GL_FLOAT, lut ); + free( lut ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, 0 ); + return 1; +} + + + +static const char *blur_sharpen_frag= +"#extension GL_ARB_texture_rectangle : enable\n" +"uniform sampler2DRect tex;\n" +"uniform float value;\n" +"void main() {\n" +" vec2 pos = gl_TexCoord[0].xy;\n" +" vec4 c1;\n" +" float K;\n" +" if ( value < 0.0 )\n" +" K = value / 8.0;\n" +" else\n" +" K = value / 4.0;\n" +" c1 = texture2DRect( tex, pos ) * (1.0 + (8.0 * K));\n" +" c1 -= texture2DRect( tex, pos + vec2( 1.0, 0.0 ) ) * K;\n" +" c1 -= texture2DRect( tex, pos + vec2( -1.0, 0.0 ) ) * K;\n" +" c1 -= texture2DRect( tex, pos + vec2( 0.0, 1.0 ) ) * K;\n" +" c1 -= texture2DRect( tex, pos + vec2( 0.0, -1.0 ) ) * K;\n" +" c1 -= texture2DRect( tex, pos + vec2( 1.0, 1.0 ) ) * K;\n" +" c1 -= texture2DRect( tex, pos + vec2( -1.0, 1.0 ) ) * K;\n" +" c1 -= texture2DRect( tex, pos + vec2( -1.0, -1.0 ) ) * K;\n" +" c1 -= texture2DRect( tex, pos + vec2( 1.0, -1.0 ) ) * K;\n" +" gl_FragColor = c1 ;\n" +"}\n"; + + + +static const char *yuv420_frag= +"#extension GL_ARB_texture_rectangle : enable\n" +"uniform sampler2DRect texY, texU, texV;\n" +"uniform vec4 r_coefs, g_coefs, b_coefs;\n" +"void main(void) {\n" +" vec3 rgb;\n" +" vec4 yuv;\n" +" vec2 coord = gl_TexCoord[0].xy;\n" +" yuv.r = texture2DRect(texY, vec2(coord.x, coord.y)).r;\n" +" yuv.g = texture2DRect(texU, vec2(coord.x/2.0, coord.y/2.0)).r;\n" +" yuv.b = texture2DRect(texV, vec2(coord.x/2.0, coord.y/2.0)).r;\n" +" yuv.a = 1.0;\n" +" rgb.r = dot( yuv, r_coefs );\n" +" rgb.g = dot( yuv, g_coefs );\n" +" rgb.b = dot( yuv, b_coefs );\n" +" gl_FragColor = vec4(rgb, 1.0);\n" +"}\n"; + + + +static const char *yuv422_frag= +"#extension GL_ARB_texture_rectangle : enable\n" +"uniform sampler2DRect texYUV;\n" +"uniform vec4 r_coefs, g_coefs, b_coefs;\n" +"void main(void) {\n" +" vec3 rgb;\n" +" vec4 yuv;\n" +" vec4 coord = gl_TexCoord[0].xyxx;\n" +" coord.z -= step(1.0, mod(coord.x, 2.0));\n" +" coord.w = coord.z + 1.0;\n" +" yuv.r = texture2DRect(texYUV, coord.xy).r;\n" +" yuv.g = texture2DRect(texYUV, coord.zy).a;\n" +" yuv.b = texture2DRect(texYUV, coord.wy).a;\n" +" yuv.a = 1.0;\n" +" rgb.r = dot( yuv, r_coefs );\n" +" rgb.g = dot( yuv, g_coefs );\n" +" rgb.b = dot( yuv, b_coefs );\n" +" gl_FragColor = vec4(rgb, 1.0);\n" +"}\n"; + + + +float yuv_601[] = { + 1.16438, 0.00000, 1.59603, -0.87420, + 1.16438, -0.39176, -0.81297, 0.53167, + 1.16438, 2.01723, 0.00000, -1.08563 +}; + +float yuv_709[] = { + 1.16438, 0.00000, 1.79274, -0.97295, + 1.16438, -0.21325, -0.53291, 0.30148, + 1.16438, 2.11240, 0.00000, -1.13340 +}; + +float yuv_240[] = { + 1.16438, 0.00000, 1.79411, -0.97363, + 1.16438, -0.25798, -0.54258, 0.32879, + 1.16438, 2.07871, 0.00000, -1.11649 +}; + +static void load_csc_matrix( GLuint prog, float *cf ) +{ + glUniform4f( glGetUniformLocationARB( prog, "r_coefs" ), cf[0], cf[1], cf[2], cf[3] ); + glUniform4f( glGetUniformLocationARB( prog, "g_coefs" ), cf[4], cf[5], cf[6], cf[7] ); + glUniform4f( glGetUniformLocationARB( prog, "b_coefs" ), cf[8], cf[9], cf[10], cf[11] ); +} + + + +static int opengl2_build_program( opengl2_program_t *prog, const char **source, const char *name ) +{ + fprintf(stderr, "vo_opengl2 : compiling shader %s\n", name); + if ( !(prog->shader = glCreateShader( GL_FRAGMENT_SHADER )) ) + return 0; + if ( !(prog->program = glCreateProgram()) ) + return 0; + + glShaderSource( prog->shader, 1, source, NULL ); + glCompileShader( prog->shader ); + + GLint length; + GLchar *log; + glGetShaderiv( prog->shader, GL_INFO_LOG_LENGTH, &length ); + log = (GLchar*)malloc( length ); + if ( !log ) + return 0; + + glGetShaderInfoLog( prog->shader, length, &length, log ); + if ( length ) { + fprintf( stderr, "Shader %s Compilation Log:\n", name ); + fprintf( stderr, "%s", log ); + } + free( log ); + + glAttachShader( prog->program, prog->shader ); + glLinkProgram( prog->program ); + glGetProgramiv( prog->program, GL_INFO_LOG_LENGTH, &length ); + log = (GLchar*)malloc( length ); + if ( !log ) + return 0; + + glGetProgramInfoLog( prog->program, length, &length, log ); + if ( length ) { + fprintf( stderr, "Linking Log:\n" ); + fprintf( stderr, "%s", log ); + } + free( log ); + + prog->compiled = 1; + + return 1; +} + + + +static void opengl2_delete_program( opengl2_program_t *prog ) +{ + glDeleteProgram( prog->program ); + glDeleteShader( prog->shader ); +} + + + +static int opengl2_check_textures_size( opengl2_driver_t *this_gen, int w, int h ) +{ + opengl2_driver_t *this = this_gen; + opengl2_yuvtex_t *ytex = &this->yuvtex; + + if ( (w == ytex->width) && (h == ytex->height) ) + return 1; + + if ( ytex->y ) + glDeleteTextures( 1, &ytex->y ); + if ( ytex->u ) + glDeleteTextures( 1, &ytex->u ); + if ( ytex->v ) + glDeleteTextures( 1, &ytex->v ); + if ( ytex->yuv ) + glDeleteTextures( 1, &ytex->yuv ); + if ( this->videoTex ) + glDeleteTextures( 1, &this->videoTex ); + if ( this->videoTex2 ) + glDeleteTextures( 1, &this->videoTex2 ); + + if ( !this->videoPBO ) { + glGenBuffers( 1, &this->videoPBO ); + if ( !this->videoPBO ) + return 0; + } + + if ( !this->fbo ) { + glGenFramebuffers( 1, &this->fbo ); + if ( !this->fbo ) + return 0; + } + + glGenTextures( 1, &this->videoTex ); + if ( !this->videoTex ) + return 0; + + glGenTextures( 1, &this->videoTex2 ); + if ( !this->videoTex2 ) + return 0; + + glGenTextures( 1, &ytex->y ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, ytex->y ); + glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glGenTextures( 1, &ytex->u ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, ytex->u ); + glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, w/2, h/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glGenTextures( 1, &ytex->v ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, ytex->v ); + glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, w/2, h/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glGenTextures( 1, &ytex->yuv ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, ytex->yuv ); + glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE_ALPHA, w, h, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, 0 ); + + ytex->width = w; + ytex->height = h; + + glGenTextures( 1, &this->videoTex ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, this->videoTex ); + glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, 0 ); + + glGenTextures( 1, &this->videoTex2 ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, this->videoTex2 ); + glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, 0 ); + + glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, this->videoPBO ); + glBufferData( GL_PIXEL_UNPACK_BUFFER_ARB, w * h * 2, NULL, GL_STREAM_DRAW ); + glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, 0 ); + + glBindFramebuffer( GL_FRAMEBUFFER, this->fbo ); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, this->videoTex, 0 ); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_RECTANGLE_ARB, this->videoTex2, 0 ); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + + return 1; +} + + + +static void opengl2_overlay_clut_yuv2rgb(opengl2_driver_t *this, vo_overlay_t *overlay, opengl2_frame_t *frame) +{ + //fprintf(stderr, "opengl2_overlay_clut_yuv2rgb\n"); + int i; + clut_t* clut = (clut_t*) overlay->color; + + if (!overlay->rgb_clut) { + for ( i=0; icolor)/sizeof(overlay->color[0]); i++ ) { + *((uint32_t *)&clut[i]) = this->ovl_yuv2rgb->yuv2rgb_single_pixel_fun(this->ovl_yuv2rgb, clut[i].y, clut[i].cb, clut[i].cr); + } + overlay->rgb_clut++; + } + if (!overlay->hili_rgb_clut) { + clut = (clut_t*) overlay->hili_color; + for ( i=0; icolor)/sizeof(overlay->color[0]); i++) { + *((uint32_t *)&clut[i]) = this->ovl_yuv2rgb->yuv2rgb_single_pixel_fun(this->ovl_yuv2rgb, clut[i].y, clut[i].cb, clut[i].cr); + } + overlay->hili_rgb_clut++; + } +} + + +static int opengl2_process_ovl( opengl2_driver_t *this_gen, vo_overlay_t *overlay ) +{ + //fprintf(stderr, "opengl2_process_ovl\n"); + opengl2_overlay_t *ovl = &this_gen->overlays[this_gen->ovl_changed-1]; + + if ( overlay->width<=0 || overlay->height<=0 ) + return 0; + + if ( (overlay->width*overlay->height)!=(ovl->ovl_w*ovl->ovl_h) ) + ovl->ovl_rgba = (uint8_t*)realloc( ovl->ovl_rgba, overlay->width*overlay->height*4 ); + ovl->ovl_w = overlay->width; + ovl->ovl_h = overlay->height; + ovl->ovl_x = overlay->x; + ovl->ovl_y = overlay->y; + ovl->unscaled = overlay->unscaled; + if ( overlay->extent_width == -1 ) + ovl->vid_scale = 1; + else + ovl->vid_scale = 0; + + int num_rle = overlay->num_rle; + rle_elem_t *rle = overlay->rle; + uint8_t *rgba = ovl->ovl_rgba; + clut_t *low_colors = (clut_t*)overlay->color; + clut_t *hili_colors = (clut_t*)overlay->hili_color; + uint8_t *low_trans = overlay->trans; + uint8_t *hili_trans = overlay->hili_trans; + clut_t *colors; + uint8_t *trans; + uint8_t alpha; + int rlelen = 0; + uint8_t clr = 0; + int i, pos=0, x, y; + + while ( num_rle>0 ) { + x = pos%ovl->ovl_w; + y = pos/ovl->ovl_w; + if ( (x>=overlay->hili_left && x<=overlay->hili_right) && (y>=overlay->hili_top && y<=overlay->hili_bottom) ) { + colors = hili_colors; + trans = hili_trans; + } + else { + colors = low_colors; + trans = low_trans; + } + rlelen = rle->len; + clr = rle->color; + alpha = trans[clr]; + for ( i=0; iovl_changed = 1; +} + + +static void opengl2_overlay_blend (vo_driver_t *this_gen, vo_frame_t *frame_gen, vo_overlay_t *overlay) +{ + //fprintf(stderr, "opengl2_overlay_blend\n"); + opengl2_driver_t *this = (opengl2_driver_t *) this_gen; + opengl2_frame_t *frame = (opengl2_frame_t *) frame_gen; + + if ( !this->ovl_changed || this->ovl_changed>XINE_VORAW_MAX_OVL ) + return; + + if (overlay->rle) { + if (!overlay->rgb_clut || !overlay->hili_rgb_clut) + opengl2_overlay_clut_yuv2rgb(this, overlay, frame); + if ( opengl2_process_ovl( this, overlay ) ) + ++this->ovl_changed; + } +} + + +static void opengl2_overlay_end (vo_driver_t *this_gen, vo_frame_t *vo_img) +{ + //fprintf(stderr, "opengl2_overlay_end\n"); + opengl2_driver_t *this = (opengl2_driver_t *) this_gen; + + if ( !this->ovl_changed ) + return; + + this->num_ovls = this->ovl_changed - 1; +} + + + + +static void opengl2_frame_proc_slice( vo_frame_t *vo_img, uint8_t **src ) +{ + vo_img->proc_called = 1; +} + + + +static void opengl2_frame_field( vo_frame_t *vo_img, int which_field ) +{ +} + + + +static void opengl2_frame_dispose( vo_frame_t *vo_img ) +{ + opengl2_frame_t *frame = (opengl2_frame_t *) vo_img ; + + av_free (frame->vo_frame.base[0]); + av_free (frame->vo_frame.base[1]); + av_free (frame->vo_frame.base[2]); + free (frame); +} + + + +static vo_frame_t *opengl2_alloc_frame( vo_driver_t *this_gen ) +{ + opengl2_frame_t *frame; + + frame = (opengl2_frame_t *) calloc(1, sizeof(opengl2_frame_t)); + + if (!frame) + return NULL; + + frame->vo_frame.base[0] = frame->vo_frame.base[1] = frame->vo_frame.base[2] = NULL; + frame->width = frame->height = frame->format = frame->flags = 0; + + pthread_mutex_init (&frame->vo_frame.mutex, NULL); + + /* + * supply required functions/fields + */ + frame->vo_frame.proc_slice = opengl2_frame_proc_slice; + frame->vo_frame.proc_frame = NULL; + frame->vo_frame.field = opengl2_frame_field; + frame->vo_frame.dispose = opengl2_frame_dispose; + frame->vo_frame.driver = this_gen; + + return (vo_frame_t *) frame; +} + + + +static void opengl2_update_frame_format( vo_driver_t *this_gen, vo_frame_t *frame_gen, + uint32_t width, uint32_t height, double ratio, int format, int flags ) +{ + opengl2_frame_t *frame = (opengl2_frame_t *) frame_gen; + + /* Check frame size and format and reallocate if necessary */ + if ( (frame->width != width) || (frame->height != height) || (frame->format != format) || (frame->flags != flags) ) { + + /* (re-) allocate render space */ + av_freep (&frame->vo_frame.base[0]); + av_freep (&frame->vo_frame.base[1]); + av_freep (&frame->vo_frame.base[2]); + + if (format == XINE_IMGFMT_YV12) { + frame->vo_frame.pitches[0] = 8*((width + 7) / 8); + frame->vo_frame.pitches[1] = 8*((width + 15) / 16); + frame->vo_frame.pitches[2] = 8*((width + 15) / 16); + frame->vo_frame.base[0] = av_mallocz (frame->vo_frame.pitches[0] * height); + frame->vo_frame.base[1] = av_mallocz (frame->vo_frame.pitches[1] * ((height+1)/2)); + frame->vo_frame.base[2] = av_mallocz (frame->vo_frame.pitches[2] * ((height+1)/2)); + } else if (format == XINE_IMGFMT_YUY2){ + frame->vo_frame.pitches[0] = 8*((width + 3) / 4); + frame->vo_frame.base[0] = av_mallocz (frame->vo_frame.pitches[0] * height); + } + + frame->width = width; + frame->height = height; + frame->format = format; + frame->flags = flags; + } + + frame->ratio = ratio; +} + + + +static int opengl2_redraw_needed( vo_driver_t *this_gen ) +{ + opengl2_driver_t *this = (opengl2_driver_t *) this_gen; + + _x_vo_scale_compute_ideal_size( &this->sc ); + if ( _x_vo_scale_redraw_needed( &this->sc ) ) { + _x_vo_scale_compute_output_size( &this->sc ); + return 1; + } + return 0; +} + + + +static void opengl2_update_csc_matrix( opengl2_driver_t *that, opengl2_frame_t *frame ) +{ + float *color_standard = (frame->height > 576) ? yuv_709 : yuv_601; + + if ( that->update_csc || that->color_standard != color_standard ) { + float hue = that->hue/100.0; + float saturation = that->saturation/100.0; + float contrast = that->contrast/100.0; + float brightness = that->brightness/100.0; + float uvcos = saturation * cos( hue ); + float uvsin = saturation * sin( hue ); + float uc, vc; + int i; + + for (i=0; i<3; ++i ) { + that->csc_matrix[(i * 4) + 0] = color_standard[(i * 4) + 0]; + uc = color_standard[(i * 4) + 1]; + vc = color_standard[(i * 4) + 2]; + + that->csc_matrix[(i * 4) + 3] = brightness - (16.0/219.0); + that->csc_matrix[(i * 4) + 1] = (uvcos * uc) + (uvsin * vc); + that->csc_matrix[(i * 4) + 3] += ((-128.0 / 255.0) * uvcos * uc) - ((128.0 / 255.0) * uvsin * vc); + that->csc_matrix[(i * 4) + 2] = (uvsin * uc) + (uvcos * vc); + that->csc_matrix[(i * 4) + 3] += ((-128.0 / 255.0) * uvsin * uc) - ((128.0 / 255.0) * uvcos * vc); + + that->csc_matrix[(i * 4) + 0] *= contrast; + that->csc_matrix[(i * 4) + 1] *= contrast; + that->csc_matrix[(i * 4) + 2] *= contrast; + that->csc_matrix[(i * 4) + 3] *= contrast; + } + + that->color_standard = color_standard; + that->update_csc = 0; + } +} + + + +static void opengl2_update_overlays( opengl2_driver_t *that ) +{ + int i, vid_scale=0, cancel_vid_scale=0; + opengl2_overlay_t *o; + + if ( that->ovl_changed ) { + that->ovl_vid_scale = 0; + for ( i=0; inum_ovls; ++i ) { + o = &that->overlays[i]; + + /* handle DVB subs scaling, e.g. 720x576->1920x1080 */ + if ( o->vid_scale ) { + vid_scale = 1; + if ( o->ovl_w > 720 || o->ovl_h > 576 ) + cancel_vid_scale = 1; + } + + if ( o->tex && ((o->tex_w != o->ovl_w) || (o->tex_h != o->ovl_h)) ) { + glDeleteTextures( 1, &o->tex ); + o->tex = 0; + } + if ( !o->tex ) { + glGenTextures( 1, &o->tex ); + o->tex_w = o->ovl_w; + o->tex_h = o->ovl_h; + } + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, o->tex ); + glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, o->tex_w, o->tex_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, o->ovl_rgba ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, 0 ); + } + } + + if ( that->ovl_changed && vid_scale && !cancel_vid_scale ) + that->ovl_vid_scale = 1; + + that->ovl_changed = 0; +} + + + +static void opengl2_draw_scaled_overlays( opengl2_driver_t *that, opengl2_frame_t *frame ) +{ + int i, ox, oy, ow, oh; + opengl2_overlay_t *o; + + glEnable( GL_BLEND ); + for ( i=0; inum_ovls; ++i ) { + o = &that->overlays[i]; + if ( o->unscaled ) + continue; + ox = o->ovl_x; oy = o->ovl_y; + ow = o->ovl_w; oh = o->ovl_h; + if ( o->vid_scale && that->ovl_vid_scale ) { + float fx = frame->width / 720.0, fy = frame->height / 576.0; + ox *= fx; oy *= fy; + ow *= fx; oh *= fy; + } + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, o->tex ); + + glBegin( GL_QUADS ); + glTexCoord2f( 0, 0 ); glVertex3f( ox, oy, 0.); + glTexCoord2f( 0, o->tex_h ); glVertex3f( ox, oy + oh, 0.); + glTexCoord2f( o->tex_w, o->tex_h ); glVertex3f( ox + ow, oy + oh, 0.); + glTexCoord2f( o->tex_w, 0 ); glVertex3f( ox + ow, oy, 0.); + glEnd(); + } + glDisable( GL_BLEND ); +} + + + +static void opengl2_draw_unscaled_overlays( opengl2_driver_t *that ) +{ + int i; + opengl2_overlay_t *o; + + glEnable( GL_BLEND ); + for ( i=0; inum_ovls; ++i ) { + o = &that->overlays[i]; + if ( !o->unscaled ) + continue; + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, o->tex ); + + glBegin( GL_QUADS ); + glTexCoord2f( 0, 0 ); glVertex3f( o->ovl_x, o->ovl_y, 0.); + glTexCoord2f( 0, o->tex_h ); glVertex3f( o->ovl_x, o->ovl_y + o->ovl_h, 0.); + glTexCoord2f( o->tex_w, o->tex_h ); glVertex3f( o->ovl_x + o->ovl_w, o->ovl_y + o->ovl_h, 0.); + glTexCoord2f( o->tex_w, 0 ); glVertex3f( o->ovl_x + o->ovl_w, o->ovl_y, 0.); + glEnd(); + } + glDisable( GL_BLEND ); +} + + + +static GLuint opengl2_swap_textures( opengl2_driver_t *that, GLuint current_dest ) +{ + if ( current_dest == that->videoTex ) { + glDrawBuffer( GL_COLOR_ATTACHMENT1 ); + return that->videoTex2; + } + + glDrawBuffer( GL_COLOR_ATTACHMENT0 ); + return that->videoTex; +} + + + +static GLuint opengl2_sharpness( opengl2_driver_t *that, opengl2_frame_t *frame, GLuint video_texture ) +{ + GLuint ret = video_texture; + + if ( !that->sharpness_program.compiled ) { + if ( !opengl2_build_program( &that->sharpness_program, &blur_sharpen_frag, "blur_sharpen_frag" ) ) + return ret; + } + + ret = opengl2_swap_textures( that, video_texture ); + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, video_texture ); + + float value = that->sharpness / 100.0 * frame->width / 1920.0; + + glUseProgram( that->sharpness_program.program ); + glUniform1i( glGetUniformLocationARB( that->sharpness_program.program, "tex" ), 0 ); + glUniform1f( glGetUniformLocationARB( that->sharpness_program.program, "value" ), value ); + //fprintf(stderr, "vo_opengl2 : sharpness = %f\n", value); + + glBegin( GL_QUADS ); + glTexCoord2f( 0, 0 ); glVertex3f( 0, 0, 0.); + glTexCoord2f( 0, frame->height ); glVertex3f( 0, frame->height, 0.); + glTexCoord2f( frame->width, frame->height ); glVertex3f( frame->width, frame->height, 0.); + glTexCoord2f( frame->width, 0 ); glVertex3f( frame->width, 0, 0.); + glEnd(); + + glUseProgram( 0 ); + + return ret; +} + + + +static int opengl2_draw_video_bicubic( opengl2_driver_t *that, int guiw, int guih, GLfloat u, GLfloat v, GLfloat u1, GLfloat v1, + GLfloat x, GLfloat y, GLfloat x1, GLfloat y1, GLuint video_texture ) +{ + if ( !that->bicubic_lut_texture ) { + if ( !create_lut_texture( that ) ) + return 0; + } + + if ( !that->bicubic_pass1_program.compiled && !opengl2_build_program( &that->bicubic_pass1_program, &bicubic_pass1_frag, "bicubic_pass1_frag" ) ) + return 0; + if ( !that->bicubic_pass2_program.compiled && !opengl2_build_program( &that->bicubic_pass2_program, &bicubic_pass2_frag, "bicubic_pass2_frag" ) ) + return 0; + if ( !that->bicubic_fbo ) { + glGenFramebuffers( 1, &that->bicubic_fbo ); + if ( !that->bicubic_fbo ) + return 0; + } + if ( (that->bicubic_pass1_texture_width != (x1 - x)) || (that->bicubic_pass1_texture_height != (v1 - v) ) ) { + if ( that->bicubic_pass1_texture ) + glDeleteTextures( 1, &that->bicubic_pass1_texture ); + glGenTextures( 1, &that->bicubic_pass1_texture ); + if ( !that->bicubic_pass1_texture ) + return 0; + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, that->bicubic_pass1_texture ); + glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, x1 - x, v1 - v, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameterf( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, 0 ); + that->bicubic_pass1_texture_width = x1 - x; + that->bicubic_pass1_texture_height = v1 - v; + } + glBindFramebuffer( GL_FRAMEBUFFER, that->bicubic_fbo ); + glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, that->bicubic_pass1_texture, 0 ); + + glViewport( 0, 0, x1 - x, v1 - v ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0.0, x1 - x, 0.0, v1 - v, -1.0, 1.0 ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, video_texture ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glActiveTexture( GL_TEXTURE1 ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, that->bicubic_lut_texture ); + glUseProgram( that->bicubic_pass1_program.program ); + glUniform1i( glGetUniformLocationARB( that->bicubic_pass1_program.program, "tex" ), 0 ); + glUniform1i( glGetUniformLocationARB( that->bicubic_pass1_program.program, "lut" ), 1 ); + glUniform1f( glGetUniformLocationARB( that->bicubic_pass1_program.program, "spline" ), CATMULLROM_SPLINE ); + + glBegin( GL_QUADS ); + glTexCoord2f( u, v ); glVertex3f( 0.0, 0.0, 0.); + glTexCoord2f( u, v1 ); glVertex3f( 0.0, v1 - v, 0.); + glTexCoord2f( u1, v1 ); glVertex3f( x1 - x, v1 - v, 0.); + glTexCoord2f( u1, v ); glVertex3f( x1 - x, 0.0, 0.); + glEnd(); + + glActiveTexture( GL_TEXTURE0 ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + + glViewport( 0, 0, guiw, guih ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0.0, guiw, guih, 0.0, -1.0, 1.0 ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, that->bicubic_pass1_texture ); + glActiveTexture( GL_TEXTURE1 ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, that->bicubic_lut_texture ); + glUseProgram( that->bicubic_pass2_program.program ); + glUniform1i( glGetUniformLocationARB( that->bicubic_pass2_program.program, "tex" ), 0 ); + glUniform1i( glGetUniformLocationARB( that->bicubic_pass2_program.program, "lut" ), 1 ); + glUniform1f( glGetUniformLocationARB( that->bicubic_pass2_program.program, "spline" ), CATMULLROM_SPLINE ); + + glBegin( GL_QUADS ); + glTexCoord2f( 0.0, 0.0 ); glVertex3f( x, y, 0.); + glTexCoord2f( 0.0, v1 - v ); glVertex3f( x, y1, 0.); + glTexCoord2f( x1 - x, v1 - v ); glVertex3f( x1, y1, 0.); + glTexCoord2f( x1 - x, 0.0 ); glVertex3f( x1, y, 0.); + glEnd(); + + glUseProgram( 0 ); + + return 1; +} + + + +static void opengl2_draw_video_bilinear( opengl2_driver_t *that, int guiw, int guih, GLfloat u, GLfloat v, GLfloat u1, GLfloat v1, + GLfloat x, GLfloat y, GLfloat x1, GLfloat y1, GLuint video_texture ) +{ + glViewport( 0, 0, guiw, guih ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0.0, guiw, guih, 0.0, -1.0, 1.0 ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, video_texture ); + + glBegin( GL_QUADS ); + glTexCoord2f( u, v ); glVertex3f( x, y, 0.); + glTexCoord2f( u, v1 ); glVertex3f( x, y1, 0.); + glTexCoord2f( u1, v1 ); glVertex3f( x1, y1, 0.); + glTexCoord2f( u1, v ); glVertex3f( x1, y, 0.); + glEnd(); +} + + + +static void opengl2_draw( opengl2_driver_t *that, opengl2_frame_t *frame ) +{ + glXMakeCurrent( that->display, that->drawable, that->context ); + + if ( !opengl2_check_textures_size( that, frame->width, frame->height ) ) { + glXMakeCurrent( that->display, None, NULL ); + return; + } + + opengl2_update_csc_matrix( that, frame ); + + glBindFramebuffer( GL_FRAMEBUFFER, that->fbo ); + + if ( frame->format == XINE_IMGFMT_YV12 ) { + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, that->yuvtex.y ); + glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, that->videoPBO ); + void *mem = glMapBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY ); + memcpy( mem, frame->vo_frame.base[0], frame->vo_frame.pitches[0] * frame->height ); + glUnmapBuffer( GL_PIXEL_UNPACK_BUFFER_ARB ); + glTexSubImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, frame->width, frame->height, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0 ); + glActiveTexture( GL_TEXTURE1 ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, that->yuvtex.u ); + mem = glMapBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY ); + memcpy( mem, frame->vo_frame.base[1], frame->vo_frame.pitches[1] * ((frame->height+1)/2) ); + glUnmapBuffer( GL_PIXEL_UNPACK_BUFFER_ARB ); + glTexSubImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, frame->width/2, frame->height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0 ); + glActiveTexture( GL_TEXTURE2 ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, that->yuvtex.v ); + mem = glMapBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY ); + memcpy( mem, frame->vo_frame.base[2], frame->vo_frame.pitches[2] * ((frame->height+1)/2) ); + glUnmapBuffer( GL_PIXEL_UNPACK_BUFFER_ARB ); + glTexSubImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, frame->width/2, frame->height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0 ); + glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, 0 ); + + glUseProgram( that->yuv420_program.program ); + glUniform1i( glGetUniformLocationARB( that->yuv420_program.program, "texY" ), 0 ); + glUniform1i( glGetUniformLocationARB( that->yuv420_program.program, "texU" ), 1 ); + glUniform1i( glGetUniformLocationARB( that->yuv420_program.program, "texV" ), 2 ); + load_csc_matrix( that->yuv420_program.program, that->csc_matrix ); + } + else if ( frame->format == XINE_IMGFMT_YUY2 ) { + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_RECTANGLE_ARB, that->yuvtex.yuv ); + glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, that->videoPBO ); + void *mem = glMapBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY ); + memcpy( mem, frame->vo_frame.base[0], frame->vo_frame.pitches[0] * frame->height ); + glUnmapBuffer( GL_PIXEL_UNPACK_BUFFER_ARB ); + glTexSubImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, frame->width, frame->height, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 0 ); + glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, 0 ); + + glUseProgram( that->yuv422_program.program ); + glUniform1i( glGetUniformLocationARB( that->yuv422_program.program, "texYUV" ), 0 ); + load_csc_matrix( that->yuv422_program.program, that->csc_matrix ); + } + else { + /* unknown format */ + fprintf(stderr, "vo_opengl2: unknown image format\n" ); + } + + glViewport( 0, 0, frame->width, frame->height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0.0, frame->width, 0.0, frame->height, -1.0, 1.0 ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + GLuint video_texture = that->videoTex; + glDrawBuffer( GL_COLOR_ATTACHMENT0 ); + + glBegin( GL_QUADS ); + glTexCoord2f( 0, 0 ); glVertex3f( 0, 0, 0.); + glTexCoord2f( 0, frame->height ); glVertex3f( 0, frame->height, 0.); + glTexCoord2f( frame->width, frame->height ); glVertex3f( frame->width, frame->height, 0.); + glTexCoord2f( frame->width, 0 ); glVertex3f( frame->width, 0, 0.); + glEnd(); + + glUseProgram( 0 ); + + // post-processing + if ( that->sharpness != 0 ) + video_texture = opengl2_sharpness( that, frame, video_texture ); + + // draw scaled overlays + opengl2_update_overlays( that ); + opengl2_draw_scaled_overlays( that, frame ); + + glBindFramebuffer( GL_FRAMEBUFFER, 0 ); + + // draw on screen + GLfloat u, u1, v, v1, x, x1, y, y1; + + u = that->sc.displayed_xoffset; + v = that->sc.displayed_yoffset; + u1 = that->sc.displayed_width + that->sc.displayed_xoffset; + v1 = that->sc.displayed_height + that->sc.displayed_yoffset; + x = that->sc.output_xoffset; + y = that->sc.output_yoffset; + x1 = that->sc.output_xoffset + that->sc.output_width; + y1 = that->sc.output_yoffset + that->sc.output_height; + + if ( that->scale_bicubic ) { + if ( !opengl2_draw_video_bicubic( that, that->sc.gui_width, that->sc.gui_height, u, v, u1, v1, x, y, x1, y1, video_texture ) ) + opengl2_draw_video_bilinear( that, that->sc.gui_width, that->sc.gui_height, u, v, u1, v1, x, y, x1, y1, video_texture ); + } + else + opengl2_draw_video_bilinear( that, that->sc.gui_width, that->sc.gui_height, u, v, u1, v1, x, y, x1, y1, video_texture ); + + // draw unscaled overlays + opengl2_draw_unscaled_overlays( that ); + + //if ( that->mglXSwapInterval ) + //that->mglXSwapInterval( 1 ); + + glXSwapBuffers( that->display, that->drawable ); + + glXMakeCurrent( that->display, None, NULL ); +} + + + +static void opengl2_display_frame( vo_driver_t *this_gen, vo_frame_t *frame_gen ) +{ + opengl2_driver_t *this = (opengl2_driver_t *) this_gen; + opengl2_frame_t *frame = (opengl2_frame_t *) frame_gen; + + if ( (frame->width != this->sc.delivered_width) || + (frame->height != this->sc.delivered_height) || + (frame->ratio != this->sc.delivered_ratio) || + (frame->vo_frame.crop_left != this->sc.crop_left) || + (frame->vo_frame.crop_right != this->sc.crop_right) || + (frame->vo_frame.crop_top != this->sc.crop_top) || + (frame->vo_frame.crop_bottom != this->sc.crop_bottom) ) { + this->sc.force_redraw = 1; /* trigger re-calc of output size */ + } + + this->sc.delivered_height = frame->height; + this->sc.delivered_width = frame->width; + this->sc.delivered_ratio = frame->ratio; + this->sc.crop_left = frame->vo_frame.crop_left; + this->sc.crop_right = frame->vo_frame.crop_right; + this->sc.crop_top = frame->vo_frame.crop_top; + this->sc.crop_bottom = frame->vo_frame.crop_bottom; + + opengl2_redraw_needed( this_gen ); + + pthread_mutex_lock(&this->drawable_lock); /* protect drawable from being changed */ + opengl2_draw( this, frame ); + pthread_mutex_unlock(&this->drawable_lock); /* allow changing drawable again */ + + frame->vo_frame.free( &frame->vo_frame ); +} + + + +static int opengl2_get_property( vo_driver_t *this_gen, int property ) +{ + opengl2_driver_t *this = (opengl2_driver_t*)this_gen; + + switch (property) { + case VO_PROP_MAX_NUM_FRAMES: + return 15; + case VO_PROP_WINDOW_WIDTH: + return this->sc.gui_width; + case VO_PROP_WINDOW_HEIGHT: + return this->sc.gui_height; + case VO_PROP_OUTPUT_WIDTH: + return this->sc.output_width; + case VO_PROP_OUTPUT_HEIGHT: + return this->sc.output_height; + case VO_PROP_OUTPUT_XOFFSET: + return this->sc.output_xoffset; + case VO_PROP_OUTPUT_YOFFSET: + return this->sc.output_yoffset; + case VO_PROP_HUE: + return this->hue; + case VO_PROP_SATURATION: + return this->saturation; + case VO_PROP_CONTRAST: + return this->contrast; + case VO_PROP_BRIGHTNESS: + return this->brightness; + case VO_PROP_SHARPNESS: + return this->sharpness; + case VO_PROP_ZOOM_X: + return this->zoom_x; + case VO_PROP_ZOOM_Y: + return this->zoom_y; + case VO_PROP_ASPECT_RATIO: + return this->sc.user_ratio; + } + + return -1; +} + + + +static int opengl2_set_property( vo_driver_t *this_gen, int property, int value ) +{ + opengl2_driver_t *this = (opengl2_driver_t*)this_gen; + + //fprintf(stderr,"opengl2_set_property: property=%d, value=%d\n", property, value ); + + switch (property) { + case VO_PROP_ZOOM_X: + if ((value >= XINE_VO_ZOOM_MIN) && (value <= XINE_VO_ZOOM_MAX)) { + this->zoom_x = value; + this->sc.zoom_factor_x = (double)value / (double)XINE_VO_ZOOM_STEP; + _x_vo_scale_compute_ideal_size( &this->sc ); + this->sc.force_redraw = 1; //trigger re-calc of output size + } + break; + case VO_PROP_ZOOM_Y: + if ((value >= XINE_VO_ZOOM_MIN) && (value <= XINE_VO_ZOOM_MAX)) { + this->zoom_y = value; + this->sc.zoom_factor_y = (double)value / (double)XINE_VO_ZOOM_STEP; + _x_vo_scale_compute_ideal_size( &this->sc ); + this->sc.force_redraw = 1; // trigger re-calc of output size + } + break; + case VO_PROP_ASPECT_RATIO: + if ( value>=XINE_VO_ASPECT_NUM_RATIOS ) + value = XINE_VO_ASPECT_AUTO; + this->sc.user_ratio = value; + this->sc.force_redraw = 1; // trigger re-calc of output size + break; + case VO_PROP_HUE: this->hue = value; this->update_csc = 1; break; + case VO_PROP_SATURATION: this->saturation = value; this->update_csc = 1; break; + case VO_PROP_CONTRAST: this->contrast = value; this->update_csc = 1; break; + case VO_PROP_BRIGHTNESS: this->brightness = value; this->update_csc = 1; break; + case VO_PROP_SHARPNESS: this->sharpness = value; break; + } + + return value; +} + + + +static void opengl2_get_property_min_max( vo_driver_t *this_gen, int property, int *min, int *max ) +{ + switch ( property ) { + case VO_PROP_HUE: + *max = 314; *min = -314; break; + case VO_PROP_SATURATION: + *max = 1000; *min = 0; break; + case VO_PROP_CONTRAST: + *max = 1000; *min = 0; break; + case VO_PROP_BRIGHTNESS: + *max = 100; *min = -100; break; + case VO_PROP_SHARPNESS: + *max = 100; *min = -100; break; + default: + *max = 0; *min = 0; + } +} + + + +static int opengl2_gui_data_exchange( vo_driver_t *this_gen, int data_type, void *data ) +{ + opengl2_driver_t *this = (opengl2_driver_t*)this_gen; + + switch (data_type) { +#ifndef XINE_DISABLE_DEPRECATED_FEATURES + case XINE_GUI_SEND_COMPLETION_EVENT: + break; +#endif + + case XINE_GUI_SEND_EXPOSE_EVENT: { + this->sc.force_redraw = 1; + break; + } + + case XINE_GUI_SEND_DRAWABLE_CHANGED: { + pthread_mutex_lock(&this->drawable_lock); /* wait for other thread which is currently displaying */ + this->drawable = (Drawable)data; + pthread_mutex_unlock(&this->drawable_lock); + this->sc.force_redraw = 1; + break; + } + + case XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO: { + int x1, y1, x2, y2; + x11_rectangle_t *rect = data; + + _x_vo_scale_translate_gui2video(&this->sc, rect->x, rect->y, &x1, &y1); + _x_vo_scale_translate_gui2video(&this->sc, rect->x + rect->w, rect->y + rect->h, &x2, &y2); + rect->x = x1; + rect->y = y1; + rect->w = x2-x1; + rect->h = y2-y1; + break; + } + + default: + return -1; + } + + return 0; +} + + + +static uint32_t opengl2_get_capabilities( vo_driver_t *this_gen ) +{ + opengl2_driver_t *this = (opengl2_driver_t *) this_gen; + + return this->capabilities; +} + + + +static void opengl2_set_bicubic( void *this_gen, xine_cfg_entry_t *entry ) +{ + opengl2_driver_t *this = (opengl2_driver_t *) this_gen; + + this->scale_bicubic = entry->num_value; + fprintf(stderr, "vo_opengl2: scale_bicubic=%d\n", this->scale_bicubic ); +} + + + +static void opengl2_dispose( vo_driver_t *this_gen ) +{ + opengl2_driver_t *this = (opengl2_driver_t *) this_gen; + + pthread_mutex_destroy(&this->drawable_lock); + + glXMakeCurrent( this->display, this->drawable, this->context ); + + opengl2_delete_program( &this->yuv420_program ); + opengl2_delete_program( &this->yuv422_program ); + + if ( this->sharpness_program.compiled ) + opengl2_delete_program( &this->sharpness_program ); + + if ( this->bicubic_pass1_program.compiled ) + opengl2_delete_program( &this->bicubic_pass1_program ); + if ( this->bicubic_pass2_program.compiled ) + opengl2_delete_program( &this->bicubic_pass2_program ); + if ( this->bicubic_lut_texture ) + glDeleteTextures( 1, &this->bicubic_lut_texture ); + if ( this->bicubic_pass1_texture ) + glDeleteTextures( 1, &this->bicubic_pass1_texture ); + if ( this->bicubic_fbo ) + glDeleteFramebuffers( 1, &this->bicubic_fbo ); + + if ( this->yuvtex.y ) + glDeleteTextures( 1, &this->yuvtex.y ); + if ( this->yuvtex.u ) + glDeleteTextures( 1, &this->yuvtex.u ); + if ( this->yuvtex.v ) + glDeleteTextures( 1, &this->yuvtex.v ); + if ( this->yuvtex.yuv ) + glDeleteTextures( 1, &this->yuvtex.yuv ); + if ( this->videoTex ) + glDeleteTextures( 1, &this->videoTex ); + if ( this->videoTex2 ) + glDeleteTextures( 1, &this->videoTex2 ); + if ( this->fbo ) + glDeleteFramebuffers( 1, &this->fbo ); + if ( this->videoPBO ) + glDeleteBuffers( 1, &this->videoPBO ); + + int i; + for ( i=0; ioverlays[i].ovl_rgba ); + glDeleteTextures( 1, &this->overlays[i].tex ); + } + + glXMakeCurrent( this->display, None, NULL ); + + glXDestroyContext( this->display, this->context ); + + this->ovl_yuv2rgb->dispose(this->ovl_yuv2rgb); + this->yuv2rgb_factory->dispose (this->yuv2rgb_factory); + + free (this); +} + + + +static vo_driver_t *opengl2_open_plugin( video_driver_class_t *class_gen, const void *visual_gen ) +{ + opengl2_class_t *class = (opengl2_class_t *) class_gen; + x11_visual_t *visual = (x11_visual_t *) visual_gen; + opengl2_driver_t *this; + config_values_t *config = class->xine->config; + + this = (opengl2_driver_t *) calloc(1, sizeof(opengl2_driver_t)); + + if (!this) + return NULL; + + this->display = visual->display; + this->screen = visual->screen; + this->drawable = visual->d; + this->context = class->ctx; + pthread_mutex_init(&this->drawable_lock, 0); + + _x_vo_scale_init(&this->sc, 1, 0, config); + this->sc.frame_output_cb = visual->frame_output_cb; + this->sc.dest_size_cb = visual->dest_size_cb; + this->sc.user_data = visual->user_data; + this->sc.user_ratio = XINE_VO_ASPECT_AUTO; + + this->zoom_x = 100; + this->zoom_y = 100; + + this->xine = class->xine; + this->config = config; + + this->vo_driver.get_capabilities = opengl2_get_capabilities; + this->vo_driver.alloc_frame = opengl2_alloc_frame; + this->vo_driver.update_frame_format = opengl2_update_frame_format; + this->vo_driver.overlay_begin = opengl2_overlay_begin; + this->vo_driver.overlay_blend = opengl2_overlay_blend; + this->vo_driver.overlay_end = opengl2_overlay_end; + this->vo_driver.display_frame = opengl2_display_frame; + this->vo_driver.get_property = opengl2_get_property; + this->vo_driver.set_property = opengl2_set_property; + this->vo_driver.get_property_min_max = opengl2_get_property_min_max; + this->vo_driver.gui_data_exchange = opengl2_gui_data_exchange; + this->vo_driver.dispose = opengl2_dispose; + this->vo_driver.redraw_needed = opengl2_redraw_needed; + + if ( !glXMakeCurrent( this->display, this->drawable, this->context ) ) { + fprintf(stderr, "vo_opengl2: MakeCurrent failed\n"); + return NULL; + } + + glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + glClearDepth( 1.0f ); + glDepthFunc( GL_LEQUAL ); + glDisable( GL_DEPTH_TEST ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glDisable( GL_BLEND ); + glShadeModel( GL_SMOOTH ); + glEnable( GL_TEXTURE_RECTANGLE_ARB ); + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + + const char *extensions = glGetString( GL_EXTENSIONS ); + if ( strstr( extensions, "ARB_texture_float" ) ) + this->texture_float = 1; + else + this->texture_float = 0; + +#define INITWIDTH 720 +#define INITHEIGHT 576 + + this->yuvtex.y = this->yuvtex.u = this->yuvtex.v = this->yuvtex.yuv = 0; + this->yuvtex.width = this->yuvtex.height = 0; + this->fbo = this->videoPBO = this->videoTex = this->videoTex2 = 0; + if ( !opengl2_check_textures_size( this, INITWIDTH, INITHEIGHT ) ) + return 0; + + if ( !opengl2_build_program( &this->yuv420_program, &yuv420_frag, "yuv420_frag" ) ) + return NULL; + if ( !opengl2_build_program( &this->yuv422_program, &yuv422_frag, "yuv422_frag" ) ) + return NULL; + + this->mglXSwapInterval = (GLXSWAPINTERVALSGI)glXGetProcAddressARB( (const GLubyte*)"glXSwapIntervalSGI" ); + + glXMakeCurrent( this->display, None, NULL ); + + this->capabilities = VO_CAP_YV12 | VO_CAP_YUY2 | VO_CAP_CROP | VO_CAP_UNSCALED_OVERLAY | VO_CAP_CUSTOM_EXTENT_OVERLAY;// | VO_CAP_ARGB_LAYER_OVERLAY | VO_CAP_VIDEO_WINDOW_OVERLAY; + + this->capabilities |= VO_CAP_HUE; + this->capabilities |= VO_CAP_SATURATION; + this->capabilities |= VO_CAP_CONTRAST; + this->capabilities |= VO_CAP_BRIGHTNESS; + + this->update_csc = 1; + this->color_standard = yuv_601; + this->hue = 0; + this->saturation = 100; + this->contrast = 100; + this->brightness = 0; + this->sharpness = 0; + this->sharpness_program.compiled = 0; + + this->bicubic_pass1_program.compiled = 0; + this->bicubic_pass2_program.compiled = 0; + this->bicubic_lut_texture = 0; + this->bicubic_pass1_texture = 0; + this->bicubic_pass1_texture_width = 0; + this->bicubic_pass1_texture_height = 0; + this->bicubic_fbo = 0; + + int i; + for ( i=0; ioverlays[i].ovl_w = this->overlays[i].ovl_h = 0; + this->overlays[i].ovl_rgba = (uint8_t*)malloc(2*2*4); + this->overlays[i].ovl_x = this->overlays[i].ovl_y = 0; + this->overlays[i].unscaled = 0; + this->overlays[i].tex = 0; + this->overlays[i].tex_w = this->overlays[i].tex_h = 0; + } + this->ovl_changed = 0; + this->num_ovls = 0; + + this->yuv2rgb_factory = yuv2rgb_factory_init (MODE_24_BGR, 0, NULL); + this->ovl_yuv2rgb = this->yuv2rgb_factory->create_converter( this->yuv2rgb_factory ); + + if ( this->texture_float ) { + this->scale_bicubic = config->register_bool( config, "video.output.opengl2_bicubic_scaling", 0, + _("opengl2: use a bicubic algo to scale the video"), + _("Set to true if you want bicubic scaling.\n\n"), + 10, opengl2_set_bicubic, this ); + } + else + this->scale_bicubic = 0; + + fprintf(stderr, "vo_opengl2: initialized.\n"); + + return &this->vo_driver; +} + + + +static int opengl2_check_platform( opengl2_class_t *this_gen, x11_visual_t *vis ) +{ + int attribs[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_DEPTH_SIZE, 16, None }; + + Window root; + XVisualInfo *visinfo; + GLXContext ctx; + int ret = 1; + + if ( !vis || !vis->display || !( root = RootWindow( vis->display, vis->screen ) ) ) + return 0; + + if ( !( visinfo = glXChooseVisual( vis->display, vis->screen, attribs ) ) ) + return 0; + + if ( !( ctx = glXCreateContext( vis->display, visinfo, NULL, GL_TRUE ) ) ) + return 0; + + if ( glXMakeCurrent( vis->display, root, ctx ) ) { + if ( !glXIsDirect( vis->display, ctx ) ) + ret = 0; + const char *extensions = glGetString( GL_EXTENSIONS ); + if ( !strstr( extensions, "ARB_texture_rectangle" ) ) + ret = 0; + if ( !strstr( extensions, "ARB_texture_non_power_of_two" ) ) + ret = 0; + if ( !strstr( extensions, "ARB_pixel_buffer_object" ) ) + ret = 0; + if ( !strstr( extensions, "ARB_framebuffer_object" ) ) + ret = 0; + if ( !strstr( extensions, "ARB_fragment_shader" ) ) + ret = 0; + if ( !strstr( extensions, "ARB_vertex_shader" ) ) + ret = 0; + glXMakeCurrent( vis->display, None, NULL ); + } + else + ret = 0; + + if ( !ret ) + glXDestroyContext( vis->display, ctx ); + else + this_gen->ctx = ctx; + + return ret; +} + +/* + * class functions + */ + +static void *opengl2_init_class( xine_t *xine, void *visual_gen ) +{ + opengl2_class_t *this = (opengl2_class_t *) calloc(1, sizeof(opengl2_class_t)); + + if ( !opengl2_check_platform( this, (x11_visual_t *)visual_gen ) ) + return NULL; + + this->driver_class.open_plugin = opengl2_open_plugin; + this->driver_class.identifier = "opengl2"; + this->driver_class.description = N_("xine video output plugin using opengl 2.0"); + this->driver_class.dispose = default_video_driver_class_dispose; + this->xine = xine; + + return this; +} + + + +static const vo_info_t vo_info_opengl2 = { + 7, /* priority */ + XINE_VISUAL_TYPE_X11 /* visual type */ +}; + + +/* + * exported plugin catalog entry + */ + +const plugin_info_t xine_plugin_info[] EXPORTED = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_VIDEO_OUT, 22, "opengl2", XINE_VERSION_CODE, &vo_info_opengl2, opengl2_init_class }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; -- cgit v1.2.3