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