]> git.sesse.net Git - mlt/blob - src/framework/mlt_transition.c
Fix regression in field rendering luma transition.
[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_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
315
316         // All transitions get scaling
317         const char *rescale = mlt_properties_get( a_props, "rescale.interp" );
318         if ( !rescale || !strcmp( rescale, "none" ) )
319                 mlt_properties_set( a_props, "rescale.interp", "nearest" );
320
321         // Ensure sane aspect ratio
322         if ( mlt_properties_get_double( a_props, "aspect_ratio" ) == 0.0 )
323                 mlt_properties_set_double( a_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
324
325         return mlt_frame_get_image( a_frame, image, format, width, height, writable );
326 }
327
328 static int get_image_b( mlt_frame b_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
329 {
330         mlt_frame a_frame = mlt_frame_pop_frame( b_frame );
331         mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
332         mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
333
334         // Set scaling from A frame if not already provided.
335         if ( !mlt_properties_get( b_props, "rescale.interp" ) )
336         {
337                 const char *rescale = mlt_properties_get( a_props, "rescale.interp" );
338                 if ( !rescale || !strcmp( rescale, "none" ) )
339                         rescale = "nearest";
340                 mlt_properties_set( b_props, "rescale.interp", rescale );
341         }
342
343         // Ensure sane aspect ratio
344         if ( mlt_properties_get_double( b_props, "aspect_ratio" ) == 0.0 )
345                 mlt_properties_set_double( b_props, "aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
346
347         mlt_properties_pass_list( b_props, a_props,
348                 "consumer_deinterlace, deinterlace_method, consumer_aspect_ratio" );
349
350         return mlt_frame_get_image( b_frame, image, format, width, height, writable );
351 }
352
353 /** Get a frame from a transition.
354
355         The logic is complex here. A transition is typically applied to frames on the a and
356         b tracks specified in the connect method above and only if both contain valid info
357         for the transition type (this is either audio or image).
358
359         However, the fixed a_track may not always contain data of the correct type, eg:
360 <pre>
361         +---------+                               +-------+
362         |c1       |                               |c5     | <-- A(0,1) <-- B(0,2) <-- get frame
363         +---------+                     +---------+-+-----+        |          |
364                                         |c4         |       <------+          |
365                  +----------+-----------+-+---------+                         |
366                  |c2        |c3           |                 <-----------------+
367                  +----------+-------------+
368 </pre>
369         During the overlap of c1 and c2, there is nothing for the A transition to do, so this
370         results in a no operation, but B is triggered. During the overlap of c2 and c3, again,
371         the A transition is inactive and because the B transition is pointing at track 0,
372         it too would be inactive. This isn't an ideal situation - it's better if the B
373         transition simply treats the frames from c3 as though they're the a track.
374
375         For this to work, we cache all frames coming from all tracks between the a and b
376         tracks.  Before we process, we determine that the b frame contains someting of the
377         right type and then we determine which frame to use as the a frame (selecting a
378         matching frame from a_track to b_track - 1). If both frames contain data of the
379         correct type, we process the transition.
380
381         This method is invoked for each track and we return the cached frames as needed.
382         We clear the cache only when the requested frame is flagged as a 'last_track' frame.
383
384  * \private \memberof mlt_transition_s
385  * \param service a service
386  * \param[out] frame a frame by reference
387  * \param index 0-based track index
388  * \return true on error
389  */
390
391 static int transition_get_frame( mlt_service service, mlt_frame_ptr frame, int index )
392 {
393         int error = 0;
394         mlt_transition self = service->child;
395
396         mlt_properties properties = MLT_TRANSITION_PROPERTIES( self );
397
398         int accepts_blanks = mlt_properties_get_int( properties, "accepts_blanks" );
399         int a_track = mlt_properties_get_int( properties, "a_track" );
400         int b_track = mlt_properties_get_int( properties, "b_track" );
401         mlt_position in = mlt_properties_get_position( properties, "in" );
402         mlt_position out = mlt_properties_get_position( properties, "out" );
403         int always_active = mlt_properties_get_int( properties, "always_active" );
404         int type = mlt_properties_get_int( properties, "_transition_type" );
405         int reverse_order = 0;
406
407         // Ensure that we have the correct order
408         if ( a_track > b_track )
409         {
410                 reverse_order = 1;
411                 a_track = b_track;
412                 b_track = mlt_properties_get_int( properties, "a_track" );
413         }
414
415         // Only act on this operation once per multitrack iteration from the tractor
416         if ( !self->held )
417         {
418                 int active = 0;
419                 int i = 0;
420                 int a_frame = a_track;
421                 int b_frame = b_track;
422                 mlt_position position;
423                 int ( *invalid )( mlt_frame ) = type == 1 ? mlt_frame_is_test_card : mlt_frame_is_test_audio;
424
425                 // Initialise temporary store
426                 if ( self->frames == NULL )
427                         self->frames = calloc( sizeof( mlt_frame ), b_track + 1 );
428
429                 // Get all frames between a and b
430                 for( i = a_track; i <= b_track; i ++ )
431                         mlt_service_get_frame( self->producer, &self->frames[ i ], i );
432
433                 // We're holding these frames until the last_track frame property is received
434                 self->held = 1;
435
436                 // When we need to locate the a_frame
437                 switch( type )
438                 {
439                         case 1:
440                         case 2:
441                                 // Some transitions (esp. audio) may accept blank frames
442                                 active = accepts_blanks;
443
444                                 // If we're not active then...
445                                 if ( !active )
446                                 {
447                                         // Hunt for the a_frame
448                                         while( a_frame <= b_frame && invalid( self->frames[ a_frame ] ) )
449                                                 a_frame ++;
450
451                                         // Determine if we're active now
452                                         active = a_frame != b_frame && !invalid( self->frames[ b_frame ] );
453                                 }
454                                 break;
455
456                         default:
457                                 mlt_log( service, MLT_LOG_ERROR, "invalid transition type\n" );
458                                 break;
459                 }
460
461                 // Now handle the non-always active case
462                 if ( active && !always_active )
463                 {
464                         // For non-always-active transitions, we need the current position of the a frame
465                         position = mlt_frame_get_position( self->frames[ a_frame ] );
466
467                         // If a is in range, we're active
468                         active = position >= in && ( out == 0 || position <= out );
469                 }
470
471                 // Finally, process the a and b frames
472                 if ( active )
473                 {
474                         mlt_frame a_frame_ptr = self->frames[ !reverse_order ? a_frame : b_frame ];
475                         mlt_frame b_frame_ptr = self->frames[ !reverse_order ? b_frame : a_frame ];
476                         int a_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide" );
477                         int b_hide = mlt_properties_get_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide" );
478                         if ( !( a_hide & type ) && !( b_hide & type ) )
479                         {
480                                 // Add hooks for pre-processing frames
481                                 mlt_frame_push_get_image( a_frame_ptr, get_image_a );
482                                 mlt_frame_push_frame( b_frame_ptr, a_frame_ptr );
483                                 mlt_frame_push_get_image( b_frame_ptr, get_image_b );
484
485                                 // Process the transition
486                                 *frame = mlt_transition_process( self, a_frame_ptr, b_frame_ptr );
487
488                                 // We need to ensure that the tractor doesn't consider this frame for output
489                                 if ( *frame == a_frame_ptr )
490                                         b_hide |= type;
491                                 else
492                                         a_hide |= type;
493
494                                 mlt_properties_set_int( MLT_FRAME_PROPERTIES( a_frame_ptr ), "hide", a_hide );
495                                 mlt_properties_set_int( MLT_FRAME_PROPERTIES( b_frame_ptr ), "hide", b_hide );
496                         }
497                 }
498         }
499
500         // Obtain the frame from the cache or the producer we're attached to
501         if ( index >= a_track && index <= b_track )
502                 *frame = self->frames[ index ];
503         else
504                 error = mlt_service_get_frame( self->producer, frame, index );
505
506         // Determine if that was the last track
507         self->held = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( *frame ), "last_track" );
508
509         return error;
510 }
511
512 /** Close and destroy the transition.
513  *
514  * \public \memberof mlt_transition_s
515  * \param self a transition
516  */
517
518 void mlt_transition_close( mlt_transition self )
519 {
520         if ( self != NULL && mlt_properties_dec_ref( MLT_TRANSITION_PROPERTIES( self ) ) <= 0 )
521         {
522                 self->parent.close = NULL;
523                 if ( self->close != NULL )
524                 {
525                         self->close( self );
526                 }
527                 else
528                 {
529                         mlt_service_close( &self->parent );
530                         free( self->frames );
531                         free( self );
532                 }
533         }
534 }