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