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