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