]> git.sesse.net Git - mlt/blob - mlt/src/framework/mlt_transition.c
Initial revision
[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                 service->get_frame = transition_get_frame;
46                 return 0;
47         }
48         return 1;
49 }
50
51 /** Get the service associated to the transition.
52 */
53
54 mlt_service mlt_transition_service( mlt_transition this )
55 {
56         return &this->parent;
57 }
58
59 /** Connect this transition with a producers a and b tracks.
60 */
61
62 int mlt_transition_connect( mlt_transition this, mlt_service producer, int a_track, int b_track )
63 {
64         int ret = mlt_service_connect_producer( &this->parent, producer, a_track );
65         if ( ret == 0 )
66         {
67                 this->producer = producer;
68                 this->a_track = a_track;
69                 this->b_track = b_track;
70                 this->in = 0;
71                 this->out = 0;
72         }
73         return ret;
74 }
75
76 /** Set the in and out points.
77 */
78
79 void mlt_transition_set_in_and_out( mlt_transition this, mlt_timecode in, mlt_timecode out )
80 {
81         this->in = in;
82         this->out = out;
83 }
84
85 /** Get the index of the a track.
86 */
87
88 int mlt_transition_get_a_track( mlt_transition this )
89 {
90         return this->a_track;
91 }
92
93 /** Get the index of the b track.
94 */
95
96 int mlt_transition_get_b_track( mlt_transition this )
97 {
98         return this->b_track;
99 }
100
101 /** Get the in point.
102 */
103
104 mlt_timecode mlt_transition_get_in( mlt_transition this )
105 {
106         return this->in;
107 }
108
109 /** Get the out point.
110 */
111
112 mlt_timecode mlt_transition_get_out( mlt_transition this )
113 {
114         return this->out;
115 }
116
117 /** Process the frame.
118 */
119
120 static mlt_frame transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame )
121 {
122         if ( this->process == NULL )
123         {
124                 if ( !mlt_frame_is_test_card( a_frame ) )
125                 {
126                         mlt_frame_close( b_frame );
127                         return a_frame;
128                 }
129                 else
130                 {
131                         mlt_frame_close( a_frame );
132                         return b_frame;
133                 }
134         }
135         else
136         {
137                 return this->process( this, a_frame, b_frame );
138         }
139 }
140
141 /** Get a frame from this filter.
142
143         The logic is complex here. A transition is applied to frames on the a and b tracks
144         specified in the connect method above. Since all frames are obtained via this 
145         method for all tracks, we have to take special care that we only obtain the a and
146         b frames once - we do this on the first call to get a frame from either a or b.
147         
148         After that, we have 3 cases to resolve:
149         
150         1)      if the track is the a_track and we're in the time zone, then we need to call the
151                 process method to do the effect on the frame (we assign NULL to the a_frame and
152                 b_frames here) otherwise, we pass on the a_frame unmolested;
153         2)      if the track is the b_track and we're the in the time zone OR the b_frame is NULL,
154                 then we generate a test card frame, otherwise we pass on the b frame unmolested;
155         3)      For all other tracks, we get the frames on demand.
156 */
157
158 static int transition_get_frame( mlt_service service, mlt_frame_ptr frame, int index )
159 {
160         mlt_transition this = service->child;
161
162         // Fetch a and b frames together...
163         if ( ( index == this->a_track || index == this->b_track ) &&
164                  ( this->a_frame == NULL && this->b_frame == NULL ) )
165         {
166                 mlt_service_get_frame( this->producer, &this->a_frame, this->a_track );
167                 mlt_service_get_frame( this->producer, &this->b_frame, this->b_track );
168         }
169         
170         // Special case track processing
171         if ( index == this->a_track )
172         {
173                 // Determine if we're in the right time zone
174                 mlt_timecode timecode = mlt_frame_get_timecode( this->a_frame );
175                 if ( timecode >= this->in && timecode < this->out )
176                 {
177                         // Process the transition
178                         *frame = transition_process( this, this->a_frame, this->b_frame );
179                         
180                         // Important - NULL both frames now so that we know they're done...
181                         this->a_frame = NULL;
182                         this->b_frame = NULL;
183                 }
184                 else
185                 {
186                         // Pass on the 'a frame' and remember that we've done it
187                         *frame = this->a_frame;
188                         this->a_frame = NULL;
189                 }                       
190                 return 0;
191         }
192         if ( index == this->b_track )
193         {
194                 if ( this->b_frame == NULL )
195                 {
196                         // We're *probably* in the zone and the a frame has been requested
197                         *frame = mlt_frame_init( );
198                 }
199                 else
200                 {
201                         mlt_timecode timecode = mlt_frame_get_timecode( this->b_frame );
202                         if ( timecode >= this->in && timecode < this->out )
203                         {
204                                 // We're in the zone, but the 'a frame' has not been requested yet
205                                 *frame = mlt_frame_init( );
206                         }
207                         else
208                         {
209                                 // We're out of the zone, pass on b and remember that we've done it
210                                 *frame = this->b_frame;
211                                 this->b_frame = NULL;
212                         }
213                 }
214                 return 0;
215         }
216         else
217         {
218                 // Pass through
219                 return mlt_service_get_frame( this->producer, frame, index );
220         }
221 }
222
223 /** Close the transition.
224 */
225
226 void mlt_transitition_close( mlt_transition this )
227 {
228         if ( this->close != NULL )
229                 this->close( this );
230         else
231                 mlt_service_close( &this->parent );
232 }