Example #1
0
int mlt_consumer_stop( mlt_consumer self )
{
	// Get the properies
	mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );

	// Just in case...
	mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopping put waiting\n" );
	pthread_mutex_lock( &self->put_mutex );
	self->put_active = 0;
	pthread_cond_broadcast( &self->put_cond );
	pthread_mutex_unlock( &self->put_mutex );

	// Stop the consumer
	mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopping consumer\n" );
	
	// Cancel the read ahead threads
	self->ahead = 0;
	if ( self->started )
	{
		// Unblock the consumer calling mlt_consumer_rt_frame
		pthread_mutex_lock( &self->queue_mutex );
		pthread_cond_broadcast( &self->queue_cond );
		pthread_mutex_unlock( &self->queue_mutex );		
	}
	
	// Invoke the child callback
	if ( self->stop != NULL )
		self->stop( self );

	// Check if the user has requested real time or not and stop if necessary
	mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopping read_ahead\n" );
	if ( abs( self->real_time ) == 1 )
		consumer_read_ahead_stop( self );
	else if ( abs( self->real_time ) > 1 )
		consumer_work_stop( self );

	// Kill the test card
	mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL );

	// Check and run a post command
	if ( mlt_properties_get( properties, "post" ) )
		if (system( mlt_properties_get( properties, "post" ) ) == -1 )
			mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "post" ) );

	mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_DEBUG, "stopped\n" );

	return 0;
}
Example #2
0
static void foreach_consumer_stop( mlt_consumer consumer )
{
    mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
    mlt_consumer nested = NULL;
    char key[30];
    int index = 0;
    struct timespec tm = { 0, 1000 * 1000 };

    do {
        snprintf( key, sizeof(key), "%d.consumer", index++ );
        nested = mlt_properties_get_data( properties, key, NULL );
        if ( nested )
        {
            // Let consumer with terminate_on_pause stop on their own
            if ( mlt_properties_get_int( MLT_CONSUMER_PROPERTIES(nested), "terminate_on_pause" ) )
            {
                // Send additional dummy frame to unlatch nested consumer's threads
                mlt_consumer_put_frame( nested, mlt_frame_init( MLT_CONSUMER_SERVICE(consumer) ) );
                // wait for stop
                while ( !mlt_consumer_is_stopped( nested ) )
                    nanosleep( &tm, NULL );
            }
            else
            {
                mlt_consumer_stop( nested );
            }
        }
    } while ( nested );
}
Example #3
0
int mlt_consumer_put_frame( mlt_consumer self, mlt_frame frame )
{
	int error = 1;

	// Get the service assoicated to the consumer
	mlt_service service = MLT_CONSUMER_SERVICE( self );

	if ( mlt_service_producer( service ) == NULL )
	{
		struct timeval now;
		struct timespec tm;
		pthread_mutex_lock( &self->put_mutex );
		while ( self->put_active && self->put != NULL )
		{
			gettimeofday( &now, NULL );
			tm.tv_sec = now.tv_sec + 1;
			tm.tv_nsec = now.tv_usec * 1000;
			pthread_cond_timedwait( &self->put_cond, &self->put_mutex, &tm );
		}
		if ( self->put_active && self->put == NULL )
			self->put = frame;
		else
			mlt_frame_close( frame );
		pthread_cond_broadcast( &self->put_cond );
		pthread_mutex_unlock( &self->put_mutex );
	}
	else
	{
		mlt_frame_close( frame );
	}

	return error;
}
Example #4
0
	IDeckLinkDisplayMode* getDisplayMode()
	{
		mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( getConsumer() ) );
		IDeckLinkDisplayModeIterator* iter = NULL;
		IDeckLinkDisplayMode* mode = NULL;
		IDeckLinkDisplayMode* result = 0;

		if ( m_deckLinkOutput->GetDisplayModeIterator( &iter ) == S_OK )
		{
			while ( !result && iter->Next( &mode ) == S_OK )
			{
				m_width = mode->GetWidth();
				m_height = mode->GetHeight();
				mode->GetFrameRate( &m_duration, &m_timescale );
				m_fps = (double) m_timescale / m_duration;
				int p = mode->GetFieldDominance() == bmdProgressiveFrame;
				mlt_log_verbose( getConsumer(), "BMD mode %dx%d %.3f fps prog %d\n", m_width, m_height, m_fps, p );

				if ( m_width == profile->width && p == profile->progressive
					 && (int) m_fps == (int) mlt_profile_fps( profile )
					 && ( m_height == profile->height || ( m_height == 486 && profile->height == 480 ) ) )
					result = mode;
				else
					SAFE_RELEASE( mode );
			}
			SAFE_RELEASE( iter );
		}

		return result;
	}
