]> git.sesse.net Git - mlt/blob - src/framework/mlt_animation.c
14b79782368a2dbb213878413a94a203bffb81d2
[mlt] / src / framework / mlt_animation.c
1 /**
2  * \file mlt_animation.c
3  * \brief Property Animation class definition
4  * \see mlt_animation_s
5  *
6  * Copyright (C) 2004-2013 Ushodaya Enterprises Limited
7  * \author Charles Yates <charles.yates@pandora.be>
8  * \author Dan Dennedy <dan@dennedy.org>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #include "mlt_animation.h"
26 #include "mlt_tokeniser.h"
27 #include "mlt_profile.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 /** \brief animation list node pointer */
34 typedef struct animation_node_s *animation_node;
35 /** \brief private animation list node */
36 struct animation_node_s
37 {
38         struct mlt_animation_item_s item;
39         animation_node next, prev;
40 };
41
42 /** \brief Property Animation class
43  *
44  * This is the animation engine for a Property object. It is dependent upon
45  * the mlt_property API and used by the various mlt_property_anim_* functions.
46  */
47
48 struct mlt_animation_s
49 {
50         char *data;           /**< the string representing the animation */
51         int length;           /**< the maximum number of frames to use when interpreting negative keyframe positions */
52         double fps;           /**< framerate to use when converting time clock strings to frame units */
53         locale_t locale;      /**< pointer to a locale to use when converting strings to numeric values */
54         animation_node nodes; /**< a linked list of keyframes (and possibly non-keyframe values) */
55 };
56
57 /** Create a new animation object.
58  *
59  * \public \memberof mlt_animation_s
60  * \return an animation object
61  */
62
63 mlt_animation mlt_animation_new( )
64 {
65         mlt_animation self = calloc( 1, sizeof( *self ) );
66         return self;
67 }
68
69 /** Re-interpolate non-keyframe nodess after a series of insertions or removals.
70  *
71  * \public \memberof mlt_animation_s
72  * \param self an animation
73  */
74
75 void mlt_animation_interpolate( mlt_animation self )
76 {
77         // Parse all items to ensure non-keyframes are calculated correctly.
78         if ( self->nodes )
79         {
80                 animation_node current = self->nodes;
81                 while ( current )
82                 {
83                         if ( !current->item.is_key )
84                         {
85                                 double progress;
86                                 mlt_property points[4];
87                                 animation_node prev = current->prev;
88                                 animation_node next = current->next;
89
90                                 while ( prev && !prev->item.is_key ) prev = prev->prev;
91                                 while ( next && !next->item.is_key ) next = next->next;
92
93                                 if ( !prev )
94                                         current->item.is_key = 1;
95                                 points[0] = prev->prev? prev->prev->item.property : prev->item.property;
96                                 points[1] = prev->item.property;
97                                 points[2] = next->item.property;
98                                 points[3] = next->next? next->next->item.property : next->item.property;
99                                 progress = current->item.frame - prev->item.frame;
100                                 progress /= next->item.frame - prev->item.frame;
101                                 mlt_property_interpolate( current->item.property, points, progress,
102                                         self->fps, self->locale, current->item.keyframe_type );
103                         }
104
105                         // Move to the next item
106                         current = current->next;
107                 }
108         }
109 }
110
111 /** Remove a node from the linked list.
112  *
113  * \private \memberof mlt_animation_s
114  * \param self an animation
115  * \param node the node to remove
116  * \return false
117  */
118
119 static int mlt_animation_drop( mlt_animation self, animation_node node )
120 {
121         if ( node == self->nodes )
122         {
123                 self->nodes = node->next;
124                 if ( self->nodes ) {
125                         self->nodes->prev = NULL;
126                         self->nodes->item.is_key = 1;
127                 }
128         }
129         else if ( node->next && node->prev )
130         {
131                 node->prev->next = node->next;
132                 node->next->prev = node->prev;
133         }
134         else if ( node->next )
135         {
136                 node->next->prev = node->prev;
137         }
138         else if ( node->prev )
139         {
140                 node->prev->next = node->next;
141         }
142         mlt_property_close( node->item.property );
143         free( node );
144
145         return 0;
146 }
147
148 /** Reset an animation and free all strings and properties.
149  *
150  * \private \memberof mlt_animation_s
151  * \param self an animation
152  */
153
154 static void mlt_animation_clean( mlt_animation self )
155 {
156         if ( self->data )
157                 free( self->data );
158         self->data = NULL;
159         while ( self->nodes )
160                 mlt_animation_drop( self, self->nodes );
161 }
162
163 /** Parse a string representing an animation.
164  *
165  * A semicolon is the delimiter between keyframe=value items in the string.
166  * \public \memberof mlt_animation_s
167  * \param self an animation
168  * \param data the string representing an animation
169  * \param length the maximum number of frames when interpreting negative keyframe times,
170  *  <=0 if you don't care or need that
171  * \param fps the framerate to use when evaluating time strings
172  * \param locale the locale to use when converting strings to numbers
173  * \return true if there was an error
174  */
175
176 int mlt_animation_parse(mlt_animation self, const char *data, int length, double fps, locale_t locale )
177 {
178         int error = 0;
179         int i = 0;
180         struct mlt_animation_item_s item;
181         mlt_tokeniser tokens = mlt_tokeniser_init( );
182
183         // Clean the existing geometry
184         mlt_animation_clean( self );
185
186         // Update the info on the data
187         if ( data )
188                 self->data = strdup( data );
189         self->length = length;
190         self->fps = fps;
191         self->locale = locale;
192         item.property = mlt_property_init();
193
194         // Tokenise
195         if ( data )
196                 mlt_tokeniser_parse_new( tokens, (char*) data, ";" );
197
198         // Iterate through each token
199         for ( i = 0; i < mlt_tokeniser_count( tokens ); i++ )
200         {
201                 char *value = mlt_tokeniser_get_string( tokens, i );
202
203                 // If no data in keyframe, drop it (trailing semicolon)
204                 if ( !value || !strcmp( value, "" ) )
205                         continue;
206
207                 // Reset item
208                 item.frame = item.is_key = 0;
209
210                 // Now parse the item
211                 mlt_animation_parse_item( self, &item, value );
212
213                 // Now insert into place
214                 mlt_animation_insert( self, &item );
215         }
216         mlt_animation_interpolate( self );
217
218         // Cleanup
219         mlt_tokeniser_close( tokens );
220         mlt_property_close( item.property );
221
222         return error;
223 }
224
225 /** Conditionally refresh the animation if it is modified.
226  *
227  * \public \memberof mlt_animation_s
228  * \param self an animation
229  * \param data the string representing an animation
230  * \param length the maximum number of frames when interpreting negative keyframe times,
231  *  <=0 if you don't care or need that
232  * \return true if there was an error
233  */
234
235 int mlt_animation_refresh( mlt_animation self, const char *data, int length )
236 {
237         if ( ( length != self->length )|| ( data && ( !self->data || strcmp( data, self->data ) ) ) )
238                 return mlt_animation_parse( self, data, length, self->fps, self->locale );
239         return 0;
240 }
241
242 /** Get the length of the animation.
243  *
244  * If the animation was initialized with a zero or negative value, then this
245  * gets the maximum frame number from animation's list of nodes.
246  * \public \memberof mlt_animation_s
247  * \param self an animation
248  * \return the number of frames
249  */
250
251 int mlt_animation_get_length( mlt_animation self )
252 {
253         int length = 0;
254         if ( self ) {
255                 if ( self->length > 0 ) {
256                         length = self->length;
257                 }
258                 else if ( self->nodes ) {
259                         animation_node node = self->nodes;
260                         while ( node ) {
261                                 if ( node->item.frame > length )
262                                         length = node->item.frame;
263                                 node = node->next;
264                         }
265                 }
266         }
267         return length;
268 }
269
270 /** Set the length of the animation.
271  *
272  * The length is used for interpreting negative keyframe positions as relative
273  * to the length. It is also used when serializing an animation as a string.
274  * \public \memberof mlt_animation_s
275  * \param self an animation
276  * \param length the length of the animation in frame units
277  */
278
279 void mlt_animation_set_length( mlt_animation self, int length )
280 {
281         if ( self )
282                 self->length = length;
283 }
284
285 /** Parse a string representing an animation keyframe=value.
286  *
287  * This function does not affect the animation itself! But it will use some state
288  * of the animation for the parsing (e.g. fps, locale).
289  * It parses into a mlt_animation_item that you provide.
290  * \p item->frame should be specified if the string does not have an equal sign and time field.
291  * If an exclamation point (!) or vertical bar (|) character preceeds the equal sign, then
292  * the keyframe interpolation is set to discrete. If a tilde (~) preceeds the equal sign,
293  * then the keyframe interpolation is set to smooth (spline).
294  *
295  * \public \memberof mlt_animation_s
296  * \param self an animation
297  * \param item an already allocated animation item
298  * \param value the string representing an animation
299  * \return true if there was an error
300  */
301
302 int mlt_animation_parse_item( mlt_animation self, mlt_animation_item item, const char *value )
303 {
304         int error = 0;
305
306         if ( value && strcmp( value, "" ) )
307         {
308                 // Determine if a position has been specified
309                 if ( strchr( value, '=' ) )
310                 {
311                         // Parse an absolute time value.
312                         // Null terminate the string at the equal sign to prevent interpreting
313                         // a colon in the part to the right of the equal sign as indicative of a
314                         // a time value string.
315                         char *s = strdup( value );
316                         char *p = strchr( s, '=' );
317                         p[0] = '\0';
318                         mlt_property_set_string( item->property, s );
319                         item->frame = mlt_property_get_int( item->property, self->fps, self->locale );
320                         free( s );
321
322                         // The character preceeding the equal sign indicates interpolation method.
323                         p = strchr( value, '=' ) - 1;
324                         if ( p[0] == '|' || p[0] == '!' )
325                                 item->keyframe_type = mlt_keyframe_discrete;
326                         else if ( p[0] == '~' )
327                                 item->keyframe_type = mlt_keyframe_smooth;
328                         else
329                                 item->keyframe_type = mlt_keyframe_linear;
330                         value = &p[2];
331                 }
332
333                 // Special case - frame < 0
334                 if ( item->frame < 0 )
335                         item->frame += mlt_animation_get_length( self );
336
337                 // Set remainder of string as item value.
338                 mlt_property_set_string( item->property, value );
339                 item->is_key = 1;
340         }
341         else
342         {
343                 error = 1;
344         }
345
346         return error;
347 }
348
349 /** Load an animation item for an absolute position.
350  *
351  * This performs interpolation if there is no keyframe at the \p position.
352  * \public \memberof mlt_animation_s
353  * \param self an animation
354  * \param item an already allocated animation item that will be filled in
355  * \param position the frame number for the point in time
356  * \return true if there was an error
357  */
358
359 int mlt_animation_get_item( mlt_animation self, mlt_animation_item item, int position )
360 {
361         int error = 0;
362         // Need to find the nearest keyframe to the position specifed
363         animation_node node = self->nodes;
364
365         // Iterate through the keyframes until we reach last or have
366         while ( node && node->next && position >= node->next->item.frame )
367                 node = node->next;
368
369         if ( node )
370         {
371                 item->keyframe_type = node->item.keyframe_type;
372
373                 // Position is before the first keyframe.
374                 if ( position < node->item.frame )
375                 {
376                         item->is_key = 0;
377                         mlt_property_pass( item->property, node->item.property );
378                 }
379                 // Item exists.
380                 else if ( position == node->item.frame )
381                 {
382                         item->is_key = node->item.is_key;
383                         mlt_property_pass( item->property, node->item.property );
384                 }
385                 // Position is after the last keyframe.
386                 else if ( !node->next )
387                 {
388                         item->is_key = 0;
389                         mlt_property_pass( item->property, node->item.property );
390                 }
391                 // Interpolation needed.
392                 else
393                 {
394                         double progress;
395                         mlt_property points[4];
396                         points[0] = node->prev? node->prev->item.property : node->item.property;
397                         points[1] = node->item.property;
398                         points[2] = node->next->item.property;
399                         points[3] = node->next->next? node->next->next->item.property : node->next->item.property;
400                         progress = position - node->item.frame;
401                         progress /= node->next->item.frame - node->item.frame;
402                         mlt_property_interpolate( item->property, points, progress,
403                                 self->fps, self->locale, item->keyframe_type );
404                         item->is_key = 0;
405                 }
406         }
407         else
408         {
409                 item->frame = item->is_key = 0;
410                 error = 1;
411         }
412         item->frame = position;
413
414         return error;
415 }
416
417 /** Insert an animation item.
418  *
419  * \public \memberof mlt_animation_s
420  * \param self an animation
421  * \param item an animation item
422  * \return true if there was an error
423  * \see mlt_animation_parse_item
424  */
425
426 int mlt_animation_insert( mlt_animation self, mlt_animation_item item )
427 {
428         int error = 0;
429         animation_node node = calloc( 1, sizeof( *node ) );
430         node->item.frame = item->frame;
431         node->item.is_key = 1;
432         node->item.keyframe_type = item->keyframe_type;
433         node->item.property = mlt_property_init();
434         mlt_property_pass( node->item.property, item->property );
435
436         // Determine if we need to insert or append to the list, or if it's a new list
437         if ( self->nodes )
438         {
439                 // Get the first item
440                 animation_node current = self->nodes;
441
442                 // Locate an existing nearby item
443                 while ( current->next && item->frame > current->item.frame )
444                         current = current->next;
445
446                 if ( item->frame < current->item.frame )
447                 {
448                         if ( current == self->nodes )
449                                 self->nodes = node;
450                         if ( current->prev )
451                                 current->prev->next = node;
452                         node->next = current;
453                         node->prev = current->prev;
454                         current->prev = node;
455                 }
456                 else if ( item->frame > current->item.frame )
457                 {
458                         if ( current->next )
459                                 current->next->prev = node;
460                         node->next = current->next;
461                         node->prev = current;
462                         current->next = node;
463                 }
464                 else
465                 {
466                         // Update matching node.
467                         current->item.frame = item->frame;
468                         current->item.is_key = 1;
469                         current->item.keyframe_type = item->keyframe_type;
470                         mlt_property_close( current->item.property );
471                         current->item.property = node->item.property;
472                         free( node );
473                 }
474         }
475         else
476         {
477                 // Set the first item
478                 self->nodes = node;
479         }
480
481         return error;
482 }
483
484 /** Remove the keyframe at the specified position.
485  *
486  * \public \memberof mlt_animation_s
487  * \param self an animation
488  * \param position the frame number of the animation node to remove
489  * \return true if there was an error
490  */
491
492 int mlt_animation_remove( mlt_animation self, int position )
493 {
494         int error = 1;
495         animation_node node = self->nodes;
496
497         while ( node && position != node->item.frame )
498                 node = node->next;
499
500         if ( node && position == node->item.frame )
501                 error = mlt_animation_drop( self, node );
502
503         return error;
504 }
505
506 /** Get the keyfame at the position or the next following.
507  *
508  * \public \memberof mlt_animation_s
509  * \param self an animation
510  * \param item an already allocated animation item which will be updated
511  * \param position the frame number at which to start looking for the next animation node
512  * \return true if there was an error
513  */
514
515 int mlt_animation_next_key( mlt_animation self, mlt_animation_item item, int position )
516 {
517         animation_node node = self->nodes;
518
519         while ( node && position > node->item.frame )
520                 node = node->next;
521
522         if ( node )
523         {
524                 item->frame = node->item.frame;
525                 item->is_key = node->item.is_key;
526                 item->keyframe_type = node->item.keyframe_type;
527                 mlt_property_pass( item->property, node->item.property );
528         }
529
530         return ( node == NULL );
531 }
532
533 /** Get the keyfame at the position or the next preceeding.
534  *
535  * \public \memberof mlt_animation_s
536  * \param self an animation
537  * \param item an already allocated animation item which will be updated
538  * \param position the frame number at which to start looking for the previous animation node
539  * \return true if there was an error
540  */
541
542 int mlt_animation_prev_key( mlt_animation self, mlt_animation_item item, int position )
543 {
544         animation_node node = self->nodes;
545
546         while ( node && node->next && position >= node->next->item.frame )
547                 node = node->next;
548
549         if ( node )
550         {
551                 item->frame = node->item.frame;
552                 item->is_key = node->item.is_key;
553                 item->keyframe_type = node->item.keyframe_type;
554                 mlt_property_pass( item->property, node->item.property );
555         }
556
557         return ( node == NULL );
558 }
559
560 /** Serialize a cut of the animation.
561  *
562  * The caller is responsible for free-ing the returned string.
563  * \public \memberof mlt_animation_s
564  * \param self an animation
565  * \param in the frame at which to start serializing animation nodes
566  * \param out the frame at which to stop serializing nodes
567  * \return a string representing the animation
568  */
569
570 char *mlt_animation_serialize_cut( mlt_animation self, int in, int out )
571 {
572         struct mlt_animation_item_s item;
573         char *ret = malloc( 1000 );
574         size_t used = 0;
575         size_t size = 1000;
576
577         item.property = mlt_property_init();
578         if ( in == -1 )
579                 in = 0;
580         if ( out == -1 )
581                 out = mlt_animation_get_length( self );
582
583         if ( ret )
584         {
585                 strcpy( ret, "" );
586
587                 item.frame = in;
588
589                 while ( 1 )
590                 {
591                         size_t item_len = 0;
592
593                         // If it's the first frame, then it's not necessarily a key
594                         if ( item.frame == in )
595                         {
596                                 if ( mlt_animation_get_item( self, &item, item.frame ) )
597                                         break;
598
599                                 // If the first keyframe is larger than the current position
600                                 // then do nothing here
601                                 if ( self->nodes->item.frame > item.frame )
602                                 {
603                                         item.frame ++;
604                                         continue;
605                                 }
606
607                                 // To ensure correct seeding
608                                 item.is_key = 1;
609                         }
610                         // Typically, we move from keyframe to keyframe
611                         else if ( item.frame < out )
612                         {
613                                 if ( mlt_animation_next_key( self, &item, item.frame ) )
614                                         break;
615
616                                 // Special case - crop at the out point
617                                 if ( item.frame > out )
618                                         mlt_animation_get_item( self, &item, out );
619                         }
620                         // We've handled the last keyframe
621                         else
622                         {
623                                 break;
624                         }
625
626                         // Determine length of string to be appended.
627                         if ( item.frame - in != 0 )
628                                 item_len += 20;
629                         if ( item.is_key )
630                                 item_len += strlen( mlt_property_get_string_l( item.property, self->locale ) );
631
632                         // Reallocate return string to be long enough.
633                         while ( used + item_len + 2 > size ) // +2 for ';' and NULL
634                         {
635                                 size += 1000;
636                                 ret = realloc( ret, size );
637                         }
638
639                         // Append item delimiter (;) if needed.
640                         if ( ret && used > 0 )
641                         {
642                                 used ++;
643                                 strcat( ret, ";" );
644                         }
645                         if ( ret )
646                         {
647                                 // Append keyframe time and keyframe/value delimiter (=).
648                                 const char *s;
649                                 switch (item.keyframe_type) {
650                                 case mlt_keyframe_discrete:
651                                         s = "|";
652                                         break;
653                                 case mlt_keyframe_smooth:
654                                         s = "~";
655                                         break;
656                                 default:
657                                         s = "";
658                                         break;
659                                 }
660                                 sprintf( ret + used, "%d%s=", item.frame - in, s );
661
662                                 // Append item value.
663                                 if ( item.is_key )
664                                         strcat( ret, mlt_property_get_string_l( item.property, self->locale ) );
665                                 used = strlen( ret );
666                         }
667                         item.frame ++;
668                 }
669         }
670         mlt_property_close( item.property );
671
672         return ret;
673 }
674
675 /** Serialize the animation.
676  *
677  * The caller is responsible for free-ing the returned string.
678  * \public \memberof mlt_animation_s
679  * \param self an animation
680  * \return a string representing the animation
681  */
682
683 char *mlt_animation_serialize( mlt_animation self )
684 {
685         char *ret = mlt_animation_serialize_cut( self, -1, -1 );
686         if ( ret )
687         {
688                 if ( self->data )
689                         free( self->data );
690                 self->data = ret;
691         }
692         return strdup( ret );
693 }
694
695 /** Close the animation and deallocate all of its resources.
696  *
697  * \public \memberof mlt_animation_s
698  * \param self the animation to destroy
699  */
700
701 void mlt_animation_close( mlt_animation self )
702 {
703         if ( self )
704         {
705                 mlt_animation_clean( self );
706                 free( self );
707         }
708 }