1 /*
2  * consumer_sdl2_audio.c -- A Simple DirectMedia Layer audio-only consumer
3  * Copyright (C) 2009-2020 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 "common.h"
21 
22 #include <framework/mlt_consumer.h>
23 #include <framework/mlt_frame.h>
24 #include <framework/mlt_deque.h>
25 #include <framework/mlt_factory.h>
26 #include <framework/mlt_filter.h>
27 #include <framework/mlt_log.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <pthread.h>
32 #include <SDL.h>
33 #include <sys/time.h>
34 #include <stdatomic.h>
35 
36 extern pthread_mutex_t mlt_sdl_mutex;
37 
38 /** This classes definition.
39 */
40 
41 typedef struct consumer_sdl_s *consumer_sdl;
42 
43 struct consumer_sdl_s
44 {
45 	struct mlt_consumer_s parent;
46 	mlt_properties properties;
47 	mlt_deque queue;
48 	pthread_t thread;
49 	int joined;
50 	atomic_int running;
51 	uint8_t audio_buffer[ 4096 * 10 ];
52 	int audio_avail;
53 	pthread_mutex_t audio_mutex;
54 	pthread_cond_t audio_cond;
55 	pthread_mutex_t video_mutex;
56 	pthread_cond_t video_cond;
57 	int out_channels;
58 	atomic_int playing;
59 
60 	pthread_cond_t refresh_cond;
61 	pthread_mutex_t refresh_mutex;
62 	int refresh_count;
63 	int is_purge;
64 #ifdef _WIN32
65 	int no_quit_subsystem;
66 #endif
67 };
68 
69 /** Forward references to static functions.
70 */
71 
72 static int consumer_start( mlt_consumer parent );
73 static int consumer_stop( mlt_consumer parent );
74 static int consumer_is_stopped( mlt_consumer parent );
75 static void consumer_purge( mlt_consumer parent );
76 static void consumer_close( mlt_consumer parent );
77 static void *consumer_thread( void * );
78 static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer self, char *name );
79 
80 /** This is what will be called by the factory - anything can be passed in
81 	via the argument, but keep it simple.
82 */
83 
consumer_sdl2_audio_init(mlt_profile profile,mlt_service_type type,const char * id,char * arg)84 mlt_consumer consumer_sdl2_audio_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
85 {
86 	// Create the consumer object
87 	consumer_sdl self = calloc( 1, sizeof( struct consumer_sdl_s ) );
88 
89 	// If no malloc'd and consumer init ok
90 	if ( self != NULL && mlt_consumer_init( &self->parent, self, profile ) == 0 )
91 	{
92 		// Create the queue
93 		self->queue = mlt_deque_init( );
94 
95 		// Get the parent consumer object
96 		mlt_consumer parent = &self->parent;
97 
98 		// We have stuff to clean up, so override the close method
99 		parent->close = consumer_close;
100 
101 		// get a handle on properties
102 		mlt_service service = MLT_CONSUMER_SERVICE( parent );
103 		self->properties = MLT_SERVICE_PROPERTIES( service );
104 
105 		// Set the default volume
106 		mlt_properties_set_double( self->properties, "volume", 1.0 );
107 
108 		// This is the initialisation of the consumer
109 		pthread_mutex_init( &self->audio_mutex, NULL );
110 		pthread_cond_init( &self->audio_cond, NULL);
111 		pthread_mutex_init( &self->video_mutex, NULL );
112 		pthread_cond_init( &self->video_cond, NULL);
113 
114 		// Default scaler (for now we'll use nearest)
115 		mlt_properties_set( self->properties, "rescale", "nearest" );
116 		mlt_properties_set( self->properties, "deinterlace_method", "onefield" );
117 		mlt_properties_set_int( self->properties, "top_field_first", -1 );
118 
119 		// Default buffer for low latency
120 		mlt_properties_set_int( self->properties, "buffer", 1 );
121 
122 		// Default audio buffer
123 		mlt_properties_set_int( self->properties, "audio_buffer", 2048 );
124 
125 		// Ensure we don't join on a non-running object
126 		self->joined = 1;
127 
128 		// Allow thread to be started/stopped
129 		parent->start = consumer_start;
130 		parent->stop = consumer_stop;
131 		parent->is_stopped = consumer_is_stopped;
132 		parent->purge = consumer_purge;
133 
134 		// Initialize the refresh handler
135 		pthread_cond_init( &self->refresh_cond, NULL );
136 		pthread_mutex_init( &self->refresh_mutex, NULL );
137 		mlt_events_listen( MLT_CONSUMER_PROPERTIES( parent ), self, "property-changed", ( mlt_listener )consumer_refresh_cb );
138 
139 		// Return the consumer produced
140 		return parent;
141 	}
142 
143 	// malloc or consumer init failed
144 	free( self );
145 
146 	// Indicate failure
147 	return NULL;
148 }
149 
consumer_refresh_cb(mlt_consumer sdl,mlt_consumer parent,char * name)150 static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer parent, char *name )
151 {
152 	if ( !strcmp( name, "refresh" ) )
153 	{
154 		consumer_sdl self = parent->child;
155 		pthread_mutex_lock( &self->refresh_mutex );
156 		if ( self->refresh_count < 2 )
157 			self->refresh_count = self->refresh_count <= 0 ? 1 : self->refresh_count + 1;
158 		pthread_cond_broadcast( &self->refresh_cond );
159 		pthread_mutex_unlock( &self->refresh_mutex );
160 	}
161 }
162 
consumer_start(mlt_consumer parent)163 int consumer_start( mlt_consumer parent )
164 {
165 	consumer_sdl self = parent->child;
166 
167 	if ( !self->running )
168 	{
169 		consumer_stop( parent );
170 
171 		mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
172 		char *audio_driver = mlt_properties_get( properties, "audio_driver" );
173 		char *audio_device = mlt_properties_get( properties, "audio_device" );
174 
175 		if ( audio_driver && strcmp( audio_driver, "" ) )
176 			setenv( "SDL_AUDIODRIVER", audio_driver, 1 );
177 
178 		if ( audio_device && strcmp( audio_device, "" ) )
179 			setenv( "AUDIODEV", audio_device, 1 );
180 
181 		pthread_mutex_lock( &mlt_sdl_mutex );
182 		int ret = SDL_Init( SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE );
183 		pthread_mutex_unlock( &mlt_sdl_mutex );
184 		if ( ret < 0 )
185 		{
186 			mlt_log_error( MLT_CONSUMER_SERVICE(parent), "Failed to initialize SDL: %s\n", SDL_GetError() );
187 			return -1;
188 		}
189 		self->running = 1;
190 		self->joined = 0;
191 		pthread_create( &self->thread, NULL, consumer_thread, self );
192 	}
193 
194 	return 0;
195 }
196 
consumer_stop(mlt_consumer parent)197 int consumer_stop( mlt_consumer parent )
198 {
199 	// Get the actual object
200 	consumer_sdl self = parent->child;
201 
202 	if ( self->running && !self->joined )
203 	{
204 		// Kill the thread and clean up
205 		self->joined = 1;
206 		self->running = 0;
207 
208 		// Unlatch the consumer thread
209 		pthread_mutex_lock( &self->refresh_mutex );
210 		pthread_cond_broadcast( &self->refresh_cond );
211 		pthread_mutex_unlock( &self->refresh_mutex );
212 
213 		// Cleanup the main thread
214 #ifndef _WIN32
215 		if ( self->thread )
216 #endif
217 			pthread_join( self->thread, NULL );
218 
219 		// Unlatch the video thread
220 		pthread_mutex_lock( &self->video_mutex );
221 		pthread_cond_broadcast( &self->video_cond );
222 		pthread_mutex_unlock( &self->video_mutex );
223 
224 		// Unlatch the audio callback
225 		pthread_mutex_lock( &self->audio_mutex );
226 		pthread_cond_broadcast( &self->audio_cond );
227 		pthread_mutex_unlock( &self->audio_mutex );
228 #ifdef _WIN32
229 		if ( !self->no_quit_subsystem )
230 #endif
231 		SDL_QuitSubSystem( SDL_INIT_AUDIO );
232 	}
233 
234 	return 0;
235 }
236 
consumer_is_stopped(mlt_consumer parent)237 int consumer_is_stopped( mlt_consumer parent )
238 {
239 	consumer_sdl self = parent->child;
240 	return !self->running;
241 }
242 
consumer_purge(mlt_consumer parent)243 void consumer_purge( mlt_consumer parent )
244 {
245 	consumer_sdl self = parent->child;
246 	if ( self->running )
247 	{
248 		pthread_mutex_lock( &self->video_mutex );
249 		mlt_frame frame = MLT_FRAME( mlt_deque_peek_back( self->queue ) );
250 		// When playing rewind or fast forward then we need to keep one
251 		// frame in the queue to prevent playback stalling.
252 		double speed = frame? mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" ) : 0;
253 		int n = ( speed == 0.0 || speed == 1.0 ) ? 0 : 1;
254 		while ( mlt_deque_count( self->queue ) > n )
255 			mlt_frame_close( mlt_deque_pop_back( self->queue ) );
256 		self->is_purge = 1;
257 		pthread_cond_broadcast( &self->video_cond );
258 		pthread_mutex_unlock( &self->video_mutex );
259 	}
260 }
261 
sdl_fill_audio(void * udata,uint8_t * stream,int len)262 static void sdl_fill_audio( void *udata, uint8_t *stream, int len )
263 {
264 	consumer_sdl self = udata;
265 
266 	// Get the volume
267 	double volume = mlt_properties_get_double( self->properties, "volume" );
268 
269 	// Wipe the stream first
270 	memset( stream, 0, len );
271 
272 	pthread_mutex_lock( &self->audio_mutex );
273 	int bytes = MIN(len, self->audio_avail);
274 
275 	// Place in the audio buffer
276 	if ( volume != 1.0 ) {
277 		// Adjust the volume while copying.
278 		int16_t *src = (int16_t*) self->audio_buffer;
279 		int16_t *dst = (int16_t*) stream;
280 		int i = bytes / sizeof(*dst) + 1;
281 		while (--i) {
282 			*dst++ = CLAMP(volume * src[0], -32768, 32767);
283 			src++;
284 		}
285 	} else {
286 		memcpy( stream, self->audio_buffer, bytes );
287 	}
288 
289 	// Remove len from the audio available
290 	self->audio_avail -= bytes;
291 
292 	// Remove the samples
293 	memmove( self->audio_buffer, self->audio_buffer + bytes, self->audio_avail );
294 
295 	// We're definitely playing now
296 	self->playing = 1;
297 
298 	pthread_cond_broadcast( &self->audio_cond );
299 	pthread_mutex_unlock( &self->audio_mutex );
300 }
301 
consumer_play_audio(consumer_sdl self,mlt_frame frame,int init_audio,int64_t * duration)302 static int consumer_play_audio( consumer_sdl self, mlt_frame frame, int init_audio, int64_t *duration )
303 {
304 	// Get the properties of self consumer
305 	mlt_properties properties = self->properties;
306 	mlt_audio_format afmt = mlt_audio_s16;
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 	int scrub = mlt_properties_get_int( properties, "scrub_audio" );
312 	static int counter = 0;
313 
314 	int samples = mlt_audio_calculate_frame_samples( mlt_properties_get_double( self->properties, "fps" ), frequency, counter++ );
315 	int16_t *pcm;
316 	mlt_frame_get_audio( frame, (void**) &pcm, &afmt, &frequency, &channels, &samples );
317 	*duration = 1000000LL * samples / frequency;
318 	pcm += mlt_properties_get_int( properties, "audio_offset" );
319 
320 	if ( mlt_properties_get_int( properties, "audio_off" ) )
321 	{
322 		self->playing = 1;
323 		init_audio = 1;
324 		return init_audio;
325 	}
326 
327 	if ( init_audio == 1 )
328 	{
329 		SDL_AudioSpec request;
330 		SDL_AudioSpec got;
331 		SDL_AudioDeviceID dev;
332 		int audio_buffer = mlt_properties_get_int( properties, "audio_buffer" );
333 
334 		// specify audio format
335 		memset( &request, 0, sizeof( SDL_AudioSpec ) );
336 		self->playing = 0;
337 		request.freq = frequency;
338 		request.format = AUDIO_S16SYS;
339 		request.channels = mlt_properties_get_int( properties, "channels" );
340 		request.samples = audio_buffer;
341 		request.callback = sdl_fill_audio;
342 		request.userdata = (void *)self;
343 
344 		dev = sdl2_open_audio( &request, &got );
345 		if( dev == 0 )
346 		{
347 			mlt_log_error( MLT_CONSUMER_SERVICE( self ), "SDL failed to open audio\n" );
348 			init_audio = 2;
349 		}
350 		else
351 		{
352 			if( got.channels != request.channels )
353 			{
354 				mlt_log_info( MLT_CONSUMER_SERVICE( self ), "Unable to output %d channels. Change to %d\n", request.channels, got.channels );
355 			}
356 				mlt_log_info( MLT_CONSUMER_SERVICE( self ), "Audio Opened: driver=%s channels=%d frequency=%d\n", SDL_GetCurrentAudioDriver(), got.channels, got.freq );
357 			SDL_PauseAudioDevice( dev, 0 );
358 			init_audio = 0;
359 			self->out_channels = got.channels;
360 		}
361 	}
362 
363 	if ( init_audio == 0 )
364 	{
365 		mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
366 		int samples_copied = 0;
367 		int dst_stride = self->out_channels * sizeof( *pcm );
368 
369 		pthread_mutex_lock( &self->audio_mutex );
370 
371 		while ( self->running && samples_copied < samples )
372 		{
373 			int sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride;
374 			while ( self->running && sample_space == 0 )
375 			{
376 				struct timeval now;
377 				struct timespec tm;
378 
379 				gettimeofday( &now, NULL );
380 				tm.tv_sec = now.tv_sec + 1;
381 				tm.tv_nsec = now.tv_usec * 1000;
382 				pthread_cond_timedwait( &self->audio_cond, &self->audio_mutex, &tm );
383 				sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride;
384 
385 				if ( sample_space == 0 )
386 				{
387 					mlt_log_warning( MLT_CONSUMER_SERVICE(&self->parent), "audio timed out\n" );
388 					pthread_mutex_unlock( &self->audio_mutex );
389 #ifdef _WIN32
390 					self->no_quit_subsystem = 1;
391 #endif
392 					return 1;
393 				}
394 			}
395 			if ( self->running )
396 			{
397 				int samples_to_copy = samples - samples_copied;
398 				if ( samples_to_copy > sample_space )
399 				{
400 					samples_to_copy = sample_space;
401 				}
402 				int dst_bytes = samples_to_copy * dst_stride;
403 
404 				if ( scrub || mlt_properties_get_double( properties, "_speed" ) == 1 )
405 				{
406 					if ( channels == self->out_channels )
407 					{
408 						memcpy( &self->audio_buffer[ self->audio_avail ], pcm, dst_bytes );
409 						pcm += samples_to_copy * channels;
410 					}
411 					else
412 					{
413 						int16_t *dest = (int16_t*) &self->audio_buffer[ self->audio_avail ];
414 						int i = samples_to_copy + 1;
415 						while ( --i )
416 						{
417 							memcpy( dest, pcm, dst_stride );
418 							pcm += channels;
419 							dest += self->out_channels;
420 						}
421 					}
422 				}
423 				else
424 				{
425 					memset( &self->audio_buffer[ self->audio_avail ], 0, dst_bytes );
426 					pcm += samples_to_copy * channels;
427 				}
428 				self->audio_avail += dst_bytes;
429 				samples_copied += samples_to_copy;
430 			}
431 			pthread_cond_broadcast( &self->audio_cond );
432 		}
433 		pthread_mutex_unlock( &self->audio_mutex );
434 	}
435 	else
436 	{
437 		self->playing = 1;
438 	}
439 
440 	return init_audio;
441 }
442 
consumer_play_video(consumer_sdl self,mlt_frame frame)443 static int consumer_play_video( consumer_sdl self, mlt_frame frame )
444 {
445 	// Get the properties of this consumer
446 	mlt_properties properties = self->properties;
447 	mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
448 	return 0;
449 }
450 
video_thread(void * arg)451 static void *video_thread( void *arg )
452 {
453 	// Identify the arg
454 	consumer_sdl self = arg;
455 
456 	// Obtain time of thread start
457 	struct timeval now;
458 	int64_t start = 0;
459 	int64_t elapsed = 0;
460 	struct timespec tm;
461 	mlt_frame next = NULL;
462 	mlt_properties properties = NULL;
463 	double speed = 0;
464 
465 	// Get real time flag
466 	int real_time = mlt_properties_get_int( self->properties, "real_time" );
467 
468 	// Get the current time
469 	gettimeofday( &now, NULL );
470 
471 	// Determine start time
472 	start = ( int64_t )now.tv_sec * 1000000 + now.tv_usec;
473 
474 	while ( self->running )
475 	{
476 		// Pop the next frame
477 		pthread_mutex_lock( &self->video_mutex );
478 		next = mlt_deque_pop_front( self->queue );
479 		while ( next == NULL && self->running )
480 		{
481 			pthread_cond_wait( &self->video_cond, &self->video_mutex );
482 			next = mlt_deque_pop_front( self->queue );
483 		}
484 		pthread_mutex_unlock( &self->video_mutex );
485 
486 		if ( !self->running || next == NULL ) break;
487 
488 		// Get the properties
489 		properties =  MLT_FRAME_PROPERTIES( next );
490 
491 		// Get the speed of the frame
492 		speed = mlt_properties_get_double( properties, "_speed" );
493 
494 		// Get the current time
495 		gettimeofday( &now, NULL );
496 
497 		// Get the elapsed time
498 		elapsed = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - start;
499 
500 		// See if we have to delay the display of the current frame
501 		if ( mlt_properties_get_int( properties, "rendered" ) == 1 )
502 		{
503 			// Obtain the scheduled playout time in microseconds
504 			int64_t scheduled = mlt_properties_get_int64( properties, "playtime" );
505 
506 			// Determine the difference between the elapsed time and the scheduled playout time
507 			int64_t difference = scheduled - elapsed;
508 
509 			// Smooth playback a bit
510 			if ( real_time && ( difference > 20000 && speed == 1.0 ) )
511 			{
512 				tm.tv_sec = difference / 1000000;
513 				tm.tv_nsec = ( difference % 1000000 ) * 1000;
514 				nanosleep( &tm, NULL );
515 			}
516 
517 			// Show current frame if not too old
518 			if ( !real_time || ( difference > -10000 || speed != 1.0 || mlt_deque_count( self->queue ) < 2 ) )
519 				consumer_play_video( self, next );
520 
521 			// If the queue is empty, recalculate start to allow build up again
522 			if ( real_time && ( mlt_deque_count( self->queue ) == 0 && speed == 1.0 ) )
523 			{
524 				gettimeofday( &now, NULL );
525 				start = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - scheduled + 20000;
526 				start += mlt_properties_get_int(self->properties, "video_delay") * 1000;
527 			}
528 		}
529 
530 		// This frame can now be closed
531 		mlt_frame_close( next );
532 		next = NULL;
533 	}
534 
535 	// This consumer is stopping. But audio has already been played for all
536 	// the frames in the queue. Spit out all the frames so that the display has
537 	// the option to catch up with the audio.
538 	if ( next != NULL ) {
539 		consumer_play_video( self, next );
540 		mlt_frame_close( next );
541 		next = NULL;
542 	}
543 	while ( mlt_deque_count( self->queue ) > 0 ) {
544 		next = mlt_deque_pop_front( self->queue );
545 		consumer_play_video( self, next );
546 		mlt_frame_close( next );
547 		next = NULL;
548 	}
549 
550 	mlt_consumer_stopped( &self->parent );
551 
552 	return NULL;
553 }
554 
555 /** Threaded wrapper for pipe.
556 */
557 
consumer_thread(void * arg)558 static void *consumer_thread( void *arg )
559 {
560 	// Identify the arg
561 	consumer_sdl self = arg;
562 
563 	// Get the consumer
564 	mlt_consumer consumer = &self->parent;
565 
566 	// Get the properties
567 	mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES( consumer );
568 
569 	// Video thread
570 	pthread_t thread;
571 
572 	// internal initialization
573 	int init_audio = 1;
574 	int init_video = 1;
575 	mlt_frame frame = NULL;
576 	mlt_properties properties = NULL;
577 	int64_t duration = 0;
578 	int64_t playtime = mlt_properties_get_int(consumer_props, "video_delay") * 1000;
579 	struct timespec tm = { 0, 100000 };
580 //	int last_position = -1;
581 
582 	pthread_mutex_lock( &self->refresh_mutex );
583 	self->refresh_count = 0;
584 	pthread_mutex_unlock( &self->refresh_mutex );
585 
586 	// Loop until told not to
587 	while( self->running )
588 	{
589 		// Get a frame from the attached producer
590 		frame = mlt_consumer_rt_frame( consumer );
591 
592 		// Ensure that we have a frame
593 		if ( frame )
594 		{
595 			// Get the frame properties
596 			properties =  MLT_FRAME_PROPERTIES( frame );
597 
598 			// Get the speed of the frame
599 			double speed = mlt_properties_get_double( properties, "_speed" );
600 
601 			// Clear refresh
602 			mlt_events_block( consumer_props, consumer_props );
603 			mlt_properties_set_int( consumer_props, "refresh", 0 );
604 			mlt_events_unblock( consumer_props, consumer_props );
605 
606 			// Play audio
607 			init_audio = consumer_play_audio( self, frame, init_audio, &duration );
608 
609 			// Determine the start time now
610 			if ( self->playing && init_video )
611 			{
612 				// Create the video thread
613 				pthread_create( &thread, NULL, video_thread, self );
614 
615 				// Video doesn't need to be initialised any more
616 				init_video = 0;
617 			}
618 
619 			// Set playtime for this frame in microseconds
620 			mlt_properties_set_int64( properties, "playtime", playtime );
621 
622 			while ( self->running && speed != 0 && mlt_deque_count( self->queue ) > 15 )
623 				nanosleep( &tm, NULL );
624 
625 			// Push this frame to the back of the queue
626 			if ( self->running && speed )
627 			{
628 				pthread_mutex_lock( &self->video_mutex );
629 				if ( self->is_purge && speed == 1.0 )
630 				{
631 					mlt_frame_close( frame );
632 					frame = NULL;
633 					self->is_purge = 0;
634 				}
635 				else
636 				{
637 					mlt_deque_push_back( self->queue, frame );
638 					pthread_cond_broadcast( &self->video_cond );
639 				}
640 				pthread_mutex_unlock( &self->video_mutex );
641 
642 				// Calculate the next playtime
643 				playtime += duration;
644 			}
645 			else if ( self->running )
646 			{
647 				pthread_mutex_lock( &self->refresh_mutex );
648 				consumer_play_video( self, frame );
649 				mlt_frame_close( frame );
650 				frame = NULL;
651 				self->refresh_count --;
652 				if ( self->refresh_count <= 0 )
653 				{
654 					pthread_cond_wait( &self->refresh_cond, &self->refresh_mutex );
655 				}
656 				pthread_mutex_unlock( &self->refresh_mutex );
657 			}
658 
659 			// Optimisation to reduce latency
660 			if ( speed == 1.0 )
661 			{
662                 // TODO: disabled due to misbehavior on parallel-consumer
663 //				if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) )
664 //					mlt_consumer_purge( consumer );
665 //				last_position = mlt_frame_get_position( frame );
666 			}
667 			else if (speed == 0.0)
668 			{
669 				mlt_consumer_purge( consumer );
670 			}
671 		}
672 	}
673 
674 	// Kill the video thread
675 	if ( init_video == 0 )
676 	{
677 		pthread_mutex_lock( &self->video_mutex );
678 		pthread_cond_broadcast( &self->video_cond );
679 		pthread_mutex_unlock( &self->video_mutex );
680 		pthread_join( thread, NULL );
681 	}
682 
683 	if ( frame )
684 	{
685 		// The video thread has cleared out the queue. But the audio was played
686 		// for this frame. So play the video before stopping so the display has
687 		// the option to catch up with the audio.
688 		consumer_play_video( self, frame );
689 		mlt_frame_close( frame );
690 		frame = NULL;
691 	}
692 
693 	pthread_mutex_lock( &self->audio_mutex );
694 	self->audio_avail = 0;
695 	pthread_mutex_unlock( &self->audio_mutex );
696 
697 	return NULL;
698 }
699 
700 /** Callback to allow override of the close method.
701 */
702 
consumer_close(mlt_consumer parent)703 static void consumer_close( mlt_consumer parent )
704 {
705 	// Get the actual object
706 	consumer_sdl self = parent->child;
707 
708 	// Stop the consumer
709 	mlt_consumer_stop( parent );
710 
711 	// Now clean up the rest
712 	mlt_consumer_close( parent );
713 
714 	// Close the queue
715 	mlt_deque_close( self->queue );
716 
717 	// Destroy mutexes
718 	pthread_mutex_destroy( &self->audio_mutex );
719 	pthread_cond_destroy( &self->audio_cond );
720 	pthread_mutex_destroy( &self->video_mutex );
721 	pthread_cond_destroy( &self->video_cond );
722 	pthread_mutex_destroy( &self->refresh_mutex );
723 	pthread_cond_destroy( &self->refresh_cond );
724 
725 	// Finally clean up this
726 	free( self );
727 }
728