]> git.sesse.net Git - mlt/blob - src/modules/sdl/consumer_sdl.c
Consumer valerie, pushes, and assorted modifications
[mlt] / src / modules / sdl / consumer_sdl.c
1 /*
2  * consumer_sdl.c -- A Simple DirectMedia Layer consumer
3  * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4  * Author: Dan Dennedy <dan@dennedy.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include "consumer_sdl.h"
22 #include <framework/mlt_frame.h>
23 #include <framework/mlt_deque.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <pthread.h>
28 #include <SDL/SDL.h>
29 #include <SDL/SDL_syswm.h>
30 #include <sys/time.h>
31
32 /** This classes definition.
33 */
34
35 typedef struct consumer_sdl_s *consumer_sdl;
36
37 struct consumer_sdl_s
38 {
39         struct mlt_consumer_s parent;
40         mlt_properties properties;
41         mlt_deque queue;
42         pthread_t thread;
43         int joined;
44         int running;
45         uint8_t audio_buffer[ 4096 * 10 ];
46         int audio_avail;
47         pthread_mutex_t audio_mutex;
48         pthread_cond_t audio_cond;
49         pthread_mutex_t video_mutex;
50         pthread_cond_t video_cond;
51         int window_width;
52         int window_height;
53         float aspect_ratio;
54         float display_aspect;
55         double last_frame_aspect;
56         int width;
57         int height;
58         int playing;
59         int sdl_flags;
60         SDL_Surface *sdl_screen;
61         SDL_Overlay *sdl_overlay;
62         SDL_Rect rect;
63         uint8_t *buffer;
64 };
65
66 /** Forward references to static functions.
67 */
68
69 static int consumer_start( mlt_consumer parent );
70 static int consumer_stop( mlt_consumer parent );
71 static int consumer_is_stopped( mlt_consumer parent );
72 static void consumer_close( mlt_consumer parent );
73 static void *consumer_thread( void * );
74 static int consumer_get_dimensions( int *width, int *height );
75 static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args );
76
77 /** This is what will be called by the factory - anything can be passed in
78         via the argument, but keep it simple.
79 */
80
81 mlt_consumer consumer_sdl_init( char *arg )
82 {
83         // Create the consumer object
84         consumer_sdl this = calloc( sizeof( struct consumer_sdl_s ), 1 );
85
86         // If no malloc'd and consumer init ok
87         if ( this != NULL && mlt_consumer_init( &this->parent, this ) == 0 )
88         {
89                 // Create the queue
90                 this->queue = mlt_deque_init( );
91
92                 // Get the parent consumer object
93                 mlt_consumer parent = &this->parent;
94
95                 // We have stuff to clean up, so override the close method
96                 parent->close = consumer_close;
97
98                 // get a handle on properties
99                 mlt_service service = mlt_consumer_service( parent );
100                 this->properties = mlt_service_properties( service );
101
102                 // Set the default volume
103                 mlt_properties_set_double( this->properties, "volume", 1.0 );
104
105                 // This is the initialisation of the consumer
106                 pthread_mutex_init( &this->audio_mutex, NULL );
107                 pthread_cond_init( &this->audio_cond, NULL);
108                 pthread_mutex_init( &this->video_mutex, NULL );
109                 pthread_cond_init( &this->video_cond, NULL);
110                 
111                 // Default scaler (for now we'll use nearest)
112                 mlt_properties_set( this->properties, "rescale", "nearest" );
113
114                 // Default buffer for low latency
115                 mlt_properties_set_int( this->properties, "buffer", 1 );
116
117                 // Default progressive true
118                 mlt_properties_set_int( this->properties, "progressive", 0 );
119
120                 // Default audio buffer
121                 mlt_properties_set_int( this->properties, "audio_buffer", 1024 );
122
123                 // Get sample aspect ratio
124                 this->aspect_ratio = mlt_properties_get_double( this->properties, "aspect_ratio" );
125
126                 // Ensure we don't join on a non-running object
127                 this->joined = 1;
128                 
129                 // Default display aspect ratio
130                 this->display_aspect = 4.0 / 3.0;
131                 
132                 // process actual param
133                 if ( arg == NULL || sscanf( arg, "%dx%d", &this->width, &this->height ) != 2 )
134                 {
135                         this->width = mlt_properties_get_int( this->properties, "width" );
136                         this->height = mlt_properties_get_int( this->properties, "height" );
137                         
138                         // Default window size
139                         this->window_width = ( float )this->height * this->display_aspect;
140                         this->window_height = this->height;
141                 }
142                 else
143                 {
144                         if ( (int)( ( float )this->width / this->height * 1000 ) != 
145                                  (int)( this->display_aspect * 1000 ) ) 
146                         {
147                                 // Override these
148                                 this->display_aspect = ( float )this->width / this->height;
149                                 this->aspect_ratio = 1.0;
150                                 mlt_properties_set_double( this->properties, "aspect_ratio", this->aspect_ratio );
151                         }
152                         
153                         // Set window size
154                         this->window_width = this->width;
155                         this->window_height = this->height;
156                 }
157
158                 // Set the sdl flags
159                 this->sdl_flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL | SDL_RESIZABLE | SDL_DOUBLEBUF;
160
161                 // Allow thread to be started/stopped
162                 parent->start = consumer_start;
163                 parent->stop = consumer_stop;
164                 parent->is_stopped = consumer_is_stopped;
165
166                 // Register specific events
167                 mlt_events_register( this->properties, "consumer-sdl-event", ( mlt_transmitter )consumer_sdl_event );
168
169                 // Return the consumer produced
170                 return parent;
171         }
172
173         // malloc or consumer init failed
174         free( this );
175
176         // Indicate failure
177         return NULL;
178 }
179
180 static void consumer_sdl_event( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
181 {
182         if ( listener != NULL )
183                 listener( owner, this, ( SDL_Event * )args[ 0 ] );
184 }
185
186 int consumer_start( mlt_consumer parent )
187 {
188         consumer_sdl this = parent->child;
189
190         if ( !this->running )
191         {
192                 pthread_attr_t thread_attributes;
193                 
194                 consumer_stop( parent );
195
196                 this->running = 1;
197                 this->joined = 0;
198
199                 // Allow the user to force resizing to window size
200                 if ( mlt_properties_get_int( this->properties, "resize" ) )
201                 {
202                         mlt_properties_set_int( this->properties, "width", this->width );
203                         mlt_properties_set_int( this->properties, "height", this->height );
204                 }
205
206                 // Inherit the scheduling priority
207                 pthread_attr_init( &thread_attributes );
208                 pthread_attr_setinheritsched( &thread_attributes, PTHREAD_INHERIT_SCHED );
209
210                 pthread_create( &this->thread, &thread_attributes, consumer_thread, this );
211         }
212
213         return 0;
214 }
215
216 int consumer_stop( mlt_consumer parent )
217 {
218         // Get the actual object
219         consumer_sdl this = parent->child;
220
221         if ( this->joined == 0 )
222         {
223                 // Kill the thread and clean up
224                 this->running = 0;
225
226                 pthread_mutex_lock( &this->audio_mutex );
227                 pthread_cond_broadcast( &this->audio_cond );
228                 pthread_mutex_unlock( &this->audio_mutex );
229
230                 pthread_join( this->thread, NULL );
231                 this->joined = 1;
232         }
233
234         return 0;
235 }
236
237 int consumer_is_stopped( mlt_consumer parent )
238 {
239         consumer_sdl this = parent->child;
240         return !this->running;
241 }
242
243 static int sdl_lock_display( )
244 {
245         SDL_Surface *screen = SDL_GetVideoSurface( );
246         return screen != NULL && ( !SDL_MUSTLOCK( screen ) || SDL_LockSurface( screen ) >= 0 );
247 }
248
249 static void sdl_unlock_display( )
250 {
251         SDL_Surface *screen = SDL_GetVideoSurface( );
252         if ( screen != NULL && SDL_MUSTLOCK( screen ) )
253                 SDL_UnlockSurface( screen );
254 }
255
256 static void sdl_fill_audio( void *udata, uint8_t *stream, int len )
257 {
258         consumer_sdl this = udata;
259
260         // Get the volume
261         float volume = mlt_properties_get_double( this->properties, "volume" );
262
263         pthread_mutex_lock( &this->audio_mutex );
264
265         // Block until audio received
266         while ( this->running && len > this->audio_avail )
267                 pthread_cond_wait( &this->audio_cond, &this->audio_mutex );
268
269         if ( this->audio_avail >= len )
270         {
271                 // Place in the audio buffer
272                 memcpy( stream, this->audio_buffer, len );
273
274                 // Remove len from the audio available
275                 this->audio_avail -= len;
276
277                 // Remove the samples
278                 memmove( this->audio_buffer, this->audio_buffer + len, this->audio_avail );
279         }
280         else
281         {
282                 // Just to be safe, wipe the stream first
283                 memset( stream, 0, len );
284
285                 // Copy what we have into the stream
286                 memcpy( stream, this->audio_buffer, this->audio_avail );
287
288                 // Mix the audio 
289                 SDL_MixAudio( stream, stream, len, ( int )( ( float )SDL_MIX_MAXVOLUME * volume ) );
290
291                 // No audio left
292                 this->audio_avail = 0;
293         }
294
295         // We're definitely playing now
296         this->playing = 1;
297
298         pthread_cond_broadcast( &this->audio_cond );
299         pthread_mutex_unlock( &this->audio_mutex );
300 }
301
302 static int consumer_play_audio( consumer_sdl this, mlt_frame frame, int init_audio, int *duration )
303 {
304         // Get the properties of this consumer
305         mlt_properties properties = this->properties;
306         mlt_audio_format afmt = mlt_audio_pcm;
307
308         // Set the preferred params of the test card signal
309         int channels = mlt_properties_get_int( properties, "channels" );
310         int frequency = mlt_properties_get_int( properties, "frequency" );
311         static int counter = 0;
312
313         int samples = mlt_sample_calculator( mlt_properties_get_double( this->properties, "fps" ), frequency, counter++ );
314         
315         int16_t *pcm;
316         int bytes;
317
318         mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples );
319         *duration = ( ( samples * 1000 ) / frequency );
320
321         if ( mlt_properties_get_int( properties, "audio_off" ) )
322         {
323                 this->playing = 1;
324                 init_audio = 1;
325                 return init_audio;
326         }
327
328         if ( init_audio == 1 )
329         {
330                 SDL_AudioSpec request;
331                 SDL_AudioSpec got;
332
333                 int audio_buffer = mlt_properties_get_int( properties, "audio_buffer" );
334
335                 // specify audio format
336                 memset( &request, 0, sizeof( SDL_AudioSpec ) );
337                 this->playing = 0;
338                 request.freq = frequency;
339                 request.format = AUDIO_S16;
340                 request.channels = channels;
341                 request.samples = audio_buffer;
342                 request.callback = sdl_fill_audio;
343                 request.userdata = (void *)this;
344                 if ( SDL_OpenAudio( &request, &got ) != 0 )
345                 {
346                         fprintf( stderr, "SDL failed to open audio: %s\n", SDL_GetError() );
347                         init_audio = 2;
348                 }
349                 else if ( got.size != 0 )
350                 {
351                         SDL_PauseAudio( 0 );
352                         init_audio = 0;
353                 }
354         }
355
356         if ( init_audio == 0 )
357         {
358                 mlt_properties properties = mlt_frame_properties( frame );
359                 bytes = ( samples * channels * 2 );
360                 pthread_mutex_lock( &this->audio_mutex );
361                 while ( this->running && bytes > ( sizeof( this->audio_buffer) - this->audio_avail ) )
362                         pthread_cond_wait( &this->audio_cond, &this->audio_mutex );
363                 if ( this->running )
364                 {
365                         if ( mlt_properties_get_double( properties, "_speed" ) == 1 )
366                                 memcpy( &this->audio_buffer[ this->audio_avail ], pcm, bytes );
367                         else
368                                 memset( &this->audio_buffer[ this->audio_avail ], 0, bytes );
369                         this->audio_avail += bytes;
370                 }
371                 pthread_cond_broadcast( &this->audio_cond );
372                 pthread_mutex_unlock( &this->audio_mutex );
373         }
374         else
375         {
376                 this->playing = 1;
377         }
378
379         return init_audio;
380 }
381
382 static int consumer_play_video( consumer_sdl this, mlt_frame frame )
383 {
384         // Get the properties of this consumer
385         mlt_properties properties = this->properties;
386
387         mlt_image_format vfmt = mlt_image_yuv422;
388         int width = this->width, height = this->height;
389         uint8_t *image;
390         int changed = 0;
391
392         if ( mlt_properties_get_int( properties, "video_off" ) == 0 )
393         {
394                 // Get the image, width and height
395                 mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
396                 mlt_frame_get_image( frame, &image, &vfmt, &width, &height, 0 );
397                 
398                 // Handle events
399                 if ( this->sdl_screen != NULL )
400                 {
401                         SDL_Event event;
402         
403                         changed = consumer_get_dimensions( &this->window_width, &this->window_height );
404         
405                         while ( SDL_PollEvent( &event ) )
406                         {
407                                 mlt_events_fire( this->properties, "consumer-sdl-event", &event, NULL );
408
409                                 switch( event.type )
410                                 {
411                                         case SDL_VIDEORESIZE:
412                                                 this->window_width = event.resize.w;
413                                                 this->window_height = event.resize.h;
414                                                 changed = 1;
415                                                 break;
416                                         case SDL_QUIT:
417                                                 this->running = 0;
418                                                 break;
419                                         case SDL_KEYDOWN:
420                                                 {
421                                                         mlt_producer producer = mlt_properties_get_data( properties, "transport_producer", NULL );
422                                                         char keyboard[ 2 ] = " ";
423                                                         void (*callback)( mlt_producer, char * ) = mlt_properties_get_data( properties, "transport_callback", NULL );
424                                                         if ( callback != NULL && producer != NULL && event.key.keysym.unicode < 0x80 && event.key.keysym.unicode > 0 )
425                                                         {
426                                                                 keyboard[ 0 ] = ( char )event.key.keysym.unicode;
427                                                                 callback( producer, keyboard );
428                                                         }
429                                                 }
430                                                 break;
431                                 }
432                         }
433                 }
434         
435                 if ( width != this->width || height != this->height || 
436                          ( ( int )( this->last_frame_aspect * 1000 ) != ( int )( mlt_frame_get_aspect_ratio( frame ) * 1000 ) &&
437                          ( mlt_frame_get_aspect_ratio( frame ) != 1.0 || this->last_frame_aspect == 0.0 ) ) )
438
439                 {
440                         this->width = width;
441                         this->height = height;
442                         this->last_frame_aspect = mlt_frame_get_aspect_ratio( frame );
443                         changed = 1;
444                 }
445
446                 if ( this->sdl_screen == NULL || changed )
447                 {
448                         // Determine frame's display aspect ratio
449                         float frame_aspect = mlt_frame_get_aspect_ratio( frame ) * this->width / this->height;
450                         
451                         // Determine window's new display aspect ratio
452                         float this_aspect = ( float )this->window_width / this->window_height;
453
454                         // If using hardware scaler
455                         if ( mlt_properties_get( properties, "rescale" ) != NULL &&
456                                 !strcmp( mlt_properties_get( properties, "rescale" ), "none" ) )
457                         {
458                                 // Special case optimisation to negate odd effect of sample aspect ratio
459                                 // not corresponding exactly with image resolution.
460                                 if ( ( (int)( this_aspect * 1000 ) == (int)( this->display_aspect * 1000 ) ) && 
461                                          ( (int)( mlt_frame_get_aspect_ratio( frame ) * 1000 ) == (int)( this->aspect_ratio * 1000 ) ) )
462                                 {
463                                         this->rect.w = this->window_width;
464                                         this->rect.h = this->window_height;
465                                 }
466                                 else
467                                 {
468                                         // Use hardware scaler to normalise display aspect ratio
469                                         this->rect.w = frame_aspect / this_aspect * this->window_width;
470                                         this->rect.h = this->window_height;
471                                         if ( this->rect.w > this->window_width )
472                                         {
473                                                 this->rect.w = this->window_width;
474                                                 this->rect.h = this_aspect / frame_aspect * this->window_height;
475                                         }
476                                 }
477                         }
478                         // Special case optimisation to negate odd effect of sample aspect ratio
479                         // not corresponding exactly with image resolution.
480                         else if ( (int)( this_aspect * 1000 ) == (int)( this->display_aspect * 1000 ) ) 
481                         {
482                                 this->rect.w = this->window_width;
483                                 this->rect.h = this->window_height;
484                         }
485                         // Use hardware scaler to normalise sample aspect ratio
486                         else if ( this->window_height * this->display_aspect > this->window_width )
487                         {
488                                 this->rect.w = this->window_width;
489                                 this->rect.h = this->window_width / this->display_aspect;
490                         }
491                         else
492                         {
493                                 this->rect.w = this->window_height * this->display_aspect;
494                                 this->rect.h = this->window_height;
495                         }
496                         
497                         this->rect.x = ( this->window_width - this->rect.w ) / 2;
498                         this->rect.y = ( this->window_height - this->rect.h ) / 2;
499
500                         mlt_properties_set_int( this->properties, "rect_x", this->rect.x );
501                         mlt_properties_set_int( this->properties, "rect_y", this->rect.y );
502                         mlt_properties_set_int( this->properties, "rect_w", this->rect.w );
503                         mlt_properties_set_int( this->properties, "rect_h", this->rect.h );
504                         
505                         // Force an overlay recreation
506                         if ( this->sdl_overlay != NULL )
507                                 SDL_FreeYUVOverlay( this->sdl_overlay );
508
509                         // open SDL window with video overlay, if possible
510                         sdl_lock_display();
511                         this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags );
512                         sdl_unlock_display();
513
514                         if ( this->sdl_screen != NULL )
515                         {
516                                 SDL_SetClipRect( this->sdl_screen, &this->rect );
517                                 sdl_lock_display();
518                                 this->sdl_overlay = SDL_CreateYUVOverlay( this->width, this->height, SDL_YUY2_OVERLAY, this->sdl_screen );
519                                 sdl_unlock_display();
520                         }
521                 }
522                         
523                 if ( mlt_properties_get_int( properties, "changed" ) )
524                 {
525                         sdl_lock_display();
526                         this->sdl_screen = SDL_SetVideoMode( this->window_width, this->window_height, 0, this->sdl_flags );
527                         SDL_SetClipRect( this->sdl_screen, &this->rect );
528                         SDL_Flip( this->sdl_screen );
529                         sdl_unlock_display();
530                         mlt_properties_set_int( properties, "changed", 0 );
531                 }
532
533                 if ( this->sdl_screen != NULL && this->sdl_overlay != NULL )
534                 {
535                         this->buffer = this->sdl_overlay->pixels[ 0 ];
536                         sdl_lock_display();
537                         if ( SDL_LockYUVOverlay( this->sdl_overlay ) >= 0 )
538                         {
539                                 if ( image != NULL )
540                                         memcpy( this->buffer, image, width * height * 2 );
541                                 SDL_UnlockYUVOverlay( this->sdl_overlay );
542                                 SDL_DisplayYUVOverlay( this->sdl_overlay, &this->sdl_screen->clip_rect );
543                         }
544                         sdl_unlock_display();
545                 }
546         }
547
548         return 0;
549 }
550
551 static void *video_thread( void *arg )
552 {
553         // Identify the arg
554         consumer_sdl this = arg;
555
556         // Obtain time of thread start
557         struct timeval now;
558         int64_t start = 0;
559         int64_t elapsed = 0;
560         struct timespec tm;
561         mlt_frame next = NULL;
562         mlt_properties properties = NULL;
563         double speed = 0;
564
565         // Get real time flag
566         int real_time = mlt_properties_get_int( this->properties, "real_time" );
567
568         // Get the current time
569         gettimeofday( &now, NULL );
570
571         // Determine start time
572         start = ( int64_t )now.tv_sec * 1000000 + now.tv_usec;
573
574         while ( this->running )
575         {
576                 // Pop the next frame
577                 pthread_mutex_lock( &this->video_mutex );
578                 while ( ( next = mlt_deque_pop_front( this->queue ) ) == NULL && this->running )
579                         pthread_cond_wait( &this->video_cond, &this->video_mutex );
580                 pthread_mutex_unlock( &this->video_mutex );
581
582                 // Get the properties
583                 properties =  mlt_frame_properties( next );
584
585                 // Get the speed of the frame
586                 speed = mlt_properties_get_double( properties, "_speed" );
587
588                 // Get the current time
589                 gettimeofday( &now, NULL );
590
591                 // Get the elapsed time
592                 elapsed = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - start;
593
594                 // See if we have to delay the display of the current frame
595                 if ( mlt_properties_get_int( properties, "rendered" ) == 1 && this->running )
596                 {
597                         // Obtain the scheduled playout time
598                         mlt_position scheduled = mlt_properties_get_position( properties, "playtime" );
599
600                         // Determine the difference between the elapsed time and the scheduled playout time
601                         mlt_position difference = scheduled - elapsed;
602
603                         // Smooth playback a bit
604                         if ( real_time && ( difference > 20000 && speed == 1.0 ) )
605                         {
606                                 tm.tv_sec = difference / 1000000;
607                                 tm.tv_nsec = ( difference % 1000000 ) * 500;
608                                 nanosleep( &tm, NULL );
609                         }
610
611                         // Show current frame if not too old
612                         if ( !real_time || ( difference > -10000 || speed != 1.0 || mlt_deque_count( this->queue ) < 2 ) )
613                                 consumer_play_video( this, next );
614
615                         // If the queue is empty, recalculate start to allow build up again
616                         if ( real_time && ( mlt_deque_count( this->queue ) == 0 && speed == 1.0 ) )
617                         {
618                                 gettimeofday( &now, NULL );
619                                 start = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - scheduled + 20000;
620                         }
621                 }
622
623                 // This frame can now be closed
624                 mlt_frame_close( next );
625         }
626
627         mlt_consumer_stopped( &this->parent );
628
629         return NULL;
630 }
631
632 /** Threaded wrapper for pipe.
633 */
634
635 static void *consumer_thread( void *arg )
636 {
637         // Identify the arg
638         consumer_sdl this = arg;
639
640         // Get the consumer
641         mlt_consumer consumer = &this->parent;
642
643         // Video thread
644         pthread_t thread;
645
646         // internal intialization
647         int init_audio = 1;
648         int init_video = 1;
649         mlt_frame frame = NULL;
650         mlt_properties properties = NULL;
651         int duration = 0;
652         int64_t playtime = 0;
653         struct timespec tm = { 0, 100000 };
654
655         if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE ) < 0 )
656         {
657                 fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() );
658                 return NULL;
659         }
660
661         SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
662         SDL_EnableUNICODE( 1 );
663
664         // Loop until told not to
665         while( this->running )
666         {
667                 // Get a frame from the attached producer
668                 frame = mlt_consumer_rt_frame( consumer );
669
670                 // Ensure that we have a frame
671                 if ( frame != NULL )
672                 {
673                         // Get the frame properties
674                         properties =  mlt_frame_properties( frame );
675
676                         // Play audio
677                         init_audio = consumer_play_audio( this, frame, init_audio, &duration );
678
679                         // Determine the start time now
680                         if ( this->playing && init_video )
681                         {
682                                 // Create the video thread
683                                 pthread_create( &thread, NULL, video_thread, this );
684
685                                 // Video doesn't need to be initialised any more
686                                 init_video = 0;
687                         }
688
689                         // Set playtime for this frame
690                         mlt_properties_set_position( properties, "playtime", playtime );
691
692                         while ( this->running && mlt_deque_count( this->queue ) > 15 )
693                                 nanosleep( &tm, NULL );
694
695                         // Push this frame to the back of the queue
696                         pthread_mutex_lock( &this->video_mutex );
697                         mlt_deque_push_back( this->queue, frame );
698                         pthread_cond_broadcast( &this->video_cond );
699                         pthread_mutex_unlock( &this->video_mutex );
700
701                         // Calculate the next playtime
702                         playtime += ( duration * 1000 );
703                 }
704         }
705
706         // Kill the video thread
707         if ( init_video == 0 )
708         {
709                 pthread_mutex_lock( &this->video_mutex );
710                 pthread_cond_broadcast( &this->video_cond );
711                 pthread_mutex_unlock( &this->video_mutex );
712                 pthread_join( thread, NULL );
713         }
714
715         // internal cleanup
716         if ( this->sdl_overlay != NULL )
717                 SDL_FreeYUVOverlay( this->sdl_overlay );
718         SDL_Quit( );
719
720         while( mlt_deque_count( this->queue ) )
721                 mlt_frame_close( mlt_deque_pop_back( this->queue ) );
722
723         this->sdl_screen = NULL;
724         this->sdl_overlay = NULL;
725         this->audio_avail = 0;
726
727         return NULL;
728 }
729
730 static int consumer_get_dimensions( int *width, int *height )
731 {
732         int changed = 0;
733
734         // SDL windows manager structure
735         SDL_SysWMinfo wm;
736
737         // Specify the SDL Version
738         SDL_VERSION( &wm.version );
739
740         // Lock the display
741         sdl_lock_display();
742
743         // Get the wm structure
744         if ( SDL_GetWMInfo( &wm ) == 1 )
745         {
746                 // Check that we have the X11 wm
747                 if ( wm.subsystem == SDL_SYSWM_X11 ) 
748                 {
749                         // Get the SDL window
750                         Window window = wm.info.x11.window;
751
752                         // Get the display session
753                         Display *display = wm.info.x11.display;
754
755                         // Get the window attributes
756                         XWindowAttributes attr;
757                         XGetWindowAttributes( display, window, &attr );
758
759                         // Determine whether window has changed
760                         changed = *width != attr.width || *height != attr.height;
761
762                         // Return width and height
763                         *width = attr.width;
764                         *height = attr.height;
765                 }
766         }
767
768         // Unlock the display
769         sdl_lock_display();
770
771         return changed;
772 }
773
774 /** Callback to allow override of the close method.
775 */
776
777 static void consumer_close( mlt_consumer parent )
778 {
779         // Get the actual object
780         consumer_sdl this = parent->child;
781
782         // Stop the consumer
783         mlt_consumer_stop( parent );
784
785         // Close the queue
786         mlt_deque_close( this->queue );
787
788         // Destroy mutexes
789         pthread_mutex_destroy( &this->audio_mutex );
790         pthread_cond_destroy( &this->audio_cond );
791                 
792         // Now clean up the rest
793         mlt_consumer_close( parent );
794
795         // Finally clean up this
796         free( this );
797 }