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