From b9947f19e3bb0b9ee0a26aec912f646fdf667307 Mon Sep 17 00:00:00 2001 From: Dan Dennedy Date: Sat, 28 Aug 2010 23:10:34 -0700 Subject: [PATCH] Add an automatic profile feature to melt. Here are the main use cases this feature provides: - Given a regular (non-mlt-xml) media file, melt reads the media attributes and generates an equivalent MLT profile. This makes it easier to transcode without changing or specifying resolution, aspect, and framerate. - Given a MLT XML file containing a profile attribute or element, melt loads the specified profile. A composition typically contains profile- without you having to remember. - Given a MLT XML containing a profile but also specifying a -profile option, melt automatically uses the 'consumer' producer with the requested profiles. This is similar to the above case, but when explicitly choosing a profile different than the composition one should use the consumer producer. This just makes melt smarter and more automatic. --- src/melt/melt.c | 108 +++++++++++++++++++++++++++++-- src/modules/melt/producer_melt.c | 14 +++- 2 files changed, 114 insertions(+), 8 deletions(-) diff --git a/src/melt/melt.c b/src/melt/melt.c index a60e80f4..629392db 100644 --- a/src/melt/melt.c +++ b/src/melt/melt.c @@ -1,7 +1,8 @@ /* * melt.c -- MLT command line utility * Copyright (C) 2002-2010 Ushodaya Enterprises Limited - * Author: Charles Yates + * Authors: Charles Yates + * Dan Dennedy * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +27,7 @@ #include #include #include +#include #include @@ -162,15 +164,18 @@ static void transport_action( mlt_producer producer, char *value ) static mlt_consumer create_consumer( mlt_profile profile, char *id ) { - char *arg = id != NULL ? strchr( id, ':' ) : NULL; + char *myid = id ? strdup( id ) : NULL; + char *arg = myid ? strchr( myid, ':' ) : NULL; if ( arg != NULL ) *arg ++ = '\0'; - mlt_consumer consumer = mlt_factory_consumer( profile, id, arg ); + mlt_consumer consumer = mlt_factory_consumer( profile, myid, arg ); if ( consumer != NULL ) { mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); mlt_properties_set_data( properties, "transport_callback", transport_action, 0, NULL, NULL ); } + if ( myid ) + free( myid ); return consumer; } @@ -269,6 +274,40 @@ static void transport( mlt_producer producer, mlt_consumer consumer ) } } +static void guess_profile( mlt_producer melt, mlt_profile profile ) +{ + mlt_frame fr = NULL; + uint8_t *buffer; + mlt_image_format fmt = mlt_image_yuv422; + mlt_properties p; + int w, h; + + if ( ! mlt_service_get_frame( MLT_PRODUCER_SERVICE(melt), &fr, 0 ) && fr ) + { + if ( ! mlt_frame_get_image( fr, &buffer, &fmt, &w, &h, 0 ) ) + { + // Some source properties are not exposed until after the first get_image call. + mlt_frame_close( fr ); + mlt_service_get_frame( MLT_PRODUCER_SERVICE(melt), &fr, 0 ); + p = MLT_FRAME_PROPERTIES( fr ); + if ( mlt_properties_get_int( p, "meta.media.width" ) ) + { + profile->width = mlt_properties_get_int( p, "meta.media.width" ); + profile->height = mlt_properties_get_int( p, "meta.media.height" ); + profile->progressive = mlt_properties_get_int( p, "meta.media.progressive" ); + profile->frame_rate_num = mlt_properties_get_int( p, "meta.media.frame_rate_num" ); + profile->frame_rate_den = mlt_properties_get_int( p, "meta.media.frame_rate_den" ); + profile->sample_aspect_num = mlt_properties_get_int( p, "meta.media.sample_aspect_num" ); + profile->sample_aspect_den = mlt_properties_get_int( p, "meta.media.sample_aspect_den" ); + profile->display_aspect_num = (int) ( (double) profile->sample_aspect_num * profile->width / profile->sample_aspect_den + 0.5 ); + profile->display_aspect_den = profile->height; + } + } + } + mlt_frame_close( fr ); + mlt_producer_seek( melt, 0 ); +} + static void query_metadata( mlt_repository repo, mlt_service_type type, const char *typestr, char *id ) { mlt_properties metadata = mlt_repository_metadata( repo, type, id ); @@ -327,8 +366,10 @@ int main( int argc, char **argv ) FILE *store = NULL; char *name = NULL; mlt_profile profile = NULL; + mlt_profile backup_profile = NULL; int is_progress = 0; int is_silent = 0; + int is_profile_explicit = 0; // Construct the factory mlt_repository repo = mlt_factory_init( NULL ); @@ -425,8 +466,10 @@ query_all: // Create profile if not set explicitly if ( profile == NULL ) profile = mlt_profile_init( NULL ); + else + is_profile_explicit = 1; - // Look for the consumer option + // Look for the consumer option to load profile settings from consumer properties for ( i = 1; i < argc; i ++ ) { if ( !strcmp( argv[ i ], "-consumer" ) ) @@ -441,14 +484,64 @@ query_all: } } - // If we have no consumer, default to sdl - if ( store == NULL && consumer == NULL ) - consumer = create_consumer( profile, NULL ); + // Make backup of profile for determining if we need to use 'consumer' producer. + backup_profile = mlt_profile_init( NULL ); + memcpy( backup_profile, profile, sizeof( struct mlt_profile_s ) ); + backup_profile->description = strdup( "" ); // Get melt producer if ( argc > 1 ) melt = mlt_factory_producer( profile, "melt", &argv[ 1 ] ); + if ( melt ) + { + // If the producer changed the profile then do not try to guess it. + if ( profile->width != backup_profile->width || + profile->height != backup_profile->height || + profile->sample_aspect_num != backup_profile->sample_aspect_num || + profile->sample_aspect_den != backup_profile->sample_aspect_den ) + { + if ( is_profile_explicit ) + { + // We need to use the 'consumer' producer. + mlt_producer_close( melt ); + mlt_profile_close( profile ); + profile = backup_profile; + backup_profile = NULL; + if ( profile->description ) + free( profile->description ); + // This is a hack to signal create_producer() in producer_melt.c. + profile->description = strdup( "consumer:" ); + melt = mlt_factory_producer( profile, "melt", &argv[ 1 ] ); + } + } + else if ( ! is_profile_explicit ) + { + guess_profile( melt, profile ); + } + + // Reload the consumer with the fully qualified profile + for ( i = 1; i < argc; i ++ ) + { + if ( !strcmp( argv[ i ], "-consumer" ) ) + { + if ( consumer ) + mlt_consumer_close( consumer ); + consumer = create_consumer( profile, argv[ ++ i ] ); + if ( consumer ) + { + mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); + while ( argv[ i + 1 ] != NULL && strstr( argv[ i + 1 ], "=" ) ) + mlt_properties_parse( properties, argv[ ++ i ] ); + } + } + } + + // If we have no consumer, default to sdl + if ( store == NULL && consumer == NULL ) + consumer = create_consumer( profile, NULL ); + } + // Set transport properties on consumer and produder if ( consumer != NULL && melt != NULL ) { @@ -570,6 +663,7 @@ query_all: // Close the factory mlt_profile_close( profile ); + mlt_profile_close( backup_profile ); exit_factory: diff --git a/src/modules/melt/producer_melt.c b/src/modules/melt/producer_melt.c index 05b571fa..ac425a62 100644 --- a/src/modules/melt/producer_melt.c +++ b/src/modules/melt/producer_melt.c @@ -69,7 +69,19 @@ static void track_service( mlt_field field, void *service, mlt_destructor destru static mlt_producer create_producer( mlt_profile profile, mlt_field field, char *file ) { - mlt_producer result = mlt_factory_producer( profile, NULL, file ); + char *filedup; + if ( profile->description && strncmp( file, "consumer:", 9 ) && !strcmp( profile->description, "consumer:" ) ) + { + filedup = calloc( 1, strlen( file ) + strlen( profile->description ) + 1 ); + strcat( filedup, profile->description ); + strcat( filedup, file ); + } + else + { + filedup = strdup( file ); + } + mlt_producer result = mlt_factory_producer( profile, NULL, filedup ); + free( filedup ); if ( result != NULL ) track_service( field, result, ( mlt_destructor )mlt_producer_close ); -- 2.39.2