]> git.sesse.net Git - mlt/blob - src/framework/mlt_transition.c
Set version to 0.7.0
[mlt] / src / framework / mlt_transition.c
1 /**
2  * \file mlt_transition.c
3  * \brief abstraction for all transition services
4  * \see mlt_transition_s
5  *
6  * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
7  * \author Charles Yates <charles.yates@pandora.be>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "mlt_transition.h"
25 #include "mlt_frame.h"
26 #include "mlt_log.h"
27 #include "mlt_producer.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 /* Forward references */
34
35 static int transition_get_frame( mlt_service self, mlt_frame_ptr frame, int index );
36
37 /** Initialize a new transition.
38  *
39  * \public \memberof mlt_transition_s
40  * \param self a transition
41  * \param child the object of a subclass
42  * \return true on error
43  */
44
45 int mlt_transition_init( mlt_transition self, void *child )
46 {
47         mlt_service service = &self->parent;
48         memset( self, 0, sizeof( struct mlt_transition_s ) );
49         self->child = child;
50         if ( mlt_service_init( service, self ) == 0 )
51         {
52                 mlt_properties properties = MLT_TRANSITION_PROPERTIES( self );
53
54                 service->get_frame = transition_get_frame;
55                 service->close = ( mlt_destructor )mlt_transition_close;
56                 service->close_object = self;
57
58                 mlt_properties_set_position( properties, "in", 0 );
59                 mlt_properties_set_position( properties, "out", 0 );
60                 mlt_properties_set_int( properties, "a_track", 0 );
61                 mlt_properties_set_int( properties, "b_track", 1 );
62
63                 return 0;
64         }
65         return 1;
66 }
67
68 /** Create and initialize a new transition.
69  *
70  * \public \memberof mlt_transition_s
71  * \return a new transition
72  */
73
74 mlt_transition mlt_transition_new( )
75 {
76         mlt_transition self = calloc( 1, sizeof( struct mlt_transition_s ) );
77         if ( self != NULL )
78                 mlt_transition_init( self, NULL );
79         return self;
80 }
81
82 /** Get the service class interface.
83  *
84  * \public \memberof mlt_transition_s
85  * \param self a transition
86  * \return the service class
87  * \see MLT_TRANSITION_SERVICE
88  */
89
90 mlt_service mlt_transition_service( mlt_transition self )
91 {
92         return self != NULL ? &self->parent : NULL;
93 }
94
95 /** Get the properties interface.
96  *
97  * \public \memberof mlt_transition_s
98  * \param self a transition
99  * \return the transition's properties
100  * \see MLT_TRANSITION_PROPERTIES
101  */
102
103 mlt_properties mlt_transition_properties( mlt_transition self )
104 {
105         return MLT_TRANSITION_PROPERTIES( self );
106 }
107
108 /** Connect a transition with a producer's a and b tracks.
109  *
110  * \public \memberof mlt_transition_s
111  * \param self a transition
112  * \param producer a producer
113  * \param a_track the track index of the first input
114  * \param b_track the track index of the second index
115  * \return true on error
116  */
117
118 int mlt_transition_connect( mlt_transition self, mlt_service producer, int a_track, int b_track )
119 {
120         int ret = mlt_service_connect_producer( &self->parent, producer, a_track );
121         if ( ret == 0 )
122         {
123                 mlt_properties properties = MLT_TRANSITION_PROPERTIES( self );
124                 self->producer = producer;
125                 mlt_properties_set_int( properties, "a_track", a_track );
126                 mlt_properties_set_int( properties, "b_track", b_track );
127         }
128         return ret;
129 }
130
131 /** Set the starting and ending time for when the transition is active.
132  *
133  * \public \memberof mlt_transition_s
134  * \param self a transition
135  * \param in the starting time
136  * \param out the ending time
137  */
138
139 void mlt_transition_set_in_and_out( mlt_transition self, mlt_position in, mlt_position out )
140 {
141         mlt_properties properties = MLT_TRANSITION_PROPERTIES( self );
142         mlt_properties_set_position( properties, "in", in );
143         mlt_properties_set_position( properties, "out", out );
144 }
145
146 /** Get the index of the a track.
147  *
148  * \public \memberof mlt_transition_s
149  * \param self a transition
150  * \return the 0-based index of the track of the first producer
151  */
152
153 int mlt_transition_get_a_track( mlt_transition self )
154 {
155         return mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "a_track" );
156 }
157
158 /** Get the index of the b track.
159  *
160  * \public \memberof mlt_transition_s
161  * \param self a transition
162  * \return the 0-based index of the track of the second producer
163  */
164
165 int mlt_transition_get_b_track( mlt_transition self )
166 {
167         return mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "b_track" );
168 }
169
170 /** Get the in point.
171  *
172  * \public \memberof mlt_transition_s
173  * \param self a transition
174  * \return the starting time
175  */
176
177 mlt_position mlt_transition_get_in( mlt_transition self )
178 {
179         return mlt_properties_get_position( MLT_TRANSITION_PROPERTIES( self ), "in" );
180 }
181
182 /** Get the out point.
183  *
184  * \public \memberof mlt_transition_s
185  * \param self a transition
186  * \return the ending time
187  */
188
189 mlt_position mlt_transition_get_out( mlt_transition self )
190 {
191         return mlt_properties_get_position( MLT_TRANSITION_PROPERTIES( self ), "out" );
192 }
193
194 /** Get the duration.
195  *
196  * \public \memberof mlt_transition_s
197  * \param self a transition
198  * \return the duration or zero if unlimited
199  */
200
201 mlt_position mlt_transition_get_length( mlt_transition self )
202 {
203         mlt_properties properties = MLT_SERVICE_PROPERTIES( &self->parent );
204         mlt_position in = mlt_properties_get_position( properties, "in" );
205         mlt_position out = mlt_properties_get_position( properties, "out" );
206         return ( out > 0 ) ? ( out - in + 1 ) : 0;
207 }
208
209 /** Get the position within the transition.
210  *
211  * The position is relative to the in point.
212  *
213  * \public \memberof mlt_transition_s
214  * \param self a transition
215  * \param frame a frame
216  * \return the position
217  */
218
219 mlt_position mlt_transition_get_position( mlt_transition self, mlt_frame frame )
220 {
221         mlt_position in = mlt_transition_get_in( self );
222         mlt_position position = mlt_frame_get_position( frame );
223         return position - in;
224 }
225
226 /** Get the percent complete.
227  *
228  * \public \memberof mlt_transition_s
229  * \param self a transition
230  * \param frame a frame
231  * \return the progress in the range 0.0 to 1.0
232  */
233
234 double mlt_transition_get_progress( mlt_transition self, mlt_frame frame )
235 {
236         double progress = 0;
237         mlt_position in = mlt_transition_get_in( self );
238         mlt_position out = mlt_transition_get_out( self );
239
240         if ( out == 0 )
241         {
242                 // If always active, use the frame's producer
243                 mlt_producer producer = mlt_frame_get_original_producer( frame );
244                 if ( producer )
245                 {
246                         in = mlt_producer_get_in( producer );
247                         out = mlt_producer_get_out( producer );
248                 }
249         }
250         if ( out != 0 )
251         {
252                 mlt_position position = mlt_frame_get_position( frame );
253                 progress = ( double ) ( position - in ) / ( double ) ( out - in + 1 );
254         }
255         return progress;
256 }
257
258 /** Get the second field incremental progress.
259  *
260  * \public \memberof mlt_transition_s
261  * \param self a transition
262  * \param frame a frame
263  * \return the progress increment in the range 0.0 to 1.0
264  */
265
266 double mlt_transition_get_progress_delta( mlt_transition self, mlt_frame frame )
267 {
268         double progress = 0;
269         mlt_position in = mlt_transition_get_in( self );
270         mlt_position out = mlt_transition_get_out( self );
271
272         if ( out == 0 )
273         {
274                 // If always active, use the frame's producer
275                 mlt_producer producer = mlt_frame_get_original_producer( frame );
276                 if ( producer )
277                 {
278                         in = mlt_producer_get_in( producer );
279                         out = mlt_producer_get_out( producer );
280                 }
281         }
282         if ( out != 0 )
283         {
284                 mlt_position position = mlt_frame_get_position( frame );
285                 double length = out - in + 1;
286                 double x = ( double ) ( position - in ) / length;
287                 double y = ( double ) ( position + 1 - in ) / length;
288                 progress = length * ( y - x ) / 2.0;
289         }
290         return progress;
291 }
292
293 /** Process the frame.
294  *
295  * If we have no process method (unlikely), we simply return the a_frame unmolested.
296  *
297  * \public \memberof mlt_transition_s
298  * \param self a transition
299  * \param a_frame a frame from the first producer
300  * \param b_frame a frame from the second producer
301  * \return a frame
302  */
303
304 mlt_frame mlt_transition_process( mlt_transition self, mlt_frame a_frame, mlt_frame b_frame )
305 {
306         if ( self->process == NULL )
307                 return a_frame;
308         else
309                 return self->process( self, a_frame, b_frame );
310 }
311
312 /** Get a frame from a transition.
313
314         The logic is complex here. A transition is typically applied to frames on the a and
315         b tracks specified in the connect method above and only if both contain valid info
316         for the transition type (this is either audio or image).
317
318         However, the fixed a_track may not always contain data of the correct type, eg:
319 <pre>
320         +---------+                               +-------+
321         |c1       |                               |c5     | <-- A(0,1) <-- B(0,2) <-- get frame
322         +---------+                     +---------+-+-----+        |          |
323                                         |c4         |       <------+          |
324                  +----------+-----------+-+---------+                         |
325                  |c2        |c3           |                 <-----------------+
326                  +----------+-------------+
327 </pre>
328         During the overlap of c1 and c2, there is nothing for the A transition to do, so this
329         results in a no operation, but B is triggered. During the overlap of c2 and c3, again,
330         the A transition is inactive and because the B transition is pointing at track 0,
331         it too would be inactive. This isn't an ideal situation - it's better if the B
332         transition simply treats the frames from c3 as though they're the a track.
333
334         For this to work, we cache all frames coming from all tracks between the a and b
335         tracks.  Before we process, we determine that the b frame contains someting of the
336         right type and then we determine which frame to use as the a frame (selecting a
337         matching frame from a_track to b_track - 1). If both frames contain data of the
338         correct type, we process the transition.
339
340         This method is invoked for each track and we return the cached frames as needed.
341         We clear the cache only when the requested frame is flagged as a 'last_track' frame.
342
343  * \private \memberof mlt_transition_s
344  * \param service a service
345  * \param[out] frame a frame by reference
346  * \param index 0-based track index
347  * \return true on error
348  */
349
350 static int transition_get_frame( mlt_service service, mlt_frame_ptr frame, int index )
351 {
352         int error = 0;
353         mlt_transition self = service->child;
354
355         mlt_properties properties = MLT_TRANSITION_PROPERTIES( self );
356
357         int accepts_blanks = mlt_properties_get_int( properties, "accepts_blanks" );
358         int a_track = mlt_properties_get_int( properties, "a_track" );
359         int b_track = mlt_properties_get_int( properties, "b_track" );
360         mlt_position in = mlt_properties_get_position( properties, "in" );
361         mlt_position out = mlt_properties_get_position( properties, "out" );
362         int always_active = mlt_properties_get_int( properties, "always_active" );
363         int type = mlt_properties_get_int( properties, "_transition_type" );
364         int reverse_order = 0;
365
366         // Ensure that we have the correct order
367         if ( a_track > b_track )
368         {
369                 reverse_order = 1;
370                 a_track = b_track;
371                 b_track = mlt_properties_get_int( properties, "a_track" );
372         }
373
374         // Only act on this operation once per multitrack iteration from the tractor
375         if ( !self->held )
376         {
377                 int active = 0;
378                 int i = 0;
379                 int a_frame = a_track;
380                 int b_frame = b_track;
381                 mlt_position position;
382                 int ( *invalid )( mlt_frame ) = type == 1 ? mlt_frame_is_test_card : mlt_frame_is_test_audio;
383
384                 // Initialise temporary store
385                 if ( self->frames == NULL )
386                         self->frames = calloc( sizeof( mlt_frame ), b_track + 1 );
387
388                 // Get all frames between a and b
389                 for( i = a_track; i <= b_track; i ++ )
390                         mlt_service_get_frame( self->producer, &self->frames[ i ], i );
391
392                 // We're holding these frames until the last_track frame property is received
393                 self->held = 1;
394
395                 // When we need to locate the a_frame
396                 switch( type )
397                 {
398                         case 1:
399                         case 2:
400                                 // Some transitions (esp. audio) may accept blank frames
401                                 active = accepts_blanks;
402
403                                 // If we're not active then...
404                                 if ( !active )
405                                 {
406                                         // Hunt for the a_frame
407                                         while( a_frame <= b_frame && invalid( self->frames[ a_frame ] ) )
408                                                 a_frame ++;
409
410                                         // Determine if we're active now
411                                         active = a_frame != b_frame && !invalid( self->frames[ b_frame ] );
412                                 }
413                                 break;
414
415                         default:
416                                 mlt_log( service, MLT_LOG_ERROR, "invalid transition type\n" );
417                                 break;
418                 }
419
420                 // Now handle the non-always active case
421                 if ( active && !always_active )
422                 {
423                         // For non-always-active transitions, we need the current position of the a frame
424                         position = mlt_frame_get_position( self->frames[ a_frame ] );
425
426                         // If a is in range, we're active
427                         active = position >= in && ( out == 0 || position <= out );
428                 }
429
430                 // Finally, process the a and b frames
431                 if ( active )
432                 {
433                         mlt_frame a_frame_ptr = self->frames[ !reverse_order ? a_frame : b_frame ];
434                         mlt_frame b_frame_ptr = self->frames[ !reverse_order ? b_frame : a_frame ];
435                         int a_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide" );
436                         int b_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide" );
437                         if ( !( a_hide & type ) && !( b_hide & type ) )
438                         {
439                                 // Process the transition
440                                 *frame = mlt_transition_process( self, a_frame_ptr, b_frame_ptr );
441
442                                 // We need to ensure that the tractor doesn't consider this frame for output
443                                 if ( *frame == a_frame_ptr )
444                                         b_hide |= type;
445                                 else
446                                         a_hide |= type;
447
448                                 mlt_properties_set_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide", a_hide );
449                                 mlt_properties_set_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide", b_hide );
450                         }
451                 }
452         }
453
454         // Obtain the frame from the cache or the producer we're attached to
455         if ( index >= a_track && index <= b_track )
456                 *frame = self->frames[ index ];
457         else
458                 error = mlt_service_get_frame( self->producer, frame, index );
459
460         // Determine if that was the last track
461         self->held = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( *frame ), "last_track" );
462
463         return error;
464 }
465
466 /** Close and destroy the transition.
467  *
468  * \public \memberof mlt_transition_s
469  * \param self a transition
470  */
471
472 void mlt_transition_close( mlt_transition self )
473 {
474         if ( self != NULL && mlt_properties_dec_ref( MLT_TRANSITION_PROPERTIES( self ) ) <= 0 )
475         {
476                 self->parent.close = NULL;
477                 if ( self->close != NULL )
478                 {
479                         self->close( self );
480                 }
481                 else
482                 {
483                         mlt_service_close( &self->parent );
484                         free( self->frames );
485                         free( self );
486                 }
487         }
488 }