Example #5
0
int consumer_start( mlt_consumer parent )
{
	consumer_sdl self = parent->child;

	if ( !self->running )
	{
		consumer_stop( parent );

		mlt_properties properties = MLT_CONSUMER_PROPERTIES( parent );
		char *audio_driver = mlt_properties_get( properties, "audio_driver" );
		char *audio_device = mlt_properties_get( properties, "audio_device" );

		if ( audio_driver && strcmp( audio_driver, "" ) )
			setenv( "SDL_AUDIODRIVER", audio_driver, 1 );

		if ( audio_device && strcmp( audio_device, "" ) )
			setenv( "AUDIODEV", audio_device, 1 );

		pthread_mutex_lock( &mlt_sdl_mutex );
		int ret = SDL_Init( SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE );
		pthread_mutex_unlock( &mlt_sdl_mutex );
		if ( ret < 0 )
		{
			mlt_log_error( MLT_CONSUMER_SERVICE(parent), "Failed to initialize SDL: %s\n", SDL_GetError() );
			return -1;
		}

		self->running = 1;
		self->joined = 0;
		pthread_create( &self->thread, NULL, consumer_thread, self );
	}

	return 0;
}
Example #6
0
static mlt_consumer generate_consumer( mlt_consumer consumer, mlt_properties props, int index )
{
    mlt_profile profile = NULL;
    if ( mlt_properties_get( props, "mlt_profile" ) )
        profile = mlt_profile_init( mlt_properties_get( props, "mlt_profile" ) );
    if ( !profile )
        profile = mlt_profile_clone( mlt_service_profile( MLT_CONSUMER_SERVICE(consumer) ) );
    mlt_consumer nested = create_consumer( profile, mlt_properties_get( props, "mlt_service" ),
                                           mlt_properties_get( props, "target" ) );

    if ( nested )
    {
        mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer);
        mlt_properties nested_props = MLT_CONSUMER_PROPERTIES(nested);
        char key[30];

        snprintf( key, sizeof(key), "%d.consumer", index );
        mlt_properties_set_data( properties, key, nested, 0, (mlt_destructor) mlt_consumer_close, NULL );
        snprintf( key, sizeof(key), "%d.profile", index );
        mlt_properties_set_data( properties, key, profile, 0, (mlt_destructor) mlt_profile_close, NULL );

        mlt_properties_set_int( nested_props, "put_mode", 1 );
        mlt_properties_pass_list( nested_props, properties, "terminate_on_pause" );
        mlt_properties_set( props, "consumer", NULL );
        // set mlt_profile before other properties to facilitate presets
        mlt_properties_pass_list( nested_props, props, "mlt_profile" );
        mlt_properties_inherit( nested_props, props );

        attach_normalisers( profile, MLT_CONSUMER_SERVICE(nested) );

        // Relay the first available consumer-frame-show event
        mlt_event event = mlt_properties_get_data( properties, "frame-show-event", NULL );
        if ( !event )
        {
            event = mlt_events_listen( nested_props, properties, "consumer-frame-show", (mlt_listener) on_frame_show );
            mlt_properties_set_data( properties, "frame-show-event", event, 0, /*mlt_event_close*/ NULL, NULL );
        }
    }
    else
    {
        mlt_profile_close( profile );
    }
    return nested;
}
Example #7
0
static void *consumer_thread( void *arg )
{
    mlt_consumer consumer = arg;
    mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
    mlt_frame frame = NULL;

    // Determine whether to stop at end-of-media
    int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
    int terminated = 0;

    // Loop while running
    while ( !terminated && !is_stopped( consumer ) )
    {
        // Get the next frame
        frame = mlt_consumer_rt_frame( consumer );

        // Check for termination
        if ( terminate_on_pause && frame )
            terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0;

        // Check that we have a frame to work with
        if ( frame && !terminated && !is_stopped( consumer ) )
        {
            if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered" ) )
            {
                if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "_speed" ) == 0 )
                    foreach_consumer_refresh( consumer );
                foreach_consumer_put( consumer, frame );
            }
            else
            {
                int dropped = mlt_properties_get_int( properties, "_dropped" );
                mlt_log_info( MLT_CONSUMER_SERVICE(consumer), "dropped frame %d\n", ++dropped );
                mlt_properties_set_int( properties, "_dropped", dropped );
            }
            mlt_frame_close( frame );
        }
        else
        {
            if ( frame && terminated )
            {
                // Send this termination frame to nested consumers for their cancellation
                foreach_consumer_put( consumer, frame );
            }
            if ( frame )
                mlt_frame_close( frame );
            terminated = 1;
        }
    }

    // Indicate that the consumer is stopped
    mlt_consumer_stopped( consumer );

    return NULL;
}
Example #8
0
File: melt.c Project: hrshadhin/mlt
static void setup_jack_transport( mlt_consumer consumer, mlt_profile profile )
{
    mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
    mlt_filter jack = mlt_factory_filter( profile, "jackrack", NULL );
    mlt_properties jack_properties = MLT_FILTER_PROPERTIES(jack);

    mlt_service_attach( MLT_CONSUMER_SERVICE(consumer), jack );
    mlt_properties_set_int( properties, "audio_off", 1 );
    mlt_properties_set_data( properties, "jack_filter", jack, 0, (mlt_destructor) mlt_filter_close, NULL );
//	mlt_properties_set( jack_properties, "out_1", "system:playback_1" );
//	mlt_properties_set( jack_properties, "out_2", "system:playback_2" );
    mlt_events_listen( jack_properties, consumer, "jack-started", (mlt_listener) on_jack_started );
    mlt_events_listen( jack_properties, consumer, "jack-stopped", (mlt_listener) on_jack_stopped );
}
Example #9
0
int consumer_start( mlt_consumer parent )
{
	consumer_sdl self = parent->child;

	if ( !self->running )
	{
		consumer_stop( parent );

		pthread_mutex_lock( &mlt_sdl_mutex );
		int ret = SDL_Init( SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE );
		pthread_mutex_unlock( &mlt_sdl_mutex );
		if ( ret < 0 )
		{
			mlt_log_error( MLT_CONSUMER_SERVICE(parent), "Failed to initialize SDL: %s\n", SDL_GetError() );
			return -1;
		}

		self->running = 1;
		self->joined = 0;
		pthread_create( &self->thread, NULL, consumer_thread, self );
	}

	return 0;
}
Example #10
0
static void *consumer_thread( void *arg )
{
	// Identify the arg
	consumer_sdl self = arg;

	// Get the consumer
	mlt_consumer consumer = &self->parent;

	// Get the properties
	mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );

	// internal intialization
	mlt_frame frame = NULL;
	int last_position = -1;
	int eos = 0;
	int eos_threshold = 20;
	if ( self->play )
		eos_threshold = eos_threshold + mlt_properties_get_int( MLT_CONSUMER_PROPERTIES( self->play ), "buffer" );

	// Determine if the application is dealing with the preview
	int preview_off = mlt_properties_get_int( properties, "preview_off" );

	pthread_mutex_lock( &self->refresh_mutex );
	self->refresh_count = 0;
	pthread_mutex_unlock( &self->refresh_mutex );

	// Loop until told not to
	while( self->running )
	{
		// Get a frame from the attached producer
		frame = mlt_consumer_get_frame( consumer );

		// Ensure that we have a frame
		if ( self->running && frame != NULL )
		{
			// Get the speed of the frame
			double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" );

			// Lock during the operation
			mlt_service_lock( MLT_CONSUMER_SERVICE( consumer ) );

			// Get refresh request for the current frame
			int refresh = mlt_properties_get_int( properties, "refresh" );

			// Decrement refresh and clear changed
			mlt_events_block( properties, properties );
			mlt_properties_set_int( properties, "refresh", 0 );
			mlt_events_unblock( properties, properties );

			// Unlock after the operation
			mlt_service_unlock( MLT_CONSUMER_SERVICE( consumer ) );

			// Set the changed property on this frame for the benefit of still
			mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "refresh", refresh );

			// Make sure the recipient knows that this frame isn't really rendered
			mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 0 );

			// Optimisation to reduce latency
			if ( speed == 1.0 )
			{
				if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) )
					mlt_consumer_purge( self->play );
				last_position = mlt_frame_get_position( frame );
			}
			else
			{
				//mlt_consumer_purge( self->play );
				last_position = -1;
			}

			// If we aren't playing normally, then use the still
			if ( speed != 1 )
			{
				mlt_producer producer = MLT_PRODUCER( mlt_service_get_producer( MLT_CONSUMER_SERVICE( consumer ) ) );
				mlt_position duration = producer? mlt_producer_get_playtime( producer ) : -1;
				int pause = 0;

#ifndef SKIP_WAIT_EOS
				if ( self->active == self->play )
				{
					// Do not interrupt the play consumer near the end
					if ( duration - self->last_position > eos_threshold )
					{
						// Get a new frame at the sought position
						mlt_frame_close( frame );
						if ( producer )
							mlt_producer_seek( producer, self->last_position );
						frame = mlt_consumer_get_frame( consumer );
						pause = 1;
					}
					else
					{
						// Send frame with speed 0 to stop it
						if ( frame && !mlt_consumer_is_stopped( self->play ) )
						{
							mlt_consumer_put_frame( self->play, frame );
							frame = NULL;
							eos = 1;
						}

						// Check for end of stream
						if ( mlt_consumer_is_stopped( self->play ) )
						{
							// Stream has ended
							mlt_log_verbose( MLT_CONSUMER_SERVICE( consumer ), "END OF STREAM\n" );
							pause = 1;
							eos = 0; // reset eos indicator
						}
						else
						{
							// Prevent a tight busy loop
							struct timespec tm = { 0, 100000L }; // 100 usec
							nanosleep( &tm, NULL );
						}
					}
				}
#else
				pause = self->active == self->play;
#endif
				if ( pause )
				{
					// Start the still consumer
					if ( !mlt_consumer_is_stopped( self->play ) )
						mlt_consumer_stop( self->play );
					self->last_speed = speed;
					self->active = self->still;
					self->ignore_change = 0;
					mlt_consumer_start( self->still );
				}
				// Send the frame to the active child
				if ( frame && !eos )
				{
					mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "refresh", 1 );
					if ( self->active )
						mlt_consumer_put_frame( self->active, frame );
				}
				if ( pause && speed == 0.0 )
				{
					mlt_events_fire( properties, "consumer-sdl-paused", NULL );
				}
			}
			// Allow a little grace time before switching consumers on speed changes
			else if ( self->ignore_change -- > 0 && self->active != NULL && !mlt_consumer_is_stopped( self->active ) )
			{
				mlt_consumer_put_frame( self->active, frame );
			}
			// Otherwise use the normal player
			else
			{
				if ( !mlt_consumer_is_stopped( self->still ) )
					mlt_consumer_stop( self->still );
				if ( mlt_consumer_is_stopped( self->play ) )
				{
					self->last_speed = speed;
					self->active = self->play;
					self->ignore_change = 0;
					mlt_consumer_start( self->play );
				}
				if ( self->play )
					mlt_consumer_put_frame( self->play, frame );
			}

			// Copy the rectangle info from the active consumer
			if ( self->running && preview_off == 0 && self->active )
			{
				mlt_properties active = MLT_CONSUMER_PROPERTIES( self->active );
				mlt_service_lock( MLT_CONSUMER_SERVICE( consumer ) );
				mlt_properties_set_int( properties, "rect_x", mlt_properties_get_int( active, "rect_x" ) );
				mlt_properties_set_int( properties, "rect_y", mlt_properties_get_int( active, "rect_y" ) );
				mlt_properties_set_int( properties, "rect_w", mlt_properties_get_int( active, "rect_w" ) );
				mlt_properties_set_int( properties, "rect_h", mlt_properties_get_int( active, "rect_h" ) );
				mlt_service_unlock( MLT_CONSUMER_SERVICE( consumer ) );
			}

			if ( self->active == self->still )
			{
				pthread_mutex_lock( &self->refresh_mutex );
				if ( self->running && speed == 0 && self->refresh_count <= 0 )
				{
					mlt_events_fire( properties, "consumer-sdl-paused", NULL );
					pthread_cond_wait( &self->refresh_cond, &self->refresh_mutex );
				}
				self->refresh_count --;
				pthread_mutex_unlock( &self->refresh_mutex );
			}
		}
		else
		{
			if ( frame ) mlt_frame_close( frame );
			mlt_consumer_put_frame( self->active, NULL );
			self->running = 0;
		}
	}

	if ( self->play ) mlt_consumer_stop( self->play );
	if ( self->still ) mlt_consumer_stop( self->still );

	return NULL;
}
Example #11
0
static void *video_thread( void *arg )
{
	// Identify the arg
	consumer_sdl self = arg;

	// Obtain time of thread start
	struct timeval now;
	int64_t start = 0;
	int64_t elapsed = 0;
	struct timespec tm;
	mlt_frame next = NULL;
	mlt_properties properties = NULL;
	double speed = 0;

	// Get real time flag
	int real_time = mlt_properties_get_int( self->properties, "real_time" );

#if !defined(__APPLE__) && !defined(_WIN32)
	if ( setup_sdl_video(self) )
		self->running = 0;
#endif

	// Determine start time
	gettimeofday( &now, NULL );
	start = ( int64_t )now.tv_sec * 1000000 + now.tv_usec;

	while ( self->running )
	{
		// Pop the next frame
		pthread_mutex_lock( &self->video_mutex );
		next = mlt_deque_pop_front( self->queue );
		while ( next == NULL && self->running )
		{
			pthread_cond_wait( &self->video_cond, &self->video_mutex );
			next = mlt_deque_pop_front( self->queue );
		}
		pthread_mutex_unlock( &self->video_mutex );

		if ( !self->running || next == NULL ) break;

		// Get the properties
		properties =  MLT_FRAME_PROPERTIES( next );

		// Get the speed of the frame
		speed = mlt_properties_get_double( properties, "_speed" );

		// Get the current time
		gettimeofday( &now, NULL );

		// Get the elapsed time
		elapsed = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - start;

		// See if we have to delay the display of the current frame
		if ( mlt_properties_get_int( properties, "rendered" ) == 1 && self->running )
		{
			// Obtain the scheduled playout time
			int64_t scheduled = mlt_properties_get_int( properties, "playtime" );

			// Determine the difference between the elapsed time and the scheduled playout time
			int64_t difference = scheduled - elapsed;

			// Smooth playback a bit
			if ( real_time && ( difference > 20000 && speed == 1.0 ) )
			{
				tm.tv_sec = difference / 1000000;
				tm.tv_nsec = ( difference % 1000000 ) * 500;
				nanosleep( &tm, NULL );
			}

			// Show current frame if not too old
			if ( !real_time || ( difference > -10000 || speed != 1.0 || mlt_deque_count( self->queue ) < 2 ) )
				consumer_play_video( self, next );

			// If the queue is empty, recalculate start to allow build up again
			if ( real_time && ( mlt_deque_count( self->queue ) == 0 && speed == 1.0 ) )
			{
				gettimeofday( &now, NULL );
				start = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - scheduled + 20000;
			}
		}
		else
		{
			static int dropped = 0;
			mlt_log_info( MLT_CONSUMER_SERVICE(&self->parent), "dropped video frame %d\n", ++dropped );
		}

		// This frame can now be closed
		mlt_frame_close( next );
		next = NULL;
	}

	if ( next != NULL )
		mlt_frame_close( next );

	mlt_consumer_stopped( &self->parent );

	return NULL;
}
Example #12
0
int mlt_consumer_start( mlt_consumer self )
{
	if ( !mlt_consumer_is_stopped( self ) )
		return 0;

	// Stop listening to the property-changed event
	mlt_event_block( self->event_listener );

	// Get the properies
	mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );

	// Determine if there's a test card producer
	char *test_card = mlt_properties_get( properties, "test_card" );

	// Just to make sure nothing is hanging around...
	pthread_mutex_lock( &self->put_mutex );
	self->put = NULL;
	self->put_active = 1;
	pthread_mutex_unlock( &self->put_mutex );

	// Deal with it now.
	if ( test_card != NULL )
	{
		if ( mlt_properties_get_data( properties, "test_card_producer", NULL ) == NULL )
		{
			// Create a test card producer
			mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) );
			mlt_producer producer = mlt_factory_producer( profile, NULL, test_card );

			// Do we have a producer
			if ( producer != NULL )
			{
				// Test card should loop I guess...
				mlt_properties_set( MLT_PRODUCER_PROPERTIES( producer ), "eof", "loop" );
				//mlt_producer_set_speed( producer, 0 );
				//mlt_producer_set_in_and_out( producer, 0, 0 );

				// Set the test card on the consumer
				mlt_properties_set_data( properties, "test_card_producer", producer, 0, ( mlt_destructor )mlt_producer_close, NULL );
			}
		}
	}
	else
	{
		// Allow the hash table to speed things up
		mlt_properties_set_data( properties, "test_card_producer", NULL, 0, NULL, NULL );
	}

	// The profile could have changed between a stop and a restart.
	apply_profile_properties( self, mlt_service_profile( MLT_CONSUMER_SERVICE(self) ), properties );

	// Set the frame duration in microseconds for the frame-dropping heuristic
	int frame_rate_num = mlt_properties_get_int( properties, "frame_rate_num" );
	int frame_rate_den = mlt_properties_get_int( properties, "frame_rate_den" );
	int frame_duration = 0;

	if ( frame_rate_num && frame_rate_den )
	{
		frame_duration = 1000000 / frame_rate_num * frame_rate_den;
	}

	mlt_properties_set_int( properties, "frame_duration", frame_duration );

	// Check and run an ante command
	if ( mlt_properties_get( properties, "ante" ) )
		if ( system( mlt_properties_get( properties, "ante" ) ) == -1 )
			mlt_log( MLT_CONSUMER_SERVICE( self ), MLT_LOG_ERROR, "system(%s) failed!\n", mlt_properties_get( properties, "ante" ) );

	// Set the real_time preference
	self->real_time = mlt_properties_get_int( properties, "real_time" );

	// For worker threads implementation, buffer must be at least # threads
	if ( abs( self->real_time ) > 1 && mlt_properties_get_int( properties, "buffer" ) <= abs( self->real_time ) )
		mlt_properties_set_int( properties, "_buffer", abs( self->real_time ) + 1 );

	// Get the image format to use for rendering threads
	const char* format = mlt_properties_get( properties, "mlt_image_format" );
	if ( format )
	{
		if ( !strcmp( format, "rgb24" ) )
			self->format = mlt_image_rgb24;
		else if ( !strcmp( format, "rgb24a" ) )
			self->format = mlt_image_rgb24a;
		else if ( !strcmp( format, "yuv420p" ) )
			self->format = mlt_image_yuv420p;
		else if ( !strcmp( format, "none" ) )
			self->format = mlt_image_none;
		else
			self->format = mlt_image_yuv422;
	}

	// Start the service
	if ( self->start != NULL )
		return self->start( self );

	return 0;
}
Example #13
0
static void mlt_consumer_property_changed( mlt_properties owner, mlt_consumer self, char *name )
{
	if ( !strcmp( name, "mlt_profile" ) )
	{
		// Get the properies
		mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );

		// Get the current profile
		mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) );

		// Load the new profile
		mlt_profile new_profile = mlt_profile_init( mlt_properties_get( properties, name ) );

		if ( new_profile )
		{
			// Copy the profile
			if ( profile != NULL )
			{
				free( profile->description );
				memcpy( profile, new_profile, sizeof( struct mlt_profile_s ) );
				profile->description = strdup( new_profile->description );
			}
			else
			{
				profile = new_profile;
			}

			// Apply to properties
			apply_profile_properties( self, profile, properties );
			mlt_profile_close( new_profile );
		}
 	}
	else if ( !strcmp( name, "frame_rate_num" ) )
	{
		mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );
		mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) );
		if ( profile )
		{
			profile->frame_rate_num = mlt_properties_get_int( properties, "frame_rate_num" );
			mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) );
		}
	}
	else if ( !strcmp( name, "frame_rate_den" ) )
	{
		mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );
		mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) );
		if ( profile )
		{
			profile->frame_rate_den = mlt_properties_get_int( properties, "frame_rate_den" );
			mlt_properties_set_double( properties, "fps", mlt_profile_fps( profile ) );
		}
	}
	else if ( !strcmp( name, "width" ) )
	{
		mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );
		mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) );
		if ( profile )
			profile->width = mlt_properties_get_int( properties, "width" );
	}
	else if ( !strcmp( name, "height" ) )
	{
		mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );
		mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) );
		if ( profile )
			profile->height = mlt_properties_get_int( properties, "height" );
	}
	else if ( !strcmp( name, "progressive" ) )
	{
		mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );
		mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) );
		if ( profile )
			profile->progressive = mlt_properties_get_int( properties, "progressive" );
	}
	else if ( !strcmp( name, "sample_aspect_num" ) )
	{
		mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );
		mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) );
		if ( profile )
		{
			profile->sample_aspect_num = mlt_properties_get_int( properties, "sample_aspect_num" );
			mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile )  );
		}
	}
	else if ( !strcmp( name, "sample_aspect_den" ) )
	{
		mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );
		mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) );
		if ( profile )
		{
			profile->sample_aspect_den = mlt_properties_get_int( properties, "sample_aspect_den" );
			mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile )  );
		}
	}
	else if ( !strcmp( name, "display_aspect_num" ) )
	{
		mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );
		mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) );
		if ( profile )
		{
			profile->display_aspect_num = mlt_properties_get_int( properties, "display_aspect_num" );
			mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile )  );
		}
	}
	else if ( !strcmp( name, "display_aspect_den" ) )
	{
		mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );
		mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) );
		if ( profile )
		{
			profile->display_aspect_den = mlt_properties_get_int( properties, "display_aspect_den" );
			mlt_properties_set_double( properties, "display_ratio", mlt_profile_dar( profile )  );
		}
	}
	else if ( !strcmp( name, "colorspace" ) )
	{
		mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );
		mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( self ) );
		if ( profile )
			profile->colorspace = mlt_properties_get_int( properties, "colorspace" );
	}
}
Example #14
0
mlt_consumer consumer_sdl_audio_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
{
	// Create the consumer object
	consumer_sdl self = calloc( 1, sizeof( struct consumer_sdl_s ) );

	// If no malloc'd and consumer init ok
	if ( self != NULL && mlt_consumer_init( &self->parent, self, profile ) == 0 )
	{
		// Create the queue
		self->queue = mlt_deque_init( );

		// Get the parent consumer object
		mlt_consumer parent = &self->parent;

		// We have stuff to clean up, so override the close method
		parent->close = consumer_close;

		// get a handle on properties
		mlt_service service = MLT_CONSUMER_SERVICE( parent );
		self->properties = MLT_SERVICE_PROPERTIES( service );

		// Set the default volume
		mlt_properties_set_double( self->properties, "volume", 1.0 );

		// This is the initialisation of the consumer
		pthread_mutex_init( &self->audio_mutex, NULL );
		pthread_cond_init( &self->audio_cond, NULL);
		pthread_mutex_init( &self->video_mutex, NULL );
		pthread_cond_init( &self->video_cond, NULL);

		// Default scaler (for now we'll use nearest)
		mlt_properties_set( self->properties, "rescale", "nearest" );
		mlt_properties_set( self->properties, "deinterlace_method", "onefield" );
		mlt_properties_set_int( self->properties, "top_field_first", -1 );

		// Default buffer for low latency
		mlt_properties_set_int( self->properties, "buffer", 1 );

		// Default audio buffer
		mlt_properties_set_int( self->properties, "audio_buffer", 2048 );

		// Ensure we don't join on a non-running object
		self->joined = 1;
		
		// Allow thread to be started/stopped
		parent->start = consumer_start;
		parent->stop = consumer_stop;
		parent->is_stopped = consumer_is_stopped;
		parent->purge = consumer_purge;

		// Initialize the refresh handler
		pthread_cond_init( &self->refresh_cond, NULL );
		pthread_mutex_init( &self->refresh_mutex, NULL );
		mlt_events_listen( MLT_CONSUMER_PROPERTIES( parent ), self, "property-changed", ( mlt_listener )consumer_refresh_cb );

		// Return the consumer produced
		return parent;
	}

	// malloc or consumer init failed
	free( self );

	// Indicate failure
	return NULL;
}
Example #15
0
static int consumer_play_audio( consumer_sdl self, mlt_frame frame, int init_audio, int *duration )
{
	// Get the properties of self consumer
	mlt_properties properties = self->properties;
	mlt_audio_format afmt = mlt_audio_s16;

	// Set the preferred params of the test card signal
	int channels = mlt_properties_get_int( properties, "channels" );
	int dest_channels = channels;
	int frequency = mlt_properties_get_int( properties, "frequency" );
	static int counter = 0;

	int samples = mlt_sample_calculator( mlt_properties_get_double( self->properties, "fps" ), frequency, counter++ );
	
	int16_t *pcm;
	int bytes;

	mlt_frame_get_audio( frame, (void**) &pcm, &afmt, &frequency, &channels, &samples );
	*duration = ( ( samples * 1000 ) / frequency );
	pcm += mlt_properties_get_int( properties, "audio_offset" );

	if ( mlt_properties_get_int( properties, "audio_off" ) )
	{
		self->playing = 1;
		init_audio = 1;
		return init_audio;
	}

	if ( init_audio == 1 )
	{
		SDL_AudioSpec request;
		SDL_AudioSpec got;

		int audio_buffer = mlt_properties_get_int( properties, "audio_buffer" );

		// specify audio format
		memset( &request, 0, sizeof( SDL_AudioSpec ) );
		self->playing = 0;
		request.freq = frequency;
		request.format = AUDIO_S16SYS;
		request.channels = dest_channels;
		request.samples = audio_buffer;
		request.callback = sdl_fill_audio;
		request.userdata = (void *)self;
		if ( SDL_OpenAudio( &request, &got ) != 0 )
		{
			mlt_log_error( MLT_CONSUMER_SERVICE( self ), "SDL failed to open audio: %s\n", SDL_GetError() );
			init_audio = 2;
		}
		else if ( got.size != 0 )
		{
			SDL_PauseAudio( 0 );
			init_audio = 0;
		}
	}

	if ( init_audio == 0 )
	{
		mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
		
		bytes = samples * dest_channels * sizeof(*pcm);
		pthread_mutex_lock( &self->audio_mutex );
		while ( self->running && bytes > ( sizeof( self->audio_buffer) - self->audio_avail ) )
			pthread_cond_wait( &self->audio_cond, &self->audio_mutex );
		if ( self->running )
		{
			if ( mlt_properties_get_double( properties, "_speed" ) == 1 )
			{
				if ( channels == dest_channels )
				{
					memcpy( &self->audio_buffer[ self->audio_avail ], pcm, bytes );
				}
				else
				{
					int16_t *dest = (int16_t*) &self->audio_buffer[ self->audio_avail ];
					int i = samples + 1;
					
					while ( --i )
					{
						memcpy( dest, pcm, dest_channels * sizeof(*pcm) );
						pcm += channels;
						dest += dest_channels;
					}
				}
			}
			else
			{
				memset( &self->audio_buffer[ self->audio_avail ], 0, bytes );
			}
			self->audio_avail += bytes;
		}
		pthread_cond_broadcast( &self->audio_cond );
		pthread_mutex_unlock( &self->audio_mutex );
	}
	else
	{
		self->playing = 1;
	}

	return init_audio;
}
Example #16
0
mlt_frame mlt_consumer_get_frame( mlt_consumer self )
{
	// Frame to return
	mlt_frame frame = NULL;

	// Get the service assoicated to the consumer
	mlt_service service = MLT_CONSUMER_SERVICE( self );

	// Get the consumer properties
	mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );

	// Get the frame
	if ( mlt_service_producer( service ) == NULL && mlt_properties_get_int( properties, "put_mode" ) )
	{
		struct timeval now;
		struct timespec tm;
		pthread_mutex_lock( &self->put_mutex );
		while ( self->put_active && self->put == NULL )
		{
			gettimeofday( &now, NULL );
			tm.tv_sec = now.tv_sec + 1;
			tm.tv_nsec = now.tv_usec * 1000;
			pthread_cond_timedwait( &self->put_cond, &self->put_mutex, &tm );
		}
		frame = self->put;
		self->put = NULL;
		pthread_cond_broadcast( &self->put_cond );
		pthread_mutex_unlock( &self->put_mutex );
		if ( frame != NULL )
			mlt_service_apply_filters( service, frame, 0 );
	}
	else if ( mlt_service_producer( service ) != NULL )
	{
		mlt_service_get_frame( service, &frame, 0 );
	}
	else
	{
		frame = mlt_frame_init( service );
	}

	if ( frame != NULL )
	{
		// Get the frame properties
		mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame );

		// Get the test card producer
		mlt_producer test_card = mlt_properties_get_data( properties, "test_card_producer", NULL );

		// Attach the test frame producer to it.
		if ( test_card != NULL )
			mlt_properties_set_data( frame_properties, "test_card_producer", test_card, 0, NULL, NULL );

		// Pass along the interpolation and deinterlace options
		// TODO: get rid of consumer_deinterlace and use profile.progressive
		mlt_properties_set( frame_properties, "rescale.interp", mlt_properties_get( properties, "rescale" ) );
		mlt_properties_set_int( frame_properties, "consumer_deinterlace", mlt_properties_get_int( properties, "progressive" ) | mlt_properties_get_int( properties, "deinterlace" ) );
		mlt_properties_set( frame_properties, "deinterlace_method", mlt_properties_get( properties, "deinterlace_method" ) );
		mlt_properties_set_int( frame_properties, "consumer_tff", mlt_properties_get_int( properties, "top_field_first" ) );
	}

	// Return the frame
	return frame;
}
Example #17
0
static int setup_sdl_video( consumer_sdl self )
{
	int error = 0;
	int sdl_flags = SDL_WINDOW_RESIZABLE;
	int texture_format = SDL_PIXELFORMAT_YUY2;

	// Skip this if video is disabled.
	int video_off = mlt_properties_get_int( self->properties, "video_off" );
	int preview_off = mlt_properties_get_int( self->properties, "preview_off" );
	if ( video_off || preview_off )
		return error;

	if (!SDL_WasInit(SDL_INIT_VIDEO))
	{
		pthread_mutex_lock( &mlt_sdl_mutex );
		int ret = SDL_Init( SDL_INIT_VIDEO );
		pthread_mutex_unlock( &mlt_sdl_mutex );
		if ( ret < 0 )
		{
			mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to initialize SDL: %s\n", SDL_GetError() );
			return -1;
		}
	}

#if 0 // only yuv422 working currently
	int image_format = mlt_properties_get_int( self->properties, "mlt_image_format" );

	if ( image_format ) switch ( image_format ) {
	case mlt_image_rgb24:
		texture_format = SDL_PIXELFORMAT_RGB24;
		break;
	case mlt_image_rgb24a:
		texture_format = SDL_PIXELFORMAT_ABGR8888;
		break;
	case mlt_image_yuv420p:
		texture_format = SDL_PIXELFORMAT_IYUV;
		break;
	case mlt_image_yuv422:
		texture_format = SDL_PIXELFORMAT_YUY2;
		break;
	default:
		mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Invalid image format %s\n",
			mlt_image_format_name( image_format ) );
		return -1;
	}
