]> git.sesse.net Git - mlt/blob - mlt/src/framework/mlt_multitrack.c
77158c767c4d3ac7f7228b418426a24e3bb2462f
[mlt] / mlt / src / framework / mlt_multitrack.c
1 /*
2  * mlt_multitrack.c -- multitrack service class
3  * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include "mlt_multitrack.h"
24 #include "mlt_frame.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 /** Private definition.
30 */
31
32 struct mlt_multitrack_s
33 {
34         // We're extending producer here
35         struct mlt_producer_s parent;
36         mlt_producer *list;
37         int size;
38         int count;
39 };
40
41 /** Forward reference.
42 */
43
44 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
45
46 /** Constructor.
47 */
48
49 mlt_multitrack mlt_multitrack_init( )
50 {
51         // Allocate the multitrack object
52         mlt_multitrack this = calloc( sizeof( struct mlt_multitrack_s ), 1 );
53
54         if ( this != NULL )
55         {
56                 mlt_producer producer = &this->parent;
57                 if ( mlt_producer_init( producer, this ) == 0 )
58                 {
59                         producer->get_frame = producer_get_frame;
60                 }
61                 else
62                 {
63                         free( this );
64                         this = NULL;
65                 }
66         }
67         
68         return this;
69 }
70
71 /** Get the producer associated to this multitrack.
72 */
73
74 mlt_producer mlt_multitrack_producer( mlt_multitrack this )
75 {
76         return &this->parent;
77 }
78
79 /** Get the service associated this multitrack.
80 */
81
82 mlt_service mlt_multitrack_service( mlt_multitrack this )
83 {
84         return mlt_producer_service( mlt_multitrack_producer( this ) );
85 }
86
87 /** Get the properties associated this multitrack.
88 */
89
90 mlt_properties mlt_multitrack_properties( mlt_multitrack this )
91 {
92         return mlt_service_properties( mlt_multitrack_service( this ) );
93 }
94
95 /** Initialise timecode related information.
96 */
97
98 static void mlt_multitrack_refresh( mlt_multitrack this )
99 {
100         int i = 0;
101
102         // Obtain the properties of this multitrack
103         mlt_properties properties = mlt_multitrack_properties( this );
104
105         // We need to ensure that the multitrack reports the longest track as its length
106         mlt_timecode length = 0;
107
108         // We need to ensure that fps are the same on all services
109         double fps = 0;
110         
111         // Obtain stats on all connected services
112         for ( i = 0; i < this->count; i ++ )
113         {
114                 // Get the producer from this index
115                 mlt_producer producer = this->list[ i ];
116
117                 // If it's allocated then, update our stats
118                 if ( producer != NULL )
119                 {
120                         // Determine the longest length
121                         length = mlt_producer_get_length( producer ) > length ? mlt_producer_get_length( producer ) : length;
122                         
123                         // Handle fps
124                         if ( fps == 0 )
125                         {
126                                 // This is the first producer, so it controls the fps
127                                 fps = mlt_producer_get_fps( producer );
128                         }
129                         else if ( fps != mlt_producer_get_fps( producer ) )
130                         {
131                                 // Generate a warning for now - the following attempt to fix may fail
132                                 fprintf( stderr, "Warning: fps mismatch on track %d\n", i );
133
134                                 // It should be safe to impose fps on an image producer, but not necessarily safe for video
135                                 mlt_properties_set_double( mlt_producer_properties( producer ), "fps", fps );
136                         }
137                 }
138         }
139
140         // Update multitrack properties now - we'll not destroy the in point here
141         mlt_properties_set_timecode( properties, "length", length );
142         mlt_properties_set_timecode( properties, "out", length );
143         mlt_properties_set_timecode( properties, "playtime", length - mlt_properties_get_timecode( properties, "in" ) );
144 }
145
146 /** Connect a producer to a given track.
147 */
148
149 int mlt_multitrack_connect( mlt_multitrack this, mlt_producer producer, int track )
150 {
151         // Connect to the producer to ourselves at the specified track
152         int result = mlt_service_connect_producer( mlt_multitrack_service( this ), mlt_producer_service( producer ), track );
153
154         if ( result == 0 )
155         {
156                 // Resize the producer list if need be
157                 if ( track >= this->size )
158                 {
159                         int i;
160                         this->list = realloc( this->list, ( track + 10 ) * sizeof( mlt_producer ) );
161                         for ( i = this->size; i < track + 10; i ++ )
162                                 this->list[ i ] = NULL;
163                         this->size = track + 10;
164                 }
165                 
166                 // Assign the track in our list here
167                 this->list[ track ] = producer;
168                 
169                 // Increment the track count if need be
170                 if ( track >= this->count )
171                         this->count = track + 1;
172                         
173                 // Refresh our stats
174                 mlt_multitrack_refresh( this );
175         }
176
177         return result;
178 }
179
180 /** Get frame method.
181
182         Special case here: The multitrack must be used in a conjunction with a downstream
183         tractor-type service, ie:
184
185         Producer1 \
186         Producer2 - multitrack - { filters/transitions } - tractor - consumer
187         Producer3 /
188
189         The get_frame of a tractor pulls frames from it's connected service on all tracks and 
190         will terminate as soon as it receives a test card with a last_track property. The 
191         important case here is that the mulitrack does not move to the next frame until all
192         tracks have been pulled. 
193
194         Reasoning: In order to seek on a network such as above, the multitrack needs to ensure
195         that all producers are positioned on the same frame. It uses the 'last track' logic
196         to determine when to move to the next frame.
197
198         Flaw: if a transition is configured to read from a b-track which happens to trigger
199         the last frame logic (ie: it's configured incorrectly), then things are going to go
200         out of sync.
201
202         See playlist logic too.
203 */
204
205 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index )
206 {
207         // Get the mutiltrack object
208         mlt_multitrack this = parent->child;
209
210         // Check if we have a track for this index
211         if ( index < this->count && this->list[ index ] != NULL )
212         {
213                 // Get the producer for this track
214                 mlt_producer producer = this->list[ index ];
215
216                 // Obtain the current timecode
217                 uint64_t position = mlt_producer_frame( parent );
218
219                 // Make sure we're at the same point
220                 mlt_producer_seek_frame( producer, position );
221
222                 // Get the frame from the producer
223                 mlt_service_get_frame( mlt_producer_service( producer ), frame, 0 );
224         }
225         else
226         {
227                 // Generate a test frame
228                 *frame = mlt_frame_init( );
229
230                 // Let tractor know if we've reached the end
231                 mlt_properties_set_int( mlt_frame_properties( *frame ), "last_track", index >= this->count );
232
233                 // Update timecode on the frame we're creating
234                 mlt_frame_set_timecode( *frame, mlt_producer_position( parent ) );
235
236                 // Move on to the next frame
237                 if ( index >= this->count )
238                         mlt_producer_prepare_next( parent );
239         }
240
241         fprintf( stderr, "timestamp for %d = %f\n", index, ( float )mlt_frame_get_timecode( *frame ) );
242
243         return 0;
244 }
245
246 /** Close this instance.
247 */
248
249 void mlt_multitrack_close( mlt_multitrack this )
250 {
251         // Close the producer
252         mlt_producer_close( &this->parent );
253
254         // Free the list
255         free( this->list );
256
257         // Free the object
258         free( this );
259 }