2 * mlt_animation.c -- provides the property animation API
3 * Copyright (C) 2004-2013 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
5 * Author: Dan Dennedy <dan@dennedy.org>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "mlt_animation.h"
23 #include "mlt_tokeniser.h"
24 #include "mlt_profile.h"
30 typedef struct animation_node_s *animation_node;
31 struct animation_node_s
33 struct mlt_animation_item_s item;
34 animation_node next, prev;
37 struct mlt_animation_s
46 // Create a new geometry structure
47 mlt_animation mlt_animation_new( )
49 mlt_animation self = calloc( 1, sizeof( *self ) );
53 void mlt_animation_interpolate( mlt_animation self )
55 // Parse all items to ensure non-keyframes are calculated correctly.
58 animation_node current = self->nodes;
61 if ( !current->item.is_key )
64 mlt_property points[4];
65 animation_node prev = current->prev;
66 animation_node next = current->next;
68 while ( prev && !prev->item.is_key ) prev = prev->prev;
69 while ( next && !next->item.is_key ) next = next->next;
72 current->item.is_key = 1;
73 points[0] = prev->prev? prev->prev->item.property : prev->item.property;
74 points[1] = prev->item.property;
75 points[2] = next->item.property;
76 points[3] = next->next? next->next->item.property : next->item.property;
77 progress = current->item.frame - prev->item.frame;
78 progress /= next->item.frame - prev->item.frame;
79 mlt_property_interpolate( current->item.property, points, progress,
80 self->fps, self->locale, current->item.keyframe_type );
83 // Move to the next item
84 current = current->next;
89 static int mlt_animation_drop( mlt_animation self, animation_node node )
91 if ( node == self->nodes )
93 self->nodes = node->next;
95 self->nodes->prev = NULL;
96 self->nodes->item.is_key = 1;
99 else if ( node->next && node->prev )
101 node->prev->next = node->next;
102 node->next->prev = node->prev;
104 else if ( node->next )
106 node->next->prev = node->prev;
108 else if ( node->prev )
110 node->prev->next = node->next;
112 mlt_property_close( node->item.property );
118 static void mlt_animation_clean( mlt_animation self )
123 while ( self->nodes )
124 mlt_animation_drop( self, self->nodes );
127 int mlt_animation_parse(mlt_animation self, const char *data, int length, double fps, locale_t locale )
131 struct mlt_animation_item_s item;
132 mlt_tokeniser tokens = mlt_tokeniser_init( );
134 // Clean the existing geometry
135 mlt_animation_clean( self );
137 // Update the info on the data
139 self->data = strdup( data );
140 self->length = length;
142 self->locale = locale;
143 item.property = mlt_property_init();
147 mlt_tokeniser_parse_new( tokens, (char*) data, ";" );
149 // Iterate through each token
150 for ( i = 0; i < mlt_tokeniser_count( tokens ); i++ )
152 char *value = mlt_tokeniser_get_string( tokens, i );
154 // If no data in keyframe, drop it (trailing semicolon)
155 if ( !value || !strcmp( value, "" ) )
159 item.frame = item.is_key = 0;
161 // Now parse the item
162 mlt_animation_parse_item( self, &item, value );
164 // Now insert into place
165 mlt_animation_insert( self, &item );
167 mlt_animation_interpolate( self );
170 mlt_tokeniser_close( tokens );
171 mlt_property_close( item.property );
176 // Conditionally refresh in case of a change
177 int mlt_animation_refresh( mlt_animation self, const char *data, int length )
179 if ( ( length != self->length )|| ( data && ( !self->data || strcmp( data, self->data ) ) ) )
180 return mlt_animation_parse( self, data, length, self->fps, self->locale );
184 int mlt_animation_get_length( mlt_animation self )
188 if ( self->length > 0 ) {
189 length = self->length;
191 else if ( self->nodes ) {
192 animation_node node = self->nodes;
194 if ( node->item.frame > length )
195 length = node->item.frame;
203 void mlt_animation_set_length( mlt_animation self, int length )
206 self->length = length;
209 int mlt_animation_parse_item( mlt_animation self, mlt_animation_item item, const char *value )
213 if ( value && strcmp( value, "" ) )
215 // Determine if a position has been specified
216 if ( strchr( value, '=' ) )
218 // Parse an absolute time value.
219 // Null terminate the string at the equal sign to prevent interpreting
220 // a colon in the part to the right of the equal sign as indicative of a
221 // a time value string.
222 char *s = strdup( value );
223 char *p = strchr( s, '=' );
225 mlt_property_set_string( item->property, s );
226 item->frame = mlt_property_get_int( item->property, self->fps, self->locale );
229 // The character preceeding the equal sign indicates interpolation method.
230 p = strchr( value, '=' ) - 1;
231 if ( p[0] == '|' || p[0] == '!' )
232 item->keyframe_type = mlt_keyframe_discrete;
233 else if ( p[0] == '~' )
234 item->keyframe_type = mlt_keyframe_smooth;
236 item->keyframe_type = mlt_keyframe_linear;
240 // Special case - frame < 0
241 if ( item->frame < 0 )
242 item->frame += mlt_animation_get_length( self );
244 // Set remainder of string as item value.
245 mlt_property_set_string( item->property, value );
256 // Fetch a geometry item for an absolute position
257 int mlt_animation_get_item( mlt_animation self, mlt_animation_item item, int position )
260 // Need to find the nearest keyframe to the position specifed
261 animation_node node = self->nodes;
263 // Iterate through the keyframes until we reach last or have
264 while ( node && node->next && position >= node->next->item.frame )
269 item->keyframe_type = node->item.keyframe_type;
271 // Position is before the first keyframe.
272 if ( position < node->item.frame )
275 mlt_property_pass( item->property, node->item.property );
278 else if ( position == node->item.frame )
280 item->is_key = node->item.is_key;
281 mlt_property_pass( item->property, node->item.property );
283 // Position is after the last keyframe.
284 else if ( !node->next )
287 mlt_property_pass( item->property, node->item.property );
289 // Interpolation needed.
293 mlt_property points[4];
294 points[0] = node->prev? node->prev->item.property : node->item.property;
295 points[1] = node->item.property;
296 points[2] = node->next->item.property;
297 points[3] = node->next->next? node->next->next->item.property : node->next->item.property;
298 progress = position - node->item.frame;
299 progress /= node->next->item.frame - node->item.frame;
300 mlt_property_interpolate( item->property, points, progress,
301 self->fps, self->locale, item->keyframe_type );
307 item->frame = item->is_key = 0;
310 item->frame = position;
315 // Specify an animation item at an absolute position
316 int mlt_animation_insert( mlt_animation self, mlt_animation_item item )
319 animation_node node = calloc( 1, sizeof( *node ) );
320 node->item.frame = item->frame;
321 node->item.is_key = 1;
322 node->item.keyframe_type = item->keyframe_type;
323 node->item.property = mlt_property_init();
324 mlt_property_pass( node->item.property, item->property );
326 // Determine if we need to insert or append to the list, or if it's a new list
329 // Get the first item
330 animation_node current = self->nodes;
332 // Locate an existing nearby item
333 while ( current->next && item->frame > current->item.frame )
334 current = current->next;
336 if ( item->frame < current->item.frame )
338 if ( current == self->nodes )
341 current->prev->next = node;
342 node->next = current;
343 node->prev = current->prev;
344 current->prev = node;
346 else if ( item->frame > current->item.frame )
349 current->next->prev = node;
350 node->next = current->next;
351 node->prev = current;
352 current->next = node;
356 // Update matching node.
357 current->item.frame = item->frame;
358 current->item.is_key = 1;
359 current->item.keyframe_type = item->keyframe_type;
360 mlt_property_close( current->item.property );
361 current->item.property = node->item.property;
367 // Set the first item
374 // Remove the keyframe at the specified position
375 int mlt_animation_remove( mlt_animation self, int position )
378 animation_node node = self->nodes;
380 while ( node && position != node->item.frame )
383 if ( node && position == node->item.frame )
384 error = mlt_animation_drop( self, node );
389 // Get the keyfame at the position or the next following
390 int mlt_animation_next_key( mlt_animation self, mlt_animation_item item, int position )
392 animation_node node = self->nodes;
394 while ( node && position > node->item.frame )
399 item->frame = node->item.frame;
400 item->is_key = node->item.is_key;
401 item->keyframe_type = node->item.keyframe_type;
402 mlt_property_pass( item->property, node->item.property );
405 return ( node == NULL );
408 // Get the keyframe at the position or the previous key
409 int mlt_animation_prev_key( mlt_animation self, mlt_animation_item item, int position )
411 animation_node node = self->nodes;
413 while ( node && node->next && position >= node->next->item.frame )
418 item->frame = node->item.frame;
419 item->is_key = node->item.is_key;
420 item->keyframe_type = node->item.keyframe_type;
421 mlt_property_pass( item->property, node->item.property );
424 return ( node == NULL );
427 char *mlt_animation_serialize_cut( mlt_animation self, int in, int out )
429 struct mlt_animation_item_s item;
430 char *ret = malloc( 1000 );
434 item.property = mlt_property_init();
438 out = mlt_animation_get_length( self );
450 // If it's the first frame, then it's not necessarily a key
451 if ( item.frame == in )
453 if ( mlt_animation_get_item( self, &item, item.frame ) )
456 // If the first keyframe is larger than the current position
457 // then do nothing here
458 if ( self->nodes->item.frame > item.frame )
464 // To ensure correct seeding
467 // Typically, we move from keyframe to keyframe
468 else if ( item.frame < out )
470 if ( mlt_animation_next_key( self, &item, item.frame ) )
473 // Special case - crop at the out point
474 if ( item.frame > out )
475 mlt_animation_get_item( self, &item, out );
477 // We've handled the last keyframe
483 // Determine length of string to be appended.
484 if ( item.frame - in != 0 )
487 item_len += strlen( mlt_property_get_string_l( item.property, self->locale ) );
489 // Reallocate return string to be long enough.
490 while ( used + item_len + 2 > size ) // +2 for ';' and NULL
493 ret = realloc( ret, size );
496 // Append item delimiter (;) if needed.
497 if ( ret && used > 0 )
504 // Append keyframe time and keyframe/value delimiter (=).
506 switch (item.keyframe_type) {
507 case mlt_keyframe_discrete:
510 case mlt_keyframe_smooth:
517 sprintf( ret + used, "%d%s=", item.frame - in, s );
519 // Append item value.
521 strcat( ret, mlt_property_get_string_l( item.property, self->locale ) );
522 used = strlen( ret );
527 mlt_property_close( item.property );
532 // Serialise the current geometry
533 char *mlt_animation_serialize( mlt_animation self )
535 char *ret = mlt_animation_serialize_cut( self, -1, -1 );
542 return strdup( ret );
545 // Close the geometry
546 void mlt_animation_close( mlt_animation self )
550 mlt_animation_clean( self );