#endif

	if ( mlt_properties_get_int( self->properties, "fullscreen" ) )
	{
		self->window_width = self->width;
		self->window_height = self->height;
		sdl_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
		SDL_ShowCursor( SDL_DISABLE );
	}

	pthread_mutex_lock( &mlt_sdl_mutex );
	self->sdl_window = SDL_CreateWindow("MLT", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
		self->window_width, self->window_height, sdl_flags);
	self->sdl_renderer = SDL_CreateRenderer(self->sdl_window, -1, SDL_RENDERER_ACCELERATED);
	if ( self->sdl_renderer )
	{
		self->sdl_texture = SDL_CreateTexture( self->sdl_renderer, texture_format,
			SDL_TEXTUREACCESS_STREAMING, self->window_width, self->window_height );
		if ( self->sdl_texture ) {
			SDL_SetRenderDrawColor( self->sdl_renderer, 0, 0, 0, 255);
		} else {
			mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to create SDL texture: %s\n", SDL_GetError() );
			error = -1;
		}
	} else {
		mlt_log_error( MLT_CONSUMER_SERVICE(&self->parent), "Failed to create SDL renderer: %s\n", SDL_GetError() );
		error = -1;
	}
	pthread_mutex_unlock( &mlt_sdl_mutex );

	return error;
}
Example #18
0
static void *consumer_worker_thread( void *arg )
{
	// The argument is the consumer
	mlt_consumer self = arg;

	// Get the properties of the consumer
	mlt_properties properties = MLT_CONSUMER_PROPERTIES( self );

	// Get the width and height
	int width = mlt_properties_get_int( properties, "width" );
	int height = mlt_properties_get_int( properties, "height" );
	mlt_image_format format = self->format;

	// See if video is turned off
	int video_off = mlt_properties_get_int( properties, "video_off" );
	int preview_off = mlt_properties_get_int( properties, "preview_off" );
	int preview_format = mlt_properties_get_int( properties, "preview_format" );

	// General frame variable
	mlt_frame frame = NULL;
	uint8_t *image = NULL;

	if ( preview_off && preview_format != 0 )
		format = preview_format;

	// Continue to read ahead
	while ( self->ahead )
	{
		// Get the next unprocessed frame from the work queue
		pthread_mutex_lock( &self->queue_mutex );
		int index = first_unprocessed_frame( self );
		while ( self->ahead && index >= mlt_deque_count( self->queue ) )
		{
			mlt_log_debug( MLT_CONSUMER_SERVICE(self), "waiting in worker index = %d queue count = %d\n",
				index, mlt_deque_count( self->queue ) );
			pthread_cond_wait( &self->queue_cond, &self->queue_mutex );
			index = first_unprocessed_frame( self );
		}

		// Mark the frame for processing
		frame = mlt_deque_peek( self->queue, index );
		if ( frame )
		{
			mlt_log_debug( MLT_CONSUMER_SERVICE(self), "worker processing index = %d frame %d queue count = %d\n",
				index, mlt_frame_get_position(frame), mlt_deque_count( self->queue ) );
			frame->is_processing = 1;
			mlt_properties_inc_ref( MLT_FRAME_PROPERTIES( frame ) );
		}
		pthread_mutex_unlock( &self->queue_mutex );

		// If there's no frame, we're probably stopped...
		if ( frame == NULL )
			continue;

#ifdef DEINTERLACE_ON_NOT_NORMAL_SPEED
		// All non normal playback frames should be shown
		if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "_speed" ) != 1 )
			mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace", 1 );
#endif

		// Get the image
		if ( !video_off )
		{
			// Fetch width/height again
			width = mlt_properties_get_int( properties, "width" );
			height = mlt_properties_get_int( properties, "height" );
			mlt_events_fire( MLT_CONSUMER_PROPERTIES( self ), "consumer-frame-render", frame, NULL );
			mlt_frame_get_image( frame, &image, &format, &width, &height, 0 );
		}
		mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "rendered", 1 );
		mlt_frame_close( frame );

		// Tell a waiting thread (non-realtime main consumer thread) that we are done.
		pthread_mutex_lock( &self->done_mutex );
		pthread_cond_broadcast( &self->done_cond );
		pthread_mutex_unlock( &self->done_mutex );
	}

	return NULL;
}
Example #19
0
mlt_consumer consumer_sdl2_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
{
	// Create the consumer object
	consumer_sdl self = calloc( 1, sizeof( struct consumer_sdl_s ) );

	// If no malloc'd and consumer init ok
	if ( self != NULL && mlt_consumer_init( &self->parent, self, profile ) == 0 )
	{
		// Create the queue
		self->queue = mlt_deque_init( );

		// Get the parent consumer object
		mlt_consumer parent = &self->parent;

		// We have stuff to clean up, so override the close method
		parent->close = consumer_close;

		// get a handle on properties
		mlt_service service = MLT_CONSUMER_SERVICE( parent );
		self->properties = MLT_SERVICE_PROPERTIES( service );

		// Set the default volume
		mlt_properties_set_double( self->properties, "volume", 1.0 );

		// This is the initialisation of the consumer
		pthread_mutex_init( &self->audio_mutex, NULL );
		pthread_cond_init( &self->audio_cond, NULL);
		pthread_mutex_init( &self->video_mutex, NULL );
		pthread_cond_init( &self->video_cond, NULL);
		
		// Default scaler (for now we'll use nearest)
		mlt_properties_set( self->properties, "rescale", "nearest" );
		mlt_properties_set( self->properties, "deinterlace_method", "onefield" );
		mlt_properties_set_int( self->properties, "top_field_first", -1 );

		// Default buffer for low latency
		mlt_properties_set_int( self->properties, "buffer", 1 );

		// Default audio buffer
		mlt_properties_set_int( self->properties, "audio_buffer", 2048 );
#if defined(_WIN32) && SDL_MAJOR_VERSION == 2
		mlt_properties_set( self->properties, "audio_driver", "DirectSound" );
#endif

		// Ensure we don't join on a non-running object
		self->joined = 1;
		
		// process actual param
		if ( arg && sscanf( arg, "%dx%d", &self->width, &self->height ) )
		{
			mlt_properties_set_int( self->properties, "resolution", 1 );
		}
		else
		{
			self->width = mlt_properties_get_int( self->properties, "width" );
			self->height = mlt_properties_get_int( self->properties, "height" );
		}
	
		// Allow thread to be started/stopped
		parent->start = consumer_start;
		parent->stop = consumer_stop;
		parent->is_stopped = consumer_is_stopped;
		parent->purge = consumer_purge;

		// Register specific events
		mlt_events_register( self->properties, "consumer-sdl-event", ( mlt_transmitter )consumer_sdl_event );

		// Return the consumer produced
		return parent;
	}

	// malloc or consumer init failed
	free( self );

	// Indicate failure
	return NULL;
}
Example #20
0
static int consumer_play_audio( consumer_sdl self, mlt_frame frame, int init_audio, int *duration )
{
	// Get the properties of self consumer
	mlt_properties properties = self->properties;
	mlt_audio_format afmt = mlt_audio_s16;

	// Set the preferred params of the test card signal
	int channels = mlt_properties_get_int( properties, "channels" );
	int frequency = mlt_properties_get_int( properties, "frequency" );
	int scrub = mlt_properties_get_int( properties, "scrub_audio" );
	static int counter = 0;

	int samples = mlt_sample_calculator( mlt_properties_get_double( self->properties, "fps" ), frequency, counter++ );
	int16_t *pcm;
	mlt_frame_get_audio( frame, (void**) &pcm, &afmt, &frequency, &channels, &samples );
	*duration = ( ( samples * 1000 ) / frequency );
	pcm += mlt_properties_get_int( properties, "audio_offset" );

	if ( mlt_properties_get_int( properties, "audio_off" ) )
	{
		self->playing = 1;
		init_audio = 1;
		return init_audio;
	}

	if ( init_audio == 1 )
	{
		SDL_AudioSpec request;
		SDL_AudioSpec got;
		SDL_AudioDeviceID dev;
		int audio_buffer = mlt_properties_get_int( properties, "audio_buffer" );

		// specify audio format
		memset( &request, 0, sizeof( SDL_AudioSpec ) );
		self->playing = 0;
		request.freq = frequency;
		request.format = AUDIO_S16SYS;
		request.channels = mlt_properties_get_int( properties, "channels" );
		request.samples = audio_buffer;
		request.callback = sdl_fill_audio;
		request.userdata = (void *)self;

		dev = sdl2_open_audio( &request, &got );
		if( dev == 0 )
		{
			mlt_log_error( MLT_CONSUMER_SERVICE( self ), "SDL failed to open audio\n" );
			init_audio = 2;
		}
		else
		{
			if( got.channels != request.channels )
			{
				mlt_log_info( MLT_CONSUMER_SERVICE( self ), "Unable to output %d channels. Change to %d\n", request.channels, got.channels );
			}
			mlt_log_info( MLT_CONSUMER_SERVICE( self ), "Audio Opened: driver=%s channels=%d frequency=%d\n", SDL_GetCurrentAudioDriver(), got.channels, got.freq );
			SDL_PauseAudioDevice( dev, 0 );
			init_audio = 0;
			self->out_channels = got.channels;
		}
	}

	if ( init_audio == 0 )
	{
		mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
		int samples_copied = 0;
		int dst_stride = self->out_channels * sizeof( *pcm );

		pthread_mutex_lock( &self->audio_mutex );

		while ( self->running && samples_copied < samples )
		{
			int sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride;
			while ( self->running && sample_space == 0 )
			{
				pthread_cond_wait( &self->audio_cond, &self->audio_mutex );
				sample_space = ( sizeof( self->audio_buffer ) - self->audio_avail ) / dst_stride;
			}
			if ( self->running )
			{
				int samples_to_copy = samples - samples_copied;
				if ( samples_to_copy > sample_space )
				{
					samples_to_copy = sample_space;
				}
				int dst_bytes = samples_to_copy * dst_stride;

				if ( scrub || mlt_properties_get_double( properties, "_speed" ) == 1 )
				{
					if ( channels == self->out_channels )
					{
						memcpy( &self->audio_buffer[ self->audio_avail ], pcm, dst_bytes );
						pcm += samples_to_copy * channels;
					}
					else
					{
						int16_t *dest = (int16_t*) &self->audio_buffer[ self->audio_avail ];
						int i = samples_to_copy + 1;
						while ( --i )
						{
							memcpy( dest, pcm, dst_stride );
							pcm += channels;
							dest += self->out_channels;
						}
					}
				}
				else
				{
					memset( &self->audio_buffer[ self->audio_avail ], 0, dst_bytes );
					pcm += samples_to_copy * channels;
				}
				self->audio_avail += dst_bytes;
				samples_copied += samples_to_copy;
			}
			pthread_cond_broadcast( &self->audio_cond );
		}
		pthread_mutex_unlock( &self->audio_mutex );
	}
	else
	{
		self->playing = 1;
	}

	return init_audio;
}