From: lilo_booter Date: Thu, 18 Mar 2004 12:54:29 +0000 (+0000) Subject: provisional framework docs and corrections X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=2a800ec4c840132c7a629b764b673dce09771637;p=mlt provisional framework docs and corrections git-svn-id: https://mlt.svn.sourceforge.net/svnroot/mlt/trunk/mlt@218 d19143bc-622f-0410-bfdd-b5b2a6649095 --- diff --git a/docs/framework.txt b/docs/framework.txt new file mode 100644 index 00000000..e00d192a --- /dev/null +++ b/docs/framework.txt @@ -0,0 +1,1184 @@ +MLT FRAMEWORK +------------- + +Preamble: + + MLT is a multimedia framework designed for television broadcasting. As such, + it provides a pluggable architecture for the inclusion of new audio/video + sources, filters, transitions and playback devices. + + The MLT framework provides the structure and utility functionality on which + all of the MLT applications and services are defined. + + On its own, the framework provides little more than 'abstract classes' and + utilities for managing resources, such as memory, properties, dynamic object + loading and service instantiation. + + This document is split roughly into 3 sections. The first section provides a + basic overview of MLT, the second section shows how it's used and the final + section shows shows structure and design, with an emphasis on how the system + is extended. + + +Target Audience: + + This document is provided as a 'road map' for the framework and should be + considered mandatory reading for anyone wishing to develop code at the MLT + level. + + This includes: + + 1. framework maintainers; + 2. module developers; + 3. application developers; + 4. anyone interested in MLT. + + The emphasis of the document is in explaining the public interfaces, as + opposed to the implementation details. + + It is not required reading for the MLT client/server integration - please + refer to valerie.txt and dvcp.txt for more details on this area. + + +SECTION 1 - BASIC OVERVIEW +-------------------------- + +Basic Design Information: + + MLT is written in C. + + The framework has no dependencies other than the standard C99 and POSIX + libraries. + + It follows a basic Object Oriented design paradigm, and as such, much of the + design is loosely based on the Producer/Consumer design pattern. + + It employs Reverse Polish Notation for the application of audio and video FX. + + The framework is designed to be colour space neutral - the currently + implemented modules, however, are very much 8bit YUV422 oriented. In theory, + the modules could be entirely replaced. + + A vague understanding of these terms is assumed throughout the remainder of + this document. + + +Structure and Flow: + + The general structure of an MLT 'network' is simply the connection of a + 'producer' to a 'consumer': + + +--------+ +--------+ + |Producer|-->|Consumer| + +--------+ +--------+ + + A typical consumer requests MLT Frame objects from the producer, does + something with them and when finished with a frame, closes it. + + /\ A common confusion with the producer/consumer terminoligy used here is + /!!\ that a consumer may 'produce' something. For example, the libdv consumer + \!!/ produces DV and the libdv producer seems to consume DV. However, the + \/ naming conventions refer only to producers and consumers of MLT Frames. + + To put it another way - a producer produces MLT Frame objects and a consumer + consumes MLT Frame objects. + + An MLT Frame essentially provides an uncompressed image and its associated + audio samples. + + Filters may also be placed between the producer and the consumer: + + +--------+ +------+ +--------+ + |Producer|-->|Filter|-->|Consumer| + +--------+ +------+ +--------+ + + A service is the collective name for producers, filters, transitions and + consumers. + + The communications between a connected consumer and producer or service are + carried out in 3 phases: + + * get the frame + * get the image + * get the audio + + MLT employs 'lazy evaluation' - the image and audio need not be extracted + from the source until the get image and audio methods are invoked. + + In essence, the consumer pulls from what it's connected to - this means that + threading is typically in the domain of the consumer implementation and some + basic functionality is provided on the consumer class to ensure realtime + throughput. + + +SECTION 2 - USAGE +----------------- + +Hello World: + + Before we go in to the specifics of the framework architecture, a working + example of usage is provided. + + The following simply provides a media player: + + #include + #include + #include + + int main( int argc, char *argv[] ) + { + // Initialise the factory + if ( mlt_factory_init( NULL ) == 0 ) + { + // Create the default consumer + mlt_consumer hello = mlt_factory_consumer( NULL, NULL ); + + // Create via the default producer + mlt_producer world = mlt_factory_producer( NULL, argv[ 1 ] ); + + // Connect the producer to the consumer + mlt_consumer_connect( hello, mlt_producer_service( world ) ); + + // Start the consumer + mlt_consumer_start( hello ); + + // Wait for the consumer to terminate + while( !mlt_consumer_is_stopped( hello ) ) + sleep( 1 ); + + // Close the consumer + mlt_consumer_close( hello ); + + // Close the producer + mlt_producer_close( world ); + + // Close the factory + mlt_factory_close( ); + } + else + { + // Report an error during initialisation + fprintf( stderr, "Unable to locate factory modules\n" ); + } + + // End of program + return 0; + } + + This is a simple example - it doesn't provide any seeking capabilities or + runtime configuration options. + + The first step of any MLT application is the factory initialisation - this + ensures that the environment is configured and MLT can function. The factory + is covered in more detail below. + + All services are instantiated via the factories, as shown by the + mlt_factory_consumer and mlt_factory_producer calls above. There are similar + factories for filters and transitions. There are details on all the standard + services in services.txt. + + The defaults requested here are a special case - the NULL usage requests + that we use the default producers and consumers. + + The default producer is "fezzik". This producer matches file names to + locate a service to use and attaches 'normalising filters' (such as scalers, + deinterlacers, resamplers and field normalisers) to the loaded content - + these filters ensure that the consumer gets what it asks for. + + The default consumer is "sdl". The combination of fezzik and sdl will + provide a media player. + + In this example, we connect the producer and then start the consumer. We + then wait until the consumer is stopped (in this case, by the action of the + user closing the SDL window) and finally close the consumer, producer and + factory before exiting the application. + + Note that the consumer is threaded - waiting for an event of some sort is + always required after starting and before stopping or closing the consumer. + + Also note, you can override the defaults as follows: + + $ MLT_CONSUMER=westley ./hello file.avi + + This will create a westley xml document on stdout. + + $ MLT_CONSUMER=westley MLT_PRODUCER=avformat ./hello file.avi + + This will play the video using the avformat producer directly, thus it will + bypass the normalising functions. + + $ MLT_CONSUMER=libdv ./hello file.avi > /dev/dv1394 + + This might, if you're lucky, do on the fly, realtime conversions of file.avi + to DV and broadcast it to your DV device. + + +Factories: + + As shown in the 'Hello World' example, factories create service objects. + + The framework itself provides no services - they are provided in the form of + a plugin structure. A plugin is organised in the form of a 'module' and a + module can provide many services of different types. + + Once the factory is initialised, all the configured services are available + for use. + + The complete set of methods associated to the factory are as follows: + + int mlt_factory_init( char *prefix ); + const char *mlt_factory_prefix( ); + char *mlt_environment( char *name ); + mlt_producer mlt_factory_producer( char *name, void *input ); + mlt_filter mlt_factory_filter( char *name, void *input ); + mlt_transition mlt_factory_transition( char *name, void *input ); + mlt_consumer mlt_factory_consumer( char *name, void *input ); + void mlt_factory_close( ); + + The mlt_factory_prefix returns the path to the location of the installed + modules directory. This can be specified in the mlt_factory_init call + itself, or it can be specified via the MLT_REPOSITORY environment variable, + or in the absence of either of those, it will default to the install + prefix/shared/mlt/modules. + + The mlt_environment provides read only access to a collection of name=value + pairs as shown in the following table: + + +------------------+------------------------------------+------------------+ + |Name |Description |Values | + +------------------+------------------------------------+------------------+ + |MLT_NORMALISATION |The normalisation of the system |PAL or NTSC | + +------------------+------------------------------------+------------------+ + |MLT_PRODUCER |The default producer |"fezzik" or other | + +------------------+------------------------------------+------------------+ + |MLT_CONSUMER |The default consumer |"sdl" or other | + +------------------+------------------------------------+------------------+ + + These values are initialised from the environment variables of the same + name. + + As shown above, a producer can be created using the 'default normalising' + producer, and they can also be requested by name. Filters and transitions + are always requested by name - there is no concept of a 'default' for these. + + +Service Properties: + + As shown in the services.txt document, all services have their own set of + properties than can be manipulated to affect their behaviour. + + In order to set properties on a service, we need to retrieve the properties + object associated to it. For producers, this is done by invoking: + + mlt_properties properties = mlt_producer_properties( producer ); + + All services have a similar method associated to them. + + Once retrieved, setting and getting properties can be done directly on this + object, for example: + + mlt_properties_set( properties, "name", "value" ); + + A more complete description of the properties object is found below. + + +Playlists: + + So far, we've shown a simple producer/consumer configuration - the next + phase is to organise producers in playlists. + + Let's assume that we're adapting the Hello World example, and wish to queue + a number of files for playout, ie: + + hello *.avi + + Instead of invoking mlt_factory_producer directly, we'll create a new + function called create_playlist. This function is responsible for creating + the playlist, creating each producer, appending to the playlist and ensuring + that all the producers are cleaned up when the playlist is destroyed. The + last point is important - a close on the playlist won't explicitly these + producers. In this example, we use unique "data" properties with destructors + to ensure closing. + + mlt_producer create_playlist( int argc, char **argv ) + { + // We're creating a playlist here + mlt_playlist playlist = mlt_playlist_init( ); + + // We need the playlist properties to ensure clean up + mlt_properties properties = mlt_playlist_properties( playlist ); + + // Loop through each of the arguments + int i = 0; + for ( i = 1; i < argc; i ++ ) + { + // Definie the unique key + char key[ 256 ]; + + // Create the producer + mlt_producer producer = mlt_factory_producer( NULL, argv[ i ] ); + + // Add it to the playlist + mlt_playlist_append( playlist, producer ); + + // Create a unique key for this producer + sprintf( key, "producer%d", i ); + + // Now we need to ensure the producers are destroyed + mlt_properties_set_data( properties, key, producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); + } + + // Return the playlist as a producer + return mlt_playlist_producer( playlist ); + } + + Now all we need do is to replace these lines in the main function: + + // Create a normalised producer + mlt_producer world = mlt_factory_producer( NULL, argv[ 1 ] ); + + with: + + // Create a playlist + mlt_producer world = create_playlist( argc, argv ); + + and we have a means to play multiple clips. + + +Filters: + + Inserting filters between the producer and consumer is just a case of + instantiating the filters, connecting the first to the producer, the next + to the previous filter and the last filter to the consumer. + + For example: + + // Create a producer from something + mlt_producer producer = mlt_factory_producer( ... ); + + // Create a consumer from something + mlt_consumer consumer = mlt_factory_consumer( ... ); + + // Create a greyscale filter + mlt_filter filter = mlt_factory_filter( "greyscale", NULL ); + + // Connect the filter to the producer + mlt_filter_connect( filter, mlt_producer_service( producer ), 0 ); + + // Connect the consumer to filter + mlt_consumer_connect( consumer, mlt_filter_service( filter ) ); + + As with producers and consumers, filters can be manipulated via their + properties object - the mlt_filter_properties method can be invoked and + properties can be set as needed. + + The additional argument in the filter connection is an important one as it + dicates the 'track' on which the filter operates. For basic producers and + playlists, there's only one track (0), and as you will see in the next + section, even multiple tracks have a single track output. + + +Multiple Tracks and Transitions: + + MLT's approach to mutliple tracks is governed by two requirements: + + 1) The need for a consumer and producer to communicate with one another via + a single frame; + 2) The desire to be able to serialise and manipulate a 'network' (or filter + graph if you prefer). + + We can visualise a multitrack in the way that an NLE presents it: + + +-----------------+ +-----------------------+ + 0: |a1 | |a2 | + +---------------+-+--------------------------+-+---------------------+ + 1: |b1 | + +------------------------------+ + + The overlapping areas of track 0 and 1 would (presumably) have some kind of + transition - without a transition, the frames from a1 and a2 would be shown + during the areas of overlap. + + MLT has a multitrack object, but it is not a producer in the sense that it + can be connected directly to a consumer and everything will work correctly. + A consumer would treat it precisely as it would a normal producer, and, in + the case of the multitrack above, you would never see anything from track 1 + other than the transitions between the clips - the gap between a1 and a2 + would show test frames. + + This happens because a consumer pulls one frame from the producer it's + connected to while a multitrack will provide one frame per track. + Something, somewhere, must ensure that all frames are pulled from the + multitrack and elect the correct frame to pass on. + + Hence, MLT provides a wrapper for the multitrack, which is called a + 'tractor', and its the tractors task to ensure that all tracks are pulled + evenly, the correct frame is output and that we have 'producer like' + behaviour. + + Thus, a mulitrack is conceptually 'pulled' by a tractor as shown here: + + +----------+ + |multitrack| + | +------+ | +-------+ + | |track0|-|--->|tractor| + | +------+ | |\ | + | | | \ | + | +------+ | | \ | + | |track1|-|--->|---o---|---> + | +------+ | | / | + | | | / | + | +------+ | |/ | + | |track2|-|--->| | + | +------+ | +-------+ + +----------+ + + With a combination of the two, we can now connect multitracks to consumers. + The first non-test card will be retrieved and passed on. + + The tracks can be producers, playlists, or even other tractors. + + Now we wish to insert filters and transitions between the mulitrack and the + tractor. We can do this directly by inserting filters directly between the + tractor and the multitrack, but this involves a lot of connecting and + reconnecting left and right producers and consumers, and it seemed only fair + that we should be able to automate that process. + + So in keeping with our agricultural theme, the concept of the 'field' was + born. We 'plant' filters and transitions in the field and the tractor pulls + the multitrack (think of a combine harvester :-)) over the field and + produces a 'bail' (sorry - kidding - frame :-)). + + Conceptually, we can see it like this: + + +----------+ + |multitrack| + | +------+ | +-------------+ +-------+ + | |track0|-|--->|field |--->|tractor| + | +------+ | | | |\ | + | | | filters | | \ | + | +------+ | | and | | \ | + | |track1|-|--->| transitions |--->|---o---|---> + | +------+ | | | | / | + | | | | | / | + | +------+ | | | |/ | + | |track2|-|--->| |--->| | + | +------+ | +-------------+ +-------+ + +----------+ + + In reality, we create a field first, and from that we obtain a multitrack + and a tractor. We can then populate the multitrack, field and finally, + connect the tractor to the consumer. + + The reasoning behind this is possibly flawed - it might have made more + sense to produce the tractor and have it encapsulate the field and the + multitrack as that is how it looks to a connected consumer: + + +-----------------------------------------------+ + |tractor +--------------------------+ | + | +----------+ | +-+ +-+ +-+ +-+ | | + | |multitrack| | |f| |f| |t| |t| | | + | | +------+ | | |i| |i| |r| |r| | | + | | |track0|-|--->| |l|- ->|l|- ->|a|--->|a|\| | + | | +------+ | | |t| |t| |n| |n| | | + | | | | |e| |e| |s| |s| |\ | + | | +------+ | | |r| |r| |i| |i| | \| + | | |track1|-|- ->| |0|--->|1|--->|t|--->|t|-|--o---> + | | +------+ | | | | | | |i| |i| | /| + | | | | | | | | |o| |o| |/ | + | | +------+ | | | | | | |n| |n| | | + | | |track2|-|- ->| | |- ->| |--->|0|- ->|1|/| | + | | +------+ | | | | | | | | | | | | + | +----------+ | +-+ +-+ +-+ +-+ | | + | +--------------------------+ | + +-----------------------------------------------+ + + An example will hopefully clarify this. + + Let's assume that we want to provide a 'watermark' to our hello world + example. We have already extended the example to play multiple clips, + and now we will place a text based watermark, reading 'Hello World' in + the top left hand corner: + + mlt_producer create_tracks( int argc, char **argv ) + { + // Create the field + mlt_field field = mlt_field_init( ); + + // Obtain the multitrack + mlt_multitrack multitrack = mlt_field_multitrack( field ); + + // Obtain the tractor + mlt_tractor tractor = mlt_field_tractor( field ); + + // Obtain a composite transition + mlt_transition transition = mlt_factory_transition( "composite", "10%,10%:15%x15%" ); + + // Create track 0 + mlt_producer track0 = create_playlist( argc, argv ); + + // Create the watermark track - note we NEED fezzik for scaling here + mlt_producer track1 = mlt_factory_producer( "fezzik", "pango" ); + + // Get the length of track0 + mlt_position length = mlt_producer_get_playtime( track0 ); + + // Set the properties of track1 + mlt_properties properties = mlt_producer_properties( track1 ); + mlt_properties_set( properties, "text", "Hello\nWorld" ); + mlt_properties_set_position( properties, "in", 0 ); + mlt_properties_set_position( properties, "out", length - 1 ); + mlt_properties_set_position( properties, "length", length ); + + // Now set the properties on the transition + properties = mlt_transition_properties( transition ); + mlt_properties_set_position( properties, "in", 0 ); + mlt_properties_set_position( properties, "out", length - 1 ); + + // Add our tracks to the multitrack + mlt_multitrack_connect( multitrack, track0, 0 ); + mlt_multitrack_connect( multitrack, track1, 1 ); + + // Now plant the transition + mlt_field_plant_transition( field, transition, 0, 1 ); + + // Now set the properties on the tractor + properties = mlt_tractor_properties( tractor ); + mlt_properties_set_data( properties, "multitrack", multitrack, 0, ( mlt_destructor )mlt_multitrack_close, NULL ); + mlt_properties_set_data( properties, "field", field, 0, ( mlt_destructor )mlt_field_close, NULL ); + mlt_properties_set_data( properties, "track0", track0, 0, ( mlt_destructor )mlt_producer_close, NULL ); + mlt_properties_set_data( properties, "track1", track1, 0, ( mlt_destructor )mlt_producer_close, NULL ); + mlt_properties_set_data( properties, "transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL ); + + // Return the tractor + return mlt_tractor_producer( tractor ); + } + + Now all we need do is to replace these lines in the main function: + + // Create a playlist + mlt_producer world = create_playlist( argc, argv ); + + with: + + // Create a watermarked playlist + mlt_producer world = create_tracks( argc, argv ); + + and we have a means to play multiple clips with a horribly obtrusive + watermark - just what the world needed, right? ;-) + + +SECTION 3 - STRUCTURE AND DESIGN +-------------------------------- + +Class Heirachy: + + The mlt framework consists of an OO class heirachy which consists of the + following public classes and abstractions: + + mlt_properties + mlt_frame + mlt_service + mlt_producer + mlt_playlist + mlt_tractor + mlt_filter + mlt_transition + mlt_consumer + mlt_deque + mlt_pool + mlt_factory + + Each class defined above can be read as extending the classes above and to + the left. + + The following sections describe the properties, stacking/queuing and memory + pooling functionality provided by the framework - these are key components + and a basic understanding of these is required for the remainder of the + documentation. + + +mlt_properties: + + The properties class is the base class for the frame and service classes. + + It is designed to provide an efficient lookup table for various types of + information, such as strings, integers, floating points values and pointers + to data and data structures. + + All properties are indexed by a unique string. + + The most basic use of properties is as follows: + + // 1. Create a new, empty properties set; + mlt_properties properties = mlt_properties_new( ); + + // 2. Assign the value "world" to the property "hello"; + mlt_properties_set( properties, "hello", "world" ); + + // 3. Retrieve and print the value of "hello"; + printf( "%s\n", mlt_properties_get( properties, "hello" ) ); + + // 4. Reassign "hello" to "world!"; + mlt_properties_set( properties, "hello", "world!" ); + + // 5. Retrieve and print the value of "hello"; + printf( "%s\n", mlt_properties_get( properties, "hello" ) ); + + // 6. Assign the value "0" to "int"; + mlt_properties_set( properties, "int", "0" ); + + // 7. Retrieve and print the integer value of "int"; + printf( "%d\n", mlt_properties_get_int( properties, "int" ) ); + + // 8. Assign the integer value 50 to "int2"; + mlt_properties_set_int( properties, "int2", 50 ); + + // 9. Retrieve and print the double value of "int2"; + printf( "%s\n", mlt_properties_get( properties, "int2" ) ); + + Steps 2 through 5 demonstrate that the "name" is unique - set operations on + an existing "name" change the value. They also free up memory associated to + the previous value. Note that it also possible to change type in this way + too. + + Steps 6 and 7 demonstrate that the properties object handles deserialisation + from strings. The string value of "50" is set, the integer value of 50 is + retrieved. + + Steps 8 and 9 demonstrate that the properties object handles serialisation + to strings. + + To show all the name/value pairs in a properties, it is possible to iterate + through them: + + int i = 0; + for ( i = 0; i < mlt_properties_count( properties ); i ++ ) + printf( "%s = %s\n", mlt_properties_get_name( properties, i ), + mlt_properties_get_value( properties, i ) ); + + Note that properties are retrieved in the order in which they are set. + + Properties are also used to hold pointers to memory. This is done via the + set_data call: + + uint8_t *image = malloc( size ); + mlt_properties_set_data( properties, "image", image, size, NULL, NULL ); + + In this example, we specify that the pointer can be retrieved from + properties by a subsequent request to get_data: + + image = mlt_properties_get_data( properties, "image", &size ); + + or: + + image = mlt_properties_get_data( properties, "image", NULL ); + + if we don't wish to retrieve the size. + + Two points here: + + 1) The allocated memory remains after the properties object is closed unless + you specify a destructor. In the case above, this can be done with: + + mlt_properties_set_data( properties, "image", image, size, free, NULL ); + + When the properties are closed, or the value of "image" is changed, the + destructor is invoked. + + 2) The string value returned by mlt_properties_get is NULL. Typically, you + wouldn't wish to serialise an image as a string, but other structures + might need such functionality - you can specify a serialiser as the last + argument if required (declaration is char *serialise( void * )). + + Properties also provides some more advanced usage capabilities. + + It has the ability to inherit all serialisable values from another properties + object: + + mlt_properties_inherit( this, that ); + + It has the ability to mirror properties set on this on another set of + properties: + + mlt_properties_mirror( this, that ); + + After this call, all serialisable values set on this are passed on to that. + + +mlt_deque: + + Stacks and queues are essential components in the MLT framework. Being of a + lazy disposition, we elected to implement a 'Double Ended Queue' (deque) - + this encapsulates the functionality of both. + + The API of the deque is defined as follows: + + mlt_deque mlt_deque_init( ); + int mlt_deque_count( mlt_deque this ); + int mlt_deque_push_back( mlt_deque this, void *item ); + void *mlt_deque_pop_back( mlt_deque this ); + int mlt_deque_push_front( mlt_deque this, void *item ); + void *mlt_deque_pop_front( mlt_deque this ); + void *mlt_deque_peek_back( mlt_deque this ); + void *mlt_deque_peek_front( mlt_deque this ); + void mlt_deque_close( mlt_deque this ); + + The stacking operations are used in a number of places: + + * Reverse Polish Notation (RPN) image and audio operations + * memory pooling + + The queuing operations are used in typical frame based consumers to allow + buffering. + + +mlt_pool: + + The MLT framework provides memory pooling capabilities through the mlt_pool + API. Once initilialised, these can be seen as a straightforward drop in + replacement for malloc/realloc/free functionality. + + The background behind this API is that malloc/free operations are + notoriously inefficient, especially when dealing with large blocks of memory + (such as an image). On linux, malloc is optimised for memory allocations + less than 128k - memory blocks allocated of these sizes or less are retained + in the process heap for subsequent reuse, thus bypassing the kernel calls + for repeated allocation/frees for small blocks of memory. However, blocks of + memory larger than that require kernel calls and this has a detrimental + impact on performance. + + The mlt_pool design is simply to hold a list of stacks - there is one stack + per 2^n bytes (where n is between 8 and 31). When an alloc is called, the + requested size is rounded to the next 2^n, the stack is retrieved for that + size, and an item is popped or created if the stack is empty. + + Each item has a 'header', situated immediately before the returned address - + this holds the 'stack' to which the item belongs. + + When an item is released, we retrieve the header, obtain the stack and push + it back. + + Thus, from the programmers point of view, the API is the same as the + traditional malloc/realloc/free calls: + + void *mlt_pool_alloc( int size ); + void *mlt_pool_realloc( void *ptr, int size ); + void mlt_pool_release( void *release ); + + +mlt_frame: + + A frame object is essentially defined as: + + +------------+ + |frame | + +------------+ + | properties | + | image stack| + | audio stack| + +------------+ + + The life cycle of a frame can be represented as follows: + + +-----+----------------------+-----------------------+---------------------+ + |Stage|Producer |Filter |Consumer | + +-----+----------------------+-----------------------+---------------------+ + | 0.0 | | |Request frame | + +-----+----------------------+-----------------------+---------------------+ + | 0.1 | |Receives request | | + | | |Request frame | | + +-----+----------------------+-----------------------+---------------------+ + | 0.2 |Receives request | | | + | |Generates frame for | | | + | |current position | | | + | |Increments position | | | + +-----+----------------------+-----------------------+---------------------+ + | 0.3 | |Receives frame | | + | | |Updates frame | | + +-----+----------------------+-----------------------+---------------------+ + | 0.4 | | |Receives frame | + +-----+----------------------+-----------------------+---------------------+ + + Note that neither the filter nor the consumer have any conception of + 'position' until they receive a frame. Speed and position are properties of + the producer, and they are assigned to the frame object when the producer + creates it. + + Step 0.3 is a critical one here - if the filter determines that the frame is + of interest to it, then it should manipulate the image and/or audio stacks + and properties as required. + + Assuming that the filter deals with both image and audio, then it should + push data and methods on to the stacks which will deal with the processing. + This can be done with the mlt_frame_push_image and audio methods. In order for + the filter to register interest in the frame, the stacks should hold: + + image stack: + [ producer_get_image ] [ data1 ] [ data2 ] [ filter_get_image ] + + audio stack: + [ producer_get_audio ] [ data ] [ filter_get_audio ] + + The filter_get methods are invoked automatically when the consumer invokes a + get_image on the frame. + + +-----+----------------------+-----------------------+---------------------+ + |Stage|Producer |Filter |Consumer | + +-----+----------------------+-----------------------+---------------------+ + | 1.0 | | |frame_get_image | + +-----+----------------------+-----------------------+---------------------+ + | 1.1 | |filter_get_image: | | + | | | pop data2 and data1 | | + | | | frame_get_image | | + +-----+----------------------+-----------------------+---------------------+ + | 1.2 |producer_get_image | | | + | | Generates image | | | + +-----+----------------------+-----------------------+---------------------+ + | 1.3 | |Receives image | | + | | |Updates image | | + +-----+----------------------+-----------------------+---------------------+ + | 1.4 | | |Receives image | + +-----+----------------------+-----------------------+---------------------+ + + Obviously, if the filter isn't interested in the image, then it should leave + the stack alone, and then the consumer will retrieve its image directly from + the producer. + + Similarly, audio is handled as follows: + + +-----+----------------------+-----------------------+---------------------+ + |Stage|Producer |Filter |Consumer | + +-----+----------------------+-----------------------+---------------------+ + | 2.0 | | |frame_get_audio | + +-----+----------------------+-----------------------+---------------------+ + | 2.1 | |filter_get_audio: | | + | | | pop data | | + | | | frame_get_audio | | + +-----+----------------------+-----------------------+---------------------+ + | 2.2 |producer_get_audio | | | + | | Generates audio | | | + +-----+----------------------+-----------------------+---------------------+ + | 2.3 | |Receives audio | | + | | |Updates audio | | + +-----+----------------------+-----------------------+---------------------+ + | 2.4 | | |Receives audio | + +-----+----------------------+-----------------------+---------------------+ + + And finally, when the consumer is done with the frame, it should close it. + + Note that a consumer may not evaluate both image and audio for any given + frame, especially in a realtime environment. See 'Realtime Considerations' + below. + + By default, a frame has the following properties: + + +------------------+------------------------------------+------------------+ + |Name |Description |Values | + +------------------+------------------------------------+------------------+ + |_position |The producers frame position |0 to n | + +------------------+------------------------------------+------------------+ + |_speed |The producers speed |double | + +------------------+------------------------------------+------------------+ + |image |The generated image |NULL or pointer | + +------------------+------------------------------------+------------------+ + |alpha |The generated alpha mask |NULL or pointer | + +------------------+------------------------------------+------------------+ + |width |The width of the image | | + +------------------+------------------------------------+------------------+ + |height |The height of the image | | + +------------------+------------------------------------+------------------+ + |normalised_width |The normalised width of the image |720 | + +------------------+------------------------------------+------------------+ + |normalised_height |The normalised height of the image |576 or 480 | + +------------------+------------------------------------+------------------+ + |progressive |Indicates progressive/interlaced |0 or 1 | + +------------------+------------------------------------+------------------+ + |top_field_first |Indicates top field first |0 or 1 | + +------------------+------------------------------------+------------------+ + |audio |The generated audio |NULL or pointer | + +------------------+------------------------------------+------------------+ + |frequency |The frequency of the audio | | + +------------------+------------------------------------+------------------+ + |channels |The channels of the audio | | + +------------------+------------------------------------+------------------+ + |samples |The samples of the audio | | + +------------------+------------------------------------+------------------+ + |aspect_ratio |The aspect ratio of the image |double | + +------------------+------------------------------------+------------------+ + |test_image |Used to indicate no image available |0 or 1 | + +------------------+------------------------------------+------------------+ + |test_audio |Used to indicate no audio available |0 or 1 | + +------------------+------------------------------------+------------------+ + + The consumer can attach the following properties which affect the default + behaviour of a frame: + + +------------------+------------------------------------+------------------+ + |test_card_producer|Synthesise test images from here |NULL or pointer | + +------------------+------------------------------------+------------------+ + |consumer_aspect_ |Apply this aspect ratio to the test |double | + |ratio |card producer | | + +------------------+------------------------------------+------------------+ + |rescale.interp |Use this scale method for test image|"string" | + +------------------+------------------------------------+------------------+ + + While most of these are mainly self explainatory, the normalised_width and + normalised_height values require a little explaination. These are required + to ensure that effects are consistently handled as PAL or NTSC, regardless + of the consumers or producers width/height image request. + + The test_image and audio flags are used to determine when images and audio + should be synthesised. + + Additional properties may be provided by the producer implementation, and + filters, transitions and consumers may add additional properties to + communicate specific requests. These are documented in modules.txt. + + The complete API for the mlt frame is as follows: + + mlt_frame mlt_frame_init( ); + mlt_properties mlt_frame_properties( mlt_frame this ); + int mlt_frame_is_test_card( mlt_frame this ); + int mlt_frame_is_test_audio( mlt_frame this ); + double mlt_frame_get_aspect_ratio( mlt_frame this ); + int mlt_frame_set_aspect_ratio( mlt_frame this, double value ); + mlt_position mlt_frame_get_position( mlt_frame this ); + int mlt_frame_set_position( mlt_frame this, mlt_position value ); + int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ); + uint8_t *mlt_frame_get_alpha_mask( mlt_frame this ); + int mlt_frame_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ); + int mlt_frame_push_get_image( mlt_frame this, mlt_get_image get_image ); + mlt_get_image mlt_frame_pop_get_image( mlt_frame this ); + int mlt_frame_push_frame( mlt_frame this, mlt_frame that ); + mlt_frame mlt_frame_pop_frame( mlt_frame this ); + int mlt_frame_push_service( mlt_frame this, void *that ); + void *mlt_frame_pop_service( mlt_frame this ); + int mlt_frame_push_audio( mlt_frame this, void *that ); + void *mlt_frame_pop_audio( mlt_frame this ); + void mlt_frame_close( mlt_frame this ); + +mlt_service: + + The service base class extends properties and allows 0 to m inputs and 0 to + n outputs and is represented as follows: + + +-----------+ + - ->| |- -> + - ->| Service |- -> + - ->| | + +-----------+ + | properties| + +-----------+ + + Descendents of service impose restrictions on how inputs and outputs can be + connected and will provide a basic set of properties. Typically, the service + instance is encapsulated by the descendent in order for it to ensure that + its connection rules are followed. + + A service does not define any properties when constructed. It should be + noted that producers, filters and transitions my be serialised (say, via the + westley consumer), and care should be taken to distinguish between + serialisable and transient properties. The convention used is to prefix + transient properties with an underscore. + + The public interface is defined by the following functions: + + int mlt_service_init( mlt_service this, void *child ); + mlt_properties mlt_service_properties( mlt_service this ); + int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index ); + int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index ); + void mlt_service_close( mlt_service this ); + + Typically, only direct descendents of services need invoke these methods and + developers are encouraged to use those extensions when definining new + services. + + +mlt_producer: + + A producer has 0 inputs and 1 output: + + +-----------+ + | | + | Producer |---> + | | + +-----------+ + | service | + +-----------+ + + A producer provides an abstraction for file readers, pipes, streams or any + other image or audio input. + + When instantiated, a producer has the following properties: + + +------------------+------------------------------------+------------------+ + |Name |Description |Values | + +------------------+------------------------------------+------------------+ + |mlt_type |The producers type |mlt_producer | + +------------------+------------------------------------+------------------+ + |_position |The producers frame position |0 to n | + +------------------+------------------------------------+------------------+ + |_speed |The producers speed |double | + +------------------+------------------------------------+------------------+ + |fps |The output frames per second |25 or 29.97 | + +------------------+------------------------------------+------------------+ + |in |The in point in frames |0 to length - 1 | + +------------------+------------------------------------+------------------+ + |out |The out point in frames |in to length - 1 | + +------------------+------------------------------------+------------------+ + |length |The length of the input in frames |0 to n | + +------------------+------------------------------------+------------------+ + |aspect_ratio |aspect_ratio of the source |0 to n | + +------------------+------------------------------------+------------------+ + |eof |end of clip behaviour |"pause" or "loop" | + +------------------+------------------------------------+------------------+ + |resource |Constructor argument (ie: file name)|"" | + +------------------+------------------------------------+------------------+ + + Additional properties may be provided by the producer implementation. + + The public interface is defined by the following functions: + + int mlt_producer_init( mlt_producer this, void *child ); + mlt_service mlt_producer_service( mlt_producer this ); + mlt_properties mlt_producer_properties( mlt_producer this ); + int mlt_producer_seek( mlt_producer this, mlt_position position ); + mlt_position mlt_producer_position( mlt_producer this ); + mlt_position mlt_producer_frame( mlt_producer this ); + int mlt_producer_set_speed( mlt_producer this, double speed ); + double mlt_producer_get_speed( mlt_producer this ); + double mlt_producer_get_fps( mlt_producer this ); + int mlt_producer_set_in_and_out( mlt_producer this, mlt_position in, mlt_position out ); + mlt_position mlt_producer_get_in( mlt_producer this ); + mlt_position mlt_producer_get_out( mlt_producer this ); + mlt_position mlt_producer_get_playtime( mlt_producer this ); + mlt_position mlt_producer_get_length( mlt_producer this ); + void mlt_producer_prepare_next( mlt_producer this ); + void mlt_producer_close( mlt_producer this ); + + For the sake of discussion here, we'll assume that someone wants to provide + a new producer which simply generates green frames and silent audio :-) - + we'll call the producer 'green'. + + // Forward reference + static int producer_get_frame( mlt_service service, mlt_frame_ptr frame, int index ); + + mlt_producer producer_green( void *arg ) + { + // Create a new producer + mlt_producer this = mlt_producer_new( ); + + // Check that we were allocated a new producer + if ( this != NULL ) + { + // Get the service + mlt_service service = mlt_producer_service( this ); + + // We need to override the get_frame method + service->get_frame = producer_get_frame; + } + + // Return this producer + return this; + } + + static int producer_get_frame( mlt_service service, mlt_frame_ptr frame, int index ) + { + // Create a new frame + *frame = mlt_frame_init( ); + + // Specify the get_image + mlt_frame_push_get_image( *frame, producer_get_image ); + + // Specify the get_audio + mlt_frame_push_audio( *frame, producer_get_audio ); + + // Return that all was successful + return 0; + } + +mlt_filter: + + The public interface is defined by the following functions: + + int mlt_filter_init( mlt_filter this, void *child ); + mlt_filter mlt_filter_new( ); + mlt_service mlt_filter_service( mlt_filter this ); + mlt_properties mlt_filter_properties( mlt_filter this ); + mlt_frame mlt_filter_process( mlt_filter this, mlt_frame that ); + int mlt_filter_connect( mlt_filter this, mlt_service producer, int index ); + void mlt_filter_set_in_and_out( mlt_filter this, mlt_position in, mlt_position out ); + int mlt_filter_get_track( mlt_filter this ); + mlt_position mlt_filter_get_in( mlt_filter this ); + mlt_position mlt_filter_get_out( mlt_filter this ); + void mlt_filter_close( mlt_filter ); + + +mlt_transition: + + The public interface is defined by the following functions: + + int mlt_transition_init( mlt_transition this, void *child ); + mlt_transition mlt_transition_new( ); + mlt_service mlt_transition_service( mlt_transition this ); + mlt_properties mlt_transition_properties( mlt_transition this ); + int mlt_transition_connect( mlt_transition this, mlt_service producer, int a_track, int b_track ); + void mlt_transition_set_in_and_out( mlt_transition this, mlt_position in, mlt_position out ); + int mlt_transition_get_a_track( mlt_transition this ); + int mlt_transition_get_b_track( mlt_transition this ); + mlt_position mlt_transition_get_in( mlt_transition this ); + mlt_position mlt_transition_get_out( mlt_transition this ); + mlt_frame mlt_transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame ); + void mlt_transition_close( mlt_transition this ); + + +mlt_consumer: + + The public interface is defined by the following functions: + + int mlt_consumer_init( mlt_consumer this, void *child ); + mlt_service mlt_consumer_service( mlt_consumer this ); + mlt_properties mlt_consumer_properties( mlt_consumer this ); + int mlt_consumer_connect( mlt_consumer this, mlt_service producer ); + int mlt_consumer_start( mlt_consumer this ); + mlt_frame mlt_consumer_get_frame( mlt_consumer this ); + mlt_frame mlt_consumer_rt_frame( mlt_consumer this ); + int mlt_consumer_stop( mlt_consumer this ); + int mlt_consumer_is_stopped( mlt_consumer this ); + void mlt_consumer_close( mlt_consumer ); + + +Specialised Producers: + + There are two major types of specialised producers - playlists and tractors. + + The following sections describe these. + + +mlt_playlist: + + mlt_playlist mlt_playlist_init( ); + mlt_producer mlt_playlist_producer( mlt_playlist this ); + mlt_service mlt_playlist_service( mlt_playlist this ); + mlt_properties mlt_playlist_properties( mlt_playlist this ); + int mlt_playlist_count( mlt_playlist this ); + int mlt_playlist_clear( mlt_playlist this ); + int mlt_playlist_append( mlt_playlist this, mlt_producer producer ); + int mlt_playlist_append_io( mlt_playlist this, mlt_producer producer, mlt_position in, mlt_position out ); + int mlt_playlist_blank( mlt_playlist this, mlt_position length ); + mlt_position mlt_playlist_clip( mlt_playlist this, mlt_whence whence, int index ); + int mlt_playlist_current_clip( mlt_playlist this ); + mlt_producer mlt_playlist_current( mlt_playlist this ); + int mlt_playlist_get_clip_info( mlt_playlist this, mlt_playlist_clip_info *info, int index ); + int mlt_playlist_insert( mlt_playlist this, mlt_producer producer, int where, mlt_position in, mlt_position out ); + int mlt_playlist_remove( mlt_playlist this, int where ); + int mlt_playlist_move( mlt_playlist this, int from, int to ); + int mlt_playlist_resize_clip( mlt_playlist this, int clip, mlt_position in, mlt_position out ); + void mlt_playlist_close( mlt_playlist this ); + +mlt_tractor: + + +mlt_factory + diff --git a/docs/westley.txt b/docs/westley.txt index 3984083f..89e59288 100644 --- a/docs/westley.txt +++ b/docs/westley.txt @@ -125,7 +125,6 @@ Playlists: Interlude - Introducing Multitracks: - So far we've defined basic producers and playlists/tracks - the tractor is the element that allows us to arrange our tracks and specify filters and transitions. Similarly to a playlist, a tractor is a container. diff --git a/src/framework/config.h b/src/framework/config.h index fec9861d..7f5019f6 100644 --- a/src/framework/config.h +++ b/src/framework/config.h @@ -4,7 +4,7 @@ #define _MLT_CONFIG_H_ #define PREFIX "/usr/local" -#define PREFIX_DATA PREFIX "/share" +#define PREFIX_DATA PREFIX "/share/mlt/modules" #endif diff --git a/src/framework/mlt_factory.c b/src/framework/mlt_factory.c index 0b4df214..dee338e3 100644 --- a/src/framework/mlt_factory.c +++ b/src/framework/mlt_factory.c @@ -46,6 +46,10 @@ int mlt_factory_init( char *prefix ) // Only initialise once if ( mlt_prefix == NULL ) { + // Allow user over rides + if ( prefix == NULL ) + prefix = getenv( "MLT_REPOSITORY" ); + // If no directory is specified, default to install directory if ( prefix == NULL ) prefix = PREFIX_DATA; @@ -58,7 +62,9 @@ int mlt_factory_init( char *prefix ) // Create the global properties global_properties = mlt_properties_new( ); - mlt_properties_set( global_properties, "MLT_NORMALISATION", getenv( "MLT_NORMALISATION" ) ); + mlt_properties_set_or_default( global_properties, "MLT_NORMALISATION", getenv( "MLT_NORMALISATION" ), "PAL" ); + mlt_properties_set_or_default( global_properties, "MLT_PRODUCER", getenv( "MLT_PRODUCER" ), "fezzik" ); + mlt_properties_set_or_default( global_properties, "MLT_CONSUMER", getenv( "MLT_CONSUMER" ), "sdl" ); // Create the object list. object_list = mlt_properties_new( ); @@ -94,7 +100,15 @@ char *mlt_environment( char *name ) mlt_producer mlt_factory_producer( char *service, void *input ) { - mlt_producer obj = mlt_repository_fetch( producers, service, input ); + mlt_producer obj = NULL; + + // Pick up the default normalising producer if necessary + if ( service == NULL ) + service = mlt_environment( "MLT_PRODUCER" ); + + // Try to instantiate via the specified service + obj = mlt_repository_fetch( producers, service, input ); + if ( obj != NULL ) { mlt_properties properties = mlt_producer_properties( obj ); @@ -143,7 +157,13 @@ mlt_transition mlt_factory_transition( char *service, void *input ) mlt_consumer mlt_factory_consumer( char *service, void *input ) { - mlt_consumer obj = mlt_repository_fetch( consumers, service, input ); + mlt_consumer obj = NULL; + + if ( service == NULL ) + service = mlt_environment( "MLT_CONSUMER" ); + + obj = mlt_repository_fetch( consumers, service, input ); + if ( obj != NULL ) { mlt_properties properties = mlt_consumer_properties( obj ); diff --git a/src/framework/mlt_frame.h b/src/framework/mlt_frame.h index f1cd0fed..e288c1b5 100644 --- a/src/framework/mlt_frame.h +++ b/src/framework/mlt_frame.h @@ -24,20 +24,6 @@ #include "mlt_properties.h" #include "mlt_deque.h" -typedef enum -{ - mlt_video_standard_pal = 0, - mlt_video_standard_ntsc -} -mlt_video_standard; - -typedef enum -{ - mlt_audio_none = 0, - mlt_audio_pcm -} -mlt_audio_format; - typedef int ( *mlt_get_image )( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ); struct mlt_frame_s @@ -62,21 +48,17 @@ extern double mlt_frame_get_aspect_ratio( mlt_frame this ); extern int mlt_frame_set_aspect_ratio( mlt_frame this, double value ); extern mlt_position mlt_frame_get_position( mlt_frame this ); extern int mlt_frame_set_position( mlt_frame this, mlt_position value ); - extern int mlt_frame_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable ); extern uint8_t *mlt_frame_get_alpha_mask( mlt_frame this ); extern int mlt_frame_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ); - extern int mlt_frame_push_get_image( mlt_frame this, mlt_get_image get_image ); extern mlt_get_image mlt_frame_pop_get_image( mlt_frame this ); extern int mlt_frame_push_frame( mlt_frame this, mlt_frame that ); extern mlt_frame mlt_frame_pop_frame( mlt_frame this ); extern int mlt_frame_push_service( mlt_frame this, void *that ); extern void *mlt_frame_pop_service( mlt_frame this ); - extern int mlt_frame_push_audio( mlt_frame this, void *that ); extern void *mlt_frame_pop_audio( mlt_frame this ); - extern void mlt_frame_close( mlt_frame this ); /* convenience functions */ diff --git a/src/framework/mlt_producer.c b/src/framework/mlt_producer.c index 69b02337..63a443ff 100644 --- a/src/framework/mlt_producer.c +++ b/src/framework/mlt_producer.c @@ -66,14 +66,19 @@ int mlt_producer_init( mlt_producer this, void *child ) mlt_properties_set_position( properties, "_position", 0.0 ); mlt_properties_set_double( properties, "_frame", 0 ); if ( normalisation == NULL || strcmp( normalisation, "NTSC" ) ) + { mlt_properties_set_double( properties, "fps", 25.0 ); + mlt_properties_set_double( properties, "aspect_ratio", 72.0 / 79.0 ); + } else + { mlt_properties_set_double( properties, "fps", 30000.0 / 1001.0 ); + mlt_properties_set_double( properties, "aspect_ratio", 128.0 / 117.0 ); + } mlt_properties_set_double( properties, "_speed", 1.0 ); mlt_properties_set_position( properties, "in", 0 ); mlt_properties_set_position( properties, "out", 14999 ); mlt_properties_set_position( properties, "length", 15000 ); - mlt_properties_set_double( properties, "aspect_ratio", 128.0 / 117.0 ); mlt_properties_set( properties, "eof", "pause" ); mlt_properties_set( properties, "resource", "" ); diff --git a/src/framework/mlt_properties.c b/src/framework/mlt_properties.c index 2664edd7..3fea15e2 100644 --- a/src/framework/mlt_properties.c +++ b/src/framework/mlt_properties.c @@ -239,6 +239,14 @@ int mlt_properties_set( mlt_properties this, char *name, char *value ) return error; } +/** Set or default the property. +*/ + +int mlt_properties_set_or_default( mlt_properties this, char *name, char *value, char *def ) +{ + return mlt_properties_set( this, name, value == NULL ? def : value ); +} + /** Get a string value by name. */ diff --git a/src/framework/mlt_properties.h b/src/framework/mlt_properties.h index df66146f..6a043091 100644 --- a/src/framework/mlt_properties.h +++ b/src/framework/mlt_properties.h @@ -43,6 +43,7 @@ extern void mlt_properties_mirror( mlt_properties this, mlt_properties that ); extern int mlt_properties_inherit( mlt_properties this, mlt_properties that ); extern int mlt_properties_pass( mlt_properties this, mlt_properties that, char *prefix ); extern int mlt_properties_set( mlt_properties this, char *name, char *value ); +extern int mlt_properties_set_or_default( mlt_properties this, char *name, char *value, char *def ); extern int mlt_properties_parse( mlt_properties this, char *namevalue ); extern char *mlt_properties_get( mlt_properties this, char *name ); extern char *mlt_properties_get_name( mlt_properties this, int index ); diff --git a/src/framework/mlt_service.c b/src/framework/mlt_service.c index 62112d96..87252a61 100644 --- a/src/framework/mlt_service.c +++ b/src/framework/mlt_service.c @@ -47,7 +47,7 @@ typedef struct } mlt_service_base; -/** Friends? +/** Private methods */ static void mlt_service_disconnect( mlt_service this ); @@ -89,14 +89,6 @@ int mlt_service_connect_producer( mlt_service this, mlt_service producer, int in // Get the service base mlt_service_base *base = this->private; - // Does this service accept input? - if ( mlt_service_accepts_input( this ) == 0 ) - return 1; - - // Does the producer service accept output connections? - if ( mlt_service_accepts_output( producer ) == 0 ) - return 2; - // Check if the producer is already registered with this service for ( i = 0; i < base->count; i ++ ) if ( base->in[ i ] == producer ) @@ -143,7 +135,7 @@ int mlt_service_connect_producer( mlt_service this, mlt_service producer, int in /** Disconnect this service from its consumer. */ -void mlt_service_disconnect( mlt_service this ) +static void mlt_service_disconnect( mlt_service this ) { // Get the service base mlt_service_base *base = this->private; @@ -155,7 +147,7 @@ void mlt_service_disconnect( mlt_service this ) /** Associate this service to the its consumer. */ -void mlt_service_connect( mlt_service this, mlt_service that ) +static void mlt_service_connect( mlt_service this, mlt_service that ) { // Get the service base mlt_service_base *base = this->private; @@ -164,7 +156,6 @@ void mlt_service_connect( mlt_service this, mlt_service that ) base->out = that; } - /** Get the first connected producer service. */ @@ -180,78 +171,8 @@ mlt_service mlt_service_get_producer( mlt_service this ) return producer; } - - -/** Get the service state. -*/ - -mlt_service_state mlt_service_get_state( mlt_service this ) -{ - mlt_service_state state = mlt_state_unknown; - if ( mlt_service_has_input( this ) ) - state |= mlt_state_providing; - if ( mlt_service_has_output( this ) ) - state |= mlt_state_connected; - if ( state != ( mlt_state_providing | mlt_state_connected ) ) - state |= mlt_state_dormant; - return state; -} - -/** Get the maximum number of inputs accepted. - Returns: -1 for many, 0 for none or n for fixed. -*/ - -int mlt_service_accepts_input( mlt_service this ) -{ - if ( this->accepts_input == NULL ) - return -1; - else - return this->accepts_input( this ); -} - -/** Get the maximum number of outputs accepted. -*/ - -int mlt_service_accepts_output( mlt_service this ) -{ - if ( this->accepts_output == NULL ) - return 1; - else - return this->accepts_output( this ); -} -/** Determines if this service has input -*/ - -int mlt_service_has_input( mlt_service this ) -{ - if ( this->has_input == NULL ) - return 1; - else - return this->has_input( this ); -} - -/** Determine if this service has output -*/ - -int mlt_service_has_output( mlt_service this ) -{ - mlt_service_base *base = this->private; - if ( this->has_output == NULL ) - return base->out != NULL; - else - return this->has_output( this ); -} - -/** Check if the service is active. -*/ - -int mlt_service_is_active( mlt_service this ) -{ - return !( mlt_service_get_state( this ) & mlt_state_dormant ); -} - -/** Obtain a frame to pass on. +/** Default implementation of get_frame. */ static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index ) @@ -267,6 +188,9 @@ static int service_get_frame( mlt_service this, mlt_frame_ptr frame, int index ) return 0; } +/** Obtain a frame. +*/ + int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index ) { return this->get_frame( this, frame, index ); diff --git a/src/framework/mlt_service.h b/src/framework/mlt_service.h index 99d1966b..afdbc623 100644 --- a/src/framework/mlt_service.h +++ b/src/framework/mlt_service.h @@ -23,22 +23,6 @@ #include "mlt_properties.h" -/** State of a service. - - Note that a service may be dormant even though it's fully connected, - providing or consuming. -*/ - -typedef enum -{ - mlt_state_unknown = 0, - mlt_state_dormant = 1, - mlt_state_connected = 2, - mlt_state_providing = 4, - mlt_state_consuming = 8 -} -mlt_service_state; - /** The interface definition for all services. */ @@ -48,10 +32,6 @@ struct mlt_service_s struct mlt_properties_s parent; // Protected virtual - int ( *accepts_input )( mlt_service this ); - int ( *accepts_output )( mlt_service this ); - int ( *has_input )( mlt_service this ); - int ( *has_output )( mlt_service this ); int ( *get_frame )( mlt_service this, mlt_frame_ptr frame, int index ); // Private data @@ -64,15 +44,10 @@ struct mlt_service_s extern int mlt_service_init( mlt_service this, void *child ); extern int mlt_service_connect_producer( mlt_service this, mlt_service producer, int index ); -extern mlt_service_state mlt_service_get_state( mlt_service this ); +extern int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index ); extern void mlt_service_close( mlt_service this ); -extern int mlt_service_accepts_input( mlt_service this ); -extern int mlt_service_accepts_output( mlt_service this ); -extern int mlt_service_has_input( mlt_service this ); -extern int mlt_service_has_output( mlt_service this ); -extern int mlt_service_get_frame( mlt_service this, mlt_frame_ptr frame, int index ); -extern int mlt_service_is_active( mlt_service this ); +// I'm not sure about this one - leaving it out of docs for now (only used in consumer_westley) extern mlt_service mlt_service_get_producer( mlt_service this ); /** Return the properties object. diff --git a/src/framework/mlt_types.h b/src/framework/mlt_types.h index 5e061bbf..284c35bc 100644 --- a/src/framework/mlt_types.h +++ b/src/framework/mlt_types.h @@ -35,6 +35,13 @@ typedef enum } mlt_image_format; +typedef enum +{ + mlt_audio_none = 0, + mlt_audio_pcm +} +mlt_audio_format; + typedef enum { mlt_whence_relative_start, diff --git a/src/modules/core/transition_composite.c b/src/modules/core/transition_composite.c index 1cfed9be..3c4557ed 100644 --- a/src/modules/core/transition_composite.c +++ b/src/modules/core/transition_composite.c @@ -817,7 +817,7 @@ static int get_b_frame_image( mlt_transition this, mlt_frame b_frame, uint8_t ** *width = geometry->sw * *width / geometry->nw; *height = geometry->sh * *height / geometry->nh; - x -= x % 2; + x &= 0xfffffffe; // optimization points - no work to do if ( *width < 1 || *height < 1 ) diff --git a/src/modules/sdl/consumer_sdl.c b/src/modules/sdl/consumer_sdl.c index d3d0b7ac..589194ad 100644 --- a/src/modules/sdl/consumer_sdl.c +++ b/src/modules/sdl/consumer_sdl.c @@ -347,6 +347,9 @@ static int consumer_play_video( consumer_sdl this, mlt_frame frame ) this->window_height = event.resize.h; changed = 1; break; + case SDL_QUIT: + this->running = 0; + break; case SDL_KEYDOWN: { mlt_producer producer = mlt_properties_get_data( properties, "transport_producer", NULL ); diff --git a/src/tests/Makefile b/src/tests/Makefile index 3b48d274..8e680f1b 100644 --- a/src/tests/Makefile +++ b/src/tests/Makefile @@ -11,6 +11,9 @@ endif all: $(TARGET) +hello: hello.o + $(CC) hello.o -o $@ -L ../framework -L ../modules -lmlt + pango: pango.o $(CC) pango.o -o $@ $(LDFLAGS) diff --git a/src/tests/hello.c b/src/tests/hello.c new file mode 100644 index 00000000..d3d32df6 --- /dev/null +++ b/src/tests/hello.c @@ -0,0 +1,133 @@ +#include +#include +#include + +mlt_producer create_playlist( int argc, char **argv ) +{ + // We're creating a playlist here + mlt_playlist playlist = mlt_playlist_init( ); + + // We need the playlist properties to ensure clean up + mlt_properties properties = mlt_playlist_properties( playlist ); + + // Loop through each of the arguments + int i = 0; + for ( i = 1; i < argc; i ++ ) + { + // Definie the unique key + char key[ 256 ]; + + // Create the producer + mlt_producer producer = mlt_factory_producer( NULL, argv[ i ] ); + + // Add it to the playlist + mlt_playlist_append( playlist, producer ); + + // Create a unique key for this producer + sprintf( key, "producer%d", i ); + + // Now we need to ensure the producers are destroyed + mlt_properties_set_data( properties, key, producer, 0, ( mlt_destructor )mlt_producer_close, NULL ); + } + + // Return the playlist as a producer + return mlt_playlist_producer( playlist ); +} + +mlt_producer create_tracks( int argc, char **argv ) +{ + // Create the field + mlt_field field = mlt_field_init( ); + + // Obtain the multitrack + mlt_multitrack multitrack = mlt_field_multitrack( field ); + + // Obtain the tractor + mlt_tractor tractor = mlt_field_tractor( field ); + + // Obtain a composite transition + mlt_transition transition = mlt_factory_transition( "composite", "10%,10%:15%x15%" ); + + // Create track 0 + mlt_producer track0 = create_playlist( argc, argv ); + + // Create the watermark track + mlt_producer track1 = mlt_factory_producer( "fezzik", "pango" ); + + // Get the length of track0 + mlt_position length = mlt_producer_get_playtime( track0 ); + + // Get the properties of track1 + mlt_properties properties = mlt_producer_properties( track1 ); + + // Set the properties + mlt_properties_set( properties, "text", "Hello\nWorld" ); + mlt_properties_set_position( properties, "in", 0 ); + mlt_properties_set_position( properties, "out", length - 1 ); + mlt_properties_set_position( properties, "length", length ); + + // Now set the properties on the transition + properties = mlt_transition_properties( transition ); + mlt_properties_set_position( properties, "in", 0 ); + mlt_properties_set_position( properties, "out", length - 1 ); + + // Add our tracks to the multitrack + mlt_multitrack_connect( multitrack, track0, 0 ); + mlt_multitrack_connect( multitrack, track1, 1 ); + + // Now plant the transition + mlt_field_plant_transition( field, transition, 0, 1 ); + + // Now set the properties on the transition + properties = mlt_tractor_properties( tractor ); + + // Ensure clean up - the first two are required since this function returns the tractor + mlt_properties_set_data( properties, "multitrack", multitrack, 0, ( mlt_destructor )mlt_multitrack_close, NULL ); + mlt_properties_set_data( properties, "field", field, 0, ( mlt_destructor )mlt_field_close, NULL ); + mlt_properties_set_data( properties, "track1", track1, 0, ( mlt_destructor )mlt_producer_close, NULL ); + mlt_properties_set_data( properties, "transition", transition, 0, ( mlt_destructor )mlt_transition_close, NULL ); + + // Return the tractor + return mlt_tractor_producer( tractor ); +} + +int main( int argc, char **argv ) +{ + // Initialise the factory + if ( mlt_factory_init( NULL ) == 0 ) + { + // Create the default consumer + mlt_consumer hello = mlt_factory_consumer( NULL, NULL ); + + // Create a producer using the default normalising selecter + mlt_producer world = create_tracks( argc, argv ); + + // Connect the producer to the consumer + mlt_consumer_connect( hello, mlt_producer_service( world ) ); + + // Start the consumer + mlt_consumer_start( hello ); + + // Wait for the consumer to terminate + while( !mlt_consumer_is_stopped( hello ) ) + sleep( 1 ); + + // Close the consumer + mlt_consumer_close( hello ); + + // Close the producer + mlt_producer_close( world ); + + // Close the factory + mlt_factory_close( ); + } + else + { + // Report an error during initialisation + fprintf( stderr, "Unable to locate factory modules\n" ); + } + + // End of program + return 0; +} +