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