X-Git-Url: http://research.m1stereo.tv/gitweb?a=blobdiff_plain;f=src%2Fmodules%2Favformat%2Fproducer_avformat.c;h=bf3654f311c80224acb69fec463595b7b7a991ff;hb=7067858fa9cf333f072778d30d31947ca095a308;hp=ec20a7a7ccc4ccedd48fe4db76e55b7631bf7663;hpb=3f3a98c098f1beee60a01e61eb81347117fc62d2;p=melted diff --git a/src/modules/avformat/producer_avformat.c b/src/modules/avformat/producer_avformat.c index ec20a7a..bf3654f 100644 --- a/src/modules/avformat/producer_avformat.c +++ b/src/modules/avformat/producer_avformat.c @@ -2,6 +2,7 @@ * producer_avformat.c -- avformat producer * Copyright (C) 2003-2004 Ushodaya Enterprises Limited * Author: Charles Yates + * Much code borrowed from ffmpeg.c: Copyright (c) 2000-2003 Fabrice Bellard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,10 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -// Local header files -#include "producer_avformat.h" - // MLT Header files +#include #include // ffmpeg Header files @@ -40,13 +39,13 @@ void avformat_lock( ); void avformat_unlock( ); // Forward references. -static int producer_open( mlt_producer this, char *file ); +static int producer_open( mlt_producer this, mlt_profile profile, char *file ); static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index ); /** Constructor for libavformat. */ -mlt_producer producer_avformat_init( char *file ) +mlt_producer producer_avformat_init( mlt_profile profile, char *file ) { mlt_producer this = NULL; @@ -69,12 +68,23 @@ mlt_producer producer_avformat_init( char *file ) this->get_frame = producer_get_frame; // Open the file - if ( producer_open( this, file ) != 0 ) + if ( producer_open( this, profile, file ) != 0 ) { // Clean up mlt_producer_close( this ); this = NULL; } + else + { + // Close the file to release resources for large playlists - reopen later as needed + mlt_properties_set_data( properties, "dummy_context", NULL, 0, NULL, NULL ); + mlt_properties_set_data( properties, "audio_context", NULL, 0, NULL, NULL ); + mlt_properties_set_data( properties, "video_context", NULL, 0, NULL, NULL ); + + // Default the user-selectable indices from the auto-detected indices + mlt_properties_set_int( properties, "audio_index", mlt_properties_get_int( properties, "_audio_index" ) ); + mlt_properties_set_int( properties, "video_index", mlt_properties_get_int( properties, "_video_index" ) ); + } } } @@ -92,13 +102,13 @@ static void find_default_streams( AVFormatContext *context, int *audio_index, in for( i = 0; i < context->nb_streams; i++ ) { // Get the codec context - AVCodecContext *codec_context = context->streams[ i ]->codec; + AVCodecContext *codec_context = context->streams[ i ]->codec; if ( avcodec_find_decoder( codec_context->codec_id ) == NULL ) continue; // Determine the type and obtain the first index of each type - switch( codec_context->codec_type ) + switch( codec_context->codec_type ) { case CODEC_TYPE_VIDEO: if ( *video_index < 0 ) @@ -107,9 +117,9 @@ static void find_default_streams( AVFormatContext *context, int *audio_index, in case CODEC_TYPE_AUDIO: if ( *audio_index < 0 ) *audio_index = i; - break; + break; default: - break; + break; } } } @@ -153,7 +163,7 @@ static void producer_codec_close( void *codec ) /** Open the file. */ -static int producer_open( mlt_producer this, char *file ) +static int producer_open( mlt_producer this, mlt_profile profile, char *file ) { // Return an error code (0 == no error) int error = 0; @@ -165,7 +175,7 @@ static int producer_open( mlt_producer this, char *file ) mlt_properties properties = MLT_PRODUCER_PROPERTIES( this ); // We will treat everything with the producer fps - double fps = mlt_producer_get_fps( this ); + double fps = mlt_profile_fps( profile ); // Lock the mutex now avformat_lock( ); @@ -282,11 +292,11 @@ static int producer_open( mlt_producer this, char *file ) // Find default audio and video streams find_default_streams( context, &audio_index, &video_index ); - if ( context->start_time != AV_NOPTS_VALUE ) - mlt_properties_set_double( properties, "_start_time", context->start_time ); + if ( context->start_time != AV_NOPTS_VALUE ) + mlt_properties_set_double( properties, "_start_time", context->start_time ); // Check if we're seekable (something funny about mpeg here :-/) - if ( strcmp( file, "pipe:" ) && strncmp( file, "http://", 6 ) ) + if ( strcmp( file, "pipe:" ) && strncmp( file, "http://", 6 ) && strncmp( file, "udp:", 4 ) && strncmp( file, "tcp:", 4 ) && strncmp( file, "rtsp:", 5 ) && strncmp( file, "rtp:", 4 ) ) { mlt_properties_set_int( properties, "seekable", av_seek_frame( context, -1, mlt_properties_get_double( properties, "_start_time" ), AVSEEK_FLAG_BACKWARD ) >= 0 ); mlt_properties_set_data( properties, "dummy_context", context, 0, producer_file_close, NULL ); @@ -297,8 +307,8 @@ static int producer_open( mlt_producer this, char *file ) av_bypass = 1; // Store selected audio and video indexes on properties - mlt_properties_set_int( properties, "audio_index", audio_index ); - mlt_properties_set_int( properties, "video_index", video_index ); + mlt_properties_set_int( properties, "_audio_index", audio_index ); + mlt_properties_set_int( properties, "_video_index", video_index ); mlt_properties_set_int( properties, "_last_position", -1 ); // Fetch the width, height and aspect ratio @@ -327,7 +337,7 @@ static int producer_open( mlt_producer this, char *file ) mlt_properties_set_int(properties, "meta.attr.track.markup", context->track ); // We're going to cheat here - for a/v files, we will have two contexts (reasoning will be clear later) - if ( av == 0 && !av_bypass && audio_index != -1 && video_index != -1 ) + if ( av == 0 && audio_index != -1 && video_index != -1 ) { // We'll use the open one as our video_context mlt_properties_set_data( properties, "video_context", context, 0, producer_file_close, NULL ); @@ -641,13 +651,15 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form if ( got_picture ) { mlt_properties_set_int( frame_properties, "progressive", !av_frame->interlaced_frame ); - mlt_properties_set_int( frame_properties, "top_field_first", av_frame->top_field_first ); + mlt_properties_set_int( properties, "top_field_first", av_frame->top_field_first ); convert_image( av_frame, *buffer, codec_context->pix_fmt, *format, *width, *height ); mlt_properties_set_data( frame_properties, "image", *buffer, size, (mlt_destructor)mlt_pool_release, NULL ); mlt_properties_set_int( properties, "_current_position", int_position ); mlt_properties_set_int( properties, "_got_picture", 1 ); } } + if ( !got_picture ) + mlt_frame_get_image( frame, buffer, format, width, height, writable ); } // Very untidy - for rawvideo, the packet contains the frame, hence the free packet @@ -678,10 +690,44 @@ static void producer_set_up_video( mlt_producer this, mlt_frame frame ) // Get the video_index int index = mlt_properties_get_int( properties, "video_index" ); + // Reopen the file if necessary + if ( !context && index > -1 ) + { + mlt_events_block( properties, this ); + producer_open( this, mlt_service_profile( MLT_PRODUCER_SERVICE(this) ), + mlt_properties_get( properties, "resource" ) ); + context = mlt_properties_get_data( properties, "video_context", NULL ); + mlt_properties_set_data( properties, "dummy_context", NULL, 0, NULL, NULL ); + mlt_events_unblock( properties, this ); + } + + // Exception handling for video_index + if ( context && index >= (int) context->nb_streams ) + { + for ( index = context->nb_streams - 1; index >= 0 && context->streams[ index ]->codec->codec_type != CODEC_TYPE_VIDEO; --index ); + mlt_properties_set_int( properties, "video_index", index ); + } + if ( context && index > -1 && context->streams[ index ]->codec->codec_type != CODEC_TYPE_VIDEO ) + { + index = -1; + mlt_properties_set_int( properties, "video_index", index ); + } + + // Update the video properties if the index changed + if ( index > -1 && index != mlt_properties_get_int( properties, "_video_index" ) ) + { + // Fetch the width, height and aspect ratio + AVCodecContext *codec_context = context->streams[ index ]->codec; + mlt_properties_set_int( properties, "_video_index", index ); + mlt_properties_set_int( properties, "width", codec_context->width ); + mlt_properties_set_int( properties, "height", codec_context->height ); + mlt_properties_set_double( properties, "aspect_ratio", av_q2d( codec_context->sample_aspect_ratio ) ); + } + // Get the frame properties mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); - if ( context != NULL && index != -1 ) + if ( context != NULL && index > -1 ) { // Get the video stream AVStream *stream = context->streams[ index ]; @@ -695,10 +741,21 @@ static void producer_set_up_video( mlt_producer this, mlt_frame frame ) // Initialise the codec if necessary if ( codec == NULL ) { + // Initialise multi-threading + int thread_count = mlt_properties_get_int( properties, "threads" ); + if ( thread_count == 0 && getenv( "MLT_AVFORMAT_THREADS" ) ) + thread_count = atoi( getenv( "MLT_AVFORMAT_THREADS" ) ); + if ( thread_count > 1 ) + { + avcodec_thread_init( codec_context, thread_count ); + codec_context->thread_count = thread_count; + } + // Find the codec codec = avcodec_find_decoder( codec_context->codec_id ); // If we don't have a codec and we can't initialise it, we can't do much more... + avformat_lock( ); if ( codec != NULL && avcodec_open( codec_context, codec ) >= 0 ) { // Now store the codec with its destructor @@ -709,6 +766,7 @@ static void producer_set_up_video( mlt_producer this, mlt_frame frame ) // Remember that we can't use this later mlt_properties_set_int( properties, "video_index", -1 ); } + avformat_unlock( ); } // No codec, no show... @@ -719,6 +777,28 @@ static void producer_set_up_video( mlt_producer this, mlt_frame frame ) double force_aspect_ratio = mlt_properties_get_double( properties, "force_aspect_ratio" ); double aspect_ratio; + if ( strcmp( codec_context->codec->name, "dvvideo" ) == 0 ) + { + // Override FFmpeg's notion of DV aspect ratios, which are + // based upon a width of 704. Since we do not have a normaliser + // that crops (nor is cropping 720 wide ITU-R 601 video always desirable) + // we just coerce the values to facilitate a passive behaviour through + // the rescale normaliser when using equivalent producers and consumers. + // = display_aspect / (width * height) + if ( codec_context->sample_aspect_ratio.num == 10 && + codec_context->sample_aspect_ratio.den == 11 ) + force_aspect_ratio = 8.0/9.0; // 4:3 NTSC + else if ( codec_context->sample_aspect_ratio.num == 59 && + codec_context->sample_aspect_ratio.den == 54 ) + force_aspect_ratio = 16.0/15.0; // 4:3 PAL + else if ( codec_context->sample_aspect_ratio.num == 40 && + codec_context->sample_aspect_ratio.den == 33 ) + force_aspect_ratio = 32.0/27.0; // 16:9 NTSC + else if ( codec_context->sample_aspect_ratio.num == 118 && + codec_context->sample_aspect_ratio.den == 81 ) + force_aspect_ratio = 64.0/45.0; // 16:9 PAL + } + // XXX: We won't know the real aspect ratio until an image is decoded // but we do need it now (to satisfy filter_resize) - take a guess based // on pal/ntsc @@ -732,8 +812,7 @@ static void producer_set_up_video( mlt_producer this, mlt_frame frame ) } else { - int is_pal = mlt_producer_get_fps( this ) == 25.0; - aspect_ratio = is_pal ? 59.0/54.0 : 10.0/11.0; + aspect_ratio = 1.0; } // Determine the fps @@ -936,9 +1015,12 @@ static int producer_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_form } // If we're behind, ignore this packet - float current_pts = av_q2d( stream->time_base ) * pkt.pts; - if ( seekable && ( !ignore && current_pts <= ( real_timecode - 0.02 ) ) ) - ignore = 1; + if ( pkt.pts >= 0 ) + { + float current_pts = av_q2d( stream->time_base ) * pkt.pts; + if ( seekable && ( !ignore && current_pts <= ( real_timecode - 0.02 ) ) ) + ignore = 1; + } } // We're finished with this packet regardless @@ -993,8 +1075,31 @@ static void producer_set_up_audio( mlt_producer this, mlt_frame frame ) // Get the audio_index int index = mlt_properties_get_int( properties, "audio_index" ); + // Reopen the file if necessary + if ( !context && index > -1 ) + { + mlt_events_block( properties, this ); + producer_open( this, mlt_service_profile( MLT_PRODUCER_SERVICE(this) ), + mlt_properties_get( properties, "resource" ) ); + context = mlt_properties_get_data( properties, "audio_context", NULL ); + mlt_properties_set_data( properties, "dummy_context", NULL, 0, NULL, NULL ); + mlt_events_unblock( properties, this ); + } + + // Exception handling for audio_index + if ( context && index >= (int) context->nb_streams ) + { + for ( index = context->nb_streams - 1; index >= 0 && context->streams[ index ]->codec->codec_type != CODEC_TYPE_AUDIO; --index ); + mlt_properties_set_int( properties, "audio_index", index ); + } + if ( context && index > -1 && context->streams[ index ]->codec->codec_type != CODEC_TYPE_AUDIO ) + { + index = -1; + mlt_properties_set_int( properties, "audio_index", index ); + } + // Deal with audio context - if ( context != NULL && index != -1 ) + if ( context != NULL && index > -1 ) { // Get the frame properties mlt_properties frame_properties = MLT_FRAME_PROPERTIES( frame ); @@ -1015,6 +1120,7 @@ static void producer_set_up_audio( mlt_producer this, mlt_frame frame ) codec = avcodec_find_decoder( codec_context->codec_id ); // If we don't have a codec and we can't initialise it, we can't do much more... + avformat_lock( ); if ( codec != NULL && avcodec_open( codec_context, codec ) >= 0 ) { // Now store the codec with its destructor @@ -1026,6 +1132,7 @@ static void producer_set_up_audio( mlt_producer this, mlt_frame frame ) // Remember that we can't use this later mlt_properties_set_int( properties, "audio_index", -1 ); } + avformat_unlock( ); } // No codec, no show... @@ -1045,7 +1152,7 @@ static void producer_set_up_audio( mlt_producer this, mlt_frame frame ) static int producer_get_frame( mlt_producer this, mlt_frame_ptr frame, int index ) { // Create an empty frame - *frame = mlt_frame_init( ); + *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( this ) ); // Update timecode on the frame we're creating mlt_frame_set_position( *frame, mlt_producer_position( this ) );