]> git.sesse.net Git - mlt/blob - src/framework/mlt_property.c
Cleanup Brian's service metadata contribution.
[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         mlt_property_clear( self );
147         self->types = mlt_prop_int;
148         self->prop_int = value;
149         return 0;
150 }
151
152 /** Set the property to a floating point value.
153  *
154  * \public \memberof mlt_property_s
155  * \param self a property
156  * \param value a double precision floating point value
157  * \return false
158  */
159
160 int mlt_property_set_double( mlt_property self, double value )
161 {
162         mlt_property_clear( self );
163         self->types = mlt_prop_double;
164         self->prop_double = value;
165         return 0;
166 }
167
168 /** Set the property to a position value.
169  *
170  * Position is a relative time value in frame units.
171  * \public \memberof mlt_property_s
172  * \param self a property
173  * \param value a position value
174  * \return false
175  */
176
177 int mlt_property_set_position( mlt_property self, mlt_position value )
178 {
179         mlt_property_clear( self );
180         self->types = mlt_prop_position;
181         self->prop_position = value;
182         return 0;
183 }
184
185 /** Set the property to a string value.
186  *
187  * This makes a copy of the string you supply so you do not need to track
188  * a new reference to it.
189  * \public \memberof mlt_property_s
190  * \param self a property
191  * \param value the string to copy to the property
192  * \return true if it failed
193  */
194
195 int mlt_property_set_string( mlt_property self, const char *value )
196 {
197         if ( value != self->prop_string )
198         {
199                 mlt_property_clear( self );
200                 self->types = mlt_prop_string;
201                 if ( value != NULL )
202                         self->prop_string = strdup( value );
203         }
204         else
205         {
206                 self->types = mlt_prop_string;
207         }
208         return self->prop_string == NULL;
209 }
210
211 /** Set the property to a 64-bit integer value.
212  *
213  * \public \memberof mlt_property_s
214  * \param self a property
215  * \param value a 64-bit integer
216  * \return false
217  */
218
219 int mlt_property_set_int64( mlt_property self, int64_t value )
220 {
221         mlt_property_clear( self );
222         self->types = mlt_prop_int64;
223         self->prop_int64 = value;
224         return 0;
225 }
226
227 /** Set a property to an opaque binary value.
228  *
229  * This does not make a copy of the data. You can use a Properties object
230  * with its reference tracking and the destructor function to control
231  * the lifetime of the data. Otherwise, pass NULL for the destructor
232  * function and control the lifetime yourself.
233  * \public \memberof mlt_property_s
234  * \param self a property
235  * \param value an opaque pointer
236  * \param length the number of bytes pointed to by value (optional)
237  * \param destructor a function to use to destroy this binary data (optional, assuming you manage the resource)
238  * \param serialiser a function to use to convert this binary data to a string (optional)
239  * \return false
240  */
241
242 int mlt_property_set_data( mlt_property self, void *value, int length, mlt_destructor destructor, mlt_serialiser serialiser )
243 {
244         if ( self->data == value )
245                 self->destructor = NULL;
246         mlt_property_clear( self );
247         self->types = mlt_prop_data;
248         self->data = value;
249         self->length = length;
250         self->destructor = destructor;
251         self->serialiser = serialiser;
252         return 0;
253 }
254
255 /** Convert a base 10 or base 16 string to an integer.
256  *
257  * The string must begin with '0x' to be interpreted as hexadecimal.
258  * Otherwise, it is interpreted as base 10.
259  * If the string begins with '#' it is interpreted as a hexadecimal color value
260  * in the form RRGGBB or AARRGGBB. Color values that begin with '0x' are
261  * always in the form RRGGBBAA where the alpha components are not optional.
262  * Applications and services should expect the binary color value in bytes to
263  * be in the following order: RGBA. This means they will have to cast the int
264  * to an unsigned int. This is especially important when they need to shift
265  * right to obtain RGB without alpha in order to make it do a logical instead
266  * of arithmetic shift.
267  *
268  * \private \memberof mlt_property_s
269  * \param value a string to convert
270  * \return the resultant integer
271  */
272 static inline int mlt_property_atoi( const char *value )
273 {
274         if ( value == NULL )
275                 return 0;
276         // Parse a hex color value as #RRGGBB or #AARRGGBB.
277         if ( value[0] == '#' )
278         {
279                 unsigned int rgb = strtoul( value + 1, NULL, 16 );
280                 unsigned int alpha = ( strlen( value ) > 7 ) ? ( rgb >> 24 ) : 0xff;
281                 return ( rgb << 8 ) | alpha;
282         }
283         // Do hex and decimal explicitly to avoid decimal value with leading zeros
284         // interpreted as octal.
285         else if ( value[0] == '0' && value[1] == 'x' )
286         {
287                 return strtoul( value + 2, NULL, 16 );
288         }
289         else
290         {
291                 return strtol( value, NULL, 10 );
292         }
293 }
294
295 /** Get the property as an integer.
296  *
297  * \public \memberof mlt_property_s
298  * \param self a property
299  * \return an integer value
300  */
301
302 int mlt_property_get_int( mlt_property self )
303 {
304         if ( self->types & mlt_prop_int )
305                 return self->prop_int;
306         else if ( self->types & mlt_prop_double )
307                 return ( int )self->prop_double;
308         else if ( self->types & mlt_prop_position )
309                 return ( int )self->prop_position;
310         else if ( self->types & mlt_prop_int64 )
311                 return ( int )self->prop_int64;
312         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
313                 return mlt_property_atoi( self->prop_string );
314         return 0;
315 }
316
317 /** Get the property as a floating point.
318  *
319  * \public \memberof mlt_property_s
320  * \param self a property
321  * \return a floating point value
322  */
323
324 double mlt_property_get_double( mlt_property self )
325 {
326         if ( self->types & mlt_prop_double )
327                 return self->prop_double;
328         else if ( self->types & mlt_prop_int )
329                 return ( double )self->prop_int;
330         else if ( self->types & mlt_prop_position )
331                 return ( double )self->prop_position;
332         else if ( self->types & mlt_prop_int64 )
333                 return ( double )self->prop_int64;
334         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
335                 return atof( self->prop_string );
336         return 0;
337 }
338
339 /** Get the property (with locale) as a floating point.
340  *
341  * \public \memberof mlt_property_s
342  * \param self a property
343  * \param locale the locale to use for this conversion
344  * \return a floating point value
345  */
346
347 double mlt_property_get_double_l( mlt_property self, locale_t locale )
348 {
349         if ( self->types & mlt_prop_double )
350                 return self->prop_double;
351         else if ( self->types & mlt_prop_int )
352                 return ( double )self->prop_int;
353         else if ( self->types & mlt_prop_position )
354                 return ( double )self->prop_position;
355         else if ( self->types & mlt_prop_int64 )
356                 return ( double )self->prop_int64;
357 #if defined(__GLIBC__) || defined(__DARWIN__)
358         else if ( locale && ( self->types & mlt_prop_string ) && self->prop_string )
359                 return strtod_l( self->prop_string, NULL, locale );
360 #endif
361         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
362                 return strtod( self->prop_string, NULL );
363         return 0;
364 }
365
366 /** Get the property as a position.
367  *
368  * A position is an offset time in terms of frame units.
369  * \public \memberof mlt_property_s
370  * \param self a property
371  * \return the position in frames
372  */
373
374 mlt_position mlt_property_get_position( mlt_property self )
375 {
376         if ( self->types & mlt_prop_position )
377                 return self->prop_position;
378         else if ( self->types & mlt_prop_int )
379                 return ( mlt_position )self->prop_int;
380         else if ( self->types & mlt_prop_double )
381                 return ( mlt_position )self->prop_double;
382         else if ( self->types & mlt_prop_int64 )
383                 return ( mlt_position )self->prop_int64;
384         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
385                 return ( mlt_position )atol( self->prop_string );
386         return 0;
387 }
388
389 /** Convert a string to a 64-bit integer.
390  *
391  * If the string begins with '0x' it is interpreted as a hexadecimal value.
392  * \private \memberof mlt_property_s
393  * \param value a string
394  * \return a 64-bit integer
395  */
396
397 static inline int64_t mlt_property_atoll( const char *value )
398 {
399         if ( value == NULL )
400                 return 0;
401         else if ( value[0] == '0' && value[1] == 'x' )
402                 return strtoll( value + 2, NULL, 16 );
403         else
404                 return strtoll( value, NULL, 10 );
405 }
406
407 /** Get the property as a signed integer.
408  *
409  * \public \memberof mlt_property_s
410  * \param self a property
411  * \return a 64-bit integer
412  */
413
414 int64_t mlt_property_get_int64( mlt_property self )
415 {
416         if ( self->types & mlt_prop_int64 )
417                 return self->prop_int64;
418         else if ( self->types & mlt_prop_int )
419                 return ( int64_t )self->prop_int;
420         else if ( self->types & mlt_prop_double )
421                 return ( int64_t )self->prop_double;
422         else if ( self->types & mlt_prop_position )
423                 return ( int64_t )self->prop_position;
424         else if ( ( self->types & mlt_prop_string ) && self->prop_string )
425                 return mlt_property_atoll( self->prop_string );
426         return 0;
427 }
428
429 /** Get the property as a string.
430  *
431  * The caller is not responsible for deallocating the returned string!
432  * The string is deallocated when the Property is closed.
433  * This tries its hardest to convert the property to string including using
434  * a serialization function for binary data, if supplied.
435  * \public \memberof mlt_property_s
436  * \param self a property
437  * \return a string representation of the property or NULL if failed
438  */
439
440 char *mlt_property_get_string( mlt_property self )
441 {
442         // Construct a string if need be
443         if ( ! ( self->types & mlt_prop_string ) )
444         {
445                 if ( self->types & mlt_prop_int )
446                 {
447                         self->types |= mlt_prop_string;
448                         self->prop_string = malloc( 32 );
449                         sprintf( self->prop_string, "%d", self->prop_int );
450                 }
451                 else if ( self->types & mlt_prop_double )
452                 {
453                         self->types |= mlt_prop_string;
454                         self->prop_string = malloc( 32 );
455                         sprintf( self->prop_string, "%f", self->prop_double );
456                 }
457                 else if ( self->types & mlt_prop_position )
458                 {
459                         self->types |= mlt_prop_string;
460                         self->prop_string = malloc( 32 );
461                         sprintf( self->prop_string, "%d", (int)self->prop_position );
462                 }
463                 else if ( self->types & mlt_prop_int64 )
464                 {
465                         self->types |= mlt_prop_string;
466                         self->prop_string = malloc( 32 );
467                         sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
468                 }
469                 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
470                 {
471                         self->types |= mlt_prop_string;
472                         self->prop_string = self->serialiser( self->data, self->length );
473                 }
474         }
475
476         // Return the string (may be NULL)
477         return self->prop_string;
478 }
479
480 /** Get the property as a string (with locale).
481  *
482  * The caller is not responsible for deallocating the returned string!
483  * The string is deallocated when the Property is closed.
484  * This tries its hardest to convert the property to string including using
485  * a serialization function for binary data, if supplied.
486  * \public \memberof mlt_property_s
487  * \param self a property
488  * \param locale the locale to use for this conversion
489  * \return a string representation of the property or NULL if failed
490  */
491
492 char *mlt_property_get_string_l( mlt_property self, locale_t locale )
493 {
494         // Optimization for no locale
495         if ( !locale )
496                 return mlt_property_get_string( self );
497
498         // Construct a string if need be
499         if ( ! ( self->types & mlt_prop_string ) )
500         {
501                 // TODO: when glibc gets sprintf_l, start using it! For now, hack on setlocale.
502                 // Save the current locale
503 #if defined(__DARWIN__)
504                 const char *localename = querylocale( LC_NUMERIC, locale );
505 #elif defined(__GLIBC__)
506                 const char *localename = locale->__names[ LC_NUMERIC ];
507 #else
508                 // TODO: not yet sure what to do on other platforms
509                 const char *localename = "";
510 #endif
511                 // Protect damaging the global locale from a temporary locale on another thread.
512                 pthread_mutex_lock( &self->mutex );
513
514                 // Get the current locale
515                 const char *orig_localename = setlocale( LC_NUMERIC, NULL );
516
517                 // Set the new locale
518                 setlocale( LC_NUMERIC, localename );
519
520                 if ( self->types & mlt_prop_int )
521                 {
522                         self->types |= mlt_prop_string;
523                         self->prop_string = malloc( 32 );
524                         sprintf( self->prop_string, "%d", self->prop_int );
525                 }
526                 else if ( self->types & mlt_prop_double )
527                 {
528                         self->types |= mlt_prop_string;
529                         self->prop_string = malloc( 32 );
530                         sprintf( self->prop_string, "%f", self->prop_double );
531                 }
532                 else if ( self->types & mlt_prop_position )
533                 {
534                         self->types |= mlt_prop_string;
535                         self->prop_string = malloc( 32 );
536                         sprintf( self->prop_string, "%d", (int)self->prop_position );
537                 }
538                 else if ( self->types & mlt_prop_int64 )
539                 {
540                         self->types |= mlt_prop_string;
541                         self->prop_string = malloc( 32 );
542                         sprintf( self->prop_string, "%"PRId64, self->prop_int64 );
543                 }
544                 else if ( self->types & mlt_prop_data && self->serialiser != NULL )
545                 {
546                         self->types |= mlt_prop_string;
547                         self->prop_string = self->serialiser( self->data, self->length );
548                 }
549                 // Restore the current locale
550                 setlocale( LC_NUMERIC, orig_localename );
551                 pthread_mutex_unlock( &self->mutex );
552         }
553
554         // Return the string (may be NULL)
555         return self->prop_string;
556 }
557
558 /** Get the binary data from a property.
559  *
560  * This only works if you previously put binary data into the property.
561  * This does not return a copy of the data; it returns a pointer to it.
562  * If you supplied a destructor function when setting the binary data,
563  * the destructor is used when the Property is closed to free the memory.
564  * Therefore, only free the returned pointer if you did not supply a
565  * destructor function.
566  * \public \memberof mlt_property_s
567  * \param self a property
568  * \param[out] length the size of the binary object in bytes (optional)
569  * \return an opaque data pointer or NULL if not available
570  */
571
572 void *mlt_property_get_data( mlt_property self, int *length )
573 {
574         // Assign length if not NULL
575         if ( length != NULL )
576                 *length = self->length;
577
578         // Return the data (note: there is no conversion here)
579         return self->data;
580 }
581
582 /** Destroy a property and free all related resources.
583  *
584  * \public \memberof mlt_property_s
585  * \param self a property
586  */
587
588 void mlt_property_close( mlt_property self )
589 {
590         mlt_property_clear( self );
591         pthread_mutex_destroy( &self->mutex );
592         free( self );
593 }
594
595 /** Copy a property.
596  *
597  * A Property holding binary data only copies the data if a serialiser
598  * function was supplied when you set the Property.
599  * \public \memberof mlt_property_s
600  * \author Zach <zachary.drew@gmail.com>
601  * \param self a property
602  * \param that another property
603  */
604 void mlt_property_pass( mlt_property self, mlt_property that )
605 {
606         mlt_property_clear( self );
607
608         self->types = that->types;
609
610         if ( self->types & mlt_prop_int64 )
611                 self->prop_int64 = that->prop_int64;
612         else if ( self->types & mlt_prop_int )
613                 self->prop_int = that->prop_int;
614         else if ( self->types & mlt_prop_double )
615                 self->prop_double = that->prop_double;
616         else if ( self->types & mlt_prop_position )
617                 self->prop_position = that->prop_position;
618         else if ( self->types & mlt_prop_string )
619         {
620                 if ( that->prop_string != NULL )
621                         self->prop_string = strdup( that->prop_string );
622         }
623         else if ( self->types & mlt_prop_data && self->serialiser != NULL )
624         {
625                 self->types = mlt_prop_string;
626                 self->prop_string = self->serialiser( self->data, self->length );
627         }
628 }