1 /*
2  * consumer_sdl_preview.c -- A Simple DirectMedia Layer consumer
3  * Copyright (C) 2004-2014 Meltytech, LLC
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include <framework/mlt_consumer.h>
21 #include <framework/mlt_frame.h>
22 #include <framework/mlt_factory.h>
23 #include <framework/mlt_producer.h>
24 #include <framework/mlt_log.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <pthread.h>
28 #include <SDL.h>
29 #include <SDL_syswm.h>
30 #include <sys/time.h>
31 
32 extern pthread_mutex_t mlt_sdl_mutex;
33 
34 typedef struct consumer_sdl_s *consumer_sdl;
35 
36 struct consumer_sdl_s
37 {
38 	struct mlt_consumer_s parent;
39 	mlt_consumer active;
40 	int ignore_change;
41 	mlt_consumer play;
42 	mlt_consumer still;
43 	pthread_t thread;
44 	int joined;
45 	int running;
46 	int sdl_flags;
47 	double last_speed;
48 	mlt_position last_position;
49 
50 	pthread_cond_t refresh_cond;
51 	pthread_mutex_t refresh_mutex;
52 	int refresh_count;
53 };
54 
55 /** Forward references to static functions.
56 */
57 
58 static int consumer_start( mlt_consumer parent );
59 static int consumer_stop( mlt_consumer parent );
60 static int consumer_is_stopped( mlt_consumer parent );
61 static void consumer_purge( mlt_consumer parent );
62 static void consumer_close( mlt_consumer parent );
63 static void *consumer_thread( void * );
64 static void consumer_frame_show_cb( mlt_consumer sdl, mlt_consumer self, mlt_frame frame );
65 static void consumer_sdl_event_cb( mlt_consumer sdl, mlt_consumer self, SDL_Event *event );
66 static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer self, char *name );
67 
consumer_sdl_preview_init(mlt_profile profile,mlt_service_type type,const char * id,char * arg)68 mlt_consumer consumer_sdl_preview_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
69 {
70 	consumer_sdl self = calloc( 1, sizeof( struct consumer_sdl_s ) );
71 	if ( self != NULL && mlt_consumer_init( &self->parent, self, profile ) == 0 )
72 	{
73 		// Get the parent consumer object
74 		mlt_consumer parent = &self->parent;
75 
76 		// Get the properties
77 		mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
78 
79 		// Get the width and height
80 		int width = mlt_properties_get_int( properties, "width" );
81 		int height = mlt_properties_get_int( properties, "height" );
82 
83 		// Process actual param
84 		if ( arg == NULL || sscanf( arg, "%dx%d", &width, &height ) == 2 )
85 		{
86 			mlt_properties_set_int( properties, "width", width );
87 			mlt_properties_set_int( properties, "height", height );
88 		}
89 
90 		// Create child consumers
91 		self->play = mlt_factory_consumer( profile, "sdl", arg );
92 		self->still = mlt_factory_consumer( profile, "sdl_still", arg );
93 		mlt_properties_set( properties, "rescale", "nearest" );
94 		mlt_properties_set( properties, "deinterlace_method", "onefield" );
95 		mlt_properties_set_int( properties, "prefill", 1 );
96 		mlt_properties_set_int( properties, "top_field_first", -1 );
97 
98 		parent->close = consumer_close;
99 		parent->start = consumer_start;
100 		parent->stop = consumer_stop;
101 		parent->is_stopped = consumer_is_stopped;
102 		parent->purge = consumer_purge;
103 		self->joined = 1;
104 		mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->play ), self, "consumer-frame-show", ( mlt_listener )consumer_frame_show_cb );
105 		mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->still ), self, "consumer-frame-show", ( mlt_listener )consumer_frame_show_cb );
106 		mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->play ), self, "consumer-sdl-event", ( mlt_listener )consumer_sdl_event_cb );
107 		mlt_events_listen( MLT_CONSUMER_PROPERTIES( self->still ), self, "consumer-sdl-event", ( mlt_listener )consumer_sdl_event_cb );
108 		pthread_cond_init( &self->refresh_cond, NULL );
109 		pthread_mutex_init( &self->refresh_mutex, NULL );
110 		mlt_events_listen( MLT_CONSUMER_PROPERTIES( parent ), self, "property-changed", ( mlt_listener )consumer_refresh_cb );
111 		mlt_events_register( properties, "consumer-sdl-paused", NULL );
112 		return parent;
113 	}
114 	free( self );
115 	return NULL;
116 }
117 
consumer_frame_show_cb(mlt_consumer sdl,mlt_consumer parent,mlt_frame frame)118 void consumer_frame_show_cb( mlt_consumer sdl, mlt_consumer parent, mlt_frame frame )
119 {
120 	consumer_sdl self = parent->child;
121 	self->last_speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" );
122 	self->last_position = mlt_frame_get_position( frame );
123 	mlt_events_fire( MLT_CONSUMER_PROPERTIES( parent ), "consumer-frame-show", frame, NULL );
124 }
125 
consumer_sdl_event_cb(mlt_consumer sdl,mlt_consumer parent,SDL_Event * event)126 static void consumer_sdl_event_cb( mlt_consumer sdl, mlt_consumer parent, SDL_Event *event )
127 {
128 	mlt_events_fire( MLT_CONSUMER_PROPERTIES( parent ), "consumer-sdl-event", event, NULL );
129 }
130 
consumer_refresh_cb(mlt_consumer sdl,mlt_consumer parent,char * name)131 static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer parent, char *name )
132 {
133 	if ( !strcmp( name, "refresh" ) )
134 	{
135 		consumer_sdl self = parent->child;
136 		pthread_mutex_lock( &self->refresh_mutex );
137 		self->refresh_count = self->refresh_count <= 0 ? 1 : self->refresh_count + 1;
138 		pthread_cond_broadcast( &self->refresh_cond );
139 		pthread_mutex_unlock( &self->refresh_mutex );
140 	}
141 }
142 
consumer_start(mlt_consumer parent)143 static int consumer_start( mlt_consumer parent )
144 {
145 	consumer_sdl self = parent->child;
146 
147 	if ( !self->running )
148 	{
149 		// properties
150 		mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
151 		mlt_properties play = MLT_CONSUMER_PROPERTIES( self->play );
152 		mlt_properties still = MLT_CONSUMER_PROPERTIES( self->still );
153 
154 		char *window_id = mlt_properties_get( properties, "window_id" );
155 		char *audio_driver = mlt_properties_get( properties, "audio_driver" );
156 		char *video_driver = mlt_properties_get( properties, "video_driver" );
157 		char *audio_device = mlt_properties_get( properties, "audio_device" );
158 		char *output_display = mlt_properties_get( properties, "output_display" );
159 		int progressive = mlt_properties_get_int( properties, "progressive" ) | mlt_properties_get_int( properties, "deinterlace" );
160 
161 		consumer_stop( parent );
162 
163 		self->running = 1;
164 		self->joined = 0;
165 		self->last_speed = 1;
166 
167 		if ( output_display != NULL )
168 			setenv( "DISPLAY", output_display, 1 );
169 
170 		if ( window_id != NULL )
171 			setenv( "SDL_WINDOWID", window_id, 1 );
172 
173 		if ( video_driver != NULL )
174 			setenv( "SDL_VIDEODRIVER", video_driver, 1 );
175 
176 		if ( audio_driver != NULL )
177 			setenv( "SDL_AUDIODRIVER", audio_driver, 1 );
178 
179 		if ( audio_device != NULL )
180 			setenv( "AUDIODEV", audio_device, 1 );
181 
182 		pthread_mutex_lock( &mlt_sdl_mutex );
183 		int ret = SDL_Init( SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE );
184 		pthread_mutex_unlock( &mlt_sdl_mutex );
185 		if ( ret < 0 )
186 		{
187 			fprintf( stderr, "Failed to initialize SDL: %s\n", SDL_GetError() );
188 			return -1;
189 		}
190 
191 		SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
192 		SDL_EnableUNICODE( 1 );
193 
194 		// Pass properties down
195 		mlt_properties_set_data( play, "transport_producer", mlt_properties_get_data( properties, "transport_producer", NULL ), 0, NULL, NULL );
196 		mlt_properties_set_data( still, "transport_producer", mlt_properties_get_data( properties, "transport_producer", NULL ), 0, NULL, NULL );
197 		mlt_properties_set_data( play, "transport_callback", mlt_properties_get_data( properties, "transport_callback", NULL ), 0, NULL, NULL );
198 		mlt_properties_set_data( still, "transport_callback", mlt_properties_get_data( properties, "transport_callback", NULL ), 0, NULL, NULL );
199 
200 		mlt_properties_set_int( play, "progressive", progressive );
201 		mlt_properties_set_int( still, "progressive", progressive );
202 
203 		mlt_properties_pass_list( play, properties,
204 			"deinterlace_method,resize,rescale,width,height,aspect_ratio,display_ratio,preview_off,preview_format,window_background"
205 			",top_field_first,volume,real_time,buffer,prefill,audio_off,frequency,drop_max" );
206 		mlt_properties_pass_list( still, properties,
207 			"deinterlace_method,resize,rescale,width,height,aspect_ratio,display_ratio,preview_off,preview_format,window_background"
208 			",top_field_first");
209 
210 		mlt_properties_pass( play, properties, "play." );
211 		mlt_properties_pass( still, properties, "still." );
212 
213 		mlt_properties_set_data( play, "app_lock", mlt_properties_get_data( properties, "app_lock", NULL ), 0, NULL, NULL );
214 		mlt_properties_set_data( still, "app_lock", mlt_properties_get_data( properties, "app_lock", NULL ), 0, NULL, NULL );
215 		mlt_properties_set_data( play, "app_unlock", mlt_properties_get_data( properties, "app_unlock", NULL ), 0, NULL, NULL );
216 		mlt_properties_set_data( still, "app_unlock", mlt_properties_get_data( properties, "app_unlock", NULL ), 0, NULL, NULL );
217 
218 		mlt_properties_set_int( play, "put_mode", 1 );
219 		mlt_properties_set_int( still, "put_mode", 1 );
220 		mlt_properties_set_int( play, "terminate_on_pause", 1 );
221 
222 		// Start the still producer just to initialise the gui
223 		mlt_consumer_start( self->still );
224 		self->active = self->still;
225 
226 		// Inform child consumers that we control the sdl
227 		mlt_properties_set_int( play, "sdl_started", 1 );
228 		mlt_properties_set_int( still, "sdl_started", 1 );
229 
230 		pthread_create( &self->thread, NULL, consumer_thread, self );
231 	}
232 
233 	return 0;
234 }
235 
consumer_stop(mlt_consumer parent)236 static int consumer_stop( mlt_consumer parent )
237 {
238 	// Get the actual object
239 	consumer_sdl self = parent->child;
240 
241 	if ( self->joined == 0 )
242 	{
243 		mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
244 		int app_locked = mlt_properties_get_int( properties, "app_locked" );
245 		void ( *lock )( void ) = mlt_properties_get_data( properties, "app_lock", NULL );
246 		void ( *unlock )( void ) = mlt_properties_get_data( properties, "app_unlock", NULL );
247 
248 		if ( app_locked && unlock ) unlock( );
249 
250 		// Kill the thread and clean up
251 		self->running = 0;
252 
253 		pthread_mutex_lock( &self->refresh_mutex );
254 		pthread_cond_broadcast( &self->refresh_cond );
255 		pthread_mutex_unlock( &self->refresh_mutex );
256 #ifndef _WIN32
257 		if ( self->thread )
258 #endif
259 			pthread_join( self->thread, NULL );
260 		self->joined = 1;
261 
262 		if ( app_locked && lock ) lock( );
263 
264 		pthread_mutex_lock( &mlt_sdl_mutex );
265 		SDL_Quit( );
266 		pthread_mutex_unlock( &mlt_sdl_mutex );
267 	}
268 
269 	return 0;
270 }
271 
consumer_is_stopped(mlt_consumer parent)272 static int consumer_is_stopped( mlt_consumer parent )
273 {
274 	consumer_sdl self = parent->child;
275 	return !self->running;
276 }
277 
consumer_purge(mlt_consumer parent)278 void consumer_purge( mlt_consumer parent )
279 {
280 	consumer_sdl self = parent->child;
281 	if ( self->running )
282 		mlt_consumer_purge( self->play );
283 }
284 
consumer_thread(void * arg)285 static void *consumer_thread( void *arg )
286 {
287 	// Identify the arg
288 	consumer_sdl self = arg;
289 
290 	// Get the consumer
291 	mlt_consumer consumer = &self->parent;
292 
293 	// Get the properties
294 	mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
295 
296 	// internal initialization
297 	mlt_frame frame = NULL;
298 	int last_position = -1;
299 	int eos = 0;
300 	int eos_threshold = 20;
301 	if ( self->play )
302 		eos_threshold = eos_threshold + mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( self->play ), "buffer" );
303 
304 	// Determine if the application is dealing with the preview
305 	int preview_off = mlt_properties_get_int( properties, "preview_off" );
306 
307 	pthread_mutex_lock( &self->refresh_mutex );
308 	self->refresh_count = 0;
309 	pthread_mutex_unlock( &self->refresh_mutex );
310 
311 	// Loop until told not to
312 	while( self->running )
313 	{
314 		// Get a frame from the attached producer
315 		frame = mlt_consumer_get_frame( consumer );
316 
317 		// Ensure that we have a frame
318 		if ( self->running && frame != NULL )
319 		{
320 			// Get the speed of the frame
321 			double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" );
322 
323 			// Lock during the operation
324 			mlt_service_lock( MLT_CONSUMER_SERVICE( consumer ) );
325 
326 			// Get refresh request for the current frame
327 			int refresh = mlt_properties_get_int( properties, "refresh" );
328 
329 			// Decrement refresh and clear changed
330 			mlt_events_block( properties, properties );
331 			mlt_properties_set_int( properties, "refresh", 0 );
332 			mlt_events_unblock( properties, properties );
333 
334 			// Unlock after the operation
335 			mlt_service_unlock( MLT_CONSUMER_SERVICE( consumer ) );
336 
337 			// Set the changed property on this frame for the benefit of still
338 			mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "refresh", refresh );
339 
340 			// Make sure the recipient knows that this frame isn't really rendered
341 			mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 0 );
342 
343 			// Optimisation to reduce latency
344 			if ( speed == 1.0 )
345 			{
346 				if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) )
347 					mlt_consumer_purge( self->play );
348 				last_position = mlt_frame_get_position( frame );
349 			}
350 			else
351 			{
352 				//mlt_consumer_purge( self->play );
353 				last_position = -1;
354 			}
355 
356 			// If we aren't playing normally, then use the still
357 			if ( speed != 1 )
358 			{
359 				mlt_producer producer = MLT_PRODUCER( mlt_service_get_producer( MLT_CONSUMER_SERVICE( consumer ) ) );
360 				mlt_position duration = producer? mlt_producer_get_playtime( producer ) : -1;
361 				int pause = 0;
362 
363 #ifndef SKIP_WAIT_EOS
364 				if ( self->active == self->play )
365 				{
366 					// Do not interrupt the play consumer near the end
367 					if ( duration - self->last_position > eos_threshold )
368 					{
369 						// Get a new frame at the sought position
370 						mlt_frame_close( frame );
371 						if ( producer )
372 							mlt_producer_seek( producer, self->last_position );
373 						frame = mlt_consumer_get_frame( consumer );
374 						pause = 1;
375 					}
376 					else
377 					{
378 						// Send frame with speed 0 to stop it
379 						if ( frame && !mlt_consumer_is_stopped( self->play ) )
380 						{
381 							mlt_consumer_put_frame( self->play, frame );
382 							frame = NULL;
383 							eos = 1;
384 						}
385 
386 						// Check for end of stream
387 						if ( mlt_consumer_is_stopped( self->play ) )
388 						{
389 							// Stream has ended
390 							mlt_log_verbose( MLT_CONSUMER_SERVICE( consumer ), "END OF STREAM\n" );
391 							pause = 1;
392 							eos = 0; // reset eos indicator
393 						}
394 						else
395 						{
396 							// Prevent a tight busy loop
397 							struct timespec tm = { 0, 100000L }; // 100 usec
398 							nanosleep( &tm, NULL );
399 						}
400 					}
401 				}
402 #else
403 				pause = self->active == self->play;
404 #endif
405 				if ( pause )
406 				{
407 					// Start the still consumer
408 					if ( !mlt_consumer_is_stopped( self->play ) )
409 						mlt_consumer_stop( self->play );
410 					self->last_speed = speed;
411 					self->active = self->still;
412 					self->ignore_change = 0;
413 					mlt_consumer_start( self->still );
414 				}
415 				// Send the frame to the active child
416 				if ( frame && !eos )
417 				{
418 					mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "refresh", 1 );
419 					if ( self->active )
420 						mlt_consumer_put_frame( self->active, frame );
421 				}
422 				if ( pause && speed == 0.0 )
423 				{
424 					mlt_events_fire( properties, "consumer-sdl-paused", NULL );
425 				}
426 			}
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 ) )
429 			{
430 				mlt_consumer_put_frame( self->active, frame );
431 			}
432 			// Otherwise use the normal player
433 			else
434 			{
435 				if ( !mlt_consumer_is_stopped( self->still ) )
436 					mlt_consumer_stop( self->still );
437 				if ( mlt_consumer_is_stopped( self->play ) )
438 				{
439 					self->last_speed = speed;
440 					self->active = self->play;
441 					self->ignore_change = 0;
442 					mlt_consumer_start( self->play );
443 				}
444 				if ( self->play )
445 					mlt_consumer_put_frame( self->play, frame );
446 			}
447 
448 			// Copy the rectangle info from the active consumer
449 			if ( self->running && preview_off == 0 && self->active )
450 			{
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 ) );
458 			}
459 
460 			if ( self->active == self->still )
461 			{
462 				pthread_mutex_lock( &self->refresh_mutex );
463 				if ( self->running && speed == 0 && self->refresh_count <= 0 )
464 				{
465 					mlt_events_fire( properties, "consumer-sdl-paused", NULL );
466 					pthread_cond_wait( &self->refresh_cond, &self->refresh_mutex );
467 				}
468 				self->refresh_count --;
469 				pthread_mutex_unlock( &self->refresh_mutex );
470 			}
471 		}
472 		else
473 		{
474 			if ( frame ) mlt_frame_close( frame );
475 			mlt_consumer_put_frame( self->active, NULL );
476 			self->running = 0;
477 		}
478 	}
479 
480 	if ( self->play ) mlt_consumer_stop( self->play );
481 	if ( self->still ) mlt_consumer_stop( self->still );
482 
483 	return NULL;
484 }
485 
486 /** Callback to allow override of the close method.
487 */
488 
consumer_close(mlt_consumer parent)489 static void consumer_close( mlt_consumer parent )
490 {
491 	// Get the actual object
492 	consumer_sdl self = parent->child;
493 
494 	// Stop the consumer
495 	mlt_consumer_stop( parent );
496 
497 	// Close the child consumers
498 	mlt_consumer_close( self->play );
499 	mlt_consumer_close( self->still );
500 
501 	// Now clean up the rest
502 	mlt_consumer_close( parent );
503 
504 	// Finally clean up self
505 	free( self );
506 }
507