]> git.sesse.net Git - mlt/blob - src/framework/mlt_property.c
3cc9f8b8bdfc3151916067fb1f11b9c714bdde5e
[mlt] / src / framework / mlt_property.c
1 /**
2  * \file mlt_property.c
3  * \brief Property class definition
4  * \see mlt_property_s
5  *
6  * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
7  * \author Charles Yates <charles.yates@pandora.be>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 // For strtod_l
25 #ifndef _GNU_SOURCE
26 #define _GNU_SOURCE
27 #endif
28
29 #include "mlt_property.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <locale.h>
35 #include <pthread.h>
36
37
38 /** Bit pattern used internally to indicated representations available.
39 */
40
41 typedef enum
42 {
43         mlt_prop_none = 0,    //!< not set
44         mlt_prop_int = 1,     //!< set as an integer
45         mlt_prop_string = 2,  //!< set as string or already converted to string
46         mlt_prop_position = 4,//!< set as a position
47         mlt_prop_double = 8,  //!< set as a floating point
48         mlt_prop_data = 16,   //!< set as opaque binary
49         mlt_prop_int64 = 32   //!< set as a 64-bit integer
50 }
51 mlt_property_type;
52
53 /** \brief Property class
54  *
55  * A property is like a variant or dynamic type. They are used for many things
56  * in MLT, but in particular they are the parameter mechanism for the plugins.
57  */
58
59 struct mlt_property_s
60 {
61         /// Stores a bit pattern of types available for this property
62         mlt_property_type types;
63
64         /// Atomic type handling
65         int prop_int;
66         mlt_position prop_position;
67         double prop_double;
68         int64_t prop_int64;
69
70         /// String handling
71         char *prop_string;
72
73         /// Generic type handling
74         void *data;
75         int length;
76         mlt_destructor destructor;
77         mlt_serialiser serialiser;
78
79         pthread_mutex_t mutex;
80 };
81
82 /** Construct a property and initialize it
83  * \public \memberof mlt_property_s
84  */
85
86 mlt_property mlt_property_init( )
87 {
88         mlt_property self = malloc( sizeof( struct mlt_property_s ) );
89         if ( self != NULL )
90         {
91                 self->types = 0;
92                 self->prop_int = 0;
93                 self->prop_position = 0;
94                 self->prop_double = 0;
95                 self->prop_int64 = 0;
96                 self->prop_string = NULL;
97                 self->data = NULL;
98                 self->length = 0;
99                 self->destructor = NULL;
100                 self->serialiser = NULL;
101                 pthread_mutex_init( &self->mutex, NULL );
102         }
103         return self;
104 }
105
106 /** Clear (0/null) a property.
107  *
108  * Frees up any associated resources in the process.
109  * \private \memberof mlt_property_s
110  * \param self a property
111  */
112
113 static inline void mlt_property_clear( mlt_property self )
114 {
115         // Special case data handling
116         if ( self->types & mlt_prop_data && self->destructor != NULL )
117                 self->destructor( self->data );
118
119         // Special case string handling
120         if ( self->types & mlt_prop_string )
121                 free( self->prop_string );
122
123         // Wipe stuff
124         self->types = 0;
125         self->prop_int = 0;
126         self->prop_position = 0;
127         self->prop_double = 0;
128         self->prop_int64 = 0;
129         self->prop_string = NULL;
130         self->data = NULL;
131         self->length = 0;
132         self->destructor = NULL;
133         self->serialiser = NULL;
134 }
135
136 /** Set the property to an integer value.
137  *
138  * \public \memberof mlt_property_s
139  * \param self a property
140  * \param value an integer
141  * \return false
142  */
143
144 int mlt_property_set_int( mlt_property self, int value )
145 {
146         pthread_mutex_lock( &self->mutex );
147         mlt_property_clear( self );
148         self->types = mlt_prop_int;
149         self->prop_int = value;
150         pthread_mutex_unlock( &self->mutex );
151         return 0;
152 }
153
154 /** Set the property to a floating point value.
155  *
156  * \public \memberof mlt_property_s
157  * \param self a property
158  * \param value a double precision floating point value
159  * \return false
160  */
161
162 int mlt_property_set_double( mlt_property self, double value )
163 {
164         pthread_mutex_lock( &self->mutex );
165         mlt_property_clear( self );
166         self->types = mlt_prop_double;
167         self->prop_double = value;
168         pthread_mutex_unlock( &self->mutex );
169         return 0;
170 }
171
172 /** Set the property to a position value.
173  *
174  * Position is a relative time value in frame units.
175  * \public \memberof mlt_property_s
176  * \param self a property
177  * \param value a position value
178  * \return false
179  */
180
181 int mlt_property_set_position( mlt_property self, mlt_position value )
182 {
183         pthread_mutex_lock( &self->mutex );
184         mlt_property_clear( self );
185         self->types = mlt_prop_position;
186         self->prop_position = value;
187         pthread_mutex_unlock( &self->mutex );
188         return 0;
189 }
190
191 /** Set the property to a string value.
192  *
193  * This makes a copy of the string you supply so you do not need to track
194  * a new reference to it.
195  * \public \memberof mlt_property_s
196  * \param self a property
197  * \param value the string to copy to the property
198  * \return true if it failed
199  */
200
201 int mlt_property_set_string( mlt_property self, const char *value )
202 {
203         pthread_mutex_lock( &self->mutex );
204         if ( value != self->prop_string )
205         {
206                 mlt_property_clear( self );
207                 self->types = mlt_prop_string;
208                 if ( value != NULL )
209                         self->prop_string = strdup( value );
210         }
211         else
212         {
213                 self->types = mlt_prop_string;
214         }
215         pthread_mutex_unlock( &self->mutex );
216         return self->prop_string == NULL;
217 }
218
219 /** Set the property to a 64-bit integer value.
220  *
221  * \public \memberof mlt_property_s
222  * \param self a property
223  * \param value a 64-bit integer
224  * \return false
225  */
226
227 int mlt_property_set_int64( mlt_property self, int64_t value )
228 {
229         pthread_mutex_lock( &self->mutex );
230         mlt_property_clear( self );
231         self->types = mlt_prop_int64;
232         self->prop_int64 = value;
233         pthread_mutex_unlock( &self->mutex );
234         return 0;
235 }
236
237 /** Set a property to an opaque binary value.
238  *
239  * This does not make a copy of the data. You can use a Properties object
240  * with its reference tracking and the destructor function to control
241  * the lifetime of the data. Otherwise, pass NULL for the destructor
242  * function and control the lifetime yourself.
243  * \public \memberof mlt_property_s
244  * \param self a property
245  * \param value an opaque pointer
246  * \param length the number of bytes pointed to by value (optional)
247  * \param destructor a function to use to destroy this binary data (optional, assuming you manage the resource)
248  * \param serialiser a function to use to convert this binary data to a string (optional)
249  * \return false
250  */
251
252 int mlt_property_set_data( mlt_property self, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser )
253 {
254         pthread_mutex_lock( &self->mutex );
255         if ( self->data == value )
256                 self->destructor = NULL;
257         mlt_property_clear( self );
258         self->types = mlt_prop_data;
259         self->data = value;
260         self->length = length;
261         self->destructor = destructor;
262         self->serialiser = serialiser;
263         pthread_mutex_unlock( &self->mutex );
264         return 0;
265 }
266
267 /** Convert a base 10 or base 16 string to an integer.
268  *
269  * The string must begin with '0x' to be interpreted as hexadecimal.
270  * Otherwise, it is interpreted as base 10.
271  * If the string begins with '#' it is interpreted as a hexadecimal color value
272  * in the form RRGGBB or AARRGGBB. Color values that begin with '0x' are
273  * always in the form RRGGBBAA where the alpha components are not optional.
274  * Applications and services should expect the binary color value in bytes to
275  * be in the following order: RGBA. This means they will have to cast the int
276  * to an unsigned int. This is especially important when they need to shift
277  * right to obtain RGB without alpha in order to make it do a logical instead
278  * of arithmetic shift.
279  *
280  * \private \memberof mlt_property_s
281  * \param value a string to convert
282  * \return the resultant integer
283  */
284 static inline int mlt_property_atoi( const char *value )
285 {
286         if ( value == NULL )
287                 return 0;
288         // Parse a hex color value as #RRGGBB or #AARRGGBB.
289         if ( value[0] == '#' )
290         {
291                 unsigned int rgb = strtoul( value + 1, NULL, 16 );
292                 unsigned int alpha = ( strlen( value ) > 7 ) ? ( rgb >> 24 ) : 0xff;
293                 return ( rgb << 8 ) | alpha;
294         }
295         // Do hex and decimal explicitly to avoid decimal value with leading zeros
296         // interpreted as octal.
297         else if ( value[0] == '0' && value[1] == 'x' )
298         {
299                 return strtoul( value + 2, NULL, 16 );
300         }
301         else
302         {
303                 return strtol( value, NULL, 10 );
304         }
305 }
306
307 /** Get the property as an integer.
308  *
309  * \public \memberof mlt_property_s
310  * \param self a property
311  * \return an integer value
312  */
313
314 int mlt_property_get_int( mlt_property self )
315 {
316         if ( self->types & mlt_prop_int )
317                 return self->prop_int;
318         else if ( self->types & mlt_prop_double )
319                 return ( int )self->prop_double;
320         else if ( self->types & mlt_prop_position )
321                 return ( int )self->prop_position;
322         else if ( self->types & mlt_prop_int64 )
323                 return ( int )self->prop_int64;
324         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
325                 return mlt_property_atoi( self->prop_string );
326         return 0;
327 }
328
329 /** Get the property as a floating point.
330  *
331  * \public \memberof mlt_property_s
332  * \param self a property
333  * \return a floating point value
334  */
335
336 double mlt_property_get_double( mlt_property self )
337 {
338         if ( self->types & mlt_prop_double )
339                 return self->prop_double;
340         else if ( self->types & mlt_prop_int )
341                 return ( double )self->prop_int;
342         else if ( self->types & mlt_prop_position )
343                 return ( double )self->prop_position;
344         else if ( self->types & mlt_prop_int64 )
345                 return ( double )self->prop_int64;
346         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
347                 return atof( self->prop_string );
348         return 0;
349 }
350
351 /** Get the property (with locale) as a floating point.
352  *
353  * \public \memberof mlt_property_s
354  * \param self a property
355  * \param locale the locale to use for this conversion
356  * \return a floating point value
357  */
358
359 double mlt_property_get_double_l( mlt_property self, locale_t locale )
360 {
361         if ( self->types & mlt_prop_double )
362                 return self->prop_double;
363         else if ( self->types & mlt_prop_int )
364                 return ( double )self->prop_int;
365         else if ( self->types & mlt_prop_position )
366                 return ( double )self->prop_position;
367         else if ( self->types & mlt_prop_int64 )
368                 return ( double )self->prop_int64;
369 #if defined(__GLIBC__) || defined(__DARWIN__)
370         else if ( locale && ( self->types & mlt_prop_string ) && self->prop_string )
371                 return strtod_l( self->prop_string, NULL, locale );
372 #endif
373         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
374                 return strtod( self->prop_string, NULL );
375         return 0;
376 }
377
378 /** Get the property as a position.
379  *
380  * A position is an offset time in terms of frame units.
381  * \public \memberof mlt_property_s
382  * \param self a property
383  * \return the position in frames
384  */
385
386 mlt_position mlt_property_get_position( mlt_property self )
387 {
388         if ( self->types & mlt_prop_position )
389                 return self->prop_position;
390         else if ( self->types & mlt_prop_int )
391                 return ( mlt_position )self->prop_int;
392         else if ( self->types & mlt_prop_double )
393                 return ( mlt_position )self->prop_double;
394         else if ( self->types & mlt_prop_int64 )
395                 return ( mlt_position )self->prop_int64;
396         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
397                 return ( mlt_position )atol( self->prop_string );
398         return 0;
399 }
400
401 /** Convert a string to a 64-bit integer.
402  *
403  * If the string begins with '0x' it is interpreted as a hexadecimal value.
404  * \private \memberof mlt_property_s
405  * \param value a string
406  * \return a 64-bit integer
407  */
408
409 static inline int64_t mlt_property_atoll( const char *value )
410 {
411         if ( value == NULL )
412                 return 0;
413         else if ( value[0] == '0' && value[1] == 'x' )
414                 return strtoll( value + 2, NULL, 16 );
415         else
416                 return strtoll( value, NULL, 10 );
417 }
418
419 /** Get the property as a signed integer.
420  *
421  * \public \memberof mlt_property_s
422  * \param self a property
423  * \return a 64-bit integer
424  */
425
426 int64_t mlt_property_get_int64( mlt_property self )
427 {
428         if ( self->types & mlt_prop_int64 )
429                 return self->prop_int64;
430         else if ( self->types & mlt_prop_int )
431                 return ( int64_t )self->prop_int;
432         else if ( self->types & mlt_prop_double )
433                 return ( int64_t )self->prop_double;
434         else if ( self->types & mlt_prop_position )
435                 return ( int64_t )self->prop_position;
436         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
437                 return mlt_property_atoll( self->prop_string );
438         return 0;
439 }
440
441 /** Get the property as a string.
442  *
443  * The caller is not responsible for deallocating the returned string!
444  * The string is deallocated when the Property is closed.
445  * This tries its hardest to convert the property to string including using
446  * a serialization function for binary data, if supplied.
447  * \public \memberof mlt_property_s
448  * \param self a property
449  * \return a string representation of the property or NULL if failed
450  */
451
452 char *mlt_property_get_string( mlt_property self )
453 {
454         // Construct a string if need be
455         if ( ! ( self->types & mlt_prop_string ) )
456         {
457                 pthread_mutex_lock( &self->mutex );
458                 if ( self->types & mlt_prop_int )
459                 {
460                         self->types |= mlt_prop_string;
461                         self->prop_string = malloc( 32 );
462                         sprintf( self->prop_string, "%d", self->prop_int );
463                 }
464                 else if ( self->types & mlt_prop_double )
465                 {
466                         self->types |= mlt_prop_string;
467                         self->prop_string = malloc( 32 );
468                         sprintf( self->prop_string, "%f", self->prop_double );
469                 }
470                 else if ( self->types & mlt_prop_position )
471                 {
472                         self->types |= mlt_prop_string;
473                         self->prop_string = malloc( 32 );
474                         sprintf( self->prop_string, "%d", (int)self->prop_position );
475                 }
476                 else if ( self->types & mlt_prop_int64 )
477                 {
478                         self->types |= mlt_prop_string;
479                         self->prop_string = malloc( 32 );
480                         sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
481                 }
482                 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
483                 {
484                         self->types |= mlt_prop_string;
485                         self->prop_string = self->serialiser( self->data, self->length );
486                 }
487                 pthread_mutex_unlock( &self->mutex );
488         }
489
490         // Return the string (may be NULL)
491         return self->prop_string;
492 }
493
494 /** Get the property as a string (with locale).
495  *
496  * The caller is not responsible for deallocating the returned string!
497  * The string is deallocated when the Property is closed.
498  * This tries its hardest to convert the property to string including using
499  * a serialization function for binary data, if supplied.
500  * \public \memberof mlt_property_s
501  * \param self a property
502  * \param locale the locale to use for this conversion
503  * \return a string representation of the property or NULL if failed
504  */
505
506 char *mlt_property_get_string_l( mlt_property self, locale_t locale )
507 {
508         // Optimization for no locale
509         if ( !locale )
510                 return mlt_property_get_string( self );
511
512         // Construct a string if need be
513         if ( ! ( self->types & mlt_prop_string ) )
514         {
515                 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
516                 // Save the current locale
517 #if defined(__DARWIN__)
518                 const char *localename = querylocale( LC_NUMERIC, locale );
519 #elif defined(__GLIBC__)
520                 const char *localename = locale->__names[ LC_NUMERIC ];
521 #else
522                 // TODO: not yet sure what to do on other platforms
523                 const char *localename = "";
524 #endif
525                 // Protect damaging the global locale from a temporary locale on another thread.
526                 pthread_mutex_lock( &self->mutex );
527
528                 // Get the current locale
529                 char *orig_localename = strdup( setlocale( LC_NUMERIC, NULL ) );
530
531                 // Set the new locale
532                 setlocale( LC_NUMERIC, localename );
533
534                 if ( self->types & mlt_prop_int )
535                 {
536                         self->types |= mlt_prop_string;
537                         self->prop_string = malloc( 32 );
538                         sprintf( self->prop_string, "%d", self->prop_int );
539                 }
540                 else if ( self->types & mlt_prop_double )
541                 {
542                         self->types |= mlt_prop_string;
543                         self->prop_string = malloc( 32 );
544                         sprintf( self->prop_string, "%f", self->prop_double );
545                 }
546                 else if ( self->types & mlt_prop_position )
547                 {
548                         self->types |= mlt_prop_string;
549                         self->prop_string = malloc( 32 );
550                         sprintf( self->prop_string, "%d", (int)self->prop_position );
551                 }
552                 else if ( self->types & mlt_prop_int64 )
553                 {
554                         self->types |= mlt_prop_string;
555                         self->prop_string = malloc( 32 );
556                         sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
557                 }
558                 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
559                 {
560                         self->types |= mlt_prop_string;
561                         self->prop_string = self->serialiser( self->data, self->length );
562                 }
563                 // Restore the current locale
564                 setlocale( LC_NUMERIC, orig_localename );
565                 free( orig_localename );
566                 pthread_mutex_unlock( &self->mutex );
567         }
568
569         // Return the string (may be NULL)
570         return self->prop_string;
571 }
572
573 /** Get the binary data from a property.
574  *
575  * This only works if you previously put binary data into the property.
576  * This does not return a copy of the data; it returns a pointer to it.
577  * If you supplied a destructor function when setting the binary data,
578  * the destructor is used when the Property is closed to free the memory.
579  * Therefore, only free the returned pointer if you did not supply a
580  * destructor function.
581  * \public \memberof mlt_property_s
582  * \param self a property
583  * \param[out] length the size of the binary object in bytes (optional)
584  * \return an opaque data pointer or NULL if not available
585  */
586
587 void *mlt_property_get_data( mlt_property self, int *length )
588 {
589         // Assign length if not NULL
590         if ( length != NULL )
591                 *length = self->length;
592
593         // Return the data (note: there is no conversion here)
594         return self->data;
595 }
596
597 /** Destroy a property and free all related resources.
598  *
599  * \public \memberof mlt_property_s
600  * \param self a property
601  */
602
603 void mlt_property_close( mlt_property self )
604 {
605         mlt_property_clear( self );
606         pthread_mutex_destroy( &self->mutex );
607         free( self );
608 }
609
610 /** Copy a property.
611  *
612  * A Property holding binary data only copies the data if a serialiser
613  * function was supplied when you set the Property.
614  * \public \memberof mlt_property_s
615  * \author Zach <zachary.drew@gmail.com>
616  * \param self a property
617  * \param that another property
618  */
619 void mlt_property_pass( mlt_property self, mlt_property that )
620 {
621         pthread_mutex_lock( &self->mutex );
622         mlt_property_clear( self );
623
624         self->types = that->types;
625
626         if ( self->types & mlt_prop_int64 )
627                 self->prop_int64 = that->prop_int64;
628         else if ( self->types & mlt_prop_int )
629                 self->prop_int = that->prop_int;
630         else if ( self->types & mlt_prop_double )
631                 self->prop_double = that->prop_double;
632         else if ( self->types & mlt_prop_position )
633                 self->prop_position = that->prop_position;
634         if ( self->types & mlt_prop_string )
635         {
636                 if ( that->prop_string != NULL )
637                         self->prop_string = strdup( that->prop_string );
638         }
639         else if ( self->types & mlt_prop_data && self->serialiser != NULL )
640         {
641                 self->types = mlt_prop_string;
642                 self->prop_string = self->serialiser( self->data, self->length );
643         }
644         pthread_mutex_unlock( &self->mutex );
645 }