X-Git-Url: http://research.m1stereo.tv/gitweb?a=blobdiff_plain;f=src%2Fframework%2Fmlt_consumer.c;h=93afe447d6a13c81ea9b00cb98c29d4799aa6d76;hb=3902731cb8a9622204d584c4962c74e2a5a35428;hp=04b37618b023175956f9351db1c1be7300123749;hpb=d33f444d4ef4c7bc4074d07a49eca0ab7d108394;p=melted diff --git a/src/framework/mlt_consumer.c b/src/framework/mlt_consumer.c index 04b3761..93afe44 100644 --- a/src/framework/mlt_consumer.c +++ b/src/framework/mlt_consumer.c @@ -1,7 +1,10 @@ -/* - * mlt_consumer.c -- abstraction for all consumer services - * Copyright (C) 2003-2004 Ushodaya Enterprises Limited - * Author: Charles Yates +/** + * \file mlt_consumer.c + * \brief abstraction for all consumer services + * \see mlt_consumer_s + * + * Copyright (C) 2003-2009 Ushodaya Enterprises Limited + * \author Charles Yates * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,25 +21,37 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "config.h" #include "mlt_consumer.h" #include "mlt_factory.h" #include "mlt_producer.h" #include "mlt_frame.h" #include "mlt_profile.h" +#include "mlt_log.h" #include #include #include #include +/** Define this if you want an automatic deinterlace (if necessary) when the + * consumer's producer is not running at normal speed. + */ +#undef DEINTERLACE_ON_NOT_NORMAL_SPEED + static void mlt_consumer_frame_render( mlt_listener listener, mlt_properties owner, mlt_service this, void **args ); static void mlt_consumer_frame_show( mlt_listener listener, mlt_properties owner, mlt_service this, void **args ); static void mlt_consumer_property_changed( mlt_service owner, mlt_consumer this, char *name ); static void apply_profile_properties( mlt_consumer this, mlt_profile profile, mlt_properties properties ); -/** Public final methods -*/ +/** Initialize a consumer service. + * + * \public \memberof mlt_consumer_s + * \param this the consumer to initialize + * \param child a pointer to the object for the subclass + * \param profile the \p mlt_profile_s to use (optional but recommended, + * uses the environment variable MLT if this is NULL) + * \return true if there was an error + */ int mlt_consumer_init( mlt_consumer this, void *child, mlt_profile profile ) { @@ -48,7 +63,7 @@ int mlt_consumer_init( mlt_consumer this, void *child, mlt_profile profile ) { // Get the properties from the service mlt_properties properties = MLT_SERVICE_PROPERTIES( &this->parent ); - + // Apply profile to properties if ( profile == NULL ) { @@ -95,6 +110,14 @@ int mlt_consumer_init( mlt_consumer this, void *child, mlt_profile profile ) return error; } +/** Convert the profile into properties on the consumer. + * + * \private \memberof mlt_consumer_s + * \param this a consumer + * \param profile a profile + * \param properties a properties list (typically, the consumer's) + */ + static void apply_profile_properties( mlt_consumer this, mlt_profile profile, mlt_properties properties ) { mlt_event_block( this->event_listener ); @@ -113,6 +136,14 @@ static void apply_profile_properties( mlt_consumer this, mlt_profile profile, ml mlt_event_unblock( this->event_listener ); } +/** The property-changed event listener + * + * \private \memberof mlt_consumer_s + * \param owner the service a service (ignored) + * \param this the consumer + * \param name the name of the property that changed + */ + static void mlt_consumer_property_changed( mlt_service owner, mlt_consumer this, char *name ) { if ( !strcmp( name, "profile" ) ) @@ -224,12 +255,34 @@ static void mlt_consumer_property_changed( mlt_service owner, mlt_consumer this, } } +/** The transmitter for the consumer-frame-show event + * + * Invokes the listener. + * + * \private \memberof mlt_consumer_s + * \param listener a function pointer that will be invoked + * \param owner a properties list that will be passed to \p listener + * \param this a service that will be passed to \p listener + * \param args an array of pointers - the first entry is passed as a string to \p listener + */ + static void mlt_consumer_frame_show( mlt_listener listener, mlt_properties owner, mlt_service this, void **args ) { if ( listener != NULL ) listener( owner, this, ( mlt_frame )args[ 0 ] ); } +/** The transmitter for the consumer-frame-render event + * + * Invokes the listener. + * + * \private \memberof mlt_consumer_s + * \param listener a function pointer that will be invoked + * \param owner a properties list that will be passed to \p listener + * \param this a service that will be passed to \p listener + * \param args an array of pointers - the first entry is passed as a string to \p listener + */ + static void mlt_consumer_frame_render( mlt_listener listener, mlt_properties owner, mlt_service this, void **args ) { if ( listener != NULL ) @@ -237,7 +290,11 @@ static void mlt_consumer_frame_render( mlt_listener listener, mlt_properties own } /** Create a new consumer. -*/ + * + * \public \memberof mlt_consumer_s + * \param profile a profile (optional, but recommended) + * \return a new consumer + */ mlt_consumer mlt_consumer_new( mlt_profile profile ) { @@ -253,7 +310,12 @@ mlt_consumer mlt_consumer_new( mlt_profile profile ) } /** Get the parent service object. -*/ + * + * \public \memberof mlt_consumer_s + * \param this a consumer + * \return the parent service class + * \see MLT_CONSUMER_SERVICE + */ mlt_service mlt_consumer_service( mlt_consumer this ) { @@ -261,7 +323,12 @@ mlt_service mlt_consumer_service( mlt_consumer this ) } /** Get the consumer properties. -*/ + * + * \public \memberof mlt_consumer_s + * \param this a consumer + * \return the consumer's properties list + * \see MLT_CONSUMER_PROPERTIES + */ mlt_properties mlt_consumer_properties( mlt_consumer this ) { @@ -269,7 +336,15 @@ mlt_properties mlt_consumer_properties( mlt_consumer this ) } /** Connect the consumer to the producer. -*/ + * + * \public \memberof mlt_consumer_s + * \param this a consumer + * \param producer a producer + * \return > 0 warning, == 0 success, < 0 serious error, + * 1 = this service does not accept input, + * 2 = the producer is invalid, + * 3 = the producer is already registered with this consumer + */ int mlt_consumer_connect( mlt_consumer this, mlt_service producer ) { @@ -277,7 +352,11 @@ int mlt_consumer_connect( mlt_consumer this, mlt_service producer ) } /** Start the consumer. -*/ + * + * \public \memberof mlt_consumer_s + * \param this a consumer + * \return true if there was an error + */ int mlt_consumer_start( mlt_consumer this ) { @@ -323,9 +402,15 @@ int mlt_consumer_start( mlt_consumer this ) mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL ); } + // Set the frame duration in microseconds for the frame-dropping heuristic + int frame_duration = 1000000 / mlt_properties_get_int( properties, "frame_rate_num" ) * + mlt_properties_get_int( properties, "frame_rate_den" ); + mlt_properties_set_int( properties, "frame_duration", frame_duration ); + // Check and run an ante command if ( mlt_properties_get( properties, "ante" ) ) - system( mlt_properties_get( properties, "ante" ) ); + if ( system( mlt_properties_get( properties, "ante" ) ) == -1 ) + mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "ante" ) ); // Set the real_time preference this->real_time = mlt_properties_get_int( properties, "real_time" ); @@ -337,9 +422,15 @@ int mlt_consumer_start( mlt_consumer this ) return 0; } -/** An alternative method to feed frames into the consumer - only valid if - the consumer itself is not connected. -*/ +/** An alternative method to feed frames into the consumer. + * + * Only valid if the consumer itself is not connected. + * + * \public \memberof mlt_consumer_s + * \param this a consumer + * \param frame a frame + * \return true (ignore this for now) + */ int mlt_consumer_put_frame( mlt_consumer this, mlt_frame frame ) { @@ -376,7 +467,11 @@ int mlt_consumer_put_frame( mlt_consumer this, mlt_frame frame ) } /** Protected method for consumer to get frames from connected service -*/ + * + * \public \memberof mlt_consumer_s + * \param this a consumer + * \return a frame + */ mlt_frame mlt_consumer_get_frame( mlt_consumer this ) { @@ -443,6 +538,13 @@ mlt_frame mlt_consumer_get_frame( mlt_consumer this ) return frame; } +/** Compute the time difference between now and a time value. + * + * \private \memberof mlt_consumer_s + * \param time1 a time value to be compared against now + * \return the difference in microseconds + */ + static inline long time_difference( struct timeval *time1 ) { struct timeval time2; @@ -452,6 +554,13 @@ static inline long time_difference( struct timeval *time1 ) return time1->tv_sec * 1000000 + time1->tv_usec - time2.tv_sec * 1000000 - time2.tv_usec; } +/** The thread procedure for asynchronously pulling frames through the service + * network connected to a consumer. + * + * \private \memberof mlt_consumer_s + * \param arg a consumer + */ + static void *consumer_read_ahead_thread( void *arg ) { // The argument is the consumer @@ -571,7 +680,9 @@ static void *consumer_read_ahead_thread( void *arg ) // All non normal playback frames should be shown if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "_speed" ) != 1 ) { +#ifdef DEINTERLACE_ON_NOT_NORMAL_SPEED mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 ); +#endif skipped = 0; time_frame = 0; time_process = 0; @@ -581,7 +692,7 @@ static void *consumer_read_ahead_thread( void *arg ) } // Get the image - if ( !skip_next ) + if ( !skip_next || this->real_time == -1 ) { // Get the image, mark as rendered and time it if ( !video_off ) @@ -619,8 +730,12 @@ static void *consumer_read_ahead_thread( void *arg ) time_process += time_difference( &ante ); // Determine if the next frame should be skipped - if ( mlt_deque_count( this->queue ) <= 5 && ( ( time_wait + time_frame + time_process ) / count ) > 40000 ) - skip_next = 1; + if ( mlt_deque_count( this->queue ) <= 5 ) + { + int frame_duration = mlt_properties_get_int( properties, "frame_duration" ); + if ( ( ( time_wait + time_frame + time_process ) / count ) > frame_duration ) + skip_next = 1; + } // Unlock if there's a lock object if ( lock_object ) mlt_service_unlock( lock_object ); @@ -632,6 +747,12 @@ static void *consumer_read_ahead_thread( void *arg ) return NULL; } +/** Start the read/render thread. + * + * \private \memberof mlt_consumer_s + * \param this a consumer + */ + static void consumer_read_ahead_start( mlt_consumer this ) { // We're running now @@ -646,10 +767,33 @@ static void consumer_read_ahead_start( mlt_consumer this ) // Create the condition pthread_cond_init( &this->cond, NULL ); - // Create the read ahead - pthread_create( &this->ahead_thread, NULL, consumer_read_ahead_thread, this ); + // Create the read ahead + if ( mlt_properties_get( MLT_CONSUMER_PROPERTIES( this ), "priority" ) ) + { + struct sched_param priority; + priority.sched_priority = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( this ), "priority" ); + pthread_attr_t thread_attributes; + pthread_attr_init( &thread_attributes ); + pthread_attr_setschedpolicy( &thread_attributes, SCHED_OTHER ); + pthread_attr_setschedparam( &thread_attributes, &priority ); + pthread_attr_setinheritsched( &thread_attributes, PTHREAD_EXPLICIT_SCHED ); + pthread_attr_setscope( &thread_attributes, PTHREAD_SCOPE_SYSTEM ); + if ( pthread_create( &this->ahead_thread, &thread_attributes, consumer_read_ahead_thread, this ) < 0 ) + pthread_create( &this->ahead_thread, NULL, consumer_read_ahead_thread, this ); + pthread_attr_destroy( &thread_attributes ); + } + else + { + pthread_create( &this->ahead_thread, NULL, consumer_read_ahead_thread, this ); + } } +/** Stop the read/render thread. + * + * \private \memberof mlt_consumer_s + * \param this a consumer + */ + static void consumer_read_ahead_stop( mlt_consumer this ) { // Make sure we're running @@ -686,6 +830,12 @@ static void consumer_read_ahead_stop( mlt_consumer this ) } } +/** Flush the read/render thread's buffer. + * + * \public \memberof mlt_consumer_s + * \param this a consumer + */ + void mlt_consumer_purge( mlt_consumer this ) { if ( this->ahead ) @@ -698,6 +848,17 @@ void mlt_consumer_purge( mlt_consumer this ) } } +/** Get the next frame from the producer connected to a consumer. + * + * Typically, one uses this instead of \p mlt_consumer_get_frame to make + * the asynchronous/real-time behavior configurable at runtime. + * You should close the frame returned from this when you are done with it. + * + * \public \memberof mlt_consumer_s + * \param this a consumer + * \return a frame + */ + mlt_frame mlt_consumer_rt_frame( mlt_consumer this ) { // Frame to return @@ -720,7 +881,7 @@ mlt_frame mlt_consumer_rt_frame( mlt_consumer this ) if ( buffer > 1 ) size = prefill > 0 && prefill < buffer ? prefill : buffer; } - + // Get frame from queue pthread_mutex_lock( &this->mutex ); while( this->ahead && mlt_deque_count( this->queue ) < size ) @@ -743,7 +904,10 @@ mlt_frame mlt_consumer_rt_frame( mlt_consumer this ) } /** Callback for the implementation to indicate a stopped condition. -*/ + * + * \public \memberof mlt_consumer_s + * \param this a consumer + */ void mlt_consumer_stopped( mlt_consumer this ) { @@ -753,28 +917,31 @@ void mlt_consumer_stopped( mlt_consumer this ) } /** Stop the consumer. -*/ + * + * \public \memberof mlt_consumer_s + * \param this a consumer + * \return true if there was an error + */ int mlt_consumer_stop( mlt_consumer this ) { // Get the properies mlt_properties properties = MLT_CONSUMER_PROPERTIES( this ); - char *debug = mlt_properties_get( MLT_CONSUMER_PROPERTIES( this ), "debug" ); // Just in case... - if ( debug ) fprintf( stderr, "%s: stopping put waiting\n", debug ); + mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopping put waiting\n" ); pthread_mutex_lock( &this->put_mutex ); this->put_active = 0; pthread_cond_broadcast( &this->put_cond ); pthread_mutex_unlock( &this->put_mutex ); // Stop the consumer - if ( debug ) fprintf( stderr, "%s: stopping consumer\n", debug ); + mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopping consumer\n" ); if ( this->stop != NULL ) this->stop( this ); // Check if the user has requested real time or not and stop if necessary - if ( debug ) fprintf( stderr, "%s: stopping read_ahead\n", debug ); + mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopping read_ahead\n" ); if ( mlt_properties_get_int( properties, "real_time" ) ) consumer_read_ahead_stop( this ); @@ -783,15 +950,20 @@ int mlt_consumer_stop( mlt_consumer this ) // Check and run a post command if ( mlt_properties_get( properties, "post" ) ) - system( mlt_properties_get( properties, "post" ) ); + if (system( mlt_properties_get( properties, "post" ) ) == -1 ) + mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "post" ) ); - if ( debug ) fprintf( stderr, "%s: stopped\n", debug ); + mlt_log( MLT_CONSUMER_SERVICE( this ), MLT_LOG_DEBUG, "stopped\n" ); return 0; } /** Determine if the consumer is stopped. -*/ + * + * \public \memberof mlt_consumer_s + * \param this a consumer + * \return true if the consumer is stopped + */ int mlt_consumer_is_stopped( mlt_consumer this ) { @@ -802,8 +974,11 @@ int mlt_consumer_is_stopped( mlt_consumer this ) return 0; } -/** Close the consumer. -*/ +/** Close and destroy the consumer. + * + * \public \memberof mlt_consumer_s + * \param this a consumer + */ void mlt_consumer_close( mlt_consumer this ) {