]> git.sesse.net Git - mlt/blob - src/framework/mlt_geometry.c
Skip empty keyframes when parsing geometry.
[mlt] / src / framework / mlt_geometry.c
1 /*
2  * mlt_geometry.c -- provides the geometry API
3  * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "mlt_geometry.h"
22 #include "mlt_tokeniser.h"
23 #include "mlt_factory.h"
24 #include "mlt_profile.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 typedef struct geometry_item_s
31 {
32         struct mlt_geometry_item_s data;
33         struct geometry_item_s *next, *prev;
34 }
35 *geometry_item;
36
37 typedef struct
38 {
39         char *data;
40         int length;
41         int nw;
42         int nh;
43         geometry_item item;
44 }
45 geometry_s, *geometry;
46
47 // Create a new geometry structure
48 mlt_geometry mlt_geometry_init( )
49 {
50         mlt_geometry self = calloc( 1, sizeof( struct mlt_geometry_s ) );
51         if ( self != NULL )
52         {
53                 self->local = calloc( 1, sizeof( geometry_s ) );
54                 if ( self->local != NULL )
55                 {
56                         geometry g = self->local;
57                         g->nw = 720;
58                         g->nh = 576;
59                 }
60                 else
61                 {
62                         free( self );
63                         self = NULL;
64                 }
65         }
66         return self;
67 }
68
69 /** A linear step
70 */
71
72 static inline double linearstep( double start, double end, double position, int length )
73 {
74         double o = ( end - start ) / length;
75         return start + position * o;
76 }
77
78 void mlt_geometry_interpolate( mlt_geometry self )
79 {
80         geometry g = self->local;
81
82         // Parse of all items to ensure unspecified keys are calculated correctly
83         if ( g->item != NULL )
84         {
85                 int i = 0;
86                 for ( i = 0; i < 5; i ++ )
87                 {
88                         geometry_item current = g->item;
89                         while( current != NULL )
90                         {
91                                 int fixed = current->data.f[ i ];
92                                 if ( !fixed )
93                                 {
94                                         geometry_item prev = current->prev;
95                                         geometry_item next = current->next;
96
97                                         double prev_value = 0;
98                                         double next_value = 0;
99                                         double value = 0;
100
101                                         while( prev != NULL && !prev->data.f[ i ] ) prev = prev->prev;
102                                         while( next != NULL && !next->data.f[ i ] ) next = next->next;
103
104                                         switch( i )
105                                         {
106                                                 case 0:
107                                                         if ( prev ) prev_value = prev->data.x;
108                                                         if ( next ) next_value = next->data.x;
109                                                         break;
110                                                 case 1:
111                                                         if ( prev ) prev_value = prev->data.y;
112                                                         if ( next ) next_value = next->data.y;
113                                                         break;
114                                                 case 2:
115                                                         if ( prev ) prev_value = prev->data.w;
116                                                         if ( next ) next_value = next->data.w;
117                                                         break;
118                                                 case 3:
119                                                         if ( prev ) prev_value = prev->data.h;
120                                                         if ( next ) next_value = next->data.h;
121                                                         break;
122                                                 case 4:
123                                                         if ( prev ) prev_value = prev->data.mix;
124                                                         if ( next ) next_value = next->data.mix;
125                                                         break;
126                                         }
127
128                                         // This should never happen
129                                         if ( prev == NULL )
130                                                 current->data.f[ i ] = 1;
131                                         else if ( next == NULL )
132                                                 value = prev_value;
133                                         else 
134                                                 value = linearstep( prev_value, next_value, current->data.frame - prev->data.frame, next->data.frame - prev->data.frame );
135
136                                         switch( i )
137                                         {
138                                                 case 0: current->data.x = value; break;
139                                                 case 1: current->data.y = value; break;
140                                                 case 2: current->data.w = value; break;
141                                                 case 3: current->data.h = value; break;
142                                                 case 4: current->data.mix = value; break;
143                                         }
144                                 }
145
146                                 // Move to the next item
147                                 current = current->next;
148                         }
149                 }
150         }
151 }
152
153 static int mlt_geometry_drop( mlt_geometry self, geometry_item item )
154 {
155         geometry g = self->local;
156
157         if ( item == g->item )
158         {
159                 g->item = item->next;
160                 if ( g->item != NULL )
161                         g->item->prev = NULL;
162                 // To ensure correct seeding, ensure all values are fixed
163                 if ( g->item != NULL )
164                 {
165                         g->item->data.f[0] = 1;
166                         g->item->data.f[1] = 1;
167                         g->item->data.f[2] = 1;
168                         g->item->data.f[3] = 1;
169                         g->item->data.f[4] = 1;
170                 }
171         }
172         else if ( item->next != NULL && item->prev != NULL )
173         {
174                 item->prev->next = item->next;
175                 item->next->prev = item->prev;
176         }
177         else if ( item->next != NULL )
178         {
179                 item->next->prev = item->prev;
180         }
181         else if ( item->prev != NULL )
182         {
183                 item->prev->next = item->next;
184         }
185
186         free( item );
187
188         return 0;
189 }
190
191 static void mlt_geometry_clean( mlt_geometry self )
192 {
193         geometry g = self->local;
194         if ( g->data )
195                 free( g->data );
196         g->data = NULL;
197         while( g->item )
198                 mlt_geometry_drop( self, g->item );
199 }
200
201 // Parse the geometry specification for a given length and normalised width/height (-1 for default)
202 // data is constructed as: [frame=]X/Y:WxH[:mix][!][;[frame=]X/Y:WxH[:mix][!]]*
203 // and X, Y, W and H can have trailing % chars to indicate percentage of normalised size
204 // Append a pair's value with ! to enable distort.
205 int mlt_geometry_parse( mlt_geometry self, char *data, int length, int nw, int nh )
206 {
207         int i = 0;
208
209         // Create a tokeniser
210         mlt_tokeniser tokens = mlt_tokeniser_init( );
211
212         // Get the local/private structure
213         geometry g = self->local;
214
215         // Clean the existing geometry
216         mlt_geometry_clean( self );
217
218         // Update the info on the data
219         if ( length != -1 )
220                 g->length = length;
221         if ( nw != -1 )
222                 g->nw = nw;
223         if ( nh != -1 )
224                 g->nh = nh;
225         if ( data != NULL )
226                 g->data = strdup( data );
227
228         // Tokenise
229         if ( data != NULL )
230                 mlt_tokeniser_parse_new( tokens, data, ";" );
231
232         // Iterate through each token
233         for ( i = 0; i < mlt_tokeniser_count( tokens ); i ++ )
234         {
235                 struct mlt_geometry_item_s item;
236                 char *value = mlt_tokeniser_get_string( tokens, i );
237
238                 // If no data in keyframe, drop it (trailing semicolon)
239                 if ( value == NULL || !strcmp( value, "" ) )
240                         continue;
241
242                 // Set item to 0
243                 memset( &item, 0, sizeof( struct mlt_geometry_item_s ) );
244
245                 // Now parse the item
246                 mlt_geometry_parse_item( self, &item, value );
247
248                 // Now insert into place
249                 mlt_geometry_insert( self, &item );
250         }
251         mlt_geometry_interpolate( self );
252
253         // Remove the tokeniser
254         mlt_tokeniser_close( tokens );
255
256         // ???
257         return 0;
258 }
259
260 // Conditionally refresh in case of a change
261 int mlt_geometry_refresh( mlt_geometry self, char *data, int length, int nw, int nh )
262 {
263         geometry g = self->local;
264         int changed = ( length != -1 && length != g->length );
265         changed = changed || ( nw != -1 && nw != g->nw );
266         changed = changed || ( nh != -1 && nh != g->nh );
267         changed = changed || ( data != NULL && ( g->data == NULL || strcmp( data, g->data ) ) );
268         if ( changed )
269                 return mlt_geometry_parse( self, data, length, nw, nh );
270         return -1;
271 }
272
273 int mlt_geometry_get_length( mlt_geometry self )
274 {
275         // Get the local/private structure
276         geometry g = self->local;
277
278         // return the length
279         return g->length;
280 }
281
282 void mlt_geometry_set_length( mlt_geometry self, int length )
283 {
284         // Get the local/private structure
285         geometry g = self->local;
286
287         // set the length
288         g->length = length;
289 }
290
291 int mlt_geometry_parse_item( mlt_geometry self, mlt_geometry_item item, char *value )
292 {
293         int ret = 0;
294
295         // Get the local/private structure
296         geometry g = self->local;
297
298         if ( value != NULL && strcmp( value, "" ) )
299         {
300                 char *p = strchr( value, '=' );
301                 int count = 0;
302                 double temp;
303
304                 // Determine if a position has been specified
305                 if ( p != NULL )
306                 {
307                         temp = atof( value );
308                         if ( temp > -1 && temp < 1 )
309                                 item->frame = temp * g->length;
310                         else
311                                 item->frame = temp;
312                         value = p + 1;
313                 }
314
315                 // Special case - frame < 0
316                 if ( item->frame < 0 )
317                         item->frame += g->length;
318
319                 // Obtain the current value at this position - self allows new
320                 // frames to be created which don't specify all values
321                 mlt_geometry_fetch( self, item, item->frame );
322
323                 // Special case - when an empty string is specified, all values are fixed
324                 // TODO: Check if this is logical - it's convenient, but it's also odd...
325                 if ( !*value )
326                 {
327                         item->f[0] = 1;
328                         item->f[1] = 1;
329                         item->f[2] = 1;
330                         item->f[3] = 1;
331                         item->f[4] = 1;
332                 }
333
334                 // Iterate through the remainder of value
335                 while( *value )
336                 {
337                         // Get the value
338                         temp = strtod( value, &p );
339
340                         // Check if a value was specified
341                         if ( p != value )
342                         {
343                                 // Handle the % case
344                                 if ( *p == '%' )
345                                 {
346                                         if ( count == 0 || count == 2 )
347                                                 temp *= g->nw / 100.0;
348                                         else if ( count == 1 || count == 3 )
349                                                 temp *= g->nh / 100.0;
350                                         p ++;
351                                 }
352
353                                 // Special case - distort token
354                                 if ( *p == '!' || *p == '*' )
355                                 {
356                                         p ++;
357                                         item->distort = 1;
358                                 }
359
360                                 // Actually, we don't care about the delimiter at all..
361                                 if ( *p ) p ++;
362
363                                 // Assign to the item
364                                 switch( count )
365                                 {
366                                         case 0: item->x = temp; item->f[0] = 1; break;
367                                         case 1: item->y = temp; item->f[1] = 1; break;
368                                         case 2: item->w = temp; item->f[2] = 1; break;
369                                         case 3: item->h = temp; item->f[3] = 1; break;
370                                         case 4: item->mix = temp; item->f[4] = 1; break;
371                                 }
372                         }
373                         else
374                         {
375                                 p ++;
376                         }
377
378                         // Update the value pointer
379                         value = p;
380                         count ++;
381                 }
382         }
383         else
384         {
385                 ret = 1;
386         }
387
388         return ret;
389 }
390
391 // Fetch a geometry item for an absolute position
392 int mlt_geometry_fetch( mlt_geometry self, mlt_geometry_item item, float position )
393 {
394         // Get the local geometry
395         geometry g = self->local;
396
397         // Need to find the nearest key to the position specifed
398         geometry_item key = g->item;
399
400         // Iterate through the keys until we reach last or have 
401         while( key != NULL && key->next != NULL && position >= key->next->data.frame )
402                 key = key->next;
403
404         if ( key != NULL )
405         {
406                 // Position is situated before the first key - all zeroes
407                 if ( position < key->data.frame )
408                 {
409                         memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
410                         item->mix = 100;
411                 }
412                 // Position is a key itself - no iterpolation need
413                 else if ( position == key->data.frame )
414                 {
415                         memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
416                 }
417                 // Position is after the last key - no interpolation, but not a key frame
418                 else if ( key->next == NULL )
419                 {
420                         memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
421                         item->key = 0;
422                         item->f[ 0 ] = 0;
423                         item->f[ 1 ] = 0;
424                         item->f[ 2 ] = 0;
425                         item->f[ 3 ] = 0;
426                         item->f[ 4 ] = 0;
427                 }
428                 // Interpolation is needed - position > key and there is a following key
429                 else
430                 {
431                         item->key = 0;
432                         item->frame = position;
433                         position -= key->data.frame;
434                         item->x = linearstep( key->data.x, key->next->data.x, position, key->next->data.frame - key->data.frame );
435                         item->y = linearstep( key->data.y, key->next->data.y, position, key->next->data.frame - key->data.frame );
436                         item->w = linearstep( key->data.w, key->next->data.w, position, key->next->data.frame - key->data.frame );
437                         item->h = linearstep( key->data.h, key->next->data.h, position, key->next->data.frame - key->data.frame );
438                         item->mix = linearstep( key->data.mix, key->next->data.mix, position, key->next->data.frame - key->data.frame );
439                         item->distort = key->data.distort;
440                         position += key->data.frame;
441                 }
442
443                 item->frame = position;
444         }
445         else
446         {
447                 memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
448                 item->frame = position;
449                 item->mix = 100;
450         }
451
452         return key == NULL;
453 }
454
455 // Specify a geometry item at an absolute position
456 int mlt_geometry_insert( mlt_geometry self, mlt_geometry_item item )
457 {
458         // Get the local/private geometry structure
459         geometry g = self->local;
460
461         // Create a new local item (this may be removed if a key already exists at self position)
462         geometry_item gi = calloc( 1, sizeof( struct geometry_item_s ) );
463         memcpy( &gi->data, item, sizeof( struct mlt_geometry_item_s ) );
464         gi->data.key = 1;
465
466         // Determine if we need to insert or append to the list, or if it's a new list
467         if ( g->item != NULL )
468         {
469                 // Get the first item
470                 geometry_item place = g->item;
471
472                 // Locate an existing nearby item
473                 while ( place->next != NULL && item->frame > place->data.frame )
474                         place = place->next;
475
476                 if ( item->frame < place->data.frame )
477                 {
478                         if ( place == g->item )
479                                 g->item = gi;
480                         if ( place->prev )
481                                 place->prev->next = gi;
482                         gi->next = place;
483                         gi->prev = place->prev;
484                         place->prev = gi;
485                 }
486                 else if ( item->frame > place->data.frame )
487                 {
488                         if ( place->next )
489                                 place->next->prev = gi;
490                         gi->next = place->next;
491                         gi->prev = place;
492                         place->next = gi;
493                 }
494                 else
495                 {
496                         memcpy( &place->data, &gi->data, sizeof( struct mlt_geometry_item_s ) );
497                         free( gi );
498                 }
499         }
500         else
501         {
502                 // Set the first item
503                 g->item = gi;
504
505                 // To ensure correct seeding, ensure all values are fixed
506                 g->item->data.f[0] = 1;
507                 g->item->data.f[1] = 1;
508                 g->item->data.f[2] = 1;
509                 g->item->data.f[3] = 1;
510                 g->item->data.f[4] = 1;
511         }
512
513         // TODO: Error checking
514         return 0;
515 }
516
517 // Remove the key at the specified position
518 int mlt_geometry_remove( mlt_geometry self, int position )
519 {
520         int ret = 1;
521
522         // Get the local/private geometry structure
523         geometry g = self->local;
524
525         // Get the first item
526         geometry_item place = g->item;
527
528         while( place != NULL && position != place->data.frame )
529                 place = place->next;
530
531         if ( place != NULL && position == place->data.frame )
532                 ret = mlt_geometry_drop( self, place );
533
534         return ret;
535 }
536
537 // Get the key at the position or the next following
538 int mlt_geometry_next_key( mlt_geometry self, mlt_geometry_item item, int position )
539 {
540         // Get the local/private geometry structure
541         geometry g = self->local;
542
543         // Get the first item
544         geometry_item place = g->item;
545
546         while( place != NULL && position > place->data.frame )
547                 place = place->next;
548
549         if ( place != NULL )
550                 memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
551
552         return place == NULL;
553 }
554
555 // Get the key at the position or the previous key
556 int mlt_geometry_prev_key( mlt_geometry self, mlt_geometry_item item, int position )
557 {
558         // Get the local/private geometry structure
559         geometry g = self->local;
560
561         // Get the first item
562         geometry_item place = g->item;
563
564         while( place != NULL && place->next != NULL && position >= place->next->data.frame )
565                 place = place->next;
566
567         if ( place != NULL )
568                 memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
569
570         return place == NULL;
571 }
572
573 #define ISINT(x) ( (x) == (int64_t) (x) )
574 #define PICKFMT(x) ( ISINT(x) ? "%.0f" : "%f" )
575
576 char *mlt_geometry_serialise_cut( mlt_geometry self, int in, int out )
577 {
578         geometry g = self->local;
579         struct mlt_geometry_item_s item;
580         char *ret = malloc( 1000 );
581         int used = 0;
582         int size = 1000;
583
584         if ( in == -1 )
585                 in = 0;
586         if ( out == -1 )
587                 out = mlt_geometry_get_length( self );
588
589         if ( ret != NULL )
590         {
591                 char temp[ 100 ];
592
593                 strcpy( ret, "" );
594
595                 item.frame = in;
596
597                 while( 1 )
598                 {
599                         strcpy( temp, "" );
600
601                         // If it's the first frame, then it's not necessarily a key
602                         if ( item.frame == in )
603                         {
604                                 if ( mlt_geometry_fetch( self, &item, item.frame ) )
605                                         break;
606
607                                 // If the first key is larger than the current position
608                                 // then do nothing here
609                                 if ( g->item->data.frame > item.frame )
610                                 {
611                                         item.frame ++;
612                                         continue;
613                                 }
614
615                                 // To ensure correct seeding, ensure all values are fixed
616                                 item.f[0] = 1;
617                                 item.f[1] = 1;
618                                 item.f[2] = 1;
619                                 item.f[3] = 1;
620                                 item.f[4] = 1;
621                         }
622                         // Typically, we move from key to key
623                         else if ( item.frame < out )
624                         {
625                                 if ( mlt_geometry_next_key( self, &item, item.frame ) )
626                                         break;
627
628                                 // Special case - crop at the out point
629                                 if ( item.frame > out )
630                                         mlt_geometry_fetch( self, &item, out );
631                         }
632                         // We've handled the last key
633                         else
634                         {
635                                 break;
636                         }
637
638                         if ( item.frame - in != 0 )
639                                 sprintf( temp, "%d=", item.frame - in );
640
641                         if ( item.f[0] )
642                                 sprintf( temp + strlen( temp ), PICKFMT( item.x ), item.x );
643                         if ( item.f[1] ) {
644                                 strcat( temp, "/" );
645                                 sprintf( temp + strlen( temp ), PICKFMT( item.y ), item.y );
646                         }
647                         if ( item.f[2] ) {
648                                 strcat( temp, ":" );
649                                 sprintf( temp + strlen( temp ), PICKFMT( item.w ), item.w );
650                         }
651                         if ( item.f[3] ) {
652                                 strcat( temp, "x" );
653                                 sprintf( temp + strlen( temp ), PICKFMT( item.h ), item.h );
654                         }
655                         if ( item.f[4] ) {
656                                 strcat( temp, ":" );
657                                 sprintf( temp + strlen( temp ), PICKFMT( item.mix ), item.mix );
658                         }
659
660                         if ( used + strlen( temp ) + 2 > size ) // +2 for ';' and NULL
661                         {
662                                 size += 1000;
663                                 ret = realloc( ret, size );
664                         }
665
666                         if ( ret != NULL && used != 0 )
667                         {
668                                 used ++;
669                                 strcat( ret, ";" );
670                         }
671                         if ( ret != NULL )
672                         {
673                                 used += strlen( temp );
674                                 strcat( ret, temp );
675                         }
676
677                         item.frame ++;
678                 }
679         }
680
681         return ret;
682 }
683
684 // Serialise the current geometry
685 char *mlt_geometry_serialise( mlt_geometry self )
686 {
687         geometry g = self->local;
688         char *ret = mlt_geometry_serialise_cut( self, 0, g->length );
689         if ( ret )
690         {
691                 if ( g->data )
692                         free( g->data );
693                 g->data = ret;
694         }
695         return ret;
696 }
697
698 // Close the geometry
699 void mlt_geometry_close( mlt_geometry self )
700 {
701         if ( self != NULL )
702         {
703                 mlt_geometry_clean( self );
704                 free( self->local );
705                 free( self );
706         }
707 }
708
709