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