]> git.sesse.net Git - mlt/blob - src/framework/mlt_transition.c
A little debugging.
[mlt] / 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 #include "mlt_producer.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 /* Forward references */
34
35 static int transition_get_frame( mlt_service self, mlt_frame_ptr frame, int index );
36
37 /** Initialize a new transition.
38  *
39  * \public \memberof mlt_transition_s
40  * \param self a transition
41  * \param child the object of a subclass
42  * \return true on error
43  */
44
45 int mlt_transition_init( mlt_transition self, void *child )
46 {
47         mlt_service service = &self->parent;
48         memset( self, 0, sizeof( struct mlt_transition_s ) );
49         self->child = child;
50         if ( mlt_service_init( service, self ) == 0 )
51         {
52                 mlt_properties properties = MLT_TRANSITION_PROPERTIES( self );
53
54                 service->get_frame = transition_get_frame;
55                 service->close = ( mlt_destructor )mlt_transition_close;
56                 service->close_object = self;
57
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 );
62
63                 return 0;
64         }
65         return 1;
66 }
67
68 /** Create and initialize a new transition.
69  *
70  * \public \memberof mlt_transition_s
71  * \return a new transition
72  */
73
74 mlt_transition mlt_transition_new( )
75 {
76         mlt_transition self = calloc( 1, sizeof( struct mlt_transition_s ) );
77         if ( self != NULL )
78                 mlt_transition_init( self, NULL );
79         return self;
80 }
81
82 /** Get the service class interface.
83  *
84  * \public \memberof mlt_transition_s
85  * \param self a transition
86  * \return the service class
87  * \see MLT_TRANSITION_SERVICE
88  */
89
90 mlt_service mlt_transition_service( mlt_transition self )
91 {
92         return self != NULL ? &self->parent : NULL;
93 }
94
95 /** Get the properties interface.
96  *
97  * \public \memberof mlt_transition_s
98  * \param self a transition
99  * \return the transition's properties
100  * \see MLT_TRANSITION_PROPERTIES
101  */
102
103 mlt_properties mlt_transition_properties( mlt_transition self )
104 {
105         return MLT_TRANSITION_PROPERTIES( self );
106 }
107
108 /** Connect a transition with a producer's a and b tracks.
109  *
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
116  */
117
118 int mlt_transition_connect( mlt_transition self, mlt_service producer, int a_track, int b_track )
119 {
120         int ret = mlt_service_connect_producer( &self->parent, producer, a_track );
121         if ( ret == 0 )
122         {
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 );
127         }
128         return ret;
129 }
130
131 /** Set the starting and ending time for when the transition is active.
132  *
133  * \public \memberof mlt_transition_s
134  * \param self a transition
135  * \param in the starting time
136  * \param out the ending time
137  */
138
139 void mlt_transition_set_in_and_out( mlt_transition self, mlt_position in, mlt_position out )
140 {
141         mlt_properties properties = MLT_TRANSITION_PROPERTIES( self );
142         mlt_properties_set_position( properties, "in", in );
143         mlt_properties_set_position( properties, "out", out );
144 }
145
146 /** Get the index of the a track.
147  *
148  * \public \memberof mlt_transition_s
149  * \param self a transition
150  * \return the 0-based index of the track of the first producer
151  */
152
153 int mlt_transition_get_a_track( mlt_transition self )
154 {
155         return mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "a_track" );
156 }
157
158 /** Get the index of the b track.
159  *
160  * \public \memberof mlt_transition_s
161  * \param self a transition
162  * \return the 0-based index of the track of the second producer
163  */
164
165 int mlt_transition_get_b_track( mlt_transition self )
166 {
167         return mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "b_track" );
168 }
169
170 /** Get the in point.
171  *
172  * \public \memberof mlt_transition_s
173  * \param self a transition
174  * \return the starting time
175  */
176
177 mlt_position mlt_transition_get_in( mlt_transition self )
178 {
179         return mlt_properties_get_position( MLT_TRANSITION_PROPERTIES( self ), "in" );
180 }
181
182 /** Get the out point.
183  *
184  * \public \memberof mlt_transition_s
185  * \param self a transition
186  * \return the ending time
187  */
188
189 mlt_position mlt_transition_get_out( mlt_transition self )
190 {
191         return mlt_properties_get_position( MLT_TRANSITION_PROPERTIES( self ), "out" );
192 }
193
194 /** Get the duration.
195  *
196  * \public \memberof mlt_transition_s
197  * \param self a transition
198  * \return the duration or zero if unlimited
199  */
200
201 mlt_position mlt_transition_get_length( mlt_transition self )
202 {
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;
207 }
208
209 /** Get the position within the transition.
210  *
211  * The position is relative to the in point.
212  *
213  * \public \memberof mlt_transition_s
214  * \param self a transition
215  * \param frame a frame
216  * \return the position
217  */
218
219 mlt_position mlt_transition_get_position( mlt_transition self, mlt_frame frame )
220 {
221         mlt_position in = mlt_transition_get_in( self );
222         mlt_position position = mlt_frame_get_position( frame );
223         return position - in;
224 }
225
226 /** Get the percent complete.
227  *
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
232  */
233
234 double mlt_transition_get_progress( mlt_transition self, mlt_frame frame )
235 {
236         double progress = 0;
237         mlt_position in = mlt_transition_get_in( self );
238         mlt_position out = mlt_transition_get_out( self );
239
240         if ( out == 0 )
241         {
242                 // If always active, use the frame's producer
243                 mlt_producer producer = mlt_frame_get_original_producer( frame );
244                 if ( producer )
245                 {
246                         in = mlt_producer_get_in( producer );
247                         out = mlt_producer_get_out( producer );
248                 }
249         }
250         if ( out != 0 )
251         {
252                 mlt_position position = mlt_frame_get_position( frame );
253                 progress = ( double ) ( position - in ) / ( double ) ( out - in + 1 );
254         }
255         return progress;
256 }
257
258 /** Get the second field incremental progress.
259  *
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
264  */
265
266 double mlt_transition_get_progress_delta( mlt_transition self, mlt_frame frame )
267 {
268         double progress = 0;
269         mlt_position in = mlt_transition_get_in( self );
270         mlt_position out = mlt_transition_get_out( self );
271
272         if ( out == 0 )
273         {
274                 // If always active, use the frame's producer
275                 mlt_producer producer = mlt_frame_get_original_producer( frame );
276                 if ( producer )
277                 {
278                         in = mlt_producer_get_in( producer );
279                         out = mlt_producer_get_out( producer );
280                 }
281         }
282         if ( out != 0 )
283         {
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;
289         }
290         return progress;
291 }
292
293 /** Process the frame.
294  *
295  * If we have no process method (unlikely), we simply return the a_frame unmolested.
296  *
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
301  * \return a frame
302  */
303
304 mlt_frame mlt_transition_process( mlt_transition self, mlt_frame a_frame, mlt_frame b_frame )
305 {
306         if ( self->process == NULL )
307                 return a_frame;
308         else
309                 return self->process( self, a_frame, b_frame );
310 }
311
312 static int get_image_a( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
313 {
314         mlt_transition self = mlt_frame_pop_service( a_frame );
315         mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
316
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" );
321
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) ) ) );
325
326         return mlt_frame_get_image( a_frame, image, format, width, height, writable );
327 }
328
329 static int get_image_b( mlt_frame b_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
330 {
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 );
335
336         // Set scaling from A frame if not already provided.
337         if ( !mlt_properties_get( b_props, "rescale.interp" ) )
338         {
339                 const char *rescale = mlt_properties_get( a_props, "rescale.interp" );
340                 if ( !rescale || !strcmp( rescale, "none" ) )
341                         rescale = "nearest";
342                 mlt_properties_set( b_props, "rescale.interp", rescale );
343         }
344
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) ) ) );
348
349         mlt_properties_pass_list( b_props, a_props,
350                 "consumer_deinterlace, deinterlace_method, consumer_tff" );
351
352         return mlt_frame_get_image( b_frame, image, format, width, height, writable );
353 }
354
355 /** Get a frame from a transition.
356
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).
360
361         However, the fixed a_track may not always contain data of the correct type, eg:
362 <pre>
363         +---------+                               +-------+
364         |c1       |                               |c5     | <-- A(0,1) <-- B(0,2) <-- get frame
365         +---------+                     +---------+-+-----+        |          |
366                                         |c4         |       <------+          |
367                  +----------+-----------+-+---------+                         |
368                  |c2        |c3           |                 <-----------------+
369                  +----------+-------------+
370 </pre>
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.
376
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.
382
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.
385
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
391  */
392
393 static int transition_get_frame( mlt_service service, mlt_frame_ptr frame, int index )
394 {
395         int error = 0;
396         mlt_transition self = service->child;
397
398         mlt_properties properties = MLT_TRANSITION_PROPERTIES( self );
399
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;
408
409         // Ensure that we have the correct order
410         if ( a_track > b_track )
411         {
412                 reverse_order = 1;
413                 a_track = b_track;
414                 b_track = mlt_properties_get_int( properties, "a_track" );
415         }
416
417         // Only act on this operation once per multitrack iteration from the tractor
418         if ( !self->held )
419         {
420                 int active = 0;
421                 int i = 0;
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;
426
427                 // Initialise temporary store
428                 if ( self->frames == NULL )
429                         self->frames = calloc( sizeof( mlt_frame ), b_track + 1 );
430
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 );
434
435                 // We're holding these frames until the last_track frame property is received
436                 self->held = 1;
437
438                 // When we need to locate the a_frame
439                 switch( type )
440                 {
441                         case 1:
442                         case 2:
443                                 // Some transitions (esp. audio) may accept blank frames
444                                 active = accepts_blanks;
445
446                                 // If we're not active then...
447                                 if ( !active )
448                                 {
449                                         // Hunt for the a_frame
450                                         while( a_frame <= b_frame && invalid( self->frames[ a_frame ] ) )
451                                                 a_frame ++;
452
453                                         // Determine if we're active now
454                                         active = a_frame != b_frame && !invalid( self->frames[ b_frame ] );
455                                 }
456                                 break;
457
458                         default:
459                                 mlt_log( service, MLT_LOG_ERROR, "invalid transition type\n" );
460                                 break;
461                 }
462
463                 // Now handle the non-always active case
464                 if ( active && !always_active )
465                 {
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 ] );
468
469                         // If a is in range, we're active
470                         active = position >= in && ( out == 0 || position <= out );
471                 }
472
473                 // Finally, process the a and b frames
474                 if ( active && !mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( self ), "disable" ) )
475                 {
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 ) )
481                         {
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 );
488
489                                 // Process the transition
490                                 *frame = mlt_transition_process( self, a_frame_ptr, b_frame_ptr );
491
492                                 // We need to ensure that the tractor doesn't consider this frame for output
493                                 if ( *frame == a_frame_ptr )
494                                         b_hide |= type;
495                                 else
496                                         a_hide |= type;
497
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 );
500                         }
501                 }
502         }
503
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 ];
507         else
508                 error = mlt_service_get_frame( self->producer, frame, index );
509
510         // Determine if that was the last track
511         self->held = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( *frame ), "last_track" );
512
513         return error;
514 }
515
516 /** Close and destroy the transition.
517  *
518  * \public \memberof mlt_transition_s
519  * \param self a transition
520  */
521
522 void mlt_transition_close( mlt_transition self )
523 {
524         if ( self != NULL && mlt_properties_dec_ref( MLT_TRANSITION_PROPERTIES( self ) ) <= 0 )
525         {
526                 self->parent.close = NULL;
527                 if ( self->close != NULL )
528                 {
529                         self->close( self );
530                 }
531                 else
532                 {
533                         mlt_service_close( &self->parent );
534                         free( self->frames );
535                         free( self );
536                 }
537         }
538 }