2 * consumer_sdl_preview.c -- A Simple DirectMedia Layer consumer
3 * Copyright (C) 2004-2005, 2010 Ushodaya Enterprises Limited
4 * Author: Charles Yates
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.
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.
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
21 #include <framework/mlt_consumer.h>
22 #include <framework/mlt_frame.h>
23 #include <framework/mlt_factory.h>
24 #include <framework/mlt_producer.h>
25 #include <framework/mlt_log.h>
30 #include <SDL_syswm.h>
33 extern pthread_mutex_t mlt_sdl_mutex;
35 typedef struct consumer_sdl_s *consumer_sdl;
39 struct mlt_consumer_s parent;
49 mlt_position last_position;
51 pthread_cond_t refresh_cond;
52 pthread_mutex_t refresh_mutex;
56 /** Forward references to static functions.
59 static int consumer_start( mlt_consumer parent );
60 static int consumer_stop( mlt_consumer parent );
61 static int consumer_is_stopped( mlt_consumer parent );
62 static void consumer_purge( mlt_consumer parent );
63 static void consumer_close( mlt_consumer parent );
64 static void *consumer_thread( void * );
65 static void consumer_frame_show_cb( mlt_consumer sdl, mlt_consumer self, mlt_frame frame );
66 static void consumer_sdl_event_cb( mlt_consumer sdl, mlt_consumer self, SDL_Event *event );
67 static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer self, char *name );
69 mlt_consumer consumer_sdl_preview_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
71 consumer_sdl self = calloc( 1, sizeof( struct consumer_sdl_s ) );
72 if ( self != NULL && mlt_consumer_init( &self->parent, self, profile ) == 0 )
74 // Get the parent consumer object
75 mlt_consumer parent = &self->parent;
78 mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
80 // Get the width and height
81 int width = mlt_properties_get_int( properties, "width" );
82 int height = mlt_properties_get_int( properties, "height" );
84 // Process actual param
85 if ( arg == NULL || sscanf( arg, "%dx%d", &width, &height ) == 2 )
87 mlt_properties_set_int( properties, "width", width );
88 mlt_properties_set_int( properties, "height", height );
91 // Create child consumers
92 self->play = mlt_factory_consumer( profile, "sdl", arg );
93 self->still = mlt_factory_consumer( profile, "sdl_still", arg );
94 mlt_properties_set( properties, "rescale", "nearest" );
95 mlt_properties_set( properties, "deinterlace_method", "onefield" );
96 mlt_properties_set_int( properties, "prefill", 1 );
97 mlt_properties_set_int( properties, "top_field_first", -1 );
99 parent->close = consumer_close;
100 parent->start = consumer_start;
101 parent->stop = consumer_stop;
102 parent->is_stopped = consumer_is_stopped;
103 parent->purge = consumer_purge;
105 mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->play ), self, "consumer-frame-show", ( mlt_listener )consumer_frame_show_cb );
106 mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->still ), self, "consumer-frame-show", ( mlt_listener )consumer_frame_show_cb );
107 mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->play ), self, "consumer-sdl-event", ( mlt_listener )consumer_sdl_event_cb );
108 mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->still ), self, "consumer-sdl-event", ( mlt_listener )consumer_sdl_event_cb );
109 pthread_cond_init( &self->refresh_cond, NULL );
110 pthread_mutex_init( &self->refresh_mutex, NULL );
111 mlt_events_listen( MLT_CONSUMER_PROPERTIES( parent ), self, "property-changed", ( mlt_listener )consumer_refresh_cb );
112 mlt_events_register( properties, "consumer-sdl-paused", NULL );
119 void consumer_frame_show_cb( mlt_consumer sdl, mlt_consumer parent, mlt_frame frame )
121 consumer_sdl self = parent->child;
122 self->last_speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" );
123 self->last_position = mlt_frame_get_position( frame );
124 mlt_events_fire( MLT_CONSUMER_PROPERTIES( parent ), "consumer-frame-show", frame, NULL );
127 static void consumer_sdl_event_cb( mlt_consumer sdl, mlt_consumer parent, SDL_Event *event )
129 mlt_events_fire( MLT_CONSUMER_PROPERTIES( parent ), "consumer-sdl-event", event, NULL );
132 static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer parent, char *name )
134 if ( !strcmp( name, "refresh" ) )
136 consumer_sdl self = parent->child;
137 pthread_mutex_lock( &self->refresh_mutex );
138 self->refresh_count = self->refresh_count <= 0 ? 1 : self->refresh_count + 1;
139 pthread_cond_broadcast( &self->refresh_cond );
140 pthread_mutex_unlock( &self->refresh_mutex );
144 static int consumer_start( mlt_consumer parent )
146 consumer_sdl self = parent->child;
148 if ( !self->running )
151 mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
152 mlt_properties play = MLT_CONSUMER_PROPERTIES( self->play );
153 mlt_properties still = MLT_CONSUMER_PROPERTIES( self->still );
155 char *window_id = mlt_properties_get( properties, "window_id" );
156 char *audio_driver = mlt_properties_get( properties, "audio_driver" );
157 char *video_driver = mlt_properties_get( properties, "video_driver" );
158 char *audio_device = mlt_properties_get( properties, "audio_device" );
159 char *output_display = mlt_properties_get( properties, "output_display" );
160 int progressive = mlt_properties_get_int( properties, "progressive" ) | mlt_properties_get_int( properties, "deinterlace" );
162 consumer_stop( parent );
166 self->last_speed = 1;
168 if ( output_display != NULL )
169 setenv( "DISPLAY", output_display, 1 );
171 if ( window_id != NULL )
172 setenv( "SDL_WINDOWID", window_id, 1 );
174 if ( video_driver != NULL )
175 setenv( "SDL_VIDEODRIVER", video_driver, 1 );
177 if ( audio_driver != NULL )
178 setenv( "SDL_AUDIODRIVER", audio_driver, 1 );
180 if ( audio_device != NULL )
181 setenv( "AUDIODEV", audio_device, 1 );
183 pthread_mutex_lock( &mlt_sdl_mutex );
184 int ret = SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE );
185 pthread_mutex_unlock( &mlt_sdl_mutex );
188 fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() );
192 SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
193 SDL_EnableUNICODE( 1 );
195 // Pass properties down
196 mlt_properties_set_data( play, "transport_producer", mlt_properties_get_data( properties, "transport_producer", NULL ), 0, NULL, NULL );
197 mlt_properties_set_data( still, "transport_producer", mlt_properties_get_data( properties, "transport_producer", NULL ), 0, NULL, NULL );
198 mlt_properties_set_data( play, "transport_callback", mlt_properties_get_data( properties, "transport_callback", NULL ), 0, NULL, NULL );
199 mlt_properties_set_data( still, "transport_callback", mlt_properties_get_data( properties, "transport_callback", NULL ), 0, NULL, NULL );
201 mlt_properties_set_int( play, "progressive", progressive );
202 mlt_properties_set_int( still, "progressive", progressive );
204 mlt_properties_pass_list( play, properties,
205 "deinterlace_method,resize,rescale,width,height,aspect_ratio,display_ratio,preview_off,preview_format,window_background"
206 ",top_field_first,volume,real_time,buffer,prefill,audio_off,frequency,drop_max" );
207 mlt_properties_pass_list( still, properties,
208 "deinterlace_method,resize,rescale,width,height,aspect_ratio,display_ratio,preview_off,preview_format,window_background"
211 mlt_properties_pass( play, properties, "play." );
212 mlt_properties_pass( still, properties, "still." );
214 mlt_properties_set_data( play, "app_lock", mlt_properties_get_data( properties, "app_lock", NULL ), 0, NULL, NULL );
215 mlt_properties_set_data( still, "app_lock", mlt_properties_get_data( properties, "app_lock", NULL ), 0, NULL, NULL );
216 mlt_properties_set_data( play, "app_unlock", mlt_properties_get_data( properties, "app_unlock", NULL ), 0, NULL, NULL );
217 mlt_properties_set_data( still, "app_unlock", mlt_properties_get_data( properties, "app_unlock", NULL ), 0, NULL, NULL );
219 mlt_properties_set_int( play, "put_mode", 1 );
220 mlt_properties_set_int( still, "put_mode", 1 );
221 mlt_properties_set_int( play, "terminate_on_pause", 1 );
223 // Start the still producer just to initialise the gui
224 mlt_consumer_start( self->still );
225 self->active = self->still;
227 // Inform child consumers that we control the sdl
228 mlt_properties_set_int( play, "sdl_started", 1 );
229 mlt_properties_set_int( still, "sdl_started", 1 );
231 pthread_create( &self->thread, NULL, consumer_thread, self );
237 static int consumer_stop( mlt_consumer parent )
239 // Get the actual object
240 consumer_sdl self = parent->child;
242 if ( self->joined == 0 )
244 mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
245 int app_locked = mlt_properties_get_int( properties, "app_locked" );
246 void ( *lock )( void ) = mlt_properties_get_data( properties, "app_lock", NULL );
247 void ( *unlock )( void ) = mlt_properties_get_data( properties, "app_unlock", NULL );
249 if ( app_locked && unlock ) unlock( );
251 // Kill the thread and clean up
254 pthread_mutex_lock( &self->refresh_mutex );
255 pthread_cond_broadcast( &self->refresh_cond );
256 pthread_mutex_unlock( &self->refresh_mutex );
260 pthread_join( self->thread, NULL );
263 if ( app_locked && lock ) lock( );
265 pthread_mutex_lock( &mlt_sdl_mutex );
267 pthread_mutex_unlock( &mlt_sdl_mutex );
273 static int consumer_is_stopped( mlt_consumer parent )
275 consumer_sdl self = parent->child;
276 return !self->running;
279 void consumer_purge( mlt_consumer parent )
281 consumer_sdl self = parent->child;
283 mlt_consumer_purge( self->play );
286 static void *consumer_thread( void *arg )
289 consumer_sdl self = arg;
292 mlt_consumer consumer = &self->parent;
294 // Get the properties
295 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
297 // internal intialization
298 mlt_frame frame = NULL;
299 int last_position = -1;
301 int eos_threshold = 20;
303 eos_threshold = eos_threshold + mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( self->play ), "buffer" );
305 // Determine if the application is dealing with the preview
306 int preview_off = mlt_properties_get_int( properties, "preview_off" );
308 pthread_mutex_lock( &self->refresh_mutex );
309 self->refresh_count = 0;
310 pthread_mutex_unlock( &self->refresh_mutex );
312 // Loop until told not to
313 while( self->running )
315 // Get a frame from the attached producer
316 frame = mlt_consumer_get_frame( consumer );
318 // Ensure that we have a frame
319 if ( self->running && frame != NULL )
321 // Get the speed of the frame
322 double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" );
324 // Lock during the operation
325 mlt_service_lock( MLT_CONSUMER_SERVICE( consumer ) );
327 // Get refresh request for the current frame
328 int refresh = mlt_properties_get_int( properties, "refresh" );
330 // Decrement refresh and clear changed
331 mlt_events_block( properties, properties );
332 mlt_properties_set_int( properties, "refresh", 0 );
333 mlt_events_unblock( properties, properties );
335 // Unlock after the operation
336 mlt_service_unlock( MLT_CONSUMER_SERVICE( consumer ) );
338 // Set the changed property on this frame for the benefit of still
339 mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "refresh", refresh );
341 // Make sure the recipient knows that this frame isn't really rendered
342 mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 0 );
344 // Optimisation to reduce latency
347 if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) )
348 mlt_consumer_purge( self->play );
349 last_position = mlt_frame_get_position( frame );
353 //mlt_consumer_purge( self->play );
357 // If we aren't playing normally, then use the still
360 mlt_producer producer = MLT_PRODUCER( mlt_service_get_producer( MLT_CONSUMER_SERVICE( consumer ) ) );
361 mlt_position duration = producer? mlt_producer_get_playtime( producer ) : -1;
364 #ifndef SKIP_WAIT_EOS
365 if ( self->active == self->play )
367 // Do not interrupt the play consumer near the end
368 if ( duration - self->last_position > eos_threshold )
370 // Get a new frame at the sought position
371 mlt_frame_close( frame );
373 mlt_producer_seek( producer, self->last_position );
374 frame = mlt_consumer_get_frame( consumer );
379 // Send frame with speed 0 to stop it
380 if ( frame && !mlt_consumer_is_stopped( self->play ) )
382 mlt_consumer_put_frame( self->play, frame );
387 // Check for end of stream
388 if ( mlt_consumer_is_stopped( self->play ) )
391 mlt_log_verbose( MLT_CONSUMER_SERVICE( consumer ), "END OF STREAM\n" );
393 eos = 0; // reset eos indicator
397 // Prevent a tight busy loop
398 struct timespec tm = { 0, 100000L }; // 100 usec
399 nanosleep( &tm, NULL );
404 pause = self->active == self->play;
408 // Start the still consumer
409 if ( !mlt_consumer_is_stopped( self->play ) )
410 mlt_consumer_stop( self->play );
411 self->last_speed = speed;
412 self->active = self->still;
413 self->ignore_change = 0;
414 mlt_consumer_start( self->still );
416 // Send the frame to the active child
419 mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "refresh", 1 );
420 mlt_consumer_put_frame( self->active, frame );
422 if ( pause && speed == 0.0 )
424 mlt_events_fire( properties, "consumer-sdl-paused", NULL );
427 // Allow a little grace time before switching consumers on speed changes
428 else if ( self->ignore_change -- > 0 && self->active != NULL && !mlt_consumer_is_stopped( self->active ) )
430 mlt_consumer_put_frame( self->active, frame );
432 // Otherwise use the normal player
435 if ( !mlt_consumer_is_stopped( self->still ) )
436 mlt_consumer_stop( self->still );
437 if ( mlt_consumer_is_stopped( self->play ) )
439 self->last_speed = speed;
440 self->active = self->play;
441 self->ignore_change = 0;
442 mlt_consumer_start( self->play );
445 mlt_consumer_put_frame( self->play, frame );
448 // Copy the rectangle info from the active consumer
449 if ( self->running && preview_off == 0 )
451 mlt_properties active = MLT_CONSUMER_PROPERTIES( self->active );
452 mlt_service_lock( MLT_CONSUMER_SERVICE( consumer ) );
453 mlt_properties_set_int( properties, "rect_x", mlt_properties_get_int( active, "rect_x" ) );
454 mlt_properties_set_int( properties, "rect_y", mlt_properties_get_int( active, "rect_y" ) );
455 mlt_properties_set_int( properties, "rect_w", mlt_properties_get_int( active, "rect_w" ) );
456 mlt_properties_set_int( properties, "rect_h", mlt_properties_get_int( active, "rect_h" ) );
457 mlt_service_unlock( MLT_CONSUMER_SERVICE( consumer ) );
460 if ( self->active == self->still )
462 pthread_mutex_lock( &self->refresh_mutex );
463 if ( self->running && speed == 0 && self->refresh_count <= 0 )
465 mlt_events_fire( properties, "consumer-sdl-paused", NULL );
466 pthread_cond_wait( &self->refresh_cond, &self->refresh_mutex );
468 self->refresh_count --;
469 pthread_mutex_unlock( &self->refresh_mutex );
474 if ( frame ) mlt_frame_close( frame );
475 mlt_consumer_put_frame( self->active, NULL );
480 if ( self->play ) mlt_consumer_stop( self->play );
481 if ( self->still ) mlt_consumer_stop( self->still );
486 /** Callback to allow override of the close method.
489 static void consumer_close( mlt_consumer parent )
491 // Get the actual object
492 consumer_sdl self = parent->child;
495 mlt_consumer_stop( parent );
497 // Close the child consumers
498 mlt_consumer_close( self->play );
499 mlt_consumer_close( self->still );
501 // Now clean up the rest
502 mlt_consumer_close( parent );
504 // Finally clean up self