timecode in - in point
timecode out - out point
double fps - output frames per second (default: 25)
+ double aspect_ratio - aspect ratio of video
Read Only Properties
Read Only Properties
double fps - output frames per second
+ double aspect_ratio - aspect ratio of video
Dependencies
'file' is not populated on properties?
mcdv
+
+ Description:
+
+ Mainconcept based dv decoder for video and audio.
+
+ Constructor argument:
+
+ 'file' - produce a/v from file
+
+ Initialisation Properties
+
+ string file - file location
+ timecode in - in point
+ timecode out - out point
+
+ Read Only Properties
+
+ double fps - output frames per second
+ double aspect_ratio - aspect ratio of video
+
+ Dependencies
+
+ mainconcept dv sdk and libdv.
+
+ Known Bugs
+
+ 'file' is not populated on properties?
+
mcmpeg
+
+ Description:
+
+ Mainconcept based mpeg decoder for video and audio.
+
+ Constructor argument:
+
+ 'file' - produce a/v from file
+
+ Initialisation Properties
+
+ string file - file location
+ timecode in - in point
+ timecode out - out point
+
+ Read Only Properties
+
+ double fps - output frames per second
+ double aspect_ratio - aspect ratio of video
+
+ Dependencies
+
+ mainconcept mpeg sdk.
+
+ Known Bugs
+
+ 'file' is not populated on properties?
+
pango
pixbuf
ppm
---------
bluefish
+ ffmpeg
sdl
timecode in - in point
timecode out - out point
double fps - output frames per second (default: 25)
+ double aspect_ratio - aspect ratio of video
Read Only Properties
Read Only Properties
double fps - output frames per second
+ double aspect_ratio - aspect ratio of video
Dependencies
'file' is not populated on properties?
mcdv
+
+ Description:
+
+ Mainconcept based dv decoder for video and audio.
+
+ Constructor argument:
+
+ 'file' - produce a/v from file
+
+ Initialisation Properties
+
+ string file - file location
+ timecode in - in point
+ timecode out - out point
+
+ Read Only Properties
+
+ double fps - output frames per second
+ double aspect_ratio - aspect ratio of video
+
+ Dependencies
+
+ mainconcept dv sdk and libdv.
+
+ Known Bugs
+
+ 'file' is not populated on properties?
+
mcmpeg
+
+ Description:
+
+ Mainconcept based mpeg decoder for video and audio.
+
+ Constructor argument:
+
+ 'file' - produce a/v from file
+
+ Initialisation Properties
+
+ string file - file location
+ timecode in - in point
+ timecode out - out point
+
+ Read Only Properties
+
+ double fps - output frames per second
+ double aspect_ratio - aspect ratio of video
+
+ Dependencies
+
+ mainconcept mpeg sdk.
+
+ Known Bugs
+
+ 'file' is not populated on properties?
+
pango
pixbuf
ppm
---------
bluefish
+ ffmpeg
sdl
#include "config.h"
#include "mlt_multitrack.h"
+#include "mlt_playlist.h"
#include "mlt_frame.h"
#include <stdio.h>
mlt_producer producer = &this->parent;
if ( mlt_producer_init( producer, this ) == 0 )
{
+ mlt_properties properties = mlt_multitrack_properties( this );
producer->get_frame = producer_get_frame;
+ mlt_properties_set_data( properties, "multitrack", this, 0, NULL, NULL );
}
else
{
// Update multitrack properties now - we'll not destroy the in point here
mlt_properties_set_timecode( properties, "length", length );
mlt_properties_set_timecode( properties, "out", length );
- mlt_properties_set_timecode( properties, "playtime", length - mlt_properties_get_timecode( properties, "in" ) );
mlt_properties_set_double( properties, "fps", fps );
}
return 0;
}
+/** Determine the clip point.
+*/
+
+mlt_timecode mlt_multitrack_clip( mlt_multitrack this, mlt_whence whence, int index )
+{
+ int first = 1;
+ mlt_timecode position = 0;
+ int i = 0;
+
+ for ( i = 0; i < this->count; i ++ )
+ {
+ // Get the producer
+ mlt_producer producer = this->list[ i ];
+
+ if ( producer != NULL )
+ {
+ // Get the properties of this producer
+ mlt_properties properties = mlt_producer_properties( producer );
+
+ // Determine if it's a playlist
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+
+ // We only consider playlists
+ if ( playlist != NULL )
+ {
+ // Locate the smallest timecode
+ if ( first )
+ {
+ // First position found
+ position = mlt_playlist_clip( playlist, whence, index );
+
+ // We're no longer first
+ first = 0;
+ }
+ else
+ {
+ // Obtain the clip position in this playlist
+ mlt_timecode position2 = mlt_playlist_clip( playlist, whence, index );
+
+ // If this position is prior to the first, then use it
+ if ( position2 < position )
+ position = position2;
+ }
+ }
+ }
+ }
+
+ return position;
+}
+
/** Close this instance.
*/
extern mlt_multitrack mlt_multitrack_init( );
extern mlt_producer mlt_multitrack_producer( mlt_multitrack this );
extern mlt_service mlt_multitrack_service( mlt_multitrack this );
+extern mlt_properties mlt_multitrack_properties( mlt_multitrack this );
extern int mlt_multitrack_connect( mlt_multitrack this, mlt_producer producer, int track );
+extern mlt_timecode mlt_multitrack_clip( mlt_multitrack this, mlt_whence whence, int index );
extern void mlt_multitrack_close( mlt_multitrack this );
#endif
// Initialise blank
mlt_producer_init( &this->blank, NULL );
+
+ // Indicate that this producer is a playlist
+ mlt_properties_set_data( mlt_playlist_properties( this ), "playlist", this, 0, NULL, NULL );
}
return this;
return producer;
}
+static int mlt_playlist_current_clip( mlt_playlist this )
+{
+ // Map playlist position to real producer in virtual playlist
+ mlt_timecode position = mlt_producer_position( &this->parent );
+
+ // Loop through the virtual playlist
+ int i = 0;
+
+ for ( i = 0; i < this->count; i ++ )
+ {
+ if ( position < this->list[ i ]->playtime )
+ {
+ // Found it, now break
+ break;
+ }
+ else
+ {
+ // Decrement position by length of this entry
+ position -= this->list[ i ]->playtime;
+ }
+ }
+
+ return i;
+}
+
+/** Get the timecode which corresponds to the start of the next clip.
+*/
+
+mlt_timecode mlt_playlist_clip( mlt_playlist this, mlt_whence whence, int index )
+{
+ mlt_timecode position = 0;
+ int absolute_clip = index;
+ int i = 0;
+
+ // Determine the absolute clip
+ switch ( whence )
+ {
+ case mlt_whence_relative_start:
+ absolute_clip = index;
+ break;
+
+ case mlt_whence_relative_current:
+ absolute_clip = mlt_playlist_current_clip( this ) + index;
+ break;
+
+ case mlt_whence_relative_end:
+ absolute_clip = this->count - index;
+ break;
+ }
+
+ // Check that we're in a valid range
+ if ( absolute_clip < 0 )
+ absolute_clip = 0;
+ else if ( absolute_clip > this->count )
+ absolute_clip = this->count;
+
+ // Now determine the timecode
+ for ( i = 0; i < absolute_clip; i ++ )
+ position += this->list[ i ]->playtime;
+
+ return position;
+}
+
/** Append a producer to the playlist.
*/
extern mlt_playlist mlt_playlist_init( );
extern mlt_producer mlt_playlist_producer( mlt_playlist this );
extern mlt_service mlt_playlist_service( mlt_playlist this );
+extern mlt_properties mlt_playlist_properties( mlt_playlist this );
extern int mlt_playlist_append( mlt_playlist this, mlt_producer producer );
extern int mlt_playlist_blank( mlt_playlist this, mlt_timecode length );
+extern mlt_timecode mlt_playlist_clip( mlt_playlist this, mlt_whence whence, int index );
extern void mlt_playlist_close( mlt_playlist this );
#endif
// Set the default properties
mlt_properties_set( properties, "mlt_type", "mlt_producer" );
mlt_properties_set_timecode( properties, "position", 0.0 );
- mlt_properties_set_double( properties, "frame", 1 );
+ mlt_properties_set_double( properties, "frame", 0 );
mlt_properties_set_double( properties, "fps", 25.0 );
mlt_properties_set_double( properties, "speed", 1.0 );
mlt_properties_set_timecode( properties, "in", 0.0 );
- mlt_properties_set_timecode( properties, "out", 36000.0 );
- mlt_properties_set_timecode( properties, "playtime", 36000.0 );
- mlt_properties_set_timecode( properties, "length", 36000.0 );
+ mlt_properties_set_timecode( properties, "out", 3600.0 );
+ mlt_properties_set_timecode( properties, "length", 3600.0 );
mlt_properties_set_int( properties, "known_length", 1 );
mlt_properties_set_double( properties, "aspect_ratio", 4.0 / 3.0 );
// Set the values
mlt_properties_set_timecode( mlt_producer_properties( this ), "in", in );
mlt_properties_set_timecode( mlt_producer_properties( this ), "out", out );
- mlt_properties_set_timecode( mlt_producer_properties( this ), "playtime", out - in );
// Seek to the in point
mlt_producer_seek( this, 0 );
mlt_timecode mlt_producer_get_playtime( mlt_producer this )
{
- return mlt_properties_get_timecode( mlt_producer_properties( this ), "playtime" );
+ return mlt_producer_get_out( this ) - mlt_producer_get_in( this );
}
/** Get the total length of the producer.
return this->private == NULL;
}
+/** Constructor for stand alone object.
+*/
+
+mlt_properties mlt_properties_new( )
+{
+ // Construct a standalone properties object
+ mlt_properties this = calloc( sizeof( struct mlt_properties_s ), 1 );
+
+ // Initialise this
+ mlt_properties_init( this, NULL );
+
+ // Return the pointer
+ return this;
+}
+
+/** Inherit all serialisable properties from that into this.
+*/
+
+int mlt_properties_inherit( mlt_properties this, mlt_properties that )
+{
+ int count = mlt_properties_count( that );
+ while ( count -- )
+ {
+ char *value = mlt_properties_get_value( that, count );
+ if ( value != NULL )
+ {
+ char *name = mlt_properties_get_name( that, count );
+ mlt_properties_set( this, name, value );
+ }
+ }
+ return 0;
+}
+
/** Locate a property by name
*/
free( list->name );
free( list->value );
free( list );
+
+ // Free this now if this has no child
+ if ( this->child == NULL )
+ free( this );
}
*/
extern int mlt_properties_init( mlt_properties, void *child );
+extern mlt_properties mlt_properties_new( );
+extern int mlt_properties_inherit( mlt_properties this, mlt_properties that );
extern int mlt_properties_set( mlt_properties this, char *name, char *value );
extern int mlt_properties_parse( mlt_properties this, char *namevalue );
extern char *mlt_properties_get( mlt_properties this, char *name );
#include <stdint.h>
+typedef enum
+{
+ mlt_whence_relative_start,
+ mlt_whence_relative_current,
+ mlt_whence_relative_end
+}
+mlt_whence;
+
typedef double mlt_timecode;
typedef struct mlt_frame_s *mlt_frame, **mlt_frame_ptr;
typedef struct mlt_properties_s *mlt_properties;
void transport_action( mlt_producer producer, char *value )
{
mlt_properties properties = mlt_producer_properties( producer );
+ mlt_multitrack multitrack = mlt_properties_get_data( properties, "multitrack", NULL );
- switch( value[ 0 ] )
+ if ( strlen( value ) == 1 )
{
- case 'q':
- mlt_properties_set_int( properties, "done", 1 );
- break;
- case '0':
- mlt_producer_set_speed( producer, 1 );
- mlt_producer_seek( producer, 0 );
- break;
- case '1':
- mlt_producer_set_speed( producer, -10 );
- break;
- case '2':
- mlt_producer_set_speed( producer, -2.5 );
- break;
- case '3':
- mlt_producer_set_speed( producer, -1 );
- break;
- case '4':
- mlt_producer_set_speed( producer, -0.5 );
- break;
- case '5':
- mlt_producer_set_speed( producer, 0 );
- break;
- case '6':
- mlt_producer_set_speed( producer, 0.5 );
- break;
- case '7':
- mlt_producer_set_speed( producer, 1 );
- break;
- case '8':
- mlt_producer_set_speed( producer, 2.5 );
- break;
- case '9':
- mlt_producer_set_speed( producer, 10 );
- break;
+ switch( value[ 0 ] )
+ {
+ case 'q':
+ mlt_properties_set_int( properties, "done", 1 );
+ break;
+ case '0':
+ mlt_producer_set_speed( producer, 1 );
+ mlt_producer_seek( producer, 0 );
+ break;
+ case '1':
+ mlt_producer_set_speed( producer, -10 );
+ break;
+ case '2':
+ mlt_producer_set_speed( producer, -5 );
+ break;
+ case '3':
+ mlt_producer_set_speed( producer, -2 );
+ break;
+ case '4':
+ mlt_producer_set_speed( producer, -1 );
+ break;
+ case '5':
+ mlt_producer_set_speed( producer, 0 );
+ break;
+ case '6':
+ mlt_producer_set_speed( producer, 1 );
+ break;
+ case '7':
+ mlt_producer_set_speed( producer, 2 );
+ break;
+ case '8':
+ mlt_producer_set_speed( producer, 5 );
+ break;
+ case '9':
+ mlt_producer_set_speed( producer, 10 );
+ break;
+ case 'j':
+ if ( multitrack != NULL )
+ {
+ mlt_timecode time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, -1 );
+ mlt_producer_seek( producer, time );
+ mlt_producer_prepare_next( producer );
+ }
+ break;
+ case 'k':
+ if ( multitrack != NULL )
+ {
+ mlt_timecode time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 0 );
+ mlt_producer_seek( producer, time );
+ mlt_producer_prepare_next( producer );
+ }
+ break;
+ case 'l':
+ if ( multitrack != NULL )
+ {
+ mlt_timecode time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 1 );
+ mlt_producer_seek( producer, time );
+ mlt_producer_prepare_next( producer );
+ }
+ break;
+ }
}
}
return filter;
}
-void set_properties( mlt_service service, char *namevalue )
+void set_properties( mlt_properties properties, char *namevalue )
{
- mlt_properties properties = mlt_service_properties( service );
mlt_properties_parse( properties, namevalue );
}
mlt_properties properties = mlt_producer_properties( producer );
term_init( );
- fprintf( stderr, "Press 'q' to continue\n" );
+
+ fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" );
+ fprintf( stderr, "|1=-10| |2= -5| |3= -2| |4= -1| |5= 0| |6= 1| |7= 2| |8= 5| |9= 10|\n" );
+ fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" );
+
+ fprintf( stderr, "+---------------------------------------------------------------------+\n" );
+ fprintf( stderr, "| j = previous, k = restart current, l = next |\n" );
+ fprintf( stderr, "| 0 = restart, q = quit |\n" );
+ fprintf( stderr, "+---------------------------------------------------------------------+\n" );
+
while( mlt_properties_get_int( properties, "done" ) == 0 )
{
int value = term_read( );
int main( int argc, char **argv )
{
int i;
- mlt_service service = NULL;
mlt_consumer consumer = NULL;
mlt_multitrack multitrack = NULL;
mlt_producer producer = NULL;
mlt_playlist playlist = NULL;
mlt_field field = NULL;
+ mlt_properties group = mlt_properties_new( );
+ mlt_properties properties = group;
// Construct the factory
mlt_factory_init( getenv( "MLT_REPOSITORY" ) );
field = mlt_field_init( );
// We need to track the number of registered filters
- mlt_properties properties = mlt_field_properties( field );
- mlt_properties_set_int( properties, "registered", 0 );
+ mlt_properties field_properties = mlt_field_properties( field );
+ mlt_properties_set_int( field_properties, "registered", 0 );
// Get the multitrack from the field
multitrack = mlt_field_multitrack( field );
{
consumer = create_consumer( argv[ ++ i ], mlt_multitrack_producer( multitrack ) );
if ( consumer != NULL )
- service = mlt_consumer_service( consumer );
+ {
+ properties = mlt_consumer_properties( consumer );
+ mlt_properties_inherit( properties, group );
+ }
+ }
+ else if ( !strcmp( argv[ i ], "-group" ) )
+ {
+ if ( mlt_properties_count( group ) != 0 )
+ {
+ mlt_properties_close( group );
+ group = mlt_properties_new( );
+ }
+ if ( group != NULL )
+ properties = group;
}
else if ( !strcmp( argv[ i ], "-filter" ) )
{
mlt_filter filter = create_filter( field, argv[ ++ i ], 0 );
if ( filter != NULL )
- service = mlt_filter_service( filter );
+ {
+ properties = mlt_filter_properties( filter );
+ mlt_properties_inherit( properties, group );
+ }
}
else if ( !strstr( argv[ i ], "=" ) )
{
mlt_playlist_append( playlist, producer );
producer = create_producer( argv[ i ] );
if ( producer != NULL )
- service = mlt_producer_service( producer );
+ {
+ properties = mlt_producer_properties( producer );
+ mlt_properties_inherit( properties, group );
+ }
}
else
{
- set_properties( service, argv[ i ] );
+ set_properties( properties, argv[ i ] );
}
}
// We must have a producer at this point
if ( producer != NULL )
{
- // Connect producer to playlist
- mlt_playlist_append( playlist, producer );
-
// If we have no consumer, default to sdl
if ( consumer == NULL )
+ {
consumer = create_consumer( "sdl", mlt_multitrack_producer( multitrack ) );
+ if ( consumer != NULL )
+ {
+ properties = mlt_consumer_properties( consumer );
+ mlt_properties_inherit( properties, group );
+ }
+ }
+
+ // Connect producer to playlist
+ mlt_playlist_append( playlist, producer );
// Connect multitrack to producer
mlt_multitrack_connect( multitrack, mlt_playlist_producer( playlist ), 0 );
}
else
{
- fprintf( stderr, "Usage: inigo [ -consumer id[:arg] [ name=value ]* ]\n"
+ fprintf( stderr, "Usage: inigo [ -group [ name=value ]* ]\n"
+ " [ -consumer id[:arg] [ name=value ]* ]\n"
" [ -filter id[:arg] [ name=value ] * ]\n"
" [ producer [ name=value ] * ]+\n" );
}
// Close the field
mlt_field_close( field );
+ // Close the group
+ mlt_properties_close( group );
+
// Close the factory
mlt_factory_close( );
double fps = this->is_pal ? 25 : 30000 / 1001;
mlt_timecode length = ( mlt_timecode )( this->frames_in_file ) / fps;
mlt_properties_set_double( properties, "fps", fps );
- mlt_properties_set_timecode( properties, "playtime", length );
mlt_properties_set_timecode( properties, "length", length );
mlt_properties_set_timecode( properties, "in", 0.0 );
mlt_properties_set_timecode( properties, "out", length );
OBJS = factory.o \
producer_ffmpeg.o \
- filter_ffmpeg_dub.o
+ filter_ffmpeg_dub.o \
+ consumer_ffmpeg.o
CFLAGS = -I../../ -Wall -g -D_FILE_OFFSET_BITS=64 -pthread
ffmpeg_dub libmltffmpeg.so
EOF
+cat << EOF >> ../consumers.dat
+ffmpeg libmltffmpeg.so
+EOF
+
fi
--- /dev/null
+/*
+ * consumer_ffmpeg.c -- an ffmpeg consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "consumer_ffmpeg.h"
+#include <framework/mlt_frame.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+/** This classes definition.
+*/
+
+typedef struct consumer_ffmpeg_s *consumer_ffmpeg;
+
+struct consumer_ffmpeg_s
+{
+ struct mlt_consumer_s parent;
+ mlt_properties properties;
+ int format;
+ int video;
+ pthread_t thread;
+ int running;
+ uint8_t audio_buffer[ 4096 * 3 ];
+ int audio_avail;
+ pthread_mutex_t audio_mutex;
+ pthread_cond_t audio_cond;
+ int window_width;
+ int window_height;
+ float aspect_ratio;
+ int width;
+ int height;
+ int playing;
+ mlt_frame *queue;
+ int size;
+ int count;
+ uint8_t *buffer;
+};
+
+/** Forward references to static functions.
+*/
+
+static void consumer_close( mlt_consumer parent );
+static void *consumer_thread( void * );
+
+/** This is what will be called by the factory - anything can be passed in
+ via the argument, but keep it simple.
+*/
+
+mlt_consumer consumer_ffmpeg_init( char *arg )
+{
+ // Create the consumer object
+ consumer_ffmpeg this = calloc( sizeof( struct consumer_ffmpeg_s ), 1 );
+
+ // If no malloc'd and consumer init ok
+ if ( this != NULL && mlt_consumer_init( &this->parent, this ) == 0 )
+ {
+ // Get the parent consumer object
+ mlt_consumer parent = &this->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 );
+ this->properties = mlt_service_properties( service );
+
+ // This is the initialisation of the consumer
+ this->running = 1;
+ pthread_mutex_init( &this->audio_mutex, NULL );
+ pthread_cond_init( &this->audio_cond, NULL);
+
+ // process actual param
+ if ( arg == NULL || !strcmp( arg, "-" ) )
+ {
+ mlt_properties_set( this->properties, "video_file", "-" );
+ mlt_properties_set( this->properties, "video_format", "dv" );
+ }
+ else
+ {
+ mlt_properties_set( this->properties, "video_file", arg );
+ mlt_properties_set( this->properties, "video_format", "" );
+ }
+
+ // Create the the thread
+ pthread_create( &this->thread, NULL, consumer_thread, this );
+
+ // Return the consumer produced
+ return parent;
+ }
+
+ // malloc or consumer init failed
+ free( this );
+
+ // Indicate failure
+ return NULL;
+}
+
+static void sdl_fill_audio( void *udata, uint8_t *stream, int len )
+{
+ consumer_ffmpeg this = udata;
+
+ pthread_mutex_lock( &this->audio_mutex );
+
+ // Block until audio received
+ while ( this->running && len > this->audio_avail )
+ pthread_cond_wait( &this->audio_cond, &this->audio_mutex );
+
+ if ( this->audio_avail >= len )
+ {
+ // Remove len from the audio available
+ this->audio_avail -= len;
+
+ // Remove the samples
+ memmove( this->audio_buffer, this->audio_buffer + len, this->audio_avail );
+ }
+ else
+ {
+ // Just to be safe, wipe the stream first
+ memset( stream, 0, len );
+
+ // Copy what we have into the stream
+ memcpy( stream, this->audio_buffer, this->audio_avail );
+
+ // No audio left
+ this->audio_avail = 0;
+ }
+
+ pthread_cond_broadcast( &this->audio_cond );
+ pthread_mutex_unlock( &this->audio_mutex );
+}
+
+static int consumer_play_audio( consumer_ffmpeg this, mlt_frame frame, int init_audio )
+{
+ // Get the properties of this consumer
+ mlt_properties properties = this->properties;
+ mlt_audio_format afmt = mlt_audio_pcm;
+ int channels;
+ int samples;
+ int frequency;
+ int16_t *pcm;
+ int bytes;
+
+ mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples );
+
+ if ( mlt_properties_get_int( properties, "audio_off" ) )
+ return init_audio;
+
+ if ( init_audio == 0 )
+ {
+ bytes = ( samples * channels * 2 );
+ pthread_mutex_lock( &this->audio_mutex );
+ while ( bytes > ( sizeof( this->audio_buffer) - this->audio_avail ) )
+ pthread_cond_wait( &this->audio_cond, &this->audio_mutex );
+ mlt_properties properties = mlt_frame_properties( frame );
+ if ( mlt_properties_get_double( properties, "speed" ) == 1 )
+ memcpy( &this->audio_buffer[ this->audio_avail ], pcm, bytes );
+ else
+ memset( &this->audio_buffer[ this->audio_avail ], 0, bytes );
+ this->audio_avail += bytes;
+ pthread_cond_broadcast( &this->audio_cond );
+ pthread_mutex_unlock( &this->audio_mutex );
+ }
+ else
+ {
+ this->playing = 1;
+ }
+
+ return init_audio;
+}
+
+static int consumer_play_video( consumer_ffmpeg this, mlt_frame frame )
+{
+ // Get the properties of this consumer
+ mlt_properties properties = this->properties;
+
+ if ( mlt_properties_get_int( properties, "video_off" ) )
+ {
+ mlt_frame_close( frame );
+ return 0;
+ }
+
+ if ( this->count == this->size )
+ {
+ this->size += 25;
+ this->queue = realloc( this->queue, sizeof( mlt_frame ) * this->size );
+ }
+ this->queue[ this->count ++ ] = frame;
+
+ // We're working on the oldest frame now
+ frame = this->queue[ 0 ];
+
+ // Shunt the frames in the queue down
+ int i = 0;
+ for ( i = 1; i < this->count; i ++ )
+ this->queue[ i - 1 ] = this->queue[ i ];
+ this->count --;
+
+ return 0;
+}
+
+/** Threaded wrapper for pipe.
+*/
+
+static void *consumer_thread( void *arg )
+{
+ // Identify the arg
+ consumer_ffmpeg this = arg;
+
+ // Get the consumer
+ mlt_consumer consumer = &this->parent;
+
+ // Get the service assoicated to the consumer
+ mlt_service service = mlt_consumer_service( consumer );
+
+ // Define a frame pointer
+ mlt_frame frame;
+
+ // internal intialization
+ int init_audio = 1;
+
+ // Loop until told not to
+ while( this->running )
+ {
+ // Get a frame from the service (should never return anything other than 0)
+ if ( mlt_service_get_frame( service, &frame, 0 ) == 0 )
+ {
+ init_audio = consumer_play_audio( this, frame, init_audio );
+ consumer_play_video( this, frame );
+ }
+ }
+
+ return NULL;
+}
+
+/** Callback to allow override of the close method.
+*/
+
+static void consumer_close( mlt_consumer parent )
+{
+ // Get the actual object
+ consumer_ffmpeg this = parent->child;
+
+ // Kill the thread and clean up
+ this->running = 0;
+
+ pthread_mutex_lock( &this->audio_mutex );
+ pthread_cond_broadcast( &this->audio_cond );
+ pthread_mutex_unlock( &this->audio_mutex );
+
+ pthread_join( this->thread, NULL );
+ pthread_mutex_destroy( &this->audio_mutex );
+ pthread_cond_destroy( &this->audio_cond );
+
+ // Now clean up the rest (the close = NULL is a bit nasty but needed for now)
+ parent->close = NULL;
+ mlt_consumer_close( parent );
+
+ // Finally clean up this
+ free( this );
+}
+
--- /dev/null
+/*
+ * consumer_ffmpeg.h -- simple ffmpeg test case
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _CONSUMER_FFMPEG_H_
+#define _CONSUMER_FFMPEG_H_
+
+#include <framework/mlt_consumer.h>
+
+extern mlt_consumer consumer_ffmpeg_init( char *file );
+
+#endif
#include "producer_ffmpeg.h"
#include "filter_ffmpeg_dub.h"
+#include "consumer_ffmpeg.h"
void *mlt_create_producer( char *id, void *arg )
{
void *mlt_create_consumer( char *id, void *arg )
{
+ if ( !strcmp( id, "ffmpeg" ) )
+ return consumer_ffmpeg_init( arg );
return NULL;
}
char command[ 1024 ] = "";
float position = mlt_producer_position( &this->parent );
- if ( video_loop ) position = 0;
+ if ( video_loop || position < 0 ) position = 0;
sprintf( command, "%s/ffmpeg/video.sh \"%s\" \"%s\" \"%s\" %f %f 2>/dev/null",
mlt_prefix,
char command[ 1024 ] = "";
float position = mlt_producer_position( &this->parent );
- if ( audio_loop ) position = 0;
+ if ( audio_loop || position < 0 ) position = 0;
sprintf( command, "%s/ffmpeg/audio.sh \"%s\" \"%s\" %f %d %d %d 2>/dev/null",
mlt_prefix,
{
int samples = 0;
- if ( fps == 25 )
- {
- samples = frequency / 25;
- }
- else if ( fps >= 29.97 && fps < 29.98 )
+ if ( fps > 29 && fps <= 30 )
{
samples = frequency / 30;
samples = 0;
}
}
- else
+ else if ( fps != 0 )
{
- if ( fps != 0 )
- samples = frequency / fps;
+ samples = frequency / fps;
}
return samples;
mlt_properties_set_double( properties, "speed", speed );
// Set the out point on the producer
- if ( !this->end_of_video || !this->end_of_audio )
- mlt_producer_set_in_and_out( &this->parent, mlt_producer_get_in( &this->parent ), mlt_producer_position( &this->parent ) + 1 );
- else
+ if ( this->end_of_video && this->end_of_audio )
mlt_producer_set_in_and_out( &this->parent, mlt_producer_get_in( &this->parent ), mlt_producer_position( &this->parent ) );
// Update timecode on the frame we're creating
}
}
mlt_properties_set_timecode( properties, "out", this->count );
- mlt_properties_set_timecode( properties, "playtime", this->count );
}
else if ( strstr( filename, "/.all." ) != NULL )
{
}
mlt_properties_set_timecode( properties, "out", this->count );
- mlt_properties_set_timecode( properties, "playtime", this->count );
free( de );
free( dir_name );
}
this->filenames = realloc( this->filenames, sizeof( char * ) * ( this->count + 1 ) );
this->filenames[ this->count ++ ] = strdup( filename );
mlt_properties_set_timecode( properties, "out", 1 );
- mlt_properties_set_timecode( properties, "playtime", 1 );
}
// Initialise gobject types
SDL_UnlockSurface( screen );
}
-void sdl_fill_audio( void *udata, uint8_t *stream, int len )
+static void sdl_fill_audio( void *udata, uint8_t *stream, int len )
{
consumer_sdl this = udata;
#include "config.h"
#include "mlt_multitrack.h"
+#include "mlt_playlist.h"
#include "mlt_frame.h"
#include <stdio.h>
mlt_producer producer = &this->parent;
if ( mlt_producer_init( producer, this ) == 0 )
{
+ mlt_properties properties = mlt_multitrack_properties( this );
producer->get_frame = producer_get_frame;
+ mlt_properties_set_data( properties, "multitrack", this, 0, NULL, NULL );
}
else
{
// Update multitrack properties now - we'll not destroy the in point here
mlt_properties_set_timecode( properties, "length", length );
mlt_properties_set_timecode( properties, "out", length );
- mlt_properties_set_timecode( properties, "playtime", length - mlt_properties_get_timecode( properties, "in" ) );
mlt_properties_set_double( properties, "fps", fps );
}
return 0;
}
+/** Determine the clip point.
+*/
+
+mlt_timecode mlt_multitrack_clip( mlt_multitrack this, mlt_whence whence, int index )
+{
+ int first = 1;
+ mlt_timecode position = 0;
+ int i = 0;
+
+ for ( i = 0; i < this->count; i ++ )
+ {
+ // Get the producer
+ mlt_producer producer = this->list[ i ];
+
+ if ( producer != NULL )
+ {
+ // Get the properties of this producer
+ mlt_properties properties = mlt_producer_properties( producer );
+
+ // Determine if it's a playlist
+ mlt_playlist playlist = mlt_properties_get_data( properties, "playlist", NULL );
+
+ // We only consider playlists
+ if ( playlist != NULL )
+ {
+ // Locate the smallest timecode
+ if ( first )
+ {
+ // First position found
+ position = mlt_playlist_clip( playlist, whence, index );
+
+ // We're no longer first
+ first = 0;
+ }
+ else
+ {
+ // Obtain the clip position in this playlist
+ mlt_timecode position2 = mlt_playlist_clip( playlist, whence, index );
+
+ // If this position is prior to the first, then use it
+ if ( position2 < position )
+ position = position2;
+ }
+ }
+ }
+ }
+
+ return position;
+}
+
/** Close this instance.
*/
extern mlt_multitrack mlt_multitrack_init( );
extern mlt_producer mlt_multitrack_producer( mlt_multitrack this );
extern mlt_service mlt_multitrack_service( mlt_multitrack this );
+extern mlt_properties mlt_multitrack_properties( mlt_multitrack this );
extern int mlt_multitrack_connect( mlt_multitrack this, mlt_producer producer, int track );
+extern mlt_timecode mlt_multitrack_clip( mlt_multitrack this, mlt_whence whence, int index );
extern void mlt_multitrack_close( mlt_multitrack this );
#endif
// Initialise blank
mlt_producer_init( &this->blank, NULL );
+
+ // Indicate that this producer is a playlist
+ mlt_properties_set_data( mlt_playlist_properties( this ), "playlist", this, 0, NULL, NULL );
}
return this;
return producer;
}
+static int mlt_playlist_current_clip( mlt_playlist this )
+{
+ // Map playlist position to real producer in virtual playlist
+ mlt_timecode position = mlt_producer_position( &this->parent );
+
+ // Loop through the virtual playlist
+ int i = 0;
+
+ for ( i = 0; i < this->count; i ++ )
+ {
+ if ( position < this->list[ i ]->playtime )
+ {
+ // Found it, now break
+ break;
+ }
+ else
+ {
+ // Decrement position by length of this entry
+ position -= this->list[ i ]->playtime;
+ }
+ }
+
+ return i;
+}
+
+/** Get the timecode which corresponds to the start of the next clip.
+*/
+
+mlt_timecode mlt_playlist_clip( mlt_playlist this, mlt_whence whence, int index )
+{
+ mlt_timecode position = 0;
+ int absolute_clip = index;
+ int i = 0;
+
+ // Determine the absolute clip
+ switch ( whence )
+ {
+ case mlt_whence_relative_start:
+ absolute_clip = index;
+ break;
+
+ case mlt_whence_relative_current:
+ absolute_clip = mlt_playlist_current_clip( this ) + index;
+ break;
+
+ case mlt_whence_relative_end:
+ absolute_clip = this->count - index;
+ break;
+ }
+
+ // Check that we're in a valid range
+ if ( absolute_clip < 0 )
+ absolute_clip = 0;
+ else if ( absolute_clip > this->count )
+ absolute_clip = this->count;
+
+ // Now determine the timecode
+ for ( i = 0; i < absolute_clip; i ++ )
+ position += this->list[ i ]->playtime;
+
+ return position;
+}
+
/** Append a producer to the playlist.
*/
extern mlt_playlist mlt_playlist_init( );
extern mlt_producer mlt_playlist_producer( mlt_playlist this );
extern mlt_service mlt_playlist_service( mlt_playlist this );
+extern mlt_properties mlt_playlist_properties( mlt_playlist this );
extern int mlt_playlist_append( mlt_playlist this, mlt_producer producer );
extern int mlt_playlist_blank( mlt_playlist this, mlt_timecode length );
+extern mlt_timecode mlt_playlist_clip( mlt_playlist this, mlt_whence whence, int index );
extern void mlt_playlist_close( mlt_playlist this );
#endif
// Set the default properties
mlt_properties_set( properties, "mlt_type", "mlt_producer" );
mlt_properties_set_timecode( properties, "position", 0.0 );
- mlt_properties_set_double( properties, "frame", 1 );
+ mlt_properties_set_double( properties, "frame", 0 );
mlt_properties_set_double( properties, "fps", 25.0 );
mlt_properties_set_double( properties, "speed", 1.0 );
mlt_properties_set_timecode( properties, "in", 0.0 );
- mlt_properties_set_timecode( properties, "out", 36000.0 );
- mlt_properties_set_timecode( properties, "playtime", 36000.0 );
- mlt_properties_set_timecode( properties, "length", 36000.0 );
+ mlt_properties_set_timecode( properties, "out", 3600.0 );
+ mlt_properties_set_timecode( properties, "length", 3600.0 );
mlt_properties_set_int( properties, "known_length", 1 );
mlt_properties_set_double( properties, "aspect_ratio", 4.0 / 3.0 );
// Set the values
mlt_properties_set_timecode( mlt_producer_properties( this ), "in", in );
mlt_properties_set_timecode( mlt_producer_properties( this ), "out", out );
- mlt_properties_set_timecode( mlt_producer_properties( this ), "playtime", out - in );
// Seek to the in point
mlt_producer_seek( this, 0 );
mlt_timecode mlt_producer_get_playtime( mlt_producer this )
{
- return mlt_properties_get_timecode( mlt_producer_properties( this ), "playtime" );
+ return mlt_producer_get_out( this ) - mlt_producer_get_in( this );
}
/** Get the total length of the producer.
return this->private == NULL;
}
+/** Constructor for stand alone object.
+*/
+
+mlt_properties mlt_properties_new( )
+{
+ // Construct a standalone properties object
+ mlt_properties this = calloc( sizeof( struct mlt_properties_s ), 1 );
+
+ // Initialise this
+ mlt_properties_init( this, NULL );
+
+ // Return the pointer
+ return this;
+}
+
+/** Inherit all serialisable properties from that into this.
+*/
+
+int mlt_properties_inherit( mlt_properties this, mlt_properties that )
+{
+ int count = mlt_properties_count( that );
+ while ( count -- )
+ {
+ char *value = mlt_properties_get_value( that, count );
+ if ( value != NULL )
+ {
+ char *name = mlt_properties_get_name( that, count );
+ mlt_properties_set( this, name, value );
+ }
+ }
+ return 0;
+}
+
/** Locate a property by name
*/
free( list->name );
free( list->value );
free( list );
+
+ // Free this now if this has no child
+ if ( this->child == NULL )
+ free( this );
}
*/
extern int mlt_properties_init( mlt_properties, void *child );
+extern mlt_properties mlt_properties_new( );
+extern int mlt_properties_inherit( mlt_properties this, mlt_properties that );
extern int mlt_properties_set( mlt_properties this, char *name, char *value );
extern int mlt_properties_parse( mlt_properties this, char *namevalue );
extern char *mlt_properties_get( mlt_properties this, char *name );
#include <stdint.h>
+typedef enum
+{
+ mlt_whence_relative_start,
+ mlt_whence_relative_current,
+ mlt_whence_relative_end
+}
+mlt_whence;
+
typedef double mlt_timecode;
typedef struct mlt_frame_s *mlt_frame, **mlt_frame_ptr;
typedef struct mlt_properties_s *mlt_properties;
void transport_action( mlt_producer producer, char *value )
{
mlt_properties properties = mlt_producer_properties( producer );
+ mlt_multitrack multitrack = mlt_properties_get_data( properties, "multitrack", NULL );
- switch( value[ 0 ] )
+ if ( strlen( value ) == 1 )
{
- case 'q':
- mlt_properties_set_int( properties, "done", 1 );
- break;
- case '0':
- mlt_producer_set_speed( producer, 1 );
- mlt_producer_seek( producer, 0 );
- break;
- case '1':
- mlt_producer_set_speed( producer, -10 );
- break;
- case '2':
- mlt_producer_set_speed( producer, -2.5 );
- break;
- case '3':
- mlt_producer_set_speed( producer, -1 );
- break;
- case '4':
- mlt_producer_set_speed( producer, -0.5 );
- break;
- case '5':
- mlt_producer_set_speed( producer, 0 );
- break;
- case '6':
- mlt_producer_set_speed( producer, 0.5 );
- break;
- case '7':
- mlt_producer_set_speed( producer, 1 );
- break;
- case '8':
- mlt_producer_set_speed( producer, 2.5 );
- break;
- case '9':
- mlt_producer_set_speed( producer, 10 );
- break;
+ switch( value[ 0 ] )
+ {
+ case 'q':
+ mlt_properties_set_int( properties, "done", 1 );
+ break;
+ case '0':
+ mlt_producer_set_speed( producer, 1 );
+ mlt_producer_seek( producer, 0 );
+ break;
+ case '1':
+ mlt_producer_set_speed( producer, -10 );
+ break;
+ case '2':
+ mlt_producer_set_speed( producer, -5 );
+ break;
+ case '3':
+ mlt_producer_set_speed( producer, -2 );
+ break;
+ case '4':
+ mlt_producer_set_speed( producer, -1 );
+ break;
+ case '5':
+ mlt_producer_set_speed( producer, 0 );
+ break;
+ case '6':
+ mlt_producer_set_speed( producer, 1 );
+ break;
+ case '7':
+ mlt_producer_set_speed( producer, 2 );
+ break;
+ case '8':
+ mlt_producer_set_speed( producer, 5 );
+ break;
+ case '9':
+ mlt_producer_set_speed( producer, 10 );
+ break;
+ case 'j':
+ if ( multitrack != NULL )
+ {
+ mlt_timecode time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, -1 );
+ mlt_producer_seek( producer, time );
+ mlt_producer_prepare_next( producer );
+ }
+ break;
+ case 'k':
+ if ( multitrack != NULL )
+ {
+ mlt_timecode time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 0 );
+ mlt_producer_seek( producer, time );
+ mlt_producer_prepare_next( producer );
+ }
+ break;
+ case 'l':
+ if ( multitrack != NULL )
+ {
+ mlt_timecode time = mlt_multitrack_clip( multitrack, mlt_whence_relative_current, 1 );
+ mlt_producer_seek( producer, time );
+ mlt_producer_prepare_next( producer );
+ }
+ break;
+ }
}
}
return filter;
}
-void set_properties( mlt_service service, char *namevalue )
+void set_properties( mlt_properties properties, char *namevalue )
{
- mlt_properties properties = mlt_service_properties( service );
mlt_properties_parse( properties, namevalue );
}
mlt_properties properties = mlt_producer_properties( producer );
term_init( );
- fprintf( stderr, "Press 'q' to continue\n" );
+
+ fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" );
+ fprintf( stderr, "|1=-10| |2= -5| |3= -2| |4= -1| |5= 0| |6= 1| |7= 2| |8= 5| |9= 10|\n" );
+ fprintf( stderr, "+-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+ +-----+\n" );
+
+ fprintf( stderr, "+---------------------------------------------------------------------+\n" );
+ fprintf( stderr, "| j = previous, k = restart current, l = next |\n" );
+ fprintf( stderr, "| 0 = restart, q = quit |\n" );
+ fprintf( stderr, "+---------------------------------------------------------------------+\n" );
+
while( mlt_properties_get_int( properties, "done" ) == 0 )
{
int value = term_read( );
int main( int argc, char **argv )
{
int i;
- mlt_service service = NULL;
mlt_consumer consumer = NULL;
mlt_multitrack multitrack = NULL;
mlt_producer producer = NULL;
mlt_playlist playlist = NULL;
mlt_field field = NULL;
+ mlt_properties group = mlt_properties_new( );
+ mlt_properties properties = group;
// Construct the factory
mlt_factory_init( getenv( "MLT_REPOSITORY" ) );
field = mlt_field_init( );
// We need to track the number of registered filters
- mlt_properties properties = mlt_field_properties( field );
- mlt_properties_set_int( properties, "registered", 0 );
+ mlt_properties field_properties = mlt_field_properties( field );
+ mlt_properties_set_int( field_properties, "registered", 0 );
// Get the multitrack from the field
multitrack = mlt_field_multitrack( field );
{
consumer = create_consumer( argv[ ++ i ], mlt_multitrack_producer( multitrack ) );
if ( consumer != NULL )
- service = mlt_consumer_service( consumer );
+ {
+ properties = mlt_consumer_properties( consumer );
+ mlt_properties_inherit( properties, group );
+ }
+ }
+ else if ( !strcmp( argv[ i ], "-group" ) )
+ {
+ if ( mlt_properties_count( group ) != 0 )
+ {
+ mlt_properties_close( group );
+ group = mlt_properties_new( );
+ }
+ if ( group != NULL )
+ properties = group;
}
else if ( !strcmp( argv[ i ], "-filter" ) )
{
mlt_filter filter = create_filter( field, argv[ ++ i ], 0 );
if ( filter != NULL )
- service = mlt_filter_service( filter );
+ {
+ properties = mlt_filter_properties( filter );
+ mlt_properties_inherit( properties, group );
+ }
}
else if ( !strstr( argv[ i ], "=" ) )
{
mlt_playlist_append( playlist, producer );
producer = create_producer( argv[ i ] );
if ( producer != NULL )
- service = mlt_producer_service( producer );
+ {
+ properties = mlt_producer_properties( producer );
+ mlt_properties_inherit( properties, group );
+ }
}
else
{
- set_properties( service, argv[ i ] );
+ set_properties( properties, argv[ i ] );
}
}
// We must have a producer at this point
if ( producer != NULL )
{
- // Connect producer to playlist
- mlt_playlist_append( playlist, producer );
-
// If we have no consumer, default to sdl
if ( consumer == NULL )
+ {
consumer = create_consumer( "sdl", mlt_multitrack_producer( multitrack ) );
+ if ( consumer != NULL )
+ {
+ properties = mlt_consumer_properties( consumer );
+ mlt_properties_inherit( properties, group );
+ }
+ }
+
+ // Connect producer to playlist
+ mlt_playlist_append( playlist, producer );
// Connect multitrack to producer
mlt_multitrack_connect( multitrack, mlt_playlist_producer( playlist ), 0 );
}
else
{
- fprintf( stderr, "Usage: inigo [ -consumer id[:arg] [ name=value ]* ]\n"
+ fprintf( stderr, "Usage: inigo [ -group [ name=value ]* ]\n"
+ " [ -consumer id[:arg] [ name=value ]* ]\n"
" [ -filter id[:arg] [ name=value ] * ]\n"
" [ producer [ name=value ] * ]+\n" );
}
// Close the field
mlt_field_close( field );
+ // Close the group
+ mlt_properties_close( group );
+
// Close the factory
mlt_factory_close( );
double fps = this->is_pal ? 25 : 30000 / 1001;
mlt_timecode length = ( mlt_timecode )( this->frames_in_file ) / fps;
mlt_properties_set_double( properties, "fps", fps );
- mlt_properties_set_timecode( properties, "playtime", length );
mlt_properties_set_timecode( properties, "length", length );
mlt_properties_set_timecode( properties, "in", 0.0 );
mlt_properties_set_timecode( properties, "out", length );
OBJS = factory.o \
producer_ffmpeg.o \
- filter_ffmpeg_dub.o
+ filter_ffmpeg_dub.o \
+ consumer_ffmpeg.o
CFLAGS = -I../../ -Wall -g -D_FILE_OFFSET_BITS=64 -pthread
ffmpeg_dub libmltffmpeg.so
EOF
+cat << EOF >> ../consumers.dat
+ffmpeg libmltffmpeg.so
+EOF
+
fi
--- /dev/null
+/*
+ * consumer_ffmpeg.c -- an ffmpeg consumer
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "consumer_ffmpeg.h"
+#include <framework/mlt_frame.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+/** This classes definition.
+*/
+
+typedef struct consumer_ffmpeg_s *consumer_ffmpeg;
+
+struct consumer_ffmpeg_s
+{
+ struct mlt_consumer_s parent;
+ mlt_properties properties;
+ int format;
+ int video;
+ pthread_t thread;
+ int running;
+ uint8_t audio_buffer[ 4096 * 3 ];
+ int audio_avail;
+ pthread_mutex_t audio_mutex;
+ pthread_cond_t audio_cond;
+ int window_width;
+ int window_height;
+ float aspect_ratio;
+ int width;
+ int height;
+ int playing;
+ mlt_frame *queue;
+ int size;
+ int count;
+ uint8_t *buffer;
+};
+
+/** Forward references to static functions.
+*/
+
+static void consumer_close( mlt_consumer parent );
+static void *consumer_thread( void * );
+
+/** This is what will be called by the factory - anything can be passed in
+ via the argument, but keep it simple.
+*/
+
+mlt_consumer consumer_ffmpeg_init( char *arg )
+{
+ // Create the consumer object
+ consumer_ffmpeg this = calloc( sizeof( struct consumer_ffmpeg_s ), 1 );
+
+ // If no malloc'd and consumer init ok
+ if ( this != NULL && mlt_consumer_init( &this->parent, this ) == 0 )
+ {
+ // Get the parent consumer object
+ mlt_consumer parent = &this->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 );
+ this->properties = mlt_service_properties( service );
+
+ // This is the initialisation of the consumer
+ this->running = 1;
+ pthread_mutex_init( &this->audio_mutex, NULL );
+ pthread_cond_init( &this->audio_cond, NULL);
+
+ // process actual param
+ if ( arg == NULL || !strcmp( arg, "-" ) )
+ {
+ mlt_properties_set( this->properties, "video_file", "-" );
+ mlt_properties_set( this->properties, "video_format", "dv" );
+ }
+ else
+ {
+ mlt_properties_set( this->properties, "video_file", arg );
+ mlt_properties_set( this->properties, "video_format", "" );
+ }
+
+ // Create the the thread
+ pthread_create( &this->thread, NULL, consumer_thread, this );
+
+ // Return the consumer produced
+ return parent;
+ }
+
+ // malloc or consumer init failed
+ free( this );
+
+ // Indicate failure
+ return NULL;
+}
+
+static void sdl_fill_audio( void *udata, uint8_t *stream, int len )
+{
+ consumer_ffmpeg this = udata;
+
+ pthread_mutex_lock( &this->audio_mutex );
+
+ // Block until audio received
+ while ( this->running && len > this->audio_avail )
+ pthread_cond_wait( &this->audio_cond, &this->audio_mutex );
+
+ if ( this->audio_avail >= len )
+ {
+ // Remove len from the audio available
+ this->audio_avail -= len;
+
+ // Remove the samples
+ memmove( this->audio_buffer, this->audio_buffer + len, this->audio_avail );
+ }
+ else
+ {
+ // Just to be safe, wipe the stream first
+ memset( stream, 0, len );
+
+ // Copy what we have into the stream
+ memcpy( stream, this->audio_buffer, this->audio_avail );
+
+ // No audio left
+ this->audio_avail = 0;
+ }
+
+ pthread_cond_broadcast( &this->audio_cond );
+ pthread_mutex_unlock( &this->audio_mutex );
+}
+
+static int consumer_play_audio( consumer_ffmpeg this, mlt_frame frame, int init_audio )
+{
+ // Get the properties of this consumer
+ mlt_properties properties = this->properties;
+ mlt_audio_format afmt = mlt_audio_pcm;
+ int channels;
+ int samples;
+ int frequency;
+ int16_t *pcm;
+ int bytes;
+
+ mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples );
+
+ if ( mlt_properties_get_int( properties, "audio_off" ) )
+ return init_audio;
+
+ if ( init_audio == 0 )
+ {
+ bytes = ( samples * channels * 2 );
+ pthread_mutex_lock( &this->audio_mutex );
+ while ( bytes > ( sizeof( this->audio_buffer) - this->audio_avail ) )
+ pthread_cond_wait( &this->audio_cond, &this->audio_mutex );
+ mlt_properties properties = mlt_frame_properties( frame );
+ if ( mlt_properties_get_double( properties, "speed" ) == 1 )
+ memcpy( &this->audio_buffer[ this->audio_avail ], pcm, bytes );
+ else
+ memset( &this->audio_buffer[ this->audio_avail ], 0, bytes );
+ this->audio_avail += bytes;
+ pthread_cond_broadcast( &this->audio_cond );
+ pthread_mutex_unlock( &this->audio_mutex );
+ }
+ else
+ {
+ this->playing = 1;
+ }
+
+ return init_audio;
+}
+
+static int consumer_play_video( consumer_ffmpeg this, mlt_frame frame )
+{
+ // Get the properties of this consumer
+ mlt_properties properties = this->properties;
+
+ if ( mlt_properties_get_int( properties, "video_off" ) )
+ {
+ mlt_frame_close( frame );
+ return 0;
+ }
+
+ if ( this->count == this->size )
+ {
+ this->size += 25;
+ this->queue = realloc( this->queue, sizeof( mlt_frame ) * this->size );
+ }
+ this->queue[ this->count ++ ] = frame;
+
+ // We're working on the oldest frame now
+ frame = this->queue[ 0 ];
+
+ // Shunt the frames in the queue down
+ int i = 0;
+ for ( i = 1; i < this->count; i ++ )
+ this->queue[ i - 1 ] = this->queue[ i ];
+ this->count --;
+
+ return 0;
+}
+
+/** Threaded wrapper for pipe.
+*/
+
+static void *consumer_thread( void *arg )
+{
+ // Identify the arg
+ consumer_ffmpeg this = arg;
+
+ // Get the consumer
+ mlt_consumer consumer = &this->parent;
+
+ // Get the service assoicated to the consumer
+ mlt_service service = mlt_consumer_service( consumer );
+
+ // Define a frame pointer
+ mlt_frame frame;
+
+ // internal intialization
+ int init_audio = 1;
+
+ // Loop until told not to
+ while( this->running )
+ {
+ // Get a frame from the service (should never return anything other than 0)
+ if ( mlt_service_get_frame( service, &frame, 0 ) == 0 )
+ {
+ init_audio = consumer_play_audio( this, frame, init_audio );
+ consumer_play_video( this, frame );
+ }
+ }
+
+ return NULL;
+}
+
+/** Callback to allow override of the close method.
+*/
+
+static void consumer_close( mlt_consumer parent )
+{
+ // Get the actual object
+ consumer_ffmpeg this = parent->child;
+
+ // Kill the thread and clean up
+ this->running = 0;
+
+ pthread_mutex_lock( &this->audio_mutex );
+ pthread_cond_broadcast( &this->audio_cond );
+ pthread_mutex_unlock( &this->audio_mutex );
+
+ pthread_join( this->thread, NULL );
+ pthread_mutex_destroy( &this->audio_mutex );
+ pthread_cond_destroy( &this->audio_cond );
+
+ // Now clean up the rest (the close = NULL is a bit nasty but needed for now)
+ parent->close = NULL;
+ mlt_consumer_close( parent );
+
+ // Finally clean up this
+ free( this );
+}
+
--- /dev/null
+/*
+ * consumer_ffmpeg.h -- simple ffmpeg test case
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _CONSUMER_FFMPEG_H_
+#define _CONSUMER_FFMPEG_H_
+
+#include <framework/mlt_consumer.h>
+
+extern mlt_consumer consumer_ffmpeg_init( char *file );
+
+#endif
#include "producer_ffmpeg.h"
#include "filter_ffmpeg_dub.h"
+#include "consumer_ffmpeg.h"
void *mlt_create_producer( char *id, void *arg )
{
void *mlt_create_consumer( char *id, void *arg )
{
+ if ( !strcmp( id, "ffmpeg" ) )
+ return consumer_ffmpeg_init( arg );
return NULL;
}
char command[ 1024 ] = "";
float position = mlt_producer_position( &this->parent );
- if ( video_loop ) position = 0;
+ if ( video_loop || position < 0 ) position = 0;
sprintf( command, "%s/ffmpeg/video.sh \"%s\" \"%s\" \"%s\" %f %f 2>/dev/null",
mlt_prefix,
char command[ 1024 ] = "";
float position = mlt_producer_position( &this->parent );
- if ( audio_loop ) position = 0;
+ if ( audio_loop || position < 0 ) position = 0;
sprintf( command, "%s/ffmpeg/audio.sh \"%s\" \"%s\" %f %d %d %d 2>/dev/null",
mlt_prefix,
{
int samples = 0;
- if ( fps == 25 )
- {
- samples = frequency / 25;
- }
- else if ( fps >= 29.97 && fps < 29.98 )
+ if ( fps > 29 && fps <= 30 )
{
samples = frequency / 30;
samples = 0;
}
}
- else
+ else if ( fps != 0 )
{
- if ( fps != 0 )
- samples = frequency / fps;
+ samples = frequency / fps;
}
return samples;
mlt_properties_set_double( properties, "speed", speed );
// Set the out point on the producer
- if ( !this->end_of_video || !this->end_of_audio )
- mlt_producer_set_in_and_out( &this->parent, mlt_producer_get_in( &this->parent ), mlt_producer_position( &this->parent ) + 1 );
- else
+ if ( this->end_of_video && this->end_of_audio )
mlt_producer_set_in_and_out( &this->parent, mlt_producer_get_in( &this->parent ), mlt_producer_position( &this->parent ) );
// Update timecode on the frame we're creating
}
}
mlt_properties_set_timecode( properties, "out", this->count );
- mlt_properties_set_timecode( properties, "playtime", this->count );
}
else if ( strstr( filename, "/.all." ) != NULL )
{
}
mlt_properties_set_timecode( properties, "out", this->count );
- mlt_properties_set_timecode( properties, "playtime", this->count );
free( de );
free( dir_name );
}
this->filenames = realloc( this->filenames, sizeof( char * ) * ( this->count + 1 ) );
this->filenames[ this->count ++ ] = strdup( filename );
mlt_properties_set_timecode( properties, "out", 1 );
- mlt_properties_set_timecode( properties, "playtime", 1 );
}
// Initialise gobject types
SDL_UnlockSurface( screen );
}
-void sdl_fill_audio( void *udata, uint8_t *stream, int len )
+static void sdl_fill_audio( void *udata, uint8_t *stream, int len )
{
consumer_sdl this = udata;