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