]> git.sesse.net Git - mlt/blob - src/framework/mlt_profile.c
d23cd4b89d4cfa357b0402c9861aa9c9a88cbdba
[mlt] / src / framework / mlt_profile.c
1 /**
2  * \file mlt_profile.c
3  * \brief video output definition
4  * \see mlt_profile_s
5  *
6  * Copyright (C) 2007-2009 Ushodaya Enterprises Limited
7  * \author Dan Dennedy <dan@dennedy.org>
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 #include "mlt.h"
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <libgen.h>
29 #include <math.h>
30
31
32 /** the default subdirectory of the datadir for holding profiles */
33 #define PROFILES_DIR "/profiles/"
34
35 /** Load a profile from the system folder.
36  *
37  * The environment variable MLT_PROFILES_PATH overrides the default \p PROFILES_DIR.
38  *
39  * \private \memberof mlt_profile_s
40  * \param name the name of a profile settings file located in the standard location or
41  * the full path name to a profile settings file
42  * \return a profile or NULL on error
43  */
44
45 static mlt_profile mlt_profile_select( const char *name )
46 {
47         char *filename = NULL;
48         const char *prefix = getenv( "MLT_PROFILES_PATH" );
49         mlt_properties properties = mlt_properties_load( name );
50         mlt_profile profile = NULL;
51
52         // Try to load from file specification
53         if ( properties && mlt_properties_get_int( properties, "width" ) )
54         {
55                 filename = calloc( 1, strlen( name ) + 1 );
56         }
57         // Load from $datadir/mlt/profiles
58         else if ( prefix == NULL )
59         {
60                 prefix = mlt_environment( "MLT_DATA" );
61                 filename = calloc( 1, strlen( prefix ) + strlen( PROFILES_DIR ) + strlen( name ) + 1 );
62                 strcpy( filename, prefix );
63                 strcat( filename, PROFILES_DIR );
64         }
65         // Use environment variable instead
66         else
67         {
68                 filename = calloc( 1, strlen( prefix ) + strlen( name ) + 2 );
69                 strcpy( filename, prefix );
70                 if ( filename[ strlen( filename ) - 1 ] != '/' )
71                         filename[ strlen( filename ) ] = '/';
72         }
73
74         // Finish loading
75         strcat( filename, name );
76         profile = mlt_profile_load_file( filename );
77
78         // Cleanup
79         mlt_properties_close( properties );
80         free( filename );
81
82         return profile;
83 }
84
85 /** Construct a profile.
86  *
87  * This will never return NULL as it uses the dv_pal settings as hard-coded fallback default.
88  *
89  * \public \memberof mlt_profile_s
90  * \param name the name of a profile settings file located in the standard location or
91  * the full path name to a profile settings file
92  * \return a profile
93  */
94
95 mlt_profile mlt_profile_init( const char *name )
96 {
97         mlt_profile profile = NULL;
98
99         // Explicit profile by name gets priority over environment variables
100         if ( name )
101                 profile = mlt_profile_select( name );
102
103         // Try to load by environment variable
104         if ( profile == NULL )
105         {
106                 // MLT_PROFILE is preferred environment variable
107                 if ( getenv( "MLT_PROFILE" ) )
108                         profile = mlt_profile_select( getenv( "MLT_PROFILE" ) );
109                 // MLT_NORMALISATION backwards compatibility
110                 else if ( getenv( "MLT_NORMALISATION" ) && strcmp( getenv( "MLT_NORMALISATION" ), "PAL" ) )
111                         profile = mlt_profile_select( "dv_ntsc" );
112                 else
113                         profile = mlt_profile_select( "dv_pal" );
114
115                 // If still not loaded (no profile files), default to PAL
116                 if ( profile == NULL )
117                 {
118                         profile = calloc( 1, sizeof( struct mlt_profile_s ) );
119                         if ( profile )
120                         {
121                                 mlt_environment_set( "MLT_PROFILE", "dv_pal" );
122                                 profile->description = strdup( "PAL 4:3 DV or DVD" );
123                                 profile->frame_rate_num = 25;
124                                 profile->frame_rate_den = 1;
125                                 profile->width = 720;
126                                 profile->height = 576;
127                                 profile->progressive = 0;
128                                 profile->sample_aspect_num = 16;
129                                 profile->sample_aspect_den = 15;
130                                 profile->display_aspect_num = 4;
131                                 profile->display_aspect_den = 3;
132                                 profile->colorspace = 601;
133                         }
134                 }
135         }
136         return profile;
137 }
138
139 static void set_mlt_normalisation( const char *profile_name )
140 {
141         if ( profile_name )
142         {
143                 if ( strstr( profile_name, "_ntsc" ) ||
144                      strstr( profile_name, "_60" ) ||
145                      strstr( profile_name, "_5994" ) ||
146                      strstr( profile_name, "_2997" ) ||
147                      strstr( profile_name, "_30" ) )
148                 {
149                         mlt_environment_set( "MLT_NORMALISATION", "NTSC" );
150                 }
151                 else if ( strstr( profile_name, "_pal" ) ||
152                           strstr( profile_name, "_50" ) ||
153                           strstr( profile_name, "_25" ) )
154                 {
155                         mlt_environment_set( "MLT_NORMALISATION", "PAL" );
156                 }
157         }
158 }
159
160 /** Load a profile from specific file.
161  *
162  * \public \memberof mlt_profile_s
163  * \param file the full path name to a properties file
164  * \return a profile or NULL on error
165  */
166
167 mlt_profile mlt_profile_load_file( const char *file )
168 {
169         mlt_profile profile = NULL;
170
171         // Load the profile as properties
172         mlt_properties properties = mlt_properties_load( file );
173         if ( properties )
174         {
175                 // Simple check if the profile is valid
176                 if ( mlt_properties_get_int( properties, "width" ) )
177                 {
178                         profile = mlt_profile_load_properties( properties );
179
180                         // Set MLT_PROFILE to basename
181                         char *filename = strdup( file );
182                         mlt_environment_set( "MLT_PROFILE", basename( filename ) );
183                         set_mlt_normalisation( basename( filename ) );
184                         free( filename );
185                 }
186                 mlt_properties_close( properties );
187         }
188
189         // Set MLT_NORMALISATION to appease legacy modules
190         char *profile_name = mlt_environment( "MLT_PROFILE" );
191         set_mlt_normalisation( profile_name );
192         return profile;
193 }
194
195 /** Load a profile from a properties object.
196  *
197  * \public \memberof mlt_profile_s
198  * \param properties a properties list
199  * \return a profile or NULL if out of memory
200  */
201
202 mlt_profile mlt_profile_load_properties( mlt_properties properties )
203 {
204         mlt_profile profile = calloc( 1, sizeof( struct mlt_profile_s ) );
205         if ( profile )
206         {
207                 if ( mlt_properties_get( properties, "name" ) )
208                         mlt_environment_set( "MLT_PROFILE", mlt_properties_get( properties, "name" ) );
209                 if ( mlt_properties_get( properties, "description" ) )
210                         profile->description = strdup( mlt_properties_get( properties, "description" ) );
211                 profile->frame_rate_num = mlt_properties_get_int( properties, "frame_rate_num" );
212                 profile->frame_rate_den = mlt_properties_get_int( properties, "frame_rate_den" );
213                 profile->width = mlt_properties_get_int( properties, "width" );
214                 profile->height = mlt_properties_get_int( properties, "height" );
215                 profile->progressive = mlt_properties_get_int( properties, "progressive" );
216                 profile->sample_aspect_num = mlt_properties_get_int( properties, "sample_aspect_num" );
217                 profile->sample_aspect_den = mlt_properties_get_int( properties, "sample_aspect_den" );
218                 profile->display_aspect_num = mlt_properties_get_int( properties, "display_aspect_num" );
219                 profile->display_aspect_den = mlt_properties_get_int( properties, "display_aspect_den" );
220                 profile->colorspace = mlt_properties_get_int( properties, "colorspace" );
221         }
222         return profile;
223 }
224
225 /** Load an anonymous profile from string.
226  *
227  * \public \memberof mlt_profile_s
228  * \param string a newline-delimited list of properties as name=value pairs
229  * \return a profile or NULL if out of memory
230  */
231
232 mlt_profile mlt_profile_load_string( const char *string )
233 {
234         mlt_properties properties = mlt_properties_new();
235         mlt_profile profile = NULL;
236
237         if ( properties )
238         {
239                 const char *p = string;
240                 while ( p )
241                 {
242                         if ( strcmp( p, "" ) && p[ 0 ] != '#' )
243                                 mlt_properties_parse( properties, p );
244                         p = strchr( p, '\n' );
245                         if ( p ) p++;
246                 }
247                 profile = mlt_profile_load_properties( properties );
248                 mlt_properties_close( properties );
249         }
250         return profile;
251 }
252
253 /** Get the video frame rate as a floating point value.
254  *
255  * \public \memberof mlt_profile_s
256  * @param profile a profile
257  * @return the frame rate
258  */
259
260 double mlt_profile_fps( mlt_profile profile )
261 {
262         if ( profile )
263                 return ( double ) profile->frame_rate_num / profile->frame_rate_den;
264         else
265                 return 0;
266 }
267
268 /** Get the sample aspect ratio as a floating point value.
269  *
270  * \public \memberof mlt_profile_s
271  * \param profile a profile
272  * \return the pixel aspect ratio
273  */
274
275 double mlt_profile_sar( mlt_profile profile )
276 {
277         if ( profile )
278                 return ( double ) profile->sample_aspect_num / profile->sample_aspect_den;
279         else
280                 return 0;
281 }
282
283 /** Get the display aspect ratio as floating point value.
284  *
285  * \public \memberof mlt_profile_s
286  * \param profile a profile
287  * \return the image aspect ratio
288  */
289
290 double mlt_profile_dar( mlt_profile profile )
291 {
292         if ( profile )
293                 return ( double ) profile->display_aspect_num / profile->display_aspect_den;
294         else
295                 return 0;
296 }
297
298 /** Free up the global profile resources.
299  *
300  * \public \memberof mlt_profile_s
301  * \param profile a profile
302  */
303
304 void mlt_profile_close( mlt_profile profile )
305 {
306         if ( profile )
307         {
308                 if ( profile->description )
309                         free( profile->description );
310                 profile->description = NULL;
311                 free( profile );
312                 profile = NULL;
313         }
314 }
315
316 /** Make a copy of a profile.
317   *
318   * \public \memberof mlt_profile_s
319   * \param profile the profile to clone
320   * \return a copy of the profile
321   */
322
323 mlt_profile mlt_profile_clone( mlt_profile profile )
324 {
325         mlt_profile clone = NULL;
326
327         if ( profile )
328         {
329                 clone = calloc( 1, sizeof( *profile ) );
330                 if ( clone )
331                 {
332                         memcpy( clone, profile, sizeof( *profile ) );
333                         clone->description = strdup( profile->description );
334                 }
335         }
336         return clone;
337 }
338
339
340 /** Get the list of profiles.
341  *
342  * The caller MUST close the returned properties object!
343  * Each entry in the list is keyed on its name, and its value is another
344  * properties object that contains the attributes of the profile.
345  * \public \memberof mlt_profile_s
346  * \return a list of profiles
347  */
348
349 mlt_properties mlt_profile_list( )
350 {
351         char *filename = NULL;
352         const char *prefix = getenv( "MLT_PROFILES_PATH" );
353         mlt_properties properties = mlt_properties_new();
354         mlt_properties dir = mlt_properties_new();
355         int sort = 1;
356         const char *wildcard = NULL;
357         int i;
358
359         // Load from $datadir/mlt/profiles if no env var
360         if ( prefix == NULL )
361         {
362                 prefix = mlt_environment( "MLT_DATA" );
363                 filename = calloc( 1, strlen( prefix ) + strlen( PROFILES_DIR ) + 1 );
364                 strcpy( filename, prefix );
365                 strcat( filename, PROFILES_DIR );
366                 prefix = filename;
367         }
368
369         mlt_properties_dir_list( dir, prefix, wildcard, sort );
370
371         for ( i = 0; i < mlt_properties_count( dir ); i++ )
372         {
373                 char *filename = mlt_properties_get_value( dir, i );
374                 char *profile_name = basename( filename );
375                 if ( profile_name[0] != '.' && strcmp( profile_name, "Makefile" ) &&
376                      profile_name[ strlen( profile_name ) - 1 ] != '~' )
377                 {
378                         mlt_properties profile = mlt_properties_load( filename );
379                         if ( profile )
380                         {
381                                 mlt_properties_set_data( properties, profile_name, profile, 0,
382                                         (mlt_destructor) mlt_properties_close, NULL );
383                         }
384                 }
385         }
386         mlt_properties_close( dir );
387         if ( filename )
388                 free( filename );
389
390         return properties;
391 }
392
393 /** Update the profile using the attributes of a producer.
394  *
395  * Use this to make an "auto-profile." Typically, you need to re-open the producer
396  * after you use this because some producers (e.g. avformat) adjust their framerate
397  * to that of the profile used when you created it.
398  * \public \memberof mlt_profile_s
399  * \param profile the profile to update
400  * \param producer the producer to inspect
401  */
402
403 void mlt_profile_from_producer( mlt_profile profile, mlt_producer producer )
404 {
405         mlt_frame fr = NULL;
406         uint8_t *buffer = NULL;
407         mlt_image_format fmt = mlt_image_none;
408         mlt_properties p;
409         int w = profile->width;
410         int h = profile->height;
411
412         if ( ! mlt_service_get_frame( MLT_PRODUCER_SERVICE(producer), &fr, 0 ) && fr )
413         {
414                 if ( ! mlt_frame_get_image( fr, &buffer, &fmt, &w, &h, 0 ) )
415                 {
416                         // Some source properties are not exposed until after the first get_image call.
417                         mlt_frame_close( fr );
418                         mlt_service_get_frame( MLT_PRODUCER_SERVICE(producer), &fr, 0 );
419                         p = MLT_FRAME_PROPERTIES( fr );
420 //                      mlt_properties_dump(p, stderr);
421                         if ( mlt_properties_get_int( p, "meta.media.frame_rate_den" ) &&
422                                  mlt_properties_get_int( p, "meta.media.sample_aspect_den" ) )
423                         {
424                                 profile->width = mlt_properties_get_int( p, "meta.media.width" );
425                                 profile->height = mlt_properties_get_int( p, "meta.media.height" );
426                                 profile->progressive = mlt_properties_get_int( p, "meta.media.progressive" );
427                                 profile->frame_rate_num = mlt_properties_get_int( p, "meta.media.frame_rate_num" );
428                                 profile->frame_rate_den = mlt_properties_get_int( p, "meta.media.frame_rate_den" );
429                                 // AVCHD is mis-reported as double frame rate.
430                                 if ( profile->progressive == 0 && (
431                                      profile->frame_rate_num / profile->frame_rate_den == 50 ||
432                                      profile->frame_rate_num / profile->frame_rate_den == 59 ) )
433                                         profile->frame_rate_num /= 2;
434                                 profile->sample_aspect_num = mlt_properties_get_int( p, "meta.media.sample_aspect_num" );
435                                 profile->sample_aspect_den = mlt_properties_get_int( p, "meta.media.sample_aspect_den" );
436                                 profile->colorspace = mlt_properties_get_int( p, "meta.media.colorspace" );
437                                 profile->display_aspect_num = lrint( (double) profile->sample_aspect_num * profile->width
438                                         / profile->sample_aspect_den );
439                                 profile->display_aspect_den = profile->height;
440                                 free( profile->description );
441                                 profile->description = strdup( "automatic" );
442                                 profile->is_explicit = 0;
443                         }
444                 }
445         }
446         mlt_frame_close( fr );
447         mlt_producer_seek( producer, 0 );
448 }