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