2 * \file mlt_transition.c
3 * \brief abstraction for all transition services
4 * \see mlt_transition_s
6 * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
7 * \author Charles Yates <charles.yates@pandora.be>
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.
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.
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
24 #include "mlt_transition.h"
25 #include "mlt_frame.h"
27 #include "mlt_producer.h"
33 /* Forward references */
35 static int transition_get_frame( mlt_service self, mlt_frame_ptr frame, int index );
37 /** Initialize a new transition.
39 * \public \memberof mlt_transition_s
40 * \param self a transition
41 * \param child the object of a subclass
42 * \return true on error
45 int mlt_transition_init( mlt_transition self, void *child )
47 mlt_service service = &self->parent;
48 memset( self, 0, sizeof( struct mlt_transition_s ) );
50 if ( mlt_service_init( service, self ) == 0 )
52 mlt_properties properties = MLT_TRANSITION_PROPERTIES( self );
54 service->get_frame = transition_get_frame;
55 service->close = ( mlt_destructor )mlt_transition_close;
56 service->close_object = self;
58 mlt_properties_set_position( properties, "in", 0 );
59 mlt_properties_set_position( properties, "out", 0 );
60 mlt_properties_set_int( properties, "a_track", 0 );
61 mlt_properties_set_int( properties, "b_track", 1 );
68 /** Create and initialize a new transition.
70 * \public \memberof mlt_transition_s
71 * \return a new transition
74 mlt_transition mlt_transition_new( )
76 mlt_transition self = calloc( 1, sizeof( struct mlt_transition_s ) );
78 mlt_transition_init( self, NULL );
82 /** Get the service class interface.
84 * \public \memberof mlt_transition_s
85 * \param self a transition
86 * \return the service class
87 * \see MLT_TRANSITION_SERVICE
90 mlt_service mlt_transition_service( mlt_transition self )
92 return self != NULL ? &self->parent : NULL;
95 /** Get the properties interface.
97 * \public \memberof mlt_transition_s
98 * \param self a transition
99 * \return the transition's properties
100 * \see MLT_TRANSITION_PROPERTIES
103 mlt_properties mlt_transition_properties( mlt_transition self )
105 return MLT_TRANSITION_PROPERTIES( self );
108 /** Connect a transition with a producer's a and b tracks.
110 * \public \memberof mlt_transition_s
111 * \param self a transition
112 * \param producer a producer
113 * \param a_track the track index of the first input
114 * \param b_track the track index of the second index
115 * \return true on error
118 int mlt_transition_connect( mlt_transition self, mlt_service producer, int a_track, int b_track )
120 int ret = mlt_service_connect_producer( &self->parent, producer, a_track );
123 mlt_properties properties = MLT_TRANSITION_PROPERTIES( self );
124 self->producer = producer;
125 mlt_properties_set_int( properties, "a_track", a_track );
126 mlt_properties_set_int( properties, "b_track", b_track );
131 /** Set the starting and ending time for when the transition is active.
133 * \public \memberof mlt_transition_s
134 * \param self a transition
135 * \param in the starting time
136 * \param out the ending time
139 void mlt_transition_set_in_and_out( mlt_transition self, mlt_position in, mlt_position out )
141 mlt_properties properties = MLT_TRANSITION_PROPERTIES( self );
142 mlt_properties_set_position( properties, "in", in );
143 mlt_properties_set_position( properties, "out", out );
146 /** Get the index of the a track.
148 * \public \memberof mlt_transition_s
149 * \param self a transition
150 * \return the 0-based index of the track of the first producer
153 int mlt_transition_get_a_track( mlt_transition self )
155 return mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "a_track" );
158 /** Get the index of the b track.
160 * \public \memberof mlt_transition_s
161 * \param self a transition
162 * \return the 0-based index of the track of the second producer
165 int mlt_transition_get_b_track( mlt_transition self )
167 return mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "b_track" );
170 /** Get the in point.
172 * \public \memberof mlt_transition_s
173 * \param self a transition
174 * \return the starting time
177 mlt_position mlt_transition_get_in( mlt_transition self )
179 return mlt_properties_get_position( MLT_TRANSITION_PROPERTIES( self ), "in" );
182 /** Get the out point.
184 * \public \memberof mlt_transition_s
185 * \param self a transition
186 * \return the ending time
189 mlt_position mlt_transition_get_out( mlt_transition self )
191 return mlt_properties_get_position( MLT_TRANSITION_PROPERTIES( self ), "out" );
194 /** Get the duration.
196 * \public \memberof mlt_transition_s
197 * \param self a transition
198 * \return the duration or zero if unlimited
201 mlt_position mlt_transition_get_length( mlt_transition self )
203 mlt_properties properties = MLT_SERVICE_PROPERTIES( &self->parent );
204 mlt_position in = mlt_properties_get_position( properties, "in" );
205 mlt_position out = mlt_properties_get_position( properties, "out" );
206 return ( out > 0 ) ? ( out - in + 1 ) : 0;
209 /** Get the position within the transition.
211 * The position is relative to the in point.
213 * \public \memberof mlt_transition_s
214 * \param self a transition
215 * \param frame a frame
216 * \return the position
219 mlt_position mlt_transition_get_position( mlt_transition self, mlt_frame frame )
221 mlt_position in = mlt_transition_get_in( self );
222 mlt_position position = mlt_frame_get_position( frame );
223 return position - in;
226 /** Get the percent complete.
228 * \public \memberof mlt_transition_s
229 * \param self a transition
230 * \param frame a frame
231 * \return the progress in the range 0.0 to 1.0
234 double mlt_transition_get_progress( mlt_transition self, mlt_frame frame )
237 mlt_position in = mlt_transition_get_in( self );
238 mlt_position out = mlt_transition_get_out( self );
242 // If always active, use the frame's producer
243 mlt_producer producer = mlt_frame_get_original_producer( frame );
246 in = mlt_producer_get_in( producer );
247 out = mlt_producer_get_out( producer );
252 mlt_position position = mlt_frame_get_position( frame );
253 progress = ( double ) ( position - in ) / ( double ) ( out - in + 1 );
258 /** Get the second field incremental progress.
260 * \public \memberof mlt_transition_s
261 * \param self a transition
262 * \param frame a frame
263 * \return the progress increment in the range 0.0 to 1.0
266 double mlt_transition_get_progress_delta( mlt_transition self, mlt_frame frame )
269 mlt_position in = mlt_transition_get_in( self );
270 mlt_position out = mlt_transition_get_out( self );
274 // If always active, use the frame's producer
275 mlt_producer producer = mlt_frame_get_original_producer( frame );
278 in = mlt_producer_get_in( producer );
279 out = mlt_producer_get_out( producer );
284 mlt_position position = mlt_frame_get_position( frame );
285 double length = out - in + 1;
286 double x = ( double ) ( position - in ) / length;
287 double y = ( double ) ( position + 1 - in ) / length;
288 progress = ( y - x ) / 2.0;
293 /** Process the frame.
295 * If we have no process method (unlikely), we simply return the a_frame unmolested.
297 * \public \memberof mlt_transition_s
298 * \param self a transition
299 * \param a_frame a frame from the first producer
300 * \param b_frame a frame from the second producer
304 mlt_frame mlt_transition_process( mlt_transition self, mlt_frame a_frame, mlt_frame b_frame )
306 if ( self->process == NULL )
309 return self->process( self, a_frame, b_frame );
312 static int get_image_a( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
314 mlt_transition self = mlt_frame_pop_service( a_frame );
315 mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
317 // All transitions get scaling
318 const char *rescale = mlt_properties_get( a_props, "rescale.interp" );
319 if ( !rescale || !strcmp( rescale, "none" ) )
320 mlt_properties_set( a_props, "rescale.interp", "nearest" );
322 // Ensure sane aspect ratio
323 if ( mlt_frame_get_aspect_ratio( a_frame ) == 0.0 )
324 mlt_frame_set_aspect_ratio( a_frame, mlt_profile_sar( mlt_service_profile( MLT_TRANSITION_SERVICE(self) ) ) );
326 return mlt_frame_get_image( a_frame, image, format, width, height, writable );
329 static int get_image_b( mlt_frame b_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
331 mlt_transition self = mlt_frame_pop_service( b_frame );
332 mlt_frame a_frame = mlt_frame_pop_frame( b_frame );
333 mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
334 mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
336 // Set scaling from A frame if not already provided.
337 if ( !mlt_properties_get( b_props, "rescale.interp" ) )
339 const char *rescale = mlt_properties_get( a_props, "rescale.interp" );
340 if ( !rescale || !strcmp( rescale, "none" ) )
342 mlt_properties_set( b_props, "rescale.interp", rescale );
345 // Ensure sane aspect ratio
346 if ( mlt_frame_get_aspect_ratio( b_frame ) == 0.0 )
347 mlt_frame_set_aspect_ratio( b_frame, mlt_profile_sar( mlt_service_profile( MLT_TRANSITION_SERVICE(self) ) ) );
349 mlt_properties_pass_list( b_props, a_props,
350 "consumer_deinterlace, deinterlace_method, consumer_tff" );
352 return mlt_frame_get_image( b_frame, image, format, width, height, writable );
355 /** Get a frame from a transition.
357 The logic is complex here. A transition is typically applied to frames on the a and
358 b tracks specified in the connect method above and only if both contain valid info
359 for the transition type (this is either audio or image).
361 However, the fixed a_track may not always contain data of the correct type, eg:
363 +---------+ +-------+
364 |c1 | |c5 | <-- A(0,1) <-- B(0,2) <-- get frame
365 +---------+ +---------+-+-----+ | |
367 +----------+-----------+-+---------+ |
368 |c2 |c3 | <-----------------+
369 +----------+-------------+
371 During the overlap of c1 and c2, there is nothing for the A transition to do, so this
372 results in a no operation, but B is triggered. During the overlap of c2 and c3, again,
373 the A transition is inactive and because the B transition is pointing at track 0,
374 it too would be inactive. This isn't an ideal situation - it's better if the B
375 transition simply treats the frames from c3 as though they're the a track.
377 For this to work, we cache all frames coming from all tracks between the a and b
378 tracks. Before we process, we determine that the b frame contains someting of the
379 right type and then we determine which frame to use as the a frame (selecting a
380 matching frame from a_track to b_track - 1). If both frames contain data of the
381 correct type, we process the transition.
383 This method is invoked for each track and we return the cached frames as needed.
384 We clear the cache only when the requested frame is flagged as a 'last_track' frame.
386 * \private \memberof mlt_transition_s
387 * \param service a service
388 * \param[out] frame a frame by reference
389 * \param index 0-based track index
390 * \return true on error
393 static int transition_get_frame( mlt_service service, mlt_frame_ptr frame, int index )
396 mlt_transition self = service->child;
398 mlt_properties properties = MLT_TRANSITION_PROPERTIES( self );
400 int accepts_blanks = mlt_properties_get_int( properties, "accepts_blanks" );
401 int a_track = mlt_properties_get_int( properties, "a_track" );
402 int b_track = mlt_properties_get_int( properties, "b_track" );
403 mlt_position in = mlt_properties_get_position( properties, "in" );
404 mlt_position out = mlt_properties_get_position( properties, "out" );
405 int always_active = mlt_properties_get_int( properties, "always_active" );
406 int type = mlt_properties_get_int( properties, "_transition_type" );
407 int reverse_order = 0;
409 // Ensure that we have the correct order
410 if ( a_track > b_track )
414 b_track = mlt_properties_get_int( properties, "a_track" );
417 // Only act on this operation once per multitrack iteration from the tractor
422 int a_frame = a_track;
423 int b_frame = b_track;
424 mlt_position position;
425 int ( *invalid )( mlt_frame ) = type == 1 ? mlt_frame_is_test_card : mlt_frame_is_test_audio;
427 // Initialise temporary store
428 if ( self->frames == NULL )
429 self->frames = calloc( sizeof( mlt_frame ), b_track + 1 );
431 // Get all frames between a and b
432 for( i = a_track; i <= b_track; i ++ )
433 mlt_service_get_frame( self->producer, &self->frames[ i ], i );
435 // We're holding these frames until the last_track frame property is received
438 // When we need to locate the a_frame
443 // Some transitions (esp. audio) may accept blank frames
444 active = accepts_blanks;
446 // If we're not active then...
449 // Hunt for the a_frame
450 while( a_frame <= b_frame && invalid( self->frames[ a_frame ] ) )
453 // Determine if we're active now
454 active = a_frame != b_frame && !invalid( self->frames[ b_frame ] );
459 mlt_log( service, MLT_LOG_ERROR, "invalid transition type\n" );
463 // Now handle the non-always active case
464 if ( active && !always_active )
466 // For non-always-active transitions, we need the current position of the a frame
467 position = mlt_frame_get_position( self->frames[ a_frame ] );
469 // If a is in range, we're active
470 active = position >= in && ( out == 0 || position <= out );
473 // Finally, process the a and b frames
476 mlt_frame a_frame_ptr = self->frames[ !reverse_order ? a_frame : b_frame ];
477 mlt_frame b_frame_ptr = self->frames[ !reverse_order ? b_frame : a_frame ];
478 int a_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide" );
479 int b_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide" );
480 if ( !( a_hide & type ) && !( b_hide & type ) )
482 // Add hooks for pre-processing frames
483 mlt_frame_push_service( a_frame_ptr, self );
484 mlt_frame_push_get_image( a_frame_ptr, get_image_a );
485 mlt_frame_push_frame( b_frame_ptr, a_frame_ptr );
486 mlt_frame_push_service( b_frame_ptr, self );
487 mlt_frame_push_get_image( b_frame_ptr, get_image_b );
489 // Process the transition
490 *frame = mlt_transition_process( self, a_frame_ptr, b_frame_ptr );
492 // We need to ensure that the tractor doesn't consider this frame for output
493 if ( *frame == a_frame_ptr )
498 mlt_properties_set_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide", a_hide );
499 mlt_properties_set_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide", b_hide );
504 // Obtain the frame from the cache or the producer we're attached to
505 if ( index >= a_track && index <= b_track )
506 *frame = self->frames[ index ];
508 error = mlt_service_get_frame( self->producer, frame, index );
510 // Determine if that was the last track
511 self->held = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( *frame ), "last_track" );
516 /** Close and destroy the transition.
518 * \public \memberof mlt_transition_s
519 * \param self a transition
522 void mlt_transition_close( mlt_transition self )
524 if ( self != NULL && mlt_properties_dec_ref( MLT_TRANSITION_PROPERTIES( self ) ) <= 0 )
526 self->parent.close = NULL;
527 if ( self->close != NULL )
533 mlt_service_close( &self->parent );
534 free( self->frames );