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