]> git.sesse.net Git - mlt/blob - src/framework/mlt_transition.c
Add doxygen documentation for mlt_profile, mlt_pool, mlt_repository, and mlt_factory.
[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
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 }