]> git.sesse.net Git - mlt/blob - src/framework/mlt_animation.c
Add mlt_animation and mlt_property_interpolate().
[mlt] / src / framework / mlt_animation.c
1 /*
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>
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #include "mlt_animation.h"
23 #include "mlt_tokeniser.h"
24 #include "mlt_profile.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 typedef struct animation_node_s *animation_node;
31 struct animation_node_s
32 {
33         struct mlt_animation_item_s item;
34         animation_node next, prev;
35 };
36
37 struct mlt_animation_s
38 {
39         char *data;
40         int length;
41         double fps;
42         locale_t locale;
43         animation_node nodes;
44 };
45
46 // Create a new geometry structure
47 mlt_animation mlt_animation_new( )
48 {
49         mlt_animation self = calloc( 1, sizeof( *self ) );
50         return self;
51 }
52
53 void mlt_animation_interpolate( mlt_animation self )
54 {
55         // Parse all items to ensure non-keyframes are calculated correctly.
56         if ( self->nodes )
57         {
58                 animation_node current = self->nodes;
59                 while ( current )
60                 {
61                         if ( !current->item.is_key )
62                         {
63                                 animation_node prev = current->prev;
64                                 animation_node next = current->next;
65
66                                 while ( prev && !prev->item.is_key ) prev = prev->prev;
67                                 while ( next && !next->item.is_key ) next = next->next;
68
69                                 if ( !prev )
70                                         current->item.is_key = 1;
71                                 mlt_property_interpolate( current->item.property,
72                                         prev->item.property, next->item.property,
73                                         current->item.frame - prev->item.frame,
74                                         next->item.frame - prev->item.frame,
75                                         self->fps, self->locale );
76                         }
77
78                         // Move to the next item
79                         current = current->next;
80                 }
81         }
82 }
83
84 static int mlt_animation_drop( mlt_animation self, animation_node node )
85 {
86         if ( node == self->nodes )
87         {
88                 self->nodes = node->next;
89                 if ( self->nodes ) {
90                         self->nodes->prev = NULL;
91                         self->nodes->item.is_key = 1;
92                 }
93         }
94         else if ( node->next && node->prev )
95         {
96                 node->prev->next = node->next;
97                 node->next->prev = node->prev;
98         }
99         else if ( node->next )
100         {
101                 node->next->prev = node->prev;
102         }
103         else if ( node->prev )
104         {
105                 node->prev->next = node->next;
106         }
107         mlt_property_close( node->item.property );
108         free( node );
109
110         return 0;
111 }
112
113 static void mlt_animation_clean( mlt_animation self )
114 {
115         if ( self->data )
116                 free( self->data );
117         self->data = NULL;
118         while ( self->nodes )
119                 mlt_animation_drop( self, self->nodes );
120 }
121
122 int mlt_animation_parse(mlt_animation self, const char *data, int length, double fps, locale_t locale )
123 {
124         int error = 0;
125         int i = 0;
126         struct mlt_animation_item_s item;
127         mlt_tokeniser tokens = mlt_tokeniser_init( );
128
129         // Clean the existing geometry
130         mlt_animation_clean( self );
131
132         // Update the info on the data
133         if ( data )
134                 self->data = strdup( data );
135         self->length = length;
136         self->fps = fps;
137         self->locale = locale;
138         item.property = mlt_property_init();
139
140         // Tokenise
141         if ( data )
142                 mlt_tokeniser_parse_new( tokens, (char*) data, ";" );
143
144         // Iterate through each token
145         for ( i = 0; i < mlt_tokeniser_count( tokens ); i++ )
146         {
147                 char *value = mlt_tokeniser_get_string( tokens, i );
148
149                 // If no data in keyframe, drop it (trailing semicolon)
150                 if ( !value || !strcmp( value, "" ) )
151                         continue;
152
153                 // Reset item
154                 item.frame = item.is_key = 0;
155
156                 // Now parse the item
157                 mlt_animation_parse_item( self, &item, value );
158
159                 // Now insert into place
160                 mlt_animation_insert( self, &item );
161         }
162         mlt_animation_interpolate( self );
163
164         // Cleanup
165         mlt_tokeniser_close( tokens );
166         mlt_property_close( item.property );
167
168         return error;
169 }
170
171 // Conditionally refresh in case of a change
172 int mlt_animation_refresh(mlt_animation self, const char *data, int length)
173 {
174         if ( ( length != self->length )|| ( data && ( !self->data || strcmp( data, self->data ) ) ) )
175                 return mlt_animation_parse( self, data, length, self->fps, self->locale );
176         return 0;
177 }
178
179 int mlt_animation_get_length( mlt_animation self )
180 {
181         if ( self )
182                 return self->length;
183         else
184                 return 0;
185 }
186
187 void mlt_animation_set_length( mlt_animation self, int length )
188 {
189         if ( self )
190                 self->length = length;
191 }
192
193 int mlt_animation_parse_item( mlt_animation self, mlt_animation_item item, const char *value )
194 {
195         int error = 0;
196
197         if ( value && strcmp( value, "" ) )
198         {
199                 // Determine if a position has been specified
200                 if ( strchr( value, '=' ) )
201                 {
202                         double temp;
203                         char *p = NULL;
204 #if defined(__GLIBC__) || defined(__DARWIN__)
205                         if ( self->locale )
206                                 temp = strtod_l( value, &p, self->locale );
207                         else
208 #endif
209                         temp = strtod( value, &p );
210                         // If p == value then it is likely a time clock or time code.
211                         if ( temp > -1.0 && temp < 1.0 && p != value )
212                         {
213                                 // Parse a relative time (-1, 1).
214                                 item->frame = temp * self->length;
215                         }
216                         else
217                         {
218                                 // Parse an absolute time value.
219                                 mlt_property_set_string( item->property, value );
220                                 item->frame = mlt_property_get_int( item->property, self->fps, self->locale );
221                         }
222                         value = strchr( value, '=' ) + 1;
223
224                         // TODO the character preceeding the equal sign indicates method of interpolation.
225                 }
226
227                 // Special case - frame < 0
228                 if ( item->frame < 0 )
229                         item->frame += self->length;
230
231                 // Obtain the current value at this position - this allows new
232                 // frames to be created which don't specify all values
233                 mlt_animation_get_item( self, item, item->frame );
234
235                 // Set remainder of string as item value.
236                 mlt_property_set_string( item->property, value );
237                 item->is_key = 1;
238         }
239         else
240         {
241                 error = 1;
242         }
243
244         return error;
245 }
246
247 // Fetch a geometry item for an absolute position
248 int mlt_animation_get_item( mlt_animation self, mlt_animation_item item, int position )
249 {
250         int error = 0;
251         // Need to find the nearest keyframe to the position specifed
252         animation_node node = self->nodes;
253
254         // Iterate through the keyframes until we reach last or have
255         while ( node && node->next && position >= node->next->item.frame )
256                 node = node->next;
257
258         if ( node )
259         {
260                 // Position is before the first keyframe.
261                 if ( position < node->item.frame )
262                 {
263                         item->is_key = 0;
264                         mlt_property_pass( item->property, node->item.property );
265                 }
266                 // Item exists.
267                 else if ( position == node->item.frame )
268                 {
269                         item->is_key = node->item.is_key;
270                         mlt_property_pass( item->property, node->item.property );
271                 }
272                 // Position is after the last keyframe.
273                 else if ( !node->next )
274                 {
275                         item->is_key = 0;
276                         mlt_property_pass( item->property, node->item.property );
277                 }
278                 // Interpolation needed.
279                 else
280                 {
281                         item->is_key = 0;
282                         mlt_property_interpolate( item->property, node->item.property, node->next->item.property,
283                                 position - node->item.frame, node->next->item.frame - node->item.frame,
284                                 self->fps, self->locale );
285                 }
286         }
287         else
288         {
289                 item->frame = item->is_key = 0;
290                 error = 1;
291         }
292         item->frame = position;
293
294         return error;
295 }
296
297 // Specify an animation item at an absolute position
298 int mlt_animation_insert( mlt_animation self, mlt_animation_item item )
299 {
300         int error = 0;
301         animation_node node = calloc( 1, sizeof( *node ) );
302         node->item.frame = item->frame;
303         node->item.is_key = 1;
304         node->item.property = mlt_property_init();
305         mlt_property_pass( node->item.property, item->property );
306
307         // Determine if we need to insert or append to the list, or if it's a new list
308         if ( self->nodes )
309         {
310                 // Get the first item
311                 animation_node current = self->nodes;
312
313                 // Locate an existing nearby item
314                 while ( current->next && item->frame > current->item.frame )
315                         current = current->next;
316
317                 if ( item->frame < current->item.frame )
318                 {
319                         if ( current == self->nodes )
320                                 self->nodes = node;
321                         if ( current->prev )
322                                 current->prev->next = node;
323                         node->next = current;
324                         node->prev = current->prev;
325                         current->prev = node;
326                 }
327                 else if ( item->frame > current->item.frame )
328                 {
329                         if ( current->next )
330                                 current->next->prev = node;
331                         node->next = current->next;
332                         node->prev = current;
333                         current->next = node;
334                 }
335                 else
336                 {
337                         // Update matching node.
338                         current->item.frame = item->frame;
339                         current->item.is_key = 1;
340                         mlt_property_close( current->item.property );
341                         current->item.property = node->item.property;
342                         free( node );
343                 }
344         }
345         else
346         {
347                 // Set the first item
348                 self->nodes = node;
349         }
350
351         return error;
352 }
353
354 // Remove the keyframe at the specified position
355 int mlt_animation_remove( mlt_animation self, int position )
356 {
357         int error = 1;
358         animation_node node = self->nodes;
359
360         while ( node && position != node->item.frame )
361                 node = node->next;
362
363         if ( node && position == node->item.frame )
364                 error = mlt_animation_drop( self, node );
365
366         return error;
367 }
368
369 // Get the keyfame at the position or the next following
370 int mlt_animation_next_key( mlt_animation self, mlt_animation_item item, int position )
371 {
372         animation_node node = self->nodes;
373
374         while ( node && position > node->item.frame )
375                 node = node->next;
376
377         if ( node )
378         {
379                 item->frame = node->item.frame;
380                 item->is_key = node->item.is_key;
381                 mlt_property_pass( item->property, node->item.property );
382         }
383
384         return ( node == NULL );
385 }
386
387 // Get the keyframe at the position or the previous key
388 int mlt_animation_prev_key( mlt_animation self, mlt_animation_item item, int position )
389 {
390         animation_node node = self->nodes;
391
392         while ( node && node->next && position >= node->next->item.frame )
393                 node = node->next;
394
395         if ( node )
396         {
397                 item->frame = node->item.frame;
398                 item->is_key = node->item.is_key;
399                 mlt_property_pass( item->property, node->item.property );
400         }
401
402         return ( node == NULL );
403 }
404
405 char *mlt_animation_serialize_cut( mlt_animation self, int in, int out )
406 {
407         struct mlt_animation_item_s item;
408         char *ret = malloc( 1000 );
409         size_t used = 0;
410         size_t size = 1000;
411
412         item.property = mlt_property_init();
413         if ( in == -1 )
414                 in = 0;
415         if ( out == -1 )
416                 out = mlt_animation_get_length( self );
417
418         if ( ret )
419         {
420                 strcpy( ret, "" );
421
422                 item.frame = in;
423
424                 while ( 1 )
425                 {
426                         size_t item_len = 0;
427
428                         // If it's the first frame, then it's not necessarily a key
429                         if ( item.frame == in )
430                         {
431                                 if ( mlt_animation_get_item( self, &item, item.frame ) )
432                                         break;
433
434                                 // If the first keyframe is larger than the current position
435                                 // then do nothing here
436                                 if ( self->nodes->item.frame > item.frame )
437                                 {
438                                         item.frame ++;
439                                         continue;
440                                 }
441
442                                 // To ensure correct seeding
443                                 item.is_key = 1;
444                         }
445                         // Typically, we move from keyframe to keyframe
446                         else if ( item.frame < out )
447                         {
448                                 if ( mlt_animation_next_key( self, &item, item.frame ) )
449                                         break;
450
451                                 // Special case - crop at the out point
452                                 if ( item.frame > out )
453                                         mlt_animation_get_item( self, &item, out );
454                         }
455                         // We've handled the last keyframe
456                         else
457                         {
458                                 break;
459                         }
460
461                         // Determine length of string to be appended.
462                         if ( item.frame - in != 0 )
463                                 item_len += 20;
464                         if ( item.is_key )
465                                 item_len += strlen( mlt_property_get_string_l( item.property, self->locale ) );
466
467                         // Reallocate return string to be long enough.
468                         while ( used + item_len + 2 > size ) // +2 for ';' and NULL
469                         {
470                                 size += 1000;
471                                 ret = realloc( ret, size );
472                         }
473
474                         // Append item delimiter (;) if needed.
475                         if ( ret && used > 0 )
476                         {
477                                 used ++;
478                                 strcat( ret, ";" );
479                         }
480                         if ( ret )
481                         {
482                                 // Append keyframe time and keyframe/value delimiter (=).
483                                 if ( item.frame - in != 0 )
484                                         sprintf( ret + used, "%d=", item.frame - in );
485                                 // Append item value.
486                                 if ( item.is_key )
487                                         strcat( ret, mlt_property_get_string_l( item.property, self->locale ) );
488                                 used = strlen( ret );
489                         }
490                         item.frame ++;
491                 }
492         }
493         mlt_property_close( item.property );
494
495         return ret;
496 }
497
498 // Serialise the current geometry
499 char *mlt_animation_serialize( mlt_animation self )
500 {
501         char *ret = mlt_animation_serialize_cut( self, 0, self->length );
502         if ( ret )
503         {
504                 if ( self->data )
505                         free( self->data );
506                 self->data = ret;
507         }
508         return strdup( ret );
509 }
510
511 // Close the geometry
512 void mlt_animation_close( mlt_animation self )
513 {
514         if ( self )
515         {
516                 mlt_animation_clean( self );
517                 free( self );
518         }
519 }