]> git.sesse.net Git - mlt/blob - src/framework/mlt_consumer.c
Make the SDL consumer cooperate with VDPAU.
[mlt] / src / framework / mlt_consumer.c
1 /**
2  * \file mlt_consumer.c
3  * \brief abstraction for all consumer services
4  * \see mlt_consumer_s
5  *
6  * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
7  * \author Charles Yates <charles.yates@pandora.be>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "mlt_consumer.h"
25 #include "mlt_factory.h"
26 #include "mlt_producer.h"
27 #include "mlt_frame.h"
28 #include "mlt_profile.h"
29 #include "mlt_log.h"
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <sys/time.h>
35
36 /** Define this if you want an automatic deinterlace (if necessary) when the
37  * consumer's producer is not running at normal speed.
38  */
39 #undef DEINTERLACE_ON_NOT_NORMAL_SPEED
40
41 /** This is not the ideal place for this, but it is needed by VDPAU as well.
42  */
43 pthread_mutex_t mlt_sdl_mutex = PTHREAD_MUTEX_INITIALIZER;
44
45 static void mlt_consumer_frame_render( mlt_listener listener, mlt_properties owner, mlt_service this, void **args );
46 static void mlt_consumer_frame_show( mlt_listener listener, mlt_properties owner, mlt_service this, void **args );
47 static void mlt_consumer_property_changed( mlt_service owner, mlt_consumer this, char *name );
48 static void apply_profile_properties( mlt_consumer this, mlt_profile profile, mlt_properties properties );
49
50 /** Initialize a consumer service.
51  *
52  * \public \memberof mlt_consumer_s
53  * \param this the consumer to initialize
54  * \param child a pointer to the object for the subclass
55  * \param profile the \p mlt_profile_s to use (optional but recommended,
56  * uses the environment variable MLT if this is NULL)
57  * \return true if there was an error
58  */
59
60 int mlt_consumer_init( mlt_consumer this, void *child, mlt_profile profile )
61 {
62         int error = 0;
63         memset( this, 0, sizeof( struct mlt_consumer_s ) );
64         this->child = child;
65         error = mlt_service_init( &this->parent, this );
66         if ( error == 0 )
67         {
68                 // Get the properties from the service
69                 mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent );
70
71                 // Apply profile to properties
72                 if ( profile == NULL )
73                 {
74                         // Normally the application creates the profile and controls its lifetime
75                         // This is the fallback exception handling
76                         profile = mlt_profile_init( NULL );
77                         mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
78                         mlt_properties_set_data( properties, "_profile", profile, 0, (mlt_destructor)mlt_profile_close, NULL );
79                 }
80                 apply_profile_properties( this, profile, properties );
81
82                 // Default rescaler for all consumers
83                 mlt_properties_set( properties, "rescale", "bilinear" );
84
85                 // Default read ahead buffer size
86                 mlt_properties_set_int( properties, "buffer", 25 );
87
88                 // Default audio frequency and channels
89                 mlt_properties_set_int( properties, "frequency", 48000 );
90                 mlt_properties_set_int( properties, "channels", 2 );
91
92                 // Default of all consumers is real time
93                 mlt_properties_set_int( properties, "real_time", 1 );
94
95                 // Default to environment test card
96                 mlt_properties_set( properties, "test_card", mlt_environment( "MLT_TEST_CARD" ) );
97
98                 // Hmm - default all consumers to yuv422 :-/
99                 this->format = mlt_image_yuv422;
100
101                 mlt_events_register( properties, "consumer-frame-show", ( mlt_transmitter )mlt_consumer_frame_show );
102                 mlt_events_register( properties, "consumer-frame-render", ( mlt_transmitter )mlt_consumer_frame_render );
103                 mlt_events_register( properties, "consumer-stopped", NULL );
104
105                 // Register a property-changed listener to handle the profile property -
106                 // subsequent properties can override the profile
107                 this->event_listener = mlt_events_listen( properties, this, "property-changed", ( mlt_listener )mlt_consumer_property_changed );
108
109                 // Create the push mutex and condition
110                 pthread_mutex_init( &this->put_mutex, NULL );
111                 pthread_cond_init( &this->put_cond, NULL );
112
113         }
114         return error;
115 }
116
117 /** Convert the profile into properties on the consumer.
118  *
119  * \private \memberof mlt_consumer_s
120  * \param this a consumer
121  * \param profile a profile
122  * \param properties a properties list (typically, the consumer's)
123  */
124
125 static void apply_profile_properties( mlt_consumer this, mlt_profile profile, mlt_properties properties )
126 {
127         mlt_event_block( this->event_listener );
128         mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) );
129         mlt_properties_set_int( properties, "frame_rate_num", profile->frame_rate_num );
130         mlt_properties_set_int( properties, "frame_rate_den", profile->frame_rate_den );
131         mlt_properties_set_int( properties, "width", profile->width );
132         mlt_properties_set_int( properties, "height", profile->height );
133         mlt_properties_set_int( properties, "progressive", profile->progressive );
134         mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile )  );
135         mlt_properties_set_int( properties, "sample_aspect_num", profile->sample_aspect_num );
136         mlt_properties_set_int( properties, "sample_aspect_den", profile->sample_aspect_den );
137         mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile )  );
138         mlt_properties_set_int( properties, "display_aspect_num", profile->display_aspect_num );
139         mlt_properties_set_int( properties, "display_aspect_num", profile->display_aspect_num );
140         mlt_event_unblock( this->event_listener );
141 }
142
143 /** The property-changed event listener
144  *
145  * \private \memberof mlt_consumer_s
146  * \param owner the service a service (ignored)
147  * \param this the consumer
148  * \param name the name of the property that changed
149  */
150
151 static void mlt_consumer_property_changed( mlt_service owner, mlt_consumer this, char *name )
152 {
153         if ( !strcmp( name, "profile" ) )
154         {
155                 // Get the properies
156                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
157
158                 // Get the current profile
159                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
160
161                 // Load the new profile
162                 mlt_profile new_profile = mlt_profile_init( mlt_properties_get( properties, name ) );
163
164                 if ( new_profile )
165                 {
166                         // Copy the profile
167                         if ( profile != NULL )
168                         {
169                                 free( profile->description );
170                                 memcpy( profile, new_profile, sizeof( struct mlt_profile_s ) );
171                                 profile->description = strdup( new_profile->description );
172                                 mlt_profile_close( new_profile );
173                         }
174                         else
175                         {
176                                 profile = new_profile;
177                         }
178
179                         // Apply to properties
180                         apply_profile_properties( this, profile, properties );
181                 }
182         }
183         else if ( !strcmp( name, "frame_rate_num" ) )
184         {
185                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
186                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
187                 if ( profile )
188                 {
189                         profile->frame_rate_num = mlt_properties_get_int( properties, "frame_rate_num" );
190                         mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) );
191                 }
192         }
193         else if ( !strcmp( name, "frame_rate_den" ) )
194         {
195                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
196                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
197                 if ( profile )
198                 {
199                         profile->frame_rate_den = mlt_properties_get_int( properties, "frame_rate_den" );
200                         mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) );
201                 }
202         }
203         else if ( !strcmp( name, "width" ) )
204         {
205                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
206                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
207                 if ( profile )
208                         profile->width = mlt_properties_get_int( properties, "width" );
209         }
210         else if ( !strcmp( name, "height" ) )
211         {
212                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
213                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
214                 if ( profile )
215                         profile->height = mlt_properties_get_int( properties, "height" );
216         }
217         else if ( !strcmp( name, "progressive" ) )
218         {
219                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
220                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
221                 if ( profile )
222                         profile->progressive = mlt_properties_get_int( properties, "progressive" );
223         }
224         else if ( !strcmp( name, "sample_aspect_num" ) )
225         {
226                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
227                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
228                 profile->sample_aspect_num = mlt_properties_get_int( properties, "sample_aspect_num" );
229                 if ( profile )
230                         mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile )  );
231         }
232         else if ( !strcmp( name, "sample_aspect_den" ) )
233         {
234                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
235                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
236                 profile->sample_aspect_den = mlt_properties_get_int( properties, "sample_aspect_den" );
237                 if ( profile )
238                         mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile )  );
239         }
240         else if ( !strcmp( name, "display_aspect_num" ) )
241         {
242                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
243                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
244                 if ( profile )
245                 {
246                         profile->display_aspect_num = mlt_properties_get_int( properties, "display_aspect_num" );
247                         mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile )  );
248                 }
249         }
250         else if ( !strcmp( name, "display_aspect_den" ) )
251         {
252                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
253                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
254                 if ( profile )
255                 {
256                         profile->display_aspect_den = mlt_properties_get_int( properties, "display_aspect_den" );
257                         mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile )  );
258                 }
259         }
260 }
261
262 /** The transmitter for the consumer-frame-show event
263  *
264  * Invokes the listener.
265  *
266  * \private \memberof mlt_consumer_s
267  * \param listener a function pointer that will be invoked
268  * \param owner  a properties list that will be passed to \p listener
269  * \param this  a service that will be passed to \p listener
270  * \param args an array of pointers - the first entry is passed as a string to \p listener
271  */
272
273 static void mlt_consumer_frame_show( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
274 {
275         if ( listener != NULL )
276                 listener( owner, this, ( mlt_frame )args[ 0 ] );
277 }
278
279 /** The transmitter for the consumer-frame-render event
280  *
281  * Invokes the listener.
282  *
283  * \private \memberof mlt_consumer_s
284  * \param listener a function pointer that will be invoked
285  * \param owner  a properties list that will be passed to \p listener
286  * \param this  a service that will be passed to \p listener
287  * \param args an array of pointers - the first entry is passed as a string to \p listener
288  */
289
290 static void mlt_consumer_frame_render( mlt_listener listener, mlt_properties owner, mlt_service this, void **args )
291 {
292         if ( listener != NULL )
293                 listener( owner, this, ( mlt_frame )args[ 0 ] );
294 }
295
296 /** Create a new consumer.
297  *
298  * \public \memberof mlt_consumer_s
299  * \param profile a profile (optional, but recommended)
300  * \return a new consumer
301  */
302
303 mlt_consumer mlt_consumer_new( mlt_profile profile )
304 {
305         // Create the memory for the structure
306         mlt_consumer this = malloc( sizeof( struct mlt_consumer_s ) );
307
308         // Initialise it
309         if ( this != NULL )
310                 mlt_consumer_init( this, NULL, profile );
311
312         // Return it
313         return this;
314 }
315
316 /** Get the parent service object.
317  *
318  * \public \memberof mlt_consumer_s
319  * \param this a consumer
320  * \return the parent service class
321  * \see MLT_CONSUMER_SERVICE
322  */
323
324 mlt_service mlt_consumer_service( mlt_consumer this )
325 {
326         return this != NULL ? &this->parent : NULL;
327 }
328
329 /** Get the consumer properties.
330  *
331  * \public \memberof mlt_consumer_s
332  * \param this a consumer
333  * \return the consumer's properties list
334  * \see MLT_CONSUMER_PROPERTIES
335  */
336
337 mlt_properties mlt_consumer_properties( mlt_consumer this )
338 {
339         return this != NULL ? MLT_SERVICE_PROPERTIES( &this->parent ) : NULL;
340 }
341
342 /** Connect the consumer to the producer.
343  *
344  * \public \memberof mlt_consumer_s
345  * \param this a consumer
346  * \param producer a producer
347  * \return > 0 warning, == 0 success, < 0 serious error,
348  *         1 = this service does not accept input,
349  *         2 = the producer is invalid,
350  *         3 = the producer is already registered with this consumer
351  */
352
353 int mlt_consumer_connect( mlt_consumer this, mlt_service producer )
354 {
355         return mlt_service_connect_producer( &this->parent, producer, 0 );
356 }
357
358 /** Start the consumer.
359  *
360  * \public \memberof mlt_consumer_s
361  * \param this a consumer
362  * \return true if there was an error
363  */
364
365 int mlt_consumer_start( mlt_consumer this )
366 {
367         // Stop listening to the property-changed event
368         mlt_event_block( this->event_listener );
369
370         // Get the properies
371         mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
372
373         // Determine if there's a test card producer
374         char *test_card = mlt_properties_get( properties, "test_card" );
375
376         // Just to make sure nothing is hanging around...
377         mlt_frame_close( this->put );
378         this->put = NULL;
379         this->put_active = 1;
380
381         // Deal with it now.
382         if ( test_card != NULL )
383         {
384                 if ( mlt_properties_get_data( properties, "test_card_producer", NULL ) == NULL )
385                 {
386                         // Create a test card producer
387                         mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( this ) );
388                         mlt_producer producer = mlt_factory_producer( profile, NULL, test_card );
389
390                         // Do we have a producer
391                         if ( producer != NULL )
392                         {
393                                 // Test card should loop I guess...
394                                 mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" );
395                                 //mlt_producer_set_speed( producer, 0 );
396                                 //mlt_producer_set_in_and_out( producer, 0, 0 );
397
398                                 // Set the test card on the consumer
399                                 mlt_properties_set_data( properties, "test_card_producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
400                         }
401                 }
402         }
403         else
404         {
405                 // Allow the hash table to speed things up
406                 mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL );
407         }
408
409         // Set the frame duration in microseconds for the frame-dropping heuristic
410         int frame_duration = 1000000 / mlt_properties_get_int( properties, "frame_rate_num" ) *
411                         mlt_properties_get_int( properties, "frame_rate_den" );
412         mlt_properties_set_int( properties, "frame_duration", frame_duration );
413
414         // Check and run an ante command
415         if ( mlt_properties_get( properties, "ante" ) )
416                 if ( system( mlt_properties_get( properties, "ante" ) ) == -1 )
417                         mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "ante" ) );
418
419         // Set the real_time preference
420         this->real_time = mlt_properties_get_int( properties, "real_time" );
421
422         // Start the service
423         if ( this->start != NULL )
424                 return this->start( this );
425
426         return 0;
427 }
428
429 /** An alternative method to feed frames into the consumer.
430  *
431  * Only valid if the consumer itself is not connected.
432  *
433  * \public \memberof mlt_consumer_s
434  * \param this a consumer
435  * \param frame a frame
436  * \return true (ignore this for now)
437  */
438
439 int mlt_consumer_put_frame( mlt_consumer this, mlt_frame frame )
440 {
441         int error = 1;
442
443         // Get the service assoicated to the consumer
444         mlt_service service = MLT_CONSUMER_SERVICE( this );
445
446         if ( mlt_service_producer( service ) == NULL )
447         {
448                 struct timeval now;
449                 struct timespec tm;
450                 pthread_mutex_lock( &this->put_mutex );
451                 while ( this->put_active && this->put != NULL )
452                 {
453                         gettimeofday( &now, NULL );
454                         tm.tv_sec = now.tv_sec + 1;
455                         tm.tv_nsec = now.tv_usec * 1000;
456                         pthread_cond_timedwait( &this->put_cond, &this->put_mutex, &tm );
457                 }
458                 if ( this->put_active && this->put == NULL )
459                         this->put = frame;
460                 else
461                         mlt_frame_close( frame );
462                 pthread_cond_broadcast( &this->put_cond );
463                 pthread_mutex_unlock( &this->put_mutex );
464         }
465         else
466         {
467                 mlt_frame_close( frame );
468         }
469
470         return error;
471 }
472
473 /** Protected method for consumer to get frames from connected service
474  *
475  * \public \memberof mlt_consumer_s
476  * \param this a consumer
477  * \return a frame
478  */
479
480 mlt_frame mlt_consumer_get_frame( mlt_consumer this )
481 {
482         // Frame to return
483         mlt_frame frame = NULL;
484
485         // Get the service assoicated to the consumer
486         mlt_service service = MLT_CONSUMER_SERVICE( this );
487
488         // Get the consumer properties
489         mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
490
491         // Get the frame
492         if ( mlt_service_producer( service ) == NULL && mlt_properties_get_int( properties, "put_mode" ) )
493         {
494                 struct timeval now;
495                 struct timespec tm;
496                 pthread_mutex_lock( &this->put_mutex );
497                 while ( this->put_active && this->put == NULL )
498                 {
499                         gettimeofday( &now, NULL );
500                         tm.tv_sec = now.tv_sec + 1;
501                         tm.tv_nsec = now.tv_usec * 1000;
502                         pthread_cond_timedwait( &this->put_cond, &this->put_mutex, &tm );
503                 }
504                 frame = this->put;
505                 this->put = NULL;
506                 pthread_cond_broadcast( &this->put_cond );
507                 pthread_mutex_unlock( &this->put_mutex );
508                 if ( frame != NULL )
509                         mlt_service_apply_filters( service, frame, 0 );
510         }
511         else if ( mlt_service_producer( service ) != NULL )
512         {
513                 mlt_service_get_frame( service, &frame, 0 );
514         }
515         else
516         {
517                 frame = mlt_frame_init( service );
518         }
519
520         if ( frame != NULL )
521         {
522                 // Get the frame properties
523                 mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );
524
525                 // Get the test card producer
526                 mlt_producer test_card = mlt_properties_get_data( properties, "test_card_producer", NULL );
527
528                 // Attach the test frame producer to it.
529                 if ( test_card != NULL )
530                         mlt_properties_set_data( frame_properties, "test_card_producer", test_card, 0, NULL, NULL );
531
532                 // Attach the rescale property
533                 mlt_properties_set( frame_properties, "rescale.interp", mlt_properties_get( properties, "rescale" ) );
534
535                 // Aspect ratio and other jiggery pokery
536                 mlt_properties_set_double( frame_properties, "consumer_aspect_ratio", mlt_properties_get_double( properties, "aspect_ratio" ) );
537                 mlt_properties_set_int( frame_properties, "consumer_deinterlace", mlt_properties_get_int( properties, "progressive" ) | mlt_properties_get_int( properties, "deinterlace" ) );
538                 mlt_properties_set( frame_properties, "deinterlace_method", mlt_properties_get( properties, "deinterlace_method" ) );
539         }
540
541         // Return the frame
542         return frame;
543 }
544
545 /** Compute the time difference between now and a time value.
546  *
547  * \private \memberof mlt_consumer_s
548  * \param time1 a time value to be compared against now
549  * \return the difference in microseconds
550  */
551
552 static inline long time_difference( struct timeval *time1 )
553 {
554         struct timeval time2;
555         time2.tv_sec = time1->tv_sec;
556         time2.tv_usec = time1->tv_usec;
557         gettimeofday( time1, NULL );
558         return time1->tv_sec * 1000000 + time1->tv_usec - time2.tv_sec * 1000000 - time2.tv_usec;
559 }
560
561 /** The thread procedure for asynchronously pulling frames through the service
562  * network connected to a consumer.
563  *
564  * \private \memberof mlt_consumer_s
565  * \param arg a consumer
566  */
567
568 static void *consumer_read_ahead_thread( void *arg )
569 {
570         // The argument is the consumer
571         mlt_consumer this = arg;
572
573         // Get the properties of the consumer
574         mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
575
576         // Get the width and height
577         int width = mlt_properties_get_int( properties, "width" );
578         int height = mlt_properties_get_int( properties, "height" );
579
580         // See if video is turned off
581         int video_off = mlt_properties_get_int( properties, "video_off" );
582         int preview_off = mlt_properties_get_int( properties, "preview_off" );
583         int preview_format = mlt_properties_get_int( properties, "preview_format" );
584
585         // Get the audio settings
586         mlt_audio_format afmt = mlt_audio_s16;
587         int counter = 0;
588         double fps = mlt_properties_get_double( properties, "fps" );
589         int channels = mlt_properties_get_int( properties, "channels" );
590         int frequency = mlt_properties_get_int( properties, "frequency" );
591         int samples = 0;
592         void *audio = NULL;
593
594         // See if audio is turned off
595         int audio_off = mlt_properties_get_int( properties, "audio_off" );
596
597         // Get the maximum size of the buffer
598         int buffer = mlt_properties_get_int( properties, "buffer" ) + 1;
599
600         // General frame variable
601         mlt_frame frame = NULL;
602         uint8_t *image = NULL;
603
604         // Time structures
605         struct timeval ante;
606
607         // Average time for get_frame and get_image
608         int count = 1;
609         int skipped = 0;
610         int64_t time_wait = 0;
611         int64_t time_frame = 0;
612         int64_t time_process = 0;
613         int skip_next = 0;
614         mlt_service lock_object = NULL;
615
616         if ( preview_off && preview_format != 0 )
617                 this->format = preview_format;
618
619         // Get the first frame
620         frame = mlt_consumer_get_frame( this );
621
622         // Get the lock object
623         lock_object = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "consumer_lock_service", NULL );
624
625         // Lock it
626         if ( lock_object ) mlt_service_lock( lock_object );
627
628         // Get the image of the first frame
629         if ( !video_off )
630         {
631                 mlt_events_fire( MLT_CONSUMER_PROPERTIES( this ), "consumer-frame-render", frame, NULL );
632                 mlt_frame_get_image( frame, &image, &this->format, &width, &height, 0 );
633         }
634
635         if ( !audio_off )
636         {
637                 samples = mlt_sample_calculator( fps, frequency, counter++ );
638                 mlt_frame_get_audio( frame, &audio, &afmt, &frequency, &channels, &samples );
639         }
640
641         // Unlock the lock object
642         if ( lock_object ) mlt_service_unlock( lock_object );
643
644         // Mark as rendered
645         mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 );
646
647         // Get the starting time (can ignore the times above)
648         gettimeofday( &ante, NULL );
649
650         // Continue to read ahead
651         while ( this->ahead )
652         {
653                 // Fetch width/height again
654                 width = mlt_properties_get_int( properties, "width" );
655                 height = mlt_properties_get_int( properties, "height" );
656
657                 // Put the current frame into the queue
658                 pthread_mutex_lock( &this->mutex );
659                 while( this->ahead && mlt_deque_count( this->queue ) >= buffer )
660                         pthread_cond_wait( &this->cond, &this->mutex );
661                 mlt_deque_push_back( this->queue, frame );
662                 pthread_cond_broadcast( &this->cond );
663                 pthread_mutex_unlock( &this->mutex );
664
665                 time_wait += time_difference( &ante );
666
667                 // Get the next frame
668                 frame = mlt_consumer_get_frame( this );
669                 time_frame += time_difference( &ante );
670
671                 // If there's no frame, we're probably stopped...
672                 if ( frame == NULL )
673                         continue;
674
675                 // Attempt to fetch the lock object
676                 lock_object = mlt_properties_get_data( MLT_FRAME_PROPERTIES( frame ), "consumer_lock_service", NULL );
677
678                 // Increment the count
679                 count ++;
680
681                 // Lock if there's a lock object
682                 if ( lock_object ) mlt_service_lock( lock_object );
683
684                 // All non normal playback frames should be shown
685                 if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "_speed" ) != 1 )
686                 {
687 #ifdef DEINTERLACE_ON_NOT_NORMAL_SPEED
688                         mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 );
689 #endif
690                         skipped = 0;
691                         time_frame = 0;
692                         time_process = 0;
693                         time_wait = 0;
694                         count = 1;
695                         skip_next = 0;
696                 }
697
698                 // Get the image
699                 if ( !skip_next || this->real_time == -1 )
700                 {
701                         // Get the image, mark as rendered and time it
702                         if ( !video_off )
703                         {
704                                 mlt_events_fire( MLT_CONSUMER_PROPERTIES( this ), "consumer-frame-render", frame, NULL );
705                                 mlt_frame_get_image( frame, &image, &this->format, &width, &height, 0 );
706                         }
707                         mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 );
708                 }
709                 else
710                 {
711                         // Increment the number of sequentially skipped frames
712                         skipped ++;
713                         skip_next = 0;
714
715                         // If we've reached an unacceptable level, reset everything
716                         if ( skipped > 5 )
717                         {
718                                 skipped = 0;
719                                 time_frame = 0;
720                                 time_process = 0;
721                                 time_wait = 0;
722                                 count = 1;
723                         }
724                 }
725
726                 // Always process audio
727                 if ( !audio_off )
728                 {
729                         samples = mlt_sample_calculator( fps, frequency, counter++ );
730                         mlt_frame_get_audio( frame, &audio, &afmt, &frequency, &channels, &samples );
731                 }
732
733                 // Increment the time take for this frame
734                 time_process += time_difference( &ante );
735
736                 // Determine if the next frame should be skipped
737                 if ( mlt_deque_count( this->queue ) <= 5 )
738                 {
739                         int frame_duration = mlt_properties_get_int( properties, "frame_duration" );
740                         if ( ( ( time_wait + time_frame + time_process ) / count ) > frame_duration )
741                                 skip_next = 1;
742                 }
743
744                 // Unlock if there's a lock object
745                 if ( lock_object ) mlt_service_unlock( lock_object );
746         }
747
748         // Remove the last frame
749         mlt_frame_close( frame );
750
751         return NULL;
752 }
753
754 /** Start the read/render thread.
755  *
756  * \private \memberof mlt_consumer_s
757  * \param this a consumer
758  */
759
760 static void consumer_read_ahead_start( mlt_consumer this )
761 {
762         // We're running now
763         this->ahead = 1;
764
765         // Create the frame queue
766         this->queue = mlt_deque_init( );
767
768         // Create the mutex
769         pthread_mutex_init( &this->mutex, NULL );
770
771         // Create the condition
772         pthread_cond_init( &this->cond, NULL );
773
774         // Create the read ahead
775         if ( mlt_properties_get( MLT_CONSUMER_PROPERTIES( this ), "priority" ) )
776         {
777                 struct sched_param priority;
778                 priority.sched_priority = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( this ), "priority" );
779                 pthread_attr_t thread_attributes;
780                 pthread_attr_init( &thread_attributes );
781                 pthread_attr_setschedpolicy( &thread_attributes, SCHED_OTHER );
782                 pthread_attr_setschedparam( &thread_attributes, &priority );
783                 pthread_attr_setinheritsched( &thread_attributes, PTHREAD_EXPLICIT_SCHED );
784                 pthread_attr_setscope( &thread_attributes, PTHREAD_SCOPE_SYSTEM );
785                 if ( pthread_create( &this->ahead_thread, &thread_attributes, consumer_read_ahead_thread, this ) < 0 )
786                         pthread_create( &this->ahead_thread, NULL, consumer_read_ahead_thread, this );
787                 pthread_attr_destroy( &thread_attributes );
788         }
789         else
790         {
791                 pthread_create( &this->ahead_thread, NULL, consumer_read_ahead_thread, this );
792         }
793 }
794
795 /** Stop the read/render thread.
796  *
797  * \private \memberof mlt_consumer_s
798  * \param this a consumer
799  */
800
801 static void consumer_read_ahead_stop( mlt_consumer this )
802 {
803         // Make sure we're running
804         if ( this->ahead )
805         {
806                 // Inform thread to stop
807                 this->ahead = 0;
808
809                 // Broadcast to the condition in case it's waiting
810                 pthread_mutex_lock( &this->mutex );
811                 pthread_cond_broadcast( &this->cond );
812                 pthread_mutex_unlock( &this->mutex );
813
814                 // Broadcast to the put condition in case it's waiting
815                 pthread_mutex_lock( &this->put_mutex );
816                 pthread_cond_broadcast( &this->put_cond );
817                 pthread_mutex_unlock( &this->put_mutex );
818
819                 // Join the thread
820                 pthread_join( this->ahead_thread, NULL );
821
822                 // Destroy the mutex
823                 pthread_mutex_destroy( &this->mutex );
824
825                 // Destroy the condition
826                 pthread_cond_destroy( &this->cond );
827
828                 // Wipe the queue
829                 while ( mlt_deque_count( this->queue ) )
830                         mlt_frame_close( mlt_deque_pop_back( this->queue ) );
831
832                 // Close the queue
833                 mlt_deque_close( this->queue );
834         }
835 }
836
837 /** Flush the read/render thread's buffer.
838  *
839  * \public \memberof mlt_consumer_s
840  * \param this a consumer
841  */
842
843 void mlt_consumer_purge( mlt_consumer this )
844 {
845         if ( this->ahead )
846         {
847                 pthread_mutex_lock( &this->mutex );
848                 while ( mlt_deque_count( this->queue ) )
849                         mlt_frame_close( mlt_deque_pop_back( this->queue ) );
850                 pthread_cond_broadcast( &this->cond );
851                 pthread_mutex_unlock( &this->mutex );
852         }
853 }
854
855 /** Get the next frame from the producer connected to a consumer.
856  *
857  * Typically, one uses this instead of \p mlt_consumer_get_frame to make
858  * the asynchronous/real-time behavior configurable at runtime.
859  * You should close the frame returned from this when you are done with it.
860  *
861  * \public \memberof mlt_consumer_s
862  * \param this a consumer
863  * \return a frame
864  */
865
866 mlt_frame mlt_consumer_rt_frame( mlt_consumer this )
867 {
868         // Frame to return
869         mlt_frame frame = NULL;
870
871         // Get the properties
872         mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
873
874         // Check if the user has requested real time or not
875         if ( this->real_time )
876         {
877                 int size = 1;
878
879                 // Is the read ahead running?
880                 if ( this->ahead == 0 )
881                 {
882                         int buffer = mlt_properties_get_int( properties, "buffer" );
883                         int prefill = mlt_properties_get_int( properties, "prefill" );
884                         consumer_read_ahead_start( this );
885                         if ( buffer > 1 )
886                                 size = prefill > 0 && prefill < buffer ? prefill : buffer;
887                 }
888
889                 // Get frame from queue
890                 pthread_mutex_lock( &this->mutex );
891                 while( this->ahead && mlt_deque_count( this->queue ) < size )
892                         pthread_cond_wait( &this->cond, &this->mutex );
893                 frame = mlt_deque_pop_front( this->queue );
894                 pthread_cond_broadcast( &this->cond );
895                 pthread_mutex_unlock( &this->mutex );
896         }
897         else
898         {
899                 // Get the frame in non real time
900                 frame = mlt_consumer_get_frame( this );
901
902                 // This isn't true, but from the consumers perspective it is
903                 if ( frame != NULL )
904                         mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 );
905         }
906
907         return frame;
908 }
909
910 /** Callback for the implementation to indicate a stopped condition.
911  *
912  * \public \memberof mlt_consumer_s
913  * \param this a consumer
914  */
915
916 void mlt_consumer_stopped( mlt_consumer this )
917 {
918         mlt_properties_set_int( MLT_CONSUMER_PROPERTIES( this ), "running", 0 );
919         mlt_events_fire( MLT_CONSUMER_PROPERTIES( this ), "consumer-stopped", NULL );
920         mlt_event_unblock( this->event_listener );
921 }
922
923 /** Stop the consumer.
924  *
925  * \public \memberof mlt_consumer_s
926  * \param this a consumer
927  * \return true if there was an error
928  */
929
930 int mlt_consumer_stop( mlt_consumer this )
931 {
932         // Get the properies
933         mlt_properties properties = MLT_CONSUMER_PROPERTIES( this );
934
935         // Just in case...
936         mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopping put waiting\n" );
937         pthread_mutex_lock( &this->put_mutex );
938         this->put_active = 0;
939         pthread_cond_broadcast( &this->put_cond );
940         pthread_mutex_unlock( &this->put_mutex );
941
942         // Stop the consumer
943         mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopping consumer\n" );
944         if ( this->stop != NULL )
945                 this->stop( this );
946
947         // Check if the user has requested real time or not and stop if necessary
948         mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopping read_ahead\n" );
949         if ( mlt_properties_get_int( properties, "real_time" ) )
950                 consumer_read_ahead_stop( this );
951
952         // Kill the test card
953         mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL );
954
955         // Check and run a post command
956         if ( mlt_properties_get( properties, "post" ) )
957                 if (system( mlt_properties_get( properties, "post" ) ) == -1 )
958                         mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "post" ) );
959
960         mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopped\n" );
961
962         return 0;
963 }
964
965 /** Determine if the consumer is stopped.
966  *
967  * \public \memberof mlt_consumer_s
968  * \param this a consumer
969  * \return true if the consumer is stopped
970  */
971
972 int mlt_consumer_is_stopped( mlt_consumer this )
973 {
974         // Check if the consumer is stopped
975         if ( this->is_stopped != NULL )
976                 return this->is_stopped( this );
977
978         return 0;
979 }
980
981 /** Close and destroy the consumer.
982  *
983  * \public \memberof mlt_consumer_s
984  * \param this a consumer
985  */
986
987 void mlt_consumer_close( mlt_consumer this )
988 {
989         if ( this != NULL && mlt_properties_dec_ref( MLT_CONSUMER_PROPERTIES( this ) ) <= 0 )
990         {
991                 // Get the childs close function
992                 void ( *consumer_close )( ) = this->close;
993
994                 if ( consumer_close )
995                 {
996                         // Just in case...
997                         //mlt_consumer_stop( this );
998
999                         this->close = NULL;
1000                         consumer_close( this );
1001                 }
1002                 else
1003                 {
1004                         // Make sure it only gets called once
1005                         this->parent.close = NULL;
1006
1007                         // Destroy the push mutex and condition
1008                         pthread_mutex_destroy( &this->put_mutex );
1009                         pthread_cond_destroy( &this->put_cond );
1010
1011                         mlt_service_close( &this->parent );
1012                 }
1013         }
1014 }