]> git.sesse.net Git - mlt/blob - src/modules/sdl/consumer_sdl_still.c
Fix calloc() parameter ordering
[mlt] / src / modules / sdl / consumer_sdl_still.c
1 /*
2  * consumer_sdl_still.c -- A Simple DirectMedia Layer consumer
3  * Copyright (C) 2003-2004, 2010 Ushodaya Enterprises Limited
4  * Author: Charles Yates
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <framework/mlt_consumer.h>
22 #include <framework/mlt_frame.h>
23 #include <framework/mlt_deque.h>
24 #include <framework/mlt_factory.h>
25 #include <framework/mlt_filter.h>
26 #include <framework/mlt_log.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <pthread.h>
31 #include <SDL.h>
32 #include <SDL_syswm.h>
33 #include <sys/time.h>
34 #include "consumer_sdl_osx.h"
35
36 extern pthread_mutex_t mlt_sdl_mutex;
37
38 /** This classes definition.
39 */
40
41 typedef struct consumer_sdl_s *consumer_sdl;
42
43 struct consumer_sdl_s
44 {
45         struct mlt_consumer_s parent;
46         mlt_properties properties;
47         pthread_t thread;
48         int joined;
49         int running;
50         int window_width;
51         int window_height;
52         int width;
53         int height;
54         int playing;
55         int sdl_flags;
56         SDL_Surface *sdl_screen;
57         SDL_Rect rect;
58         uint8_t *buffer;
59         int last_position;
60         mlt_producer last_producer;
61 };
62
63 /** Forward references to static functions.
64 */
65
66 static int consumer_start( mlt_consumer parent );
67 static int consumer_stop( mlt_consumer parent );
68 static int consumer_is_stopped( mlt_consumer parent );
69 static void consumer_close( mlt_consumer parent );
70 static void *consumer_thread( void * );
71 static int consumer_get_dimensions( int *width, int *height );
72 static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args );
73
74 /** This is what will be called by the factory - anything can be passed in
75         via the argument, but keep it simple.
76 */
77
78 mlt_consumer consumer_sdl_still_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
79 {
80         // Create the consumer object
81         consumer_sdl this = calloc( 1, sizeof( struct consumer_sdl_s ) );
82
83         // If no malloc'd and consumer init ok
84         if ( this != NULL && mlt_consumer_init( &this->parent, this, profile ) == 0 )
85         {
86                 // Get the parent consumer object
87                 mlt_consumer parent = &this->parent;
88
89                 // get a handle on properties
90                 mlt_service service = MLT_CONSUMER_SERVICE( parent );
91                 this->properties = MLT_SERVICE_PROPERTIES( service );
92
93                 // We have stuff to clean up, so override the close method
94                 parent->close = consumer_close;
95
96                 // Default scaler (for now we'll use nearest)
97                 mlt_properties_set( this->properties, "rescale", "nearest" );
98
99                 // We're always going to run this in non-realtime mode
100                 mlt_properties_set( this->properties, "real_time", "0" );
101
102                 // Ensure we don't join on a non-running object
103                 this->joined = 1;
104                 
105                 // process actual param
106                 if ( arg == NULL || sscanf( arg, "%dx%d", &this->width, &this->height ) != 2 )
107                 {
108                         this->width = mlt_properties_get_int( this->properties, "width" );
109                         this->height = mlt_properties_get_int( this->properties, "height" );
110                 }
111                 else
112                 {
113                         mlt_properties_set_int( this->properties, "width", this->width );
114                         mlt_properties_set_int( this->properties, "height", this->height );
115                 }
116
117                 // Set the sdl flags
118                 this->sdl_flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_RESIZABLE | SDL_DOUBLEBUF;
119
120                 // Allow thread to be started/stopped
121                 parent->start = consumer_start;
122                 parent->stop = consumer_stop;
123                 parent->is_stopped = consumer_is_stopped;
124
125                 // Register specific events
126                 mlt_events_register( this->properties, "consumer-sdl-event", ( mlt_transmitter )consumer_sdl_event );
127
128                 // Return the consumer produced
129                 return parent;
130         }
131
132         // malloc or consumer init failed
133         free( this );
134
135         // Indicate failure
136         return NULL;
137 }
138
139 static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
140 {
141         if ( listener != NULL )
142                 listener( owner, this, ( SDL_Event * )args[ 0 ] );
143 }
144
145 static int consumer_start( mlt_consumer parent )
146 {
147         consumer_sdl this = parent->child;
148
149         if ( !this->running )
150         {
151                 int preview_off = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "preview_off" );
152                 int sdl_started = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "sdl_started" );
153
154                 consumer_stop( parent );
155
156                 this->last_position = -1;
157                 this->running = 1;
158                 this->joined = 0;
159
160                 // Allow the user to force resizing to window size
161                 this->width = mlt_properties_get_int( this->properties, "width" );
162                 this->height = mlt_properties_get_int( this->properties, "height" );
163
164                 // Default window size
165                 double display_ratio = mlt_properties_get_double( this->properties, "display_ratio" );
166                 this->window_width = ( double )this->height * display_ratio + 0.5;
167                 this->window_height = this->height;
168
169                 if ( sdl_started == 0 && preview_off == 0 )
170                 {
171                         pthread_mutex_lock( &mlt_sdl_mutex );
172                         int ret = SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE );
173                         pthread_mutex_unlock( &mlt_sdl_mutex );
174                         if ( ret < 0 )
175                         {
176                                 fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() );
177                                 return -1;
178                         }
179
180                         SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
181                         SDL_EnableUNICODE( 1 );
182                 }
183                 else if ( preview_off == 0 )
184                 {
185                         pthread_mutex_lock( &mlt_sdl_mutex );
186                         SDL_Surface *screen = SDL_GetVideoSurface( );
187                         pthread_mutex_unlock( &mlt_sdl_mutex );
188                         if ( screen != NULL )
189                         {
190                                 this->sdl_screen = screen;
191                         }
192                 }
193
194                 if ( this->sdl_screen == NULL && preview_off == 0 )
195                 {
196                         pthread_mutex_lock( &mlt_sdl_mutex );
197                         this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags );
198                         pthread_mutex_unlock( &mlt_sdl_mutex );
199                 }
200
201                 pthread_create( &this->thread, NULL, consumer_thread, this );
202         }
203
204         return 0;
205 }
206
207 static int consumer_stop( mlt_consumer parent )
208 {
209         // Get the actual object
210         consumer_sdl this = parent->child;
211
212         if ( this->joined == 0 )
213         {
214                 int preview_off = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "preview_off" );
215                 int sdl_started = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( parent ), "sdl_started" );
216         
217                 // Kill the thread and clean up
218                 this->running = 0;
219
220                 pthread_join( this->thread, NULL );
221                 this->joined = 1;
222
223                 if ( sdl_started == 0 && preview_off == 0 )
224                 {
225                         pthread_mutex_lock( &mlt_sdl_mutex );
226                         SDL_Quit( );
227                         pthread_mutex_unlock( &mlt_sdl_mutex );
228                 }
229
230                 this->sdl_screen = NULL;
231         }
232
233         return 0;
234 }
235
236 static int consumer_is_stopped( mlt_consumer parent )
237 {
238         consumer_sdl this = parent->child;
239         return !this->running;
240 }
241
242 static int sdl_lock_display( )
243 {
244         pthread_mutex_lock( &mlt_sdl_mutex );
245         SDL_Surface *screen = SDL_GetVideoSurface( );
246         int result = screen != NULL && ( !SDL_MUSTLOCK( screen ) || SDL_LockSurface( screen ) >= 0 );
247         pthread_mutex_unlock( &mlt_sdl_mutex );
248         return result;
249 }
250
251 static void sdl_unlock_display( )
252 {
253         pthread_mutex_lock( &mlt_sdl_mutex );
254         SDL_Surface *screen = SDL_GetVideoSurface( );
255         if ( screen != NULL && SDL_MUSTLOCK( screen ) )
256                 SDL_UnlockSurface( screen );
257         pthread_mutex_unlock( &mlt_sdl_mutex );
258 }
259
260 static inline void display_1( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height )
261 {
262         // Generate the affine transform scaling values
263         if ( rect.w == 0 || rect.h == 0 ) return;
264         int scale_width = ( width << 16 ) / rect.w;
265         int scale_height = ( height << 16 ) / rect.h;
266         int stride = width * 4;
267         int x, y, row_index;
268         uint8_t *q, *row;
269
270         // Constants defined for clarity and optimisation
271         int scanlength = screen->pitch;
272         uint8_t *start = ( uint8_t * )screen->pixels + rect.y * scanlength + rect.x;
273         uint8_t *p;
274
275         // Iterate through the screen using a very basic scaling algorithm
276         for ( y = 0; y < rect.h; y ++ )
277         {
278                 p = start;
279                 row_index = ( 32768 + scale_height * y ) >> 16;
280                 row = image + stride * row_index;
281                 for ( x = 0; x < rect.w; x ++ )
282                 {
283                         q = row + ( ( ( 32768 + scale_width * x ) >> 16 ) * 4 );
284                         *p ++ = SDL_MapRGB( screen->format, *q, *( q + 1 ), *( q + 2 ) );
285                 }
286                 start += scanlength;
287         }
288 }
289
290 static inline void display_2( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height )
291 {
292         // Generate the affine transform scaling values
293         if ( rect.w == 0 || rect.h == 0 ) return;
294         int scale_width = ( width << 16 ) / rect.w;
295         int scale_height = ( height << 16 ) / rect.h;
296         int stride = width * 4;
297         int x, y, row_index;
298         uint8_t *q, *row;
299
300         // Constants defined for clarity and optimisation
301         int scanlength = screen->pitch / 2;
302         uint16_t *start = ( uint16_t * )screen->pixels + rect.y * scanlength + rect.x;
303         uint16_t *p;
304
305         // Iterate through the screen using a very basic scaling algorithm
306         for ( y = 0; y < rect.h; y ++ )
307         {
308                 p = start;
309                 row_index = ( 32768 + scale_height * y ) >> 16;
310                 row = image + stride * row_index;
311                 for ( x = 0; x < rect.w; x ++ )
312                 {
313                         q = row + ( ( ( 32768 + scale_width * x ) >> 16 ) * 4 );
314                         *p ++ = SDL_MapRGB( screen->format, *q, *( q + 1 ), *( q + 2 ) );
315                 }
316                 start += scanlength;
317         }
318 }
319
320 static inline void display_3( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height )
321 {
322         // Generate the affine transform scaling values
323         if ( rect.w == 0 || rect.h == 0 ) return;
324         int scale_width = ( width << 16 ) / rect.w;
325         int scale_height = ( height << 16 ) / rect.h;
326         int stride = width * 4;
327         int x, y, row_index;
328         uint8_t *q, *row;
329
330         // Constants defined for clarity and optimisation
331         int scanlength = screen->pitch;
332         uint8_t *start = ( uint8_t * )screen->pixels + rect.y * scanlength + rect.x;
333         uint8_t *p;
334         uint32_t pixel;
335
336         // Iterate through the screen using a very basic scaling algorithm
337         for ( y = 0; y < rect.h; y ++ )
338         {
339                 p = start;
340                 row_index = ( 32768 + scale_height * y ) >> 16;
341                 row = image + stride * row_index;
342                 for ( x = 0; x < rect.w; x ++ )
343                 {
344                         q = row + ( ( ( 32768 + scale_width * x ) >> 16 ) * 4 );
345                         pixel = SDL_MapRGB( screen->format, *q, *( q + 1 ), *( q + 2 ) );
346                         *p ++ = (pixel & 0xFF0000) >> 16;
347                         *p ++ = (pixel & 0x00FF00) >> 8;
348                         *p ++ = (pixel & 0x0000FF);
349                 }
350                 start += scanlength;
351         }
352 }
353
354 static inline void display_4( SDL_Surface *screen, SDL_Rect rect, uint8_t *image, int width, int height )
355 {
356         // Generate the affine transform scaling values
357         if ( rect.w == 0 || rect.h == 0 ) return;
358         int scale_width = ( width << 16 ) / rect.w;
359         int scale_height = ( height << 16 ) / rect.h;
360         int stride = width * 4;
361         int x, y, row_index;
362         uint8_t *q, *row;
363
364         // Constants defined for clarity and optimisation
365         int scanlength = screen->pitch / 4;
366         uint32_t *start = ( uint32_t * )screen->pixels + rect.y * scanlength + rect.x;
367         uint32_t *p;
368
369         // Iterate through the screen using a very basic scaling algorithm
370         for ( y = 0; y < rect.h; y ++ )
371         {
372                 p = start;
373                 row_index = ( 32768 + scale_height * y ) >> 16;
374                 row = image + stride * row_index;
375                 for ( x = 0; x < rect.w; x ++ )
376                 {
377                         q = row + ( ( ( 32768 + scale_width * x ) >> 16 ) * 4 );
378                         *p ++ = SDL_MapRGB( screen->format, *q, *( q + 1 ), *( q + 2 ) );
379                 }
380                 start += scanlength;
381         }
382 }
383
384 static int consumer_play_video( consumer_sdl this, mlt_frame frame )
385 {
386         // Get the properties of this consumer
387         mlt_properties properties = this->properties;
388
389         mlt_image_format vfmt = mlt_image_rgb24a;
390         int height = this->height;
391         int width = this->width;
392         uint8_t *image = NULL;
393         int changed = 0;
394         double display_ratio = mlt_properties_get_double( this->properties, "display_ratio" );
395
396         void ( *lock )( void ) = mlt_properties_get_data( properties, "app_lock", NULL );
397         void ( *unlock )( void ) = mlt_properties_get_data( properties, "app_unlock", NULL );
398
399         if ( lock != NULL ) lock( );
400         void *pool = mlt_cocoa_autorelease_init();
401         sdl_lock_display();
402         
403         // Handle events
404         if ( this->sdl_screen != NULL )
405         {
406                 SDL_Event event;
407
408                 pthread_mutex_lock( &mlt_sdl_mutex );
409                 changed = consumer_get_dimensions( &this->window_width, &this->window_height );
410                 pthread_mutex_unlock( &mlt_sdl_mutex );
411
412                 while ( SDL_PollEvent( &event ) )
413                 {
414                         mlt_events_fire( this->properties, "consumer-sdl-event", &event, NULL );
415
416                         switch( event.type )
417                         {
418                                 case SDL_VIDEORESIZE:
419                                         this->window_width = event.resize.w;
420                                         this->window_height = event.resize.h;
421                                         changed = 1;
422                                         break;
423                                 case SDL_QUIT:
424                                         this->running = 0;
425                                         break;
426                                 case SDL_KEYDOWN:
427                                         {
428                                                 mlt_producer producer = mlt_properties_get_data( properties, "transport_producer", NULL );
429                                                 char keyboard[ 2 ] = " ";
430                                                 void (*callback)( mlt_producer, char * ) = mlt_properties_get_data( properties, "transport_callback", NULL );
431                                                 if ( callback != NULL && producer != NULL && event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0 )
432                                                 {
433                                                         keyboard[ 0 ] = ( char )event.key.keysym.unicode;
434                                                         callback( producer, keyboard );
435                                                 }
436                                         }
437                                         break;
438                         }
439                 }
440         }
441
442         if ( this->sdl_screen == NULL || changed )
443         {
444                 // open SDL window
445                 pthread_mutex_lock( &mlt_sdl_mutex );
446                 this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags );
447                 if ( consumer_get_dimensions( &this->window_width, &this->window_height ) )
448                         this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags );
449
450                 uint32_t color = mlt_properties_get_int( this->properties, "window_background" );
451                 if ( this->sdl_screen )
452                 {
453                         SDL_FillRect( this->sdl_screen, NULL, color >> 8 );
454                         changed = 1;
455                 }
456                 pthread_mutex_unlock( &mlt_sdl_mutex );
457         }
458         mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "test_audio", 1 );
459         if ( changed == 0 &&
460                  this->last_position == mlt_frame_get_position( frame ) &&
461                  this->last_producer == mlt_frame_get_original_producer( frame ) &&
462                  !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "refresh" ) )
463         {
464                 sdl_unlock_display( );
465                 mlt_cocoa_autorelease_close( pool );
466                 if ( unlock != NULL ) unlock( );
467                 struct timespec tm = { 0, 100000 };
468                 nanosleep( &tm, NULL );
469                 return 0;
470         }
471
472         // Update last frame shown info
473         this->last_position = mlt_frame_get_position( frame );
474         this->last_producer = mlt_frame_get_original_producer( frame );
475
476         // Get the image, width and height
477         mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 );
478
479         if ( image != NULL )
480         {
481                 char *rescale = mlt_properties_get( properties, "rescale" );
482                 if ( rescale != NULL && strcmp( rescale, "none" ) )
483                 {
484                         double this_aspect = display_ratio / ( ( double )this->window_width / ( double )this->window_height );
485                         this->rect.w = this_aspect * this->window_width;
486                         this->rect.h = this->window_height;
487                         if ( this->rect.w > this->window_width )
488                         {
489                                 this->rect.w = this->window_width;
490                                 this->rect.h = ( 1.0 / this_aspect ) * this->window_height;
491                         }
492                 }
493                 else
494                 {
495                         double frame_aspect = mlt_frame_get_aspect_ratio( frame ) * width / height;
496                         this->rect.w = frame_aspect * this->window_height;
497                         this->rect.h = this->window_height;
498                         if ( this->rect.w > this->window_width )
499                         {
500                                 this->rect.w = this->window_width;
501                                 this->rect.h = ( 1.0 / frame_aspect ) * this->window_width;
502                         }
503                 }
504
505                 this->rect.x = ( this->window_width - this->rect.w ) / 2;
506                 this->rect.y = ( this->window_height - this->rect.h ) / 2;
507
508                 mlt_properties_set_int( this->properties, "rect_x", this->rect.x );
509                 mlt_properties_set_int( this->properties, "rect_y", this->rect.y );
510                 mlt_properties_set_int( this->properties, "rect_w", this->rect.w );
511                 mlt_properties_set_int( this->properties, "rect_h", this->rect.h );
512         }
513         
514         pthread_mutex_lock( &mlt_sdl_mutex );
515         SDL_Surface *screen = SDL_GetVideoSurface( );
516         if ( !mlt_consumer_is_stopped( &this->parent ) && screen != NULL && this->sdl_screen != NULL && this->sdl_screen->pixels != NULL )
517         {
518                 switch( this->sdl_screen->format->BytesPerPixel )
519                 {
520                         case 1:
521                                 display_1( this->sdl_screen, this->rect, image, width, height );
522                                 break;
523                         case 2:
524                                 display_2( this->sdl_screen, this->rect, image, width, height );
525                                 break;
526                         case 3:
527                                 display_3( this->sdl_screen, this->rect, image, width, height );
528                                 break;
529                         case 4:
530                                 display_4( this->sdl_screen, this->rect, image, width, height );
531                                 break;
532                         default:
533                                 fprintf( stderr, "Unsupported video depth %d\n", this->sdl_screen->format->BytesPerPixel );
534                                 break;
535                 }
536
537                 // Flip it into sight
538                 SDL_Flip( this->sdl_screen );
539         }
540         pthread_mutex_unlock( &mlt_sdl_mutex );
541
542         sdl_unlock_display();
543         mlt_cocoa_autorelease_close( pool );
544         if ( unlock != NULL ) unlock( );
545         mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
546
547         return 1;
548 }
549
550 /** Threaded wrapper for pipe.
551 */
552
553 static void *consumer_thread( void *arg )
554 {
555         // Identify the arg
556         consumer_sdl this = arg;
557
558         // Get the consumer
559         mlt_consumer consumer = &this->parent;
560         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
561         mlt_frame frame = NULL;
562
563         // Allow the hosting app to provide the preview
564         int preview_off = mlt_properties_get_int( properties, "preview_off" );
565
566         // Loop until told not to
567         while( this->running )
568         {
569                 // Get a frame from the attached producer
570                 frame = mlt_consumer_rt_frame( consumer );
571
572                 // Ensure that we have a frame
573                 if ( this->running && frame != NULL )
574                 {
575                         if ( preview_off == 0 )
576                         {
577                                 consumer_play_video( this, frame );
578                         }
579                         else
580                         {
581                                 mlt_image_format vfmt = mlt_image_rgb24a;
582                                 int height = this->height;
583                                 int width = this->width;
584                                 uint8_t *image = NULL;
585                                 mlt_image_format preview_format = mlt_properties_get_int( properties, "preview_format" );
586
587                                 // Check if a specific colour space has been requested
588                                 if ( preview_off && preview_format != mlt_image_none )
589                                         vfmt = preview_format;
590                         
591                                 mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 );
592                                 mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "format", vfmt );
593                                 mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
594                         }
595                         mlt_frame_close( frame );
596                 }
597                 else
598                 {
599                         if ( frame ) mlt_frame_close( frame );
600                         this->running = 0;
601                 }
602         }
603
604         return NULL;
605 }
606
607 static int consumer_get_dimensions( int *width, int *height )
608 {
609         int changed = 0;
610
611         // SDL windows manager structure
612         SDL_SysWMinfo wm;
613
614         // Specify the SDL Version
615         SDL_VERSION( &wm.version );
616
617 #ifndef __DARWIN__
618         // Get the wm structure
619         if ( SDL_GetWMInfo( &wm ) == 1 )
620         {
621 #ifndef WIN32
622                 // Check that we have the X11 wm
623                 if ( wm.subsystem == SDL_SYSWM_X11 ) 
624                 {
625                         // Get the SDL window
626                         Window window = wm.info.x11.window;
627
628                         // Get the display session
629                         Display *display = wm.info.x11.display;
630
631                         // Get the window attributes
632                         XWindowAttributes attr;
633                         XGetWindowAttributes( display, window, &attr );
634
635                         // Determine whether window has changed
636                         changed = *width != attr.width || *height != attr.height;
637
638                         // Return width and height
639                         *width = attr.width;
640                         *height = attr.height;
641                 }
642 #endif
643         }
644 #endif
645
646         return changed;
647 }
648
649 /** Callback to allow override of the close method.
650 */
651
652 static void consumer_close( mlt_consumer parent )
653 {
654         // Get the actual object
655         consumer_sdl this = parent->child;
656
657         // Stop the consumer
658         mlt_consumer_stop( parent );
659
660         // Now clean up the rest
661         mlt_consumer_close( parent );
662
663         // Finally clean up this
664         free( this );
665 }