]> git.sesse.net Git - mlt/blob - mlt/src/framework/mlt_transition.c
albino
[mlt] / mlt / src / framework / mlt_transition.c
1 /*
2  * mlt_transition.c -- abstraction for all transition services
3  * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include "mlt_transition.h"
24 #include "mlt_frame.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 /** Forward references.
31 */
32
33 static int transition_get_frame( mlt_service this, mlt_frame_ptr frame, int index );
34
35 /** Constructor.
36 */
37
38 int mlt_transition_init( mlt_transition this, void *child )
39 {
40         mlt_service service = &this->parent;
41         memset( this, 0, sizeof( struct mlt_transition_s ) );
42         this->child = child;
43         if ( mlt_service_init( service, this ) == 0 )
44         {
45                 mlt_properties properties = mlt_transition_properties( this );
46
47                 service->get_frame = transition_get_frame;
48
49                 mlt_properties_set_timecode( properties, "in", 0 );
50                 mlt_properties_set_timecode( properties, "out", 0 );
51                 mlt_properties_set_int( properties, "a_track", 0 );
52                 mlt_properties_set_int( properties, "b_track", 1 );
53
54                 return 0;
55         }
56         return 1;
57 }
58
59 /** Get the service associated to the transition.
60 */
61
62 mlt_service mlt_transition_service( mlt_transition this )
63 {
64         return &this->parent;
65 }
66
67 /** Get the properties interface.
68 */
69
70 mlt_properties mlt_transition_properties( mlt_transition this )
71 {
72         return mlt_service_properties( mlt_transition_service( this ) );
73 }
74
75 /** Connect this transition with a producers a and b tracks.
76 */
77
78 int mlt_transition_connect( mlt_transition this, mlt_service producer, int a_track, int b_track )
79 {
80         int ret = mlt_service_connect_producer( &this->parent, producer, a_track );
81         if ( ret == 0 )
82         {
83                 mlt_properties properties = mlt_transition_properties( this );
84                 this->producer = producer;
85                 mlt_properties_set_int( properties, "a_track", a_track );
86                 mlt_properties_set_int( properties, "b_track", b_track );
87         }
88         return ret;
89 }
90
91 /** Set the in and out points.
92 */
93
94 void mlt_transition_set_in_and_out( mlt_transition this, mlt_timecode in, mlt_timecode out )
95 {
96         mlt_properties properties = mlt_transition_properties( this );
97         mlt_properties_set_timecode( properties, "in", in );
98         mlt_properties_set_timecode( properties, "out", out );
99 }
100
101 /** Get the index of the a track.
102 */
103
104 int mlt_transition_get_a_track( mlt_transition this )
105 {
106         mlt_properties properties = mlt_transition_properties( this );
107         return mlt_properties_get_int( properties, "a_track" );
108 }
109
110 /** Get the index of the b track.
111 */
112
113 int mlt_transition_get_b_track( mlt_transition this )
114 {
115         mlt_properties properties = mlt_transition_properties( this );
116         return mlt_properties_get_int( properties, "b_track" );
117 }
118
119 /** Get the in point.
120 */
121
122 mlt_timecode mlt_transition_get_in( mlt_transition this )
123 {
124         mlt_properties properties = mlt_transition_properties( this );
125         return mlt_properties_get_timecode( properties, "in" );
126 }
127
128 /** Get the out point.
129 */
130
131 mlt_timecode mlt_transition_get_out( mlt_transition this )
132 {
133         mlt_properties properties = mlt_transition_properties( this );
134         return mlt_properties_get_timecode( properties, "out" );
135 }
136
137 /** Process the frame.
138 */
139
140 static mlt_frame transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame )
141 {
142         if ( this->process == NULL )
143         {
144                 if ( !mlt_frame_is_test_card( a_frame ) )
145                 {
146                         mlt_frame_close( b_frame );
147                         return a_frame;
148                 }
149                 else
150                 {
151                         mlt_frame_close( a_frame );
152                         return b_frame;
153                 }
154         }
155         else
156         {
157                 return this->process( this, a_frame, b_frame );
158         }
159 }
160
161 /** Get a frame from this filter.
162
163         The logic is complex here. A transition is applied to frames on the a and b tracks
164         specified in the connect method above. Since all frames are obtained via this 
165         method for all tracks, we have to take special care that we only obtain the a and
166         b frames once - we do this on the first call to get a frame from either a or b.
167         
168         After that, we have 3 cases to resolve:
169         
170         1)      if the track is the a_track and we're in the time zone, then we need to call the
171                 process method to do the effect on the frame (we assign NULL to the a_frame and
172                 b_frames here) otherwise, we pass on the a_frame unmolested;
173         2)      if the track is the b_track and we're the in the time zone OR the b_frame is NULL,
174                 then we generate a test card frame, otherwise we pass on the b frame unmolested;
175         3)      For all other tracks, we get the frames on demand.
176 */
177
178 static int transition_get_frame( mlt_service service, mlt_frame_ptr frame, int index )
179 {
180         mlt_transition this = service->child;
181
182         mlt_properties properties = mlt_transition_properties( this );
183
184         int a_track = mlt_properties_get_int( properties, "a_track" );
185         int b_track = mlt_properties_get_int( properties, "b_track" );
186         mlt_timecode in = mlt_properties_get_timecode( properties, "in" );
187         mlt_timecode out = mlt_properties_get_timecode( properties, "out" );
188
189         // Fetch a and b frames together...
190         if ( ( index == a_track || index == b_track ) &&
191                  ( this->a_frame == NULL && this->b_frame == NULL ) )
192         {
193                 mlt_service_get_frame( this->producer, &this->a_frame, a_track );
194                 mlt_service_get_frame( this->producer, &this->b_frame, b_track );
195         }
196         
197         // Special case track processing
198         if ( index == a_track )
199         {
200                 // Determine if we're in the right time zone
201                 mlt_timecode timecode = mlt_frame_get_timecode( this->a_frame );
202                 if ( timecode >= in && timecode < out )
203                 {
204                         // Process the transition
205                         *frame = transition_process( this, this->a_frame, this->b_frame );
206                         
207                         // Important - NULL both frames now so that we know they're done...
208                         this->a_frame = NULL;
209                         this->b_frame = NULL;
210                 }
211                 else
212                 {
213                         // Pass on the 'a frame' and remember that we've done it
214                         *frame = this->a_frame;
215                         this->a_frame = NULL;
216                 }                       
217                 return 0;
218         }
219         if ( index == b_track )
220         {
221                 if ( this->b_frame == NULL )
222                 {
223                         // We're *probably* in the zone and the a frame has been requested
224                         *frame = mlt_frame_init( );
225                 }
226                 else
227                 {
228                         mlt_timecode timecode = mlt_frame_get_timecode( this->b_frame );
229                         if ( timecode >= in && timecode < out )
230                         {
231                                 // We're in the zone, but the 'a frame' has not been requested yet
232                                 *frame = mlt_frame_init( );
233                         }
234                         else
235                         {
236                                 // We're out of the zone, pass on b and remember that we've done it
237                                 *frame = this->b_frame;
238                                 this->b_frame = NULL;
239                         }
240                 }
241                 return 0;
242         }
243         else
244         {
245                 // Pass through
246                 return mlt_service_get_frame( this->producer, frame, index );
247         }
248 }
249
250 /** Close the transition.
251 */
252
253 void mlt_transition_close( mlt_transition this )
254 {
255         if ( this->close != NULL )
256                 this->close( this );
257         else
258                 mlt_service_close( &this->parent );
259 }