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