]> git.sesse.net Git - mlt/blob - src/modules/opengl/consumer_xgl.c
Let Movit effects supply their own fingerprint.
[mlt] / src / modules / opengl / consumer_xgl.c
1 /*
2  * consumer_xgl.c
3  * Copyright (C) 2012 Christophe Thommeret
4  * Author: Christophe Thommeret <hftom@free.fr>
5  * Based on Nehe's GLX port by Mihael.Vrbanec@stud.uni-karlsruhe.de
6  * http://nehe.gamedev.net/
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23
24 #define GL_GLEXT_PROTOTYPES
25
26 #include <GL/gl.h>
27 #include <GL/glext.h>
28
29 #include <GL/glx.h>
30
31 #include <X11/keysym.h>
32
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <time.h>
37 #include <sys/time.h>
38 #include <pthread.h>
39
40 #include <framework/mlt.h>
41
42
43 #define STARTWIDTH 1280
44 #define STARTHEIGHT 720
45
46
47 extern int XInitThreads();
48
49
50
51 typedef struct consumer_xgl_s *consumer_xgl;
52
53 struct consumer_xgl_s
54 {
55         struct mlt_consumer_s parent;
56         mlt_properties properties;
57         mlt_deque queue;
58         pthread_t thread;
59         int joined;
60         int running;
61         int playing;
62         int xgl_started;
63 };
64
65
66 typedef struct
67 {
68         pthread_t thread;
69         int running;
70 } thread_video;
71
72
73 typedef struct
74 {
75         int width;
76         int height;
77         double aspect_ratio;
78         GLuint texture;
79         pthread_mutex_t mutex;
80         int new;
81         mlt_frame mlt_frame_ref;
82 } frame_new;
83
84
85 typedef struct
86 {
87         int width;
88         int height;
89         GLuint fbo;
90         GLuint texture;
91 } fbo;
92
93
94 typedef struct
95 {
96         Display *dpy;
97     int screen;
98     Window win;
99     GLXContext ctx;
100 } HiddenContext;
101
102
103 typedef struct
104 {
105     Display *dpy;
106     int screen;
107     Window win;
108     GLXContext ctx;
109     XSetWindowAttributes attr;
110     int x, y;
111     unsigned int width, height;
112     unsigned int depth;
113 } GLWindow;
114
115
116 static GLWindow GLWin;
117 static HiddenContext hiddenctx;
118
119 static frame_new new_frame;
120 static fbo fb;
121 static thread_video vthread;
122 static consumer_xgl xgl;
123 static mlt_filter glsl_manager;
124
125
126
127 static void* video_thread( void *arg );
128
129 static void update()
130 {
131         int _width = GLWin.width;
132         int _height = GLWin.height;
133         GLfloat left, right, top, bottom;
134         GLfloat war = (GLfloat)_width/(GLfloat)_height;
135
136         if ( war < new_frame.aspect_ratio ) {
137                 left = -1.0;
138                 right = 1.0;
139                 top = war / new_frame.aspect_ratio;
140                 bottom = -war / new_frame.aspect_ratio;
141         }
142         else {
143                 top = 1.0;
144                 bottom = -1.0;
145                 left = -new_frame.aspect_ratio / war;
146                 right = new_frame.aspect_ratio / war;
147         }
148
149         glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
150         glLoadIdentity();
151
152         glPushMatrix();
153
154         glTranslatef( _width/2, _height/2, 0 );
155         glScalef( _width/2, _height/2, 1.0 );
156
157         glBindTexture( GL_TEXTURE_2D, fb.texture );
158
159         glBegin( GL_QUADS );
160                 glTexCoord2f( 0.0f, 0.0f ); glVertex2f( left, top );
161                 glTexCoord2f( 0.0f, 1.0f ); glVertex2f( left, bottom );
162                 glTexCoord2f( 1.0f, 1.0f ); glVertex2f( right, bottom );
163                 glTexCoord2f( 1.0f, 0.0f ); glVertex2f( right, top );
164         glEnd();
165
166         glPopMatrix();
167         
168         glXSwapBuffers( GLWin.dpy, GLWin.win );
169         
170         if ( !vthread.running ) {
171                 pthread_create( &vthread.thread, NULL, video_thread, NULL );
172                 vthread.running = 1;
173         }
174 }
175
176
177
178 static void show_frame()
179 {
180         if ( (fb.width != new_frame.width) || (fb.height != new_frame.height) ) {
181                 glDeleteFramebuffers( 1, &fb.fbo );
182                 glDeleteTextures( 1, &fb.texture );
183                 fb.fbo = 0;
184                 fb.width = new_frame.width;
185                 fb.height = new_frame.height;
186                 glGenFramebuffers( 1, &fb.fbo );
187                 glGenTextures( 1, &fb.texture );
188                 glBindTexture( GL_TEXTURE_2D, fb.texture );
189                 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, fb.width, fb.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
190                 glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
191                 glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
192                 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
193                 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
194                 glBindFramebuffer( GL_FRAMEBUFFER, fb.fbo );
195                 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.texture, 0 );
196                 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
197         }
198
199         glPushAttrib(GL_VIEWPORT_BIT);
200     glMatrixMode(GL_PROJECTION);
201     glPushMatrix();
202         
203         glBindFramebuffer( GL_FRAMEBUFFER, fb.fbo );
204         
205         glViewport( 0, 0, new_frame.width, new_frame.height );
206         glMatrixMode( GL_PROJECTION );
207         glLoadIdentity();
208         glOrtho( 0.0, new_frame.width, 0.0, new_frame.height, -1.0, 1.0 );
209         glMatrixMode( GL_MODELVIEW );
210         glLoadIdentity();
211
212         glActiveTexture( GL_TEXTURE0 );
213     glBindTexture( GL_TEXTURE_2D, new_frame.texture );
214
215         glBegin( GL_QUADS );
216                 glTexCoord2f( 0.0f, 0.0f ); glVertex2f( 0.0f, 0.0f );
217                 glTexCoord2f( 0.0f, 1.0f ); glVertex2f( 0.0f, new_frame.height );
218                 glTexCoord2f( 1.0f, 1.0f ); glVertex2f( new_frame.width, new_frame.height );
219                 glTexCoord2f( 1.0f, 0.0f ); glVertex2f( new_frame.width, 0.0f );
220         glEnd();
221
222         glBindFramebuffer( GL_FRAMEBUFFER, 0 );
223         mlt_events_fire( MLT_CONSUMER_PROPERTIES(&xgl->parent), "consumer-frame-show", new_frame.mlt_frame_ref, NULL );
224         mlt_frame_close( new_frame.mlt_frame_ref );
225         new_frame.mlt_frame_ref = NULL;
226
227         glMatrixMode(GL_PROJECTION);
228     glPopMatrix();
229     glMatrixMode(GL_MODELVIEW);
230     glPopAttrib();
231         
232         update();
233         
234         new_frame.new = 0;
235 }
236
237
238
239 void* video_thread( void *arg )
240 {
241         mlt_frame next = NULL;
242         mlt_consumer consumer = &xgl->parent;
243         mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES( consumer );
244         struct timeval start, end;
245         double duration = 0;
246         
247         gettimeofday( &start, NULL );
248         
249         while ( vthread.running )
250         {
251                 // Get a frame from the attached producer
252                 next = mlt_consumer_rt_frame( consumer );
253
254                 if ( !mlt_properties_get_int( MLT_FILTER_PROPERTIES( glsl_manager ), "glsl_supported" ) ) {
255                         mlt_log_error( MLT_CONSUMER_SERVICE(consumer), "OpenGL Shading Language is not supported on this machine.\n" );
256                         xgl->running = 0;
257                         break;
258                 }
259
260                 // Ensure that we have a frame
261                 if ( next )
262                 {
263                         mlt_properties properties =  MLT_FRAME_PROPERTIES( next );
264                         if ( mlt_properties_get_int( properties, "rendered" ) == 1 )
265                         {
266                                 // Get the image, width and height
267                                 mlt_image_format vfmt = mlt_image_glsl_texture;
268                                 int width = 0, height = 0;
269                                 GLuint *image = 0;
270                                 int error = mlt_frame_get_image( next, (uint8_t**) &image, &vfmt, &width, &height, 0 );
271                                 if ( !error && image && width && height && !new_frame.new ) {
272                                         new_frame.width = width;
273                                         new_frame.height = height;
274                                         new_frame.texture = *image;
275                                         new_frame.mlt_frame_ref = next;
276                                         new_frame.aspect_ratio = ((double)width / (double)height) * mlt_properties_get_double( properties, "aspect_ratio" );
277                                         new_frame.new = 1;
278                                         
279                                         int loop = 200;
280                                         while ( new_frame.new && --loop )
281                                                 usleep( 500 );
282                                 }
283                                 else
284                                 {
285                                         mlt_frame_close( next );
286                                 }
287                                 new_frame.new = 0;
288                                 
289                                 gettimeofday( &end, NULL );
290                                 duration = 1000000.0 / mlt_properties_get_double( consumer_props, "fps" );
291                                 duration -= ( end.tv_sec * 1000000 + end.tv_usec ) - ( start.tv_sec * 1000000 + start.tv_usec );
292                                 if ( duration > 0 )
293                                         usleep( (int)duration );
294                                 gettimeofday( &start, NULL );
295                         }
296                         else
297                         {
298                                 mlt_frame_close( next );
299                                 static int dropped = 0;
300                                 mlt_log_info( MLT_CONSUMER_SERVICE(consumer), "dropped video frame %d\n", ++dropped );
301                         }
302                 }
303                 else
304                         usleep( 1000 );
305         }
306         mlt_consumer_stopped( consumer );
307         
308         return NULL;
309 }
310
311
312
313 static void resizeGLScene()
314 {
315         glXMakeCurrent( GLWin.dpy, GLWin.win, GLWin.ctx );
316         
317         if ( GLWin.height == 0 )
318                 GLWin.height = 1;
319         if ( GLWin.width == 0 )
320                 GLWin.width = 1;
321         glViewport( 0, 0, GLWin.width, GLWin.height );
322         glMatrixMode( GL_PROJECTION );
323         glLoadIdentity();
324         glOrtho( 0.0, GLWin.width, 0.0, GLWin.height, -1.0, 1.0 );
325         glMatrixMode( GL_MODELVIEW );
326
327         update();
328 }
329
330
331
332 static void initGL( void )
333 {
334         glXMakeCurrent( GLWin.dpy, GLWin.win, GLWin.ctx );
335         
336         glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
337         glClearDepth( 1.0f );
338         glDepthFunc( GL_LEQUAL );
339         glEnable( GL_DEPTH_TEST );
340         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
341         glEnable( GL_BLEND );
342         glShadeModel( GL_SMOOTH );
343         glEnable( GL_TEXTURE_2D );
344         glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
345
346         typedef int (*GLXSWAPINTERVALSGI) ( int );
347         GLXSWAPINTERVALSGI mglXSwapInterval = (GLXSWAPINTERVALSGI)glXGetProcAddressARB( (const GLubyte*)"glXSwapIntervalSGI" );
348         if ( mglXSwapInterval )
349                 mglXSwapInterval( 1 );
350
351         fb.fbo = 0;
352         fb.width = STARTWIDTH;
353         fb.height = STARTHEIGHT;
354         glGenFramebuffers( 1, &fb.fbo );
355         glGenTextures( 1, &fb.texture );
356         glBindTexture( GL_TEXTURE_2D, fb.texture );
357         glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, fb.width, fb.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
358         glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
359     glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
360         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
361     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
362         glBindFramebuffer( GL_FRAMEBUFFER, fb.fbo );
363         glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.texture, 0 );
364         glBindFramebuffer( GL_FRAMEBUFFER, 0 );
365         
366         resizeGLScene();
367 }
368
369
370
371 static void createGLWindow()
372 {
373         const char* title = "OpenGL consumer";
374         int width = STARTWIDTH;
375         int height = STARTHEIGHT;
376         
377         int attrListSgl[] = { GLX_RGBA, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8,
378                 GLX_BLUE_SIZE, 8, GLX_DEPTH_SIZE, 16, None };
379
380         int attrListDbl[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 8,
381                 GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_DEPTH_SIZE, 16, None };
382
383         XVisualInfo *vi;
384         Colormap cmap;
385         Atom wmDelete;
386         Window winDummy;
387         unsigned int borderDummy;
388
389         GLWin.dpy = XOpenDisplay( 0 );
390         GLWin.screen = DefaultScreen( GLWin.dpy );
391
392         vi = glXChooseVisual( GLWin.dpy, GLWin.screen, attrListDbl );
393         if ( !vi )
394                 vi = glXChooseVisual( GLWin.dpy, GLWin.screen, attrListSgl );
395
396         GLWin.ctx = glXCreateContext( GLWin.dpy, vi, 0, GL_TRUE );
397
398         cmap = XCreateColormap( GLWin.dpy, RootWindow( GLWin.dpy, vi->screen ), vi->visual, AllocNone );
399         GLWin.attr.colormap = cmap;
400         GLWin.attr.border_pixel = 0;
401
402         GLWin.attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask;
403         GLWin.win = XCreateWindow( GLWin.dpy, RootWindow(GLWin.dpy, vi->screen), 0, 0, width, height,
404                 0, vi->depth, InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &GLWin.attr );
405         wmDelete = XInternAtom( GLWin.dpy, "WM_DELETE_WINDOW", True );
406         XSetWMProtocols( GLWin.dpy, GLWin.win, &wmDelete, 1 );
407         XSetStandardProperties( GLWin.dpy, GLWin.win, title, title, None, NULL, 0, NULL );
408         XMapRaised( GLWin.dpy, GLWin.win );
409
410         glXMakeCurrent( GLWin.dpy, GLWin.win, GLWin.ctx );
411         XGetGeometry( GLWin.dpy, GLWin.win, &winDummy, &GLWin.x, &GLWin.y,
412                 &GLWin.width, &GLWin.height, &borderDummy, &GLWin.depth );
413
414         // Verify GLSL works on this machine
415         hiddenctx.ctx = glXCreateContext( GLWin.dpy, vi, GLWin.ctx, GL_TRUE );
416         if ( hiddenctx.ctx ) {
417                 hiddenctx.dpy = GLWin.dpy;
418                 hiddenctx.screen = GLWin.screen;
419                 hiddenctx.win = RootWindow( hiddenctx.dpy, hiddenctx.screen );
420         }
421
422         initGL();
423 }
424
425
426
427 static void killGLWindow()
428 {               
429         if ( GLWin.ctx ) {
430                 if ( !glXMakeCurrent( GLWin.dpy, None, NULL ) ) {
431                         printf("Error releasing drawing context : killGLWindow\n");
432                 }
433                 glXDestroyContext( GLWin.dpy, GLWin.ctx );
434                 GLWin.ctx = NULL;
435         }
436         
437         if ( hiddenctx.ctx )
438                 glXDestroyContext( hiddenctx.dpy, hiddenctx.ctx );
439
440         XCloseDisplay( GLWin.dpy );
441 }
442
443
444
445 static void run()
446 {
447         XEvent event;
448
449         while ( xgl->running ) {
450                 while ( XPending( GLWin.dpy ) > 0 ) {
451                         XNextEvent( GLWin.dpy, &event );
452                         switch ( event.type ) {
453                                 case Expose:
454                                         if ( event.xexpose.count != 0 )
455                                                 break;
456                                         break;
457                                 case ConfigureNotify:
458                                         if ( (event.xconfigure.width != GLWin.width) || (event.xconfigure.height != GLWin.height) ) {
459                                                 GLWin.width = event.xconfigure.width;
460                                                 GLWin.height = event.xconfigure.height;
461                                                 resizeGLScene();
462                                         }
463                                         break;
464                                 case KeyPress:
465                                         switch ( XLookupKeysym( &event.xkey, 0 ) ) {
466                                                 case XK_Escape:                                                                 
467                                                         xgl->running = 0;
468                                                         break;
469                                                 default: {
470                                                         mlt_producer producer = mlt_properties_get_data( xgl->properties, "transport_producer", NULL );
471                                                         char keyboard[ 2 ] = " ";
472                                                         void (*callback)( mlt_producer, char * ) = mlt_properties_get_data( xgl->properties, "transport_callback", NULL );
473                                                         if ( callback != NULL && producer != NULL )
474                                                         {
475                                                                 keyboard[ 0 ] = ( char )XLookupKeysym( &event.xkey, 0 );
476                                                                 callback( producer, keyboard );
477                                                         }
478                                                         break;
479                                                 }
480                                         }
481                                         break;
482                                 case ClientMessage:
483                                         if ( *XGetAtomName( GLWin.dpy, event.xclient.message_type ) == *"WM_PROTOCOLS" )
484                                                 xgl->running = 0;
485                                         break;
486                                 default:
487                                         break;
488                         }
489                 }
490                 
491                 if ( new_frame.new )
492                         show_frame();
493                 else
494                         usleep( 1000 );
495         }
496 }
497
498
499
500 void start_xgl( consumer_xgl consumer )
501 {
502         xgl = consumer;
503         
504         pthread_mutex_init( &new_frame.mutex, NULL );
505         new_frame.aspect_ratio = 16.0 / 9.0;
506         new_frame.new = 0;
507         new_frame.width = STARTWIDTH;
508         new_frame.height = STARTHEIGHT;
509         new_frame.mlt_frame_ref = NULL;
510         
511         vthread.running = 0;
512         xgl->xgl_started = 1;
513
514         createGLWindow();
515         run();
516         if ( vthread.running ) {
517                 vthread.running = 0;
518                 pthread_join( vthread.thread, NULL );
519         }
520         xgl->running = 0;
521 }
522
523 static void on_consumer_thread_started( mlt_properties owner, HiddenContext* context )
524 {
525         // Initialize this thread's OpenGL state
526         glXMakeCurrent( context->dpy, context->win, context->ctx );
527         mlt_events_fire( MLT_FILTER_PROPERTIES(glsl_manager), "init glsl", NULL );
528 }
529
530 /** Forward references to static functions.
531 */
532
533 static int consumer_start( mlt_consumer parent );
534 static int consumer_stop( mlt_consumer parent );
535 static int consumer_is_stopped( mlt_consumer parent );
536 static void consumer_close( mlt_consumer parent );
537 static void *consumer_thread( void * );
538
539
540
541 /** This is what will be called by the factory - anything can be passed in
542         via the argument, but keep it simple.
543 */
544
545 mlt_consumer consumer_xgl_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
546 {
547         // Create the consumer object
548         consumer_xgl this = calloc( sizeof( struct consumer_xgl_s ), 1 );
549
550         // If no malloc'd and consumer init ok
551         if ( this != NULL && mlt_consumer_init( &this->parent, this, profile ) == 0 )
552         {
553                 // Create the queue
554                 this->queue = mlt_deque_init( );
555
556                 // Get the parent consumer object
557                 mlt_consumer parent = &this->parent;
558
559                 // We have stuff to clean up, so override the close method
560                 parent->close = consumer_close;
561
562                 // get a handle on properties
563                 mlt_service service = MLT_CONSUMER_SERVICE( parent );
564                 this->properties = MLT_SERVICE_PROPERTIES( service );
565
566                 // Default scaler
567                 mlt_properties_set( this->properties, "rescale", "bilinear" );
568                 mlt_properties_set( this->properties, "deinterlace_method", "onefield" );
569
570                 // default image format
571                 mlt_properties_set( this->properties, "mlt_image_format", "glsl" );
572
573                 // Default buffer for low latency
574                 mlt_properties_set_int( this->properties, "buffer", 1 );
575
576                 // Ensure we don't join on a non-running object
577                 this->joined = 1;
578                 this->xgl_started = 0;
579
580                 // Allow thread to be started/stopped
581                 parent->start = consumer_start;
582                 parent->stop = consumer_stop;
583                 parent->is_stopped = consumer_is_stopped;
584
585                 // "init glsl" is required to instantiate glsl filters.
586                 glsl_manager = mlt_factory_filter( profile, "glsl.manager", NULL );
587                 if ( glsl_manager ) {
588                         mlt_events_listen( this->properties, &hiddenctx, "consumer-thread-started", (mlt_listener) on_consumer_thread_started );
589                 } else {
590                         mlt_consumer_close( parent );
591                         parent = NULL;
592                 }
593
594                 // Return the consumer produced
595                 return parent;
596         }
597
598         // malloc or consumer init failed
599         free( this );
600
601         // Indicate failure
602         return NULL;
603 }
604
605
606
607 int consumer_start( mlt_consumer parent )
608 {
609         consumer_xgl this = parent->child;
610
611         if ( !this->running )
612         {
613                 consumer_stop( parent );
614
615                 this->running = 1;
616                 this->joined = 0;
617
618                 pthread_create( &this->thread, NULL, consumer_thread, this );
619         }
620
621         return 0;
622 }
623
624
625
626 int consumer_stop( mlt_consumer parent )
627 {
628         // Get the actual object
629         consumer_xgl this = parent->child;
630         
631         if ( this->running && this->joined == 0 )
632         {
633                 // Kill the thread and clean up
634                 this->joined = 1;
635                 this->running = 0;
636
637                 if ( this->thread )
638                         pthread_join( this->thread, NULL );
639         }
640
641         return 0;
642 }
643
644
645
646 int consumer_is_stopped( mlt_consumer parent )
647 {
648         consumer_xgl this = parent->child;
649         return !this->running;
650 }
651
652
653
654 static void *consumer_thread( void *arg )
655 {
656         // Identify the arg
657         consumer_xgl this = arg;
658
659         XInitThreads();
660         start_xgl( this );
661
662         return NULL;
663 }
664
665
666
667 /** Callback to allow override of the close method.
668 */
669
670 static void consumer_close( mlt_consumer parent )
671 {
672         // Get the actual object
673         consumer_xgl this = parent->child;
674
675         // Stop the consumer
676         ///mlt_consumer_stop( parent );
677         mlt_filter_close( glsl_manager );
678
679         // Now clean up the rest
680         mlt_consumer_close( parent );
681
682         // Close the queue
683         mlt_deque_close( this->queue );
684
685         if ( this->xgl_started )
686                 killGLWindow();
687
688         // Finally clean up this
689         free( this );
690 }