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 )
63 animation_node prev = current->prev;
64 animation_node next = current->next;
66 while ( prev && !prev->item.is_key ) prev = prev->prev;
67 while ( next && !next->item.is_key ) next = next->next;
70 current->item.is_key = 1;
71 if ( current->item.keyframe_type == mlt_keyframe_discrete )
73 mlt_property_pass( current->item.property, prev->item.property );
77 mlt_property_interpolate( current->item.property,
78 prev->item.property, next->item.property,
79 current->item.frame - prev->item.frame,
80 next->item.frame - prev->item.frame,
81 self->fps, self->locale );
85 // Move to the next item
86 current = current->next;
91 static int mlt_animation_drop( mlt_animation self, animation_node node )
93 if ( node == self->nodes )
95 self->nodes = node->next;
97 self->nodes->prev = NULL;
98 self->nodes->item.is_key = 1;
101 else if ( node->next && node->prev )
103 node->prev->next = node->next;
104 node->next->prev = node->prev;
106 else if ( node->next )
108 node->next->prev = node->prev;
110 else if ( node->prev )
112 node->prev->next = node->next;
114 mlt_property_close( node->item.property );
120 static void mlt_animation_clean( mlt_animation self )
125 while ( self->nodes )
126 mlt_animation_drop( self, self->nodes );
129 int mlt_animation_parse(mlt_animation self, const char *data, int length, double fps, locale_t locale )
133 struct mlt_animation_item_s item;
134 mlt_tokeniser tokens = mlt_tokeniser_init( );
136 // Clean the existing geometry
137 mlt_animation_clean( self );
139 // Update the info on the data
141 self->data = strdup( data );
142 self->length = length;
144 self->locale = locale;
145 item.property = mlt_property_init();
149 mlt_tokeniser_parse_new( tokens, (char*) data, ";" );
151 // Iterate through each token
152 for ( i = 0; i < mlt_tokeniser_count( tokens ); i++ )
154 char *value = mlt_tokeniser_get_string( tokens, i );
156 // If no data in keyframe, drop it (trailing semicolon)
157 if ( !value || !strcmp( value, "" ) )
161 item.frame = item.is_key = 0;
163 // Now parse the item
164 mlt_animation_parse_item( self, &item, value );
166 // Now insert into place
167 mlt_animation_insert( self, &item );
169 mlt_animation_interpolate( self );
172 mlt_tokeniser_close( tokens );
173 mlt_property_close( item.property );
178 // Conditionally refresh in case of a change
179 int mlt_animation_refresh(mlt_animation self, const char *data, int length)
181 if ( ( length != self->length )|| ( data && ( !self->data || strcmp( data, self->data ) ) ) )
182 return mlt_animation_parse( self, data, length, self->fps, self->locale );
186 int mlt_animation_get_length( mlt_animation self )
194 void mlt_animation_set_length( mlt_animation self, int length )
197 self->length = length;
200 int mlt_animation_parse_item( mlt_animation self, mlt_animation_item item, const char *value )
204 if ( value && strcmp( value, "" ) )
206 // Determine if a position has been specified
207 if ( strchr( value, '=' ) )
211 #if defined(__GLIBC__) || defined(__DARWIN__)
213 temp = strtod_l( value, &p, self->locale );
216 temp = strtod( value, &p );
217 // If p == value then it is likely a time clock or time code.
218 if ( temp > -1.0 && temp < 1.0 && p != value )
220 // Parse a relative time (-1, 1).
221 item->frame = temp * self->length;
225 // Parse an absolute time value.
226 mlt_property_set_string( item->property, value );
227 item->frame = mlt_property_get_int( item->property, self->fps, self->locale );
230 // The character preceeding the equal sign indicates interpolation method.
231 p = strchr( value, '=' ) - 1;
233 item->keyframe_type = mlt_keyframe_discrete;
235 item->keyframe_type = mlt_keyframe_linear;
239 // Special case - frame < 0
240 if ( item->frame < 0 )
241 item->frame += self->length;
243 // Obtain the current value at this position - this allows new
244 // frames to be created which don't specify all values
245 mlt_animation_get_item( self, item, item->frame );
247 // Set remainder of string as item value.
248 mlt_property_set_string( item->property, value );
259 // Fetch a geometry item for an absolute position
260 int mlt_animation_get_item( mlt_animation self, mlt_animation_item item, int position )
263 // Need to find the nearest keyframe to the position specifed
264 animation_node node = self->nodes;
266 // Iterate through the keyframes until we reach last or have
267 while ( node && node->next && position >= node->next->item.frame )
272 item->keyframe_type = node->item.keyframe_type;
274 // Position is before the first keyframe.
275 if ( position < node->item.frame )
278 mlt_property_pass( item->property, node->item.property );
281 else if ( position == node->item.frame )
283 item->is_key = node->item.is_key;
284 mlt_property_pass( item->property, node->item.property );
286 // Position is after the last keyframe.
287 else if ( !node->next )
290 mlt_property_pass( item->property, node->item.property );
292 // Interpolation needed.
296 if ( node->item.keyframe_type == mlt_keyframe_discrete )
298 mlt_property_pass( item->property, node->item.property );
302 mlt_property_interpolate( item->property, node->item.property, node->next->item.property,
303 position - node->item.frame, node->next->item.frame - node->item.frame,
304 self->fps, self->locale );
310 item->frame = item->is_key = 0;
313 item->frame = position;
318 // Specify an animation item at an absolute position
319 int mlt_animation_insert( mlt_animation self, mlt_animation_item item )
322 animation_node node = calloc( 1, sizeof( *node ) );
323 node->item.frame = item->frame;
324 node->item.is_key = 1;
325 node->item.keyframe_type = item->keyframe_type;
326 node->item.property = mlt_property_init();
327 mlt_property_pass( node->item.property, item->property );
329 // Determine if we need to insert or append to the list, or if it's a new list
332 // Get the first item
333 animation_node current = self->nodes;
335 // Locate an existing nearby item
336 while ( current->next && item->frame > current->item.frame )
337 current = current->next;
339 if ( item->frame < current->item.frame )
341 if ( current == self->nodes )
344 current->prev->next = node;
345 node->next = current;
346 node->prev = current->prev;
347 current->prev = node;
349 else if ( item->frame > current->item.frame )
352 current->next->prev = node;
353 node->next = current->next;
354 node->prev = current;
355 current->next = node;
359 // Update matching node.
360 current->item.frame = item->frame;
361 current->item.is_key = 1;
362 current->item.keyframe_type = item->keyframe_type;
363 mlt_property_close( current->item.property );
364 current->item.property = node->item.property;
370 // Set the first item
377 // Remove the keyframe at the specified position
378 int mlt_animation_remove( mlt_animation self, int position )
381 animation_node node = self->nodes;
383 while ( node && position != node->item.frame )
386 if ( node && position == node->item.frame )
387 error = mlt_animation_drop( self, node );
392 // Get the keyfame at the position or the next following
393 int mlt_animation_next_key( mlt_animation self, mlt_animation_item item, int position )
395 animation_node node = self->nodes;
397 while ( node && position > node->item.frame )
402 item->frame = node->item.frame;
403 item->is_key = node->item.is_key;
404 item->keyframe_type = node->item.keyframe_type;
405 mlt_property_pass( item->property, node->item.property );
408 return ( node == NULL );
411 // Get the keyframe at the position or the previous key
412 int mlt_animation_prev_key( mlt_animation self, mlt_animation_item item, int position )
414 animation_node node = self->nodes;
416 while ( node && node->next && position >= node->next->item.frame )
421 item->frame = node->item.frame;
422 item->is_key = node->item.is_key;
423 item->keyframe_type = node->item.keyframe_type;
424 mlt_property_pass( item->property, node->item.property );
427 return ( node == NULL );
430 char *mlt_animation_serialize_cut( mlt_animation self, int in, int out )
432 struct mlt_animation_item_s item;
433 char *ret = malloc( 1000 );
437 item.property = mlt_property_init();
441 out = mlt_animation_get_length( self );
453 // If it's the first frame, then it's not necessarily a key
454 if ( item.frame == in )
456 if ( mlt_animation_get_item( self, &item, item.frame ) )
459 // If the first keyframe is larger than the current position
460 // then do nothing here
461 if ( self->nodes->item.frame > item.frame )
467 // To ensure correct seeding
470 // Typically, we move from keyframe to keyframe
471 else if ( item.frame < out )
473 if ( mlt_animation_next_key( self, &item, item.frame ) )
476 // Special case - crop at the out point
477 if ( item.frame > out )
478 mlt_animation_get_item( self, &item, out );
480 // We've handled the last keyframe
486 // Determine length of string to be appended.
487 if ( item.frame - in != 0 )
490 item_len += strlen( mlt_property_get_string_l( item.property, self->locale ) );
492 // Reallocate return string to be long enough.
493 while ( used + item_len + 2 > size ) // +2 for ';' and NULL
496 ret = realloc( ret, size );
499 // Append item delimiter (;) if needed.
500 if ( ret && used > 0 )
507 // Append keyframe time and keyframe/value delimiter (=).
508 if ( item.frame - in != 0 )
511 switch (item.keyframe_type) {
512 case mlt_keyframe_discrete:
519 sprintf( ret + used, "%d%s=", item.frame - in, s );
521 // Append item value.
523 strcat( ret, mlt_property_get_string_l( item.property, self->locale ) );
524 used = strlen( ret );
529 mlt_property_close( item.property );
534 // Serialise the current geometry
535 char *mlt_animation_serialize( mlt_animation self )
537 char *ret = mlt_animation_serialize_cut( self, 0, self->length );
544 return strdup( ret );
547 // Close the geometry
548 void mlt_animation_close( mlt_animation self )
552 mlt_animation_clean( self );