X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmodules%2Favformat%2Fproducer_avformat.c;h=49439f8cf35036ce888411ede87133fd742a0578;hb=0a46037a4ee8acc3590b50fe0af1cb191172860a;hp=65967f23937f5b9157a7a2c8817d7dcabe394844;hpb=df06733e9c26bd43e433b334b7e7b178cb916b64;p=mlt diff --git a/src/modules/avformat/producer_avformat.c b/src/modules/avformat/producer_avformat.c index 65967f23..49439f8c 100644 --- a/src/modules/avformat/producer_avformat.c +++ b/src/modules/avformat/producer_avformat.c @@ -31,12 +31,13 @@ // ffmpeg Header files #include -#include #ifdef SWSCALE # include #endif -#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0)) -# include "audioconvert.h" +#if LIBAVCODEC_VERSION_MAJOR >= 53 +#include +#elif (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0)) +const char *avcodec_get_sample_fmt_name(int sample_fmt); #endif #ifdef VDPAU # include @@ -56,6 +57,15 @@ #define PIX_FMT_YUYV422 PIX_FMT_YUV422 #endif +#if LIBAVCODEC_VERSION_MAJOR >= 53 +#include +#define CODEC_TYPE_VIDEO AVMEDIA_TYPE_VIDEO +#define CODEC_TYPE_AUDIO AVMEDIA_TYPE_AUDIO +#define PKT_FLAG_KEY AV_PKT_FLAG_KEY +#else +#include +#endif + #define POSITION_INITIAL (-2) #define POSITION_INVALID (-1) @@ -81,14 +91,15 @@ struct producer_avformat_s int video_index; double start_time; int first_pts; - int last_position; + int64_t last_position; int seekable; - int current_position; + int64_t current_position; + mlt_position nonseek_position; int got_picture; int top_field_first; - int16_t *audio_buffer[ MAX_AUDIO_STREAMS ]; + uint8_t *audio_buffer[ MAX_AUDIO_STREAMS ]; size_t audio_buffer_size[ MAX_AUDIO_STREAMS ]; - int16_t *decode_buffer[ MAX_AUDIO_STREAMS ]; + uint8_t *decode_buffer[ MAX_AUDIO_STREAMS ]; int audio_used[ MAX_AUDIO_STREAMS ]; int audio_streams; int audio_max_stream; @@ -99,6 +110,11 @@ struct producer_avformat_s double resample_factor; mlt_cache image_cache; int colorspace; + pthread_mutex_t video_mutex; + pthread_mutex_t audio_mutex; + mlt_deque apackets; + mlt_deque vpackets; + pthread_mutex_t packets_mutex; #ifdef VDPAU struct { @@ -117,11 +133,16 @@ struct producer_avformat_s typedef struct producer_avformat_s *producer_avformat; // Forward references. -static int producer_open( producer_avformat self, mlt_profile profile, char *file ); +static int list_components( char* file ); +static int producer_open( producer_avformat self, mlt_profile profile, const char *URL, int take_lock ); static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index ); static void producer_avformat_close( producer_avformat ); static void producer_close( mlt_producer parent ); static void producer_set_up_video( producer_avformat self, mlt_frame frame ); +static void producer_set_up_audio( producer_avformat self, mlt_frame frame ); +static void apply_properties( void *obj, mlt_properties properties, int flags ); +static int video_codec_init( producer_avformat self, int index, mlt_properties properties ); +static void get_audio_streams_info( producer_avformat self ); #ifdef VDPAU #include "vdpau.c" @@ -132,45 +153,17 @@ static void producer_set_up_video( producer_avformat self, mlt_frame frame ); mlt_producer producer_avformat_init( mlt_profile profile, const char *service, char *file ) { - int skip = 0; + if ( list_components( file ) ) + return NULL; - // Report information about available demuxers and codecs as YAML Tiny - if ( file && strstr( file, "f-list" ) ) - { - fprintf( stderr, "---\nformats:\n" ); - AVInputFormat *format = NULL; - while ( ( format = av_iformat_next( format ) ) ) - fprintf( stderr, " - %s\n", format->name ); - fprintf( stderr, "...\n" ); - skip = 1; - } - if ( file && strstr( file, "acodec-list" ) ) - { - fprintf( stderr, "---\naudio_codecs:\n" ); - AVCodec *codec = NULL; - while ( ( codec = av_codec_next( codec ) ) ) - if ( codec->decode && codec->type == CODEC_TYPE_AUDIO ) - fprintf( stderr, " - %s\n", codec->name ); - fprintf( stderr, "...\n" ); - skip = 1; - } - if ( file && strstr( file, "vcodec-list" ) ) - { - fprintf( stderr, "---\nvideo_codecs:\n" ); - AVCodec *codec = NULL; - while ( ( codec = av_codec_next( codec ) ) ) - if ( codec->decode && codec->type == CODEC_TYPE_VIDEO ) - fprintf( stderr, " - %s\n", codec->name ); - fprintf( stderr, "...\n" ); - skip = 1; - } + mlt_producer producer = NULL; // Check that we have a non-NULL argument - if ( !skip && file ) + if ( file ) { // Construct the producer - mlt_producer producer = calloc( 1, sizeof( struct mlt_producer_s ) ); producer_avformat self = calloc( 1, sizeof( struct producer_avformat_s ) ); + producer = calloc( 1, sizeof( struct mlt_producer_s ) ); // Initialise it if ( mlt_producer_init( producer, self ) == 0 ) @@ -188,23 +181,20 @@ mlt_producer producer_avformat_init( mlt_profile profile, const char *service, c // Register our get_frame implementation producer->get_frame = producer_get_frame; - + if ( strcmp( service, "avformat-novalidate" ) ) { // Open the file - if ( producer_open( self, profile, file ) != 0 ) + if ( producer_open( self, profile, file, 1 ) != 0 ) { // Clean up mlt_producer_close( producer ); producer = NULL; } - else + else if ( self->seekable ) { // Close the file to release resources for large playlists - reopen later as needed avformat_lock(); - if ( self->dummy_context ) - av_close_input_file( self->dummy_context ); - self->dummy_context = NULL; if ( self->audio_format ) av_close_input_file( self->audio_format ); self->audio_format = NULL; @@ -212,38 +202,75 @@ mlt_producer producer_avformat_init( mlt_profile profile, const char *service, c av_close_input_file( self->video_format ); self->video_format = NULL; avformat_unlock(); - - // Default the user-selectable indices from the auto-detected indices - mlt_properties_set_int( properties, "audio_index", self->audio_index ); - mlt_properties_set_int( properties, "video_index", self->video_index ); - -#ifdef VDPAU - mlt_service_cache_set_size( MLT_PRODUCER_SERVICE(producer), "producer_avformat", 5 ); -#endif - mlt_service_cache_put( MLT_PRODUCER_SERVICE(producer), "producer_avformat", self, 0, (mlt_destructor) producer_avformat_close ); } } - else + if ( producer ) { + // Default the user-selectable indices from the auto-detected indices + mlt_properties_set_int( properties, "audio_index", self->audio_index ); + mlt_properties_set_int( properties, "video_index", self->video_index ); #ifdef VDPAU mlt_service_cache_set_size( MLT_PRODUCER_SERVICE(producer), "producer_avformat", 5 ); #endif mlt_service_cache_put( MLT_PRODUCER_SERVICE(producer), "producer_avformat", self, 0, (mlt_destructor) producer_avformat_close ); } - return producer; } } - return NULL; + return producer; +} + +int list_components( char* file ) +{ + int skip = 0; + + // Report information about available demuxers and codecs as YAML Tiny + if ( file && strstr( file, "f-list" ) ) + { + fprintf( stderr, "---\nformats:\n" ); + AVInputFormat *format = NULL; + while ( ( format = av_iformat_next( format ) ) ) + fprintf( stderr, " - %s\n", format->name ); + fprintf( stderr, "...\n" ); + skip = 1; + } + if ( file && strstr( file, "acodec-list" ) ) + { + fprintf( stderr, "---\naudio_codecs:\n" ); + AVCodec *codec = NULL; + while ( ( codec = av_codec_next( codec ) ) ) + if ( codec->decode && codec->type == CODEC_TYPE_AUDIO ) + fprintf( stderr, " - %s\n", codec->name ); + fprintf( stderr, "...\n" ); + skip = 1; + } + if ( file && strstr( file, "vcodec-list" ) ) + { + fprintf( stderr, "---\nvideo_codecs:\n" ); + AVCodec *codec = NULL; + while ( ( codec = av_codec_next( codec ) ) ) + if ( codec->decode && codec->type == CODEC_TYPE_VIDEO ) + fprintf( stderr, " - %s\n", codec->name ); + fprintf( stderr, "...\n" ); + skip = 1; + } + + return skip; } /** Find the default streams. */ -static mlt_properties find_default_streams( mlt_properties meta_media, AVFormatContext *context, int *audio_index, int *video_index ) +static mlt_properties find_default_streams( producer_avformat self ) { int i; char key[200]; AVMetadataTag *tag = NULL; + AVFormatContext *context = self->video_format; + mlt_properties meta_media = MLT_PRODUCER_PROPERTIES( self->parent ); + + // Default to the first audio and video streams found + self->audio_index = -1; + self->video_index = -1; mlt_properties_set_int( meta_media, "meta.media.nb_streams", context->nb_streams ); @@ -264,8 +291,9 @@ static mlt_properties find_default_streams( mlt_properties meta_media, AVFormatC switch( codec_context->codec_type ) { case CODEC_TYPE_VIDEO: - if ( *video_index < 0 ) - *video_index = i; + // Use first video stream + if ( self->video_index < 0 ) + self->video_index = i; mlt_properties_set( meta_media, key, "video" ); snprintf( key, sizeof(key), "meta.media.%d.stream.frame_rate", i ); #if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(42<<8)+0) @@ -310,10 +338,14 @@ static mlt_properties find_default_streams( mlt_properties meta_media, AVFormatC #endif break; case CODEC_TYPE_AUDIO: - if ( *audio_index < 0 ) - *audio_index = i; + // Use first audio stream + if ( self->audio_index < 0 ) + self->audio_index = i; mlt_properties_set( meta_media, key, "audio" ); -#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0)) +#if LIBAVCODEC_VERSION_MAJOR >= 53 + snprintf( key, sizeof(key), "meta.media.%d.codec.sample_fmt", i ); + mlt_properties_set( meta_media, key, av_get_sample_fmt_name( codec_context->sample_fmt ) ); +#elif (LIBAVCODEC_VERSION_INT >= ((51<<16)+(71<<8)+0)) snprintf( key, sizeof(key), "meta.media.%d.codec.sample_fmt", i ); mlt_properties_set( meta_media, key, avcodec_get_sample_fmt_name( codec_context->sample_fmt ) ); #endif @@ -510,243 +542,472 @@ static double get_aspect_ratio( mlt_properties properties, AVStream *stream, AVC return aspect_ratio; } -/** Open the file. -*/ - -static int producer_open( producer_avformat self, mlt_profile profile, char *file ) +static char* parse_url( mlt_profile profile, const char* URL, AVInputFormat **format, AVFormatParameters *params ) { - // Return an error code (0 == no error) - int error = 0; - - // Context for avformat - AVFormatContext *context = NULL; - - // Get the properties - mlt_properties properties = MLT_PRODUCER_PROPERTIES( self->parent ); + if ( !URL ) return NULL; - // We will treat everything with the producer fps - double fps = mlt_profile_fps( profile ); - - // Lock the mutex now - avformat_lock( ); - - // If "MRL", then create AVInputFormat - AVInputFormat *format = NULL; - AVFormatParameters *params = NULL; - char *standard = NULL; - char *mrl = strchr( file, ':' ); - - // AV option (0 = both, 1 = video, 2 = audio) - int av = 0; + const char *result = URL; + char *protocol = strdup( URL ); + char *url = strchr( protocol, ':' ); // Only if there is not a protocol specification that avformat can handle - if ( mrl && !url_exist( file ) ) +#if LIBAVFORMAT_VERSION_MAJOR >= 53 + if ( url && avio_check( URL, 0 ) < 0 ) +#else + if ( url && !url_exist( URL ) ) +#endif { - // 'file' becomes format abbreviation - mrl[0] = 0; + // Truncate protocol string + url[0] = 0; + mlt_log_debug( NULL, "%s: protocol=%s resource=%s\n", __FUNCTION__, protocol, url + 1 ); // Lookup the format - format = av_find_input_format( file ); + *format = av_find_input_format( protocol ); // Eat the format designator - file = ++mrl; + result = ++url; - if ( format ) + if ( *format ) { - // Allocate params - params = calloc( sizeof( AVFormatParameters ), 1 ); - - // These are required by video4linux (defaults) - params->width = 640; - params->height = 480; - params->time_base= (AVRational){1,25}; + // These are required by video4linux2 (defaults) + params->width = profile->width; + params->height = profile->height; + if ( !strstr( URL, "&frame_rate" ) ) + params->time_base = (AVRational){ profile->frame_rate_den, profile->frame_rate_num }; params->channels = 2; params->sample_rate = 48000; - } - // Parse out params - mrl = strchr( file, '?' ); - while ( mrl ) - { - mrl[0] = 0; - char *name = strdup( ++mrl ); - char *value = strchr( name, ':' ); - if ( value ) + // Parse out params + url = strchr( url, '?' ); + while ( url ) { - value[0] = 0; - value++; - char *t = strchr( value, '&' ); - if ( t ) - t[0] = 0; - if ( !strcmp( name, "frame_rate" ) ) - params->time_base.den = atoi( value ); - else if ( !strcmp( name, "frame_rate_base" ) ) - params->time_base.num = atoi( value ); - else if ( !strcmp( name, "sample_rate" ) ) - params->sample_rate = atoi( value ); - else if ( !strcmp( name, "channel" ) ) - params->channel = atoi( value ); - else if ( !strcmp( name, "channels" ) ) - params->channels = atoi( value ); + url[0] = 0; + char *name = strdup( ++url ); + char *value = strchr( name, '=' ); + if ( !value ) + // Also accept : as delimiter for backwards compatibility. + value = strchr( name, ':' ); + if ( value ) + { + value[0] = 0; + value++; + char *t = strchr( value, '&' ); + if ( t ) + t[0] = 0; + if ( !strcmp( name, "frame_rate" ) ) + params->time_base.den = atoi( value ); + else if ( !strcmp( name, "frame_rate_base" ) ) + params->time_base.num = atoi( value ); + else if ( !strcmp( name, "sample_rate" ) ) + params->sample_rate = atoi( value ); + else if ( !strcmp( name, "channel" ) ) + params->channel = atoi( value ); + else if ( !strcmp( name, "channels" ) ) + params->channels = atoi( value ); #if (LIBAVUTIL_VERSION_INT > ((50<<16)+(7<<8)+0)) - else if ( !strcmp( name, "pix_fmt" ) ) - params->pix_fmt = av_get_pix_fmt( value ); + else if ( !strcmp( name, "pix_fmt" ) ) + params->pix_fmt = av_get_pix_fmt( value ); +#endif + else if ( !strcmp( name, "width" ) ) + params->width = atoi( value ); + else if ( !strcmp( name, "height" ) ) + params->height = atoi( value ); + else if ( !strcmp( name, "standard" ) ) + params->standard = strdup( value ); + } + free( name ); + url = strchr( url, '&' ); + } + } + } + result = strdup( result ); + free( protocol ); + return result; +} + +static int get_basic_info( producer_avformat self, mlt_profile profile, const char *filename ) +{ + int error = 0; + + // Get the properties + mlt_properties properties = MLT_PRODUCER_PROPERTIES( self->parent ); + + AVFormatContext *format = self->video_format; + + // We will treat everything with the producer fps. + // TODO: make this more flexible. + double fps = mlt_profile_fps( profile ); + + // Get the duration + if ( !mlt_properties_get_int( properties, "_length_computed" ) ) + { + // The _length_computed flag prevents overwriting explicity set length/out/eof properties + // when producer_open is called after initial call when restoring or reseting the producer. + if ( format->duration != AV_NOPTS_VALUE ) + { + // This isn't going to be accurate for all formats + mlt_position frames = ( mlt_position )( ( ( double )format->duration / ( double )AV_TIME_BASE ) * fps ); + mlt_properties_set_position( properties, "out", frames - 1 ); + mlt_properties_set_position( properties, "length", frames ); + mlt_properties_set_int( properties, "_length_computed", 1 ); + } + else + { + // Set live sources to run forever + mlt_properties_set_position( properties, "length", INT_MAX ); + mlt_properties_set_position( properties, "out", INT_MAX - 1 ); + mlt_properties_set( properties, "eof", "loop" ); + mlt_properties_set_int( properties, "_length_computed", 1 ); + } + } + + if ( format->start_time != AV_NOPTS_VALUE ) + self->start_time = format->start_time; + + // Check if we're seekable + // avdevices are typically AVFMT_NOFILE and not seekable + self->seekable = !format->iformat || !( format->iformat->flags & AVFMT_NOFILE ); + if ( format->pb ) + { + // protocols can indicate if they support seeking +#if LIBAVFORMAT_VERSION_MAJOR >= 53 + self->seekable = format->pb->seekable; +#else + URLContext *uc = url_fileno( format->pb ); + if ( uc ) + self->seekable = !uc->is_streamed; #endif - else if ( !strcmp( name, "width" ) ) - params->width = atoi( value ); - else if ( !strcmp( name, "height" ) ) - params->height = atoi( value ); - else if ( !strcmp( name, "standard" ) ) + } + if ( self->seekable ) + { + // Do a more rigourous test of seekable on a disposable context + self->seekable = av_seek_frame( format, -1, self->start_time, AVSEEK_FLAG_BACKWARD ) >= 0; + mlt_properties_set_int( properties, "seekable", self->seekable ); + self->dummy_context = format; + av_open_input_file( &self->video_format, filename, NULL, 0, NULL ); + format = self->video_format; + av_find_stream_info( format ); + } + + // Fetch the width, height and aspect ratio + if ( self->video_index != -1 ) + { + AVCodecContext *codec_context = format->streams[ self->video_index ]->codec; + mlt_properties_set_int( properties, "width", codec_context->width ); + mlt_properties_set_int( properties, "height", codec_context->height ); + + if ( codec_context->codec_id == CODEC_ID_DVVIDEO ) + { + // Fetch the first frame of DV so we can read it directly + AVPacket pkt; + int ret = 0; + while ( ret >= 0 ) + { + ret = av_read_frame( format, &pkt ); + if ( ret >= 0 && pkt.stream_index == self->video_index && pkt.size > 0 ) { - standard = strdup( value ); - params->standard = standard; + get_aspect_ratio( properties, format->streams[ self->video_index ], codec_context, &pkt ); + break; } - else if ( !strcmp( name, "av" ) ) - av = atoi( value ); } - free( name ); - mrl = strchr( mrl, '&' ); } + else + { + get_aspect_ratio( properties, format->streams[ self->video_index ], codec_context, NULL ); + } + +#ifdef SWSCALE + // Verify that we can convert this to YUV 4:2:2 + // TODO: we can now also return RGB and RGBA and quite possibly more in the future. + struct SwsContext *context = sws_getContext( codec_context->width, codec_context->height, codec_context->pix_fmt, + codec_context->width, codec_context->height, PIX_FMT_YUYV422, SWS_BILINEAR, NULL, NULL, NULL); + if ( context ) + sws_freeContext( context ); + else + error = 1; +#endif } + return error; +} - // Now attempt to open the file - error = av_open_input_file( &context, file, format, 0, params ) < 0; +/** Open the file. +*/ + +static int producer_open( producer_avformat self, mlt_profile profile, const char *URL, int take_lock ) +{ + // Return an error code (0 == no error) + int error = 0; + mlt_properties properties = MLT_PRODUCER_PROPERTIES( self->parent ); + + // Lock the service + if ( take_lock ) + { + pthread_mutex_init( &self->audio_mutex, NULL ); + pthread_mutex_init( &self->video_mutex, NULL ); + pthread_mutex_init( &self->packets_mutex, NULL ); + pthread_mutex_lock( &self->audio_mutex ); + pthread_mutex_lock( &self->video_mutex ); + } + mlt_events_block( properties, self->parent ); + + // Parse URL + AVInputFormat *format = NULL; + AVFormatParameters params; + memset( ¶ms, 0, sizeof(params) ); + char *filename = parse_url( profile, URL, &format, ¶ms ); + + // Now attempt to open the file or device with filename + error = av_open_input_file( &self->video_format, filename, format, 0, ¶ms ) < 0; + if ( error ) + // If the URL is a network stream URL, then we probably need to open with full URL + error = av_open_input_file( &self->video_format, URL, format, 0, ¶ms ) < 0; + + // Set MLT properties onto video AVFormatContext + if ( !error && self->video_format ) + { + apply_properties( self->video_format, properties, AV_OPT_FLAG_DECODING_PARAM ); +#if LIBAVFORMAT_VERSION_MAJOR >= 53 + if ( self->video_format->iformat && self->video_format->iformat->priv_class && self->video_format->priv_data ) + apply_properties( self->video_format->priv_data, properties, AV_OPT_FLAG_DECODING_PARAM ); +#endif + } // Cleanup AVFormatParameters - free( standard ); - free( params ); + if ( params.standard ) + free( (void*) params.standard ); // If successful, then try to get additional info - if ( !error ) + if ( !error && self->video_format ) { // Get the stream info - error = av_find_stream_info( context ) < 0; + error = av_find_stream_info( self->video_format ) < 0; // Continue if no error - if ( !error ) + if ( !error && self->video_format ) { - // We will default to the first audio and video streams found - int audio_index = -1; - int video_index = -1; - // Find default audio and video streams - find_default_streams( properties, context, &audio_index, &video_index ); + find_default_streams( self ); + error = get_basic_info( self, profile, filename ); - // Now set properties where we can (use default unknowns if required) - if ( context->duration != AV_NOPTS_VALUE ) + // Initialize position info + self->first_pts = -1; + self->last_position = POSITION_INITIAL; + + if ( !self->audio_format ) { - // This isn't going to be accurate for all formats - mlt_position frames = ( mlt_position )( ( ( double )context->duration / ( double )AV_TIME_BASE ) * fps - 1 ); - - // Workaround some clips whose estimated duration cause problems: - // http://www.kdenlive.org/mantis/view.php?id=2003 - char *comment = mlt_properties_get( properties, "meta.attr.comment.markup" ); - if ( comment && strstr( comment, "KODAK" ) && strstr( comment, "Zx3" ) ) - frames -= 2; - - mlt_properties_set_position( properties, "out", frames - 1 ); - mlt_properties_set_position( properties, "length", frames ); + // We're going to cheat here - for seekable A/V files, we will have separate contexts + // to support independent seeking of audio from video. + // TODO: Is this really necessary? + if ( self->audio_index != -1 && self->video_index != -1 ) + { + if ( self->seekable ) + { + // And open again for our audio context + av_open_input_file( &self->audio_format, filename, NULL, 0, NULL ); + apply_properties( self->audio_format, properties, AV_OPT_FLAG_DECODING_PARAM ); +#if LIBAVFORMAT_VERSION_MAJOR >= 53 + if ( self->audio_format->iformat && self->audio_format->iformat->priv_class && self->audio_format->priv_data ) + apply_properties( self->audio_format->priv_data, properties, AV_OPT_FLAG_DECODING_PARAM ); +#endif + av_find_stream_info( self->audio_format ); + } + else + { + self->audio_format = self->video_format; + } + } + else if ( self->audio_index != -1 ) + { + // We only have an audio context + self->audio_format = self->video_format; + self->video_format = NULL; + } + else if ( self->video_index == -1 ) + { + // Something has gone wrong + error = -1; + } + if ( self->audio_format && !self->audio_streams ) + get_audio_streams_info( self ); } + } + } + if ( filename ) + free( filename ); + if ( !error ) + { + self->apackets = mlt_deque_init(); + self->vpackets = mlt_deque_init(); + } - if ( context->start_time != AV_NOPTS_VALUE ) - self->start_time = context->start_time; + if ( self->dummy_context ) + { + avformat_lock(); + av_close_input_file( self->dummy_context ); + avformat_unlock(); + self->dummy_context = NULL; + } - // Check if we're seekable - // avdevices are typically AVFMT_NOFILE and not seekable - self->seekable = !format || !( format->flags & AVFMT_NOFILE ); - if ( context->pb ) - { - // protocols can indicate if they support seeking - URLContext *uc = url_fileno( context->pb ); - if ( uc ) - self->seekable = !uc->is_streamed; - } - if ( self->seekable ) - { - self->seekable = av_seek_frame( context, -1, self->start_time, AVSEEK_FLAG_BACKWARD ) >= 0; - mlt_properties_set_int( properties, "seekable", self->seekable ); - self->dummy_context = context; - av_open_input_file( &context, file, NULL, 0, NULL ); - av_find_stream_info( context ); - } + // Unlock the service + if ( take_lock ) + { + pthread_mutex_unlock( &self->audio_mutex ); + pthread_mutex_unlock( &self->video_mutex ); + } + mlt_events_unblock( properties, self->parent ); - // Store selected audio and video indexes on properties - self->audio_index = audio_index; - self->video_index = video_index; - self->first_pts = -1; - self->last_position = POSITION_INITIAL; + return error; +} + +static void reopen_video( producer_avformat self, mlt_producer producer ) +{ + mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); + mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); + pthread_mutex_lock( &self->audio_mutex ); - // Fetch the width, height and aspect ratio - if ( video_index != -1 ) + avformat_lock(); + if ( self->video_codec ) + avcodec_close( self->video_codec ); + self->video_codec = NULL; + if ( self->dummy_context ) + av_close_input_file( self->dummy_context ); + self->dummy_context = NULL; + if ( self->video_format ) + av_close_input_file( self->video_format ); + self->video_format = NULL; + avformat_unlock(); + + int audio_index = self->audio_index; + int video_index = self->video_index; + + producer_open( self, mlt_service_profile( MLT_PRODUCER_SERVICE(producer) ), + mlt_properties_get( properties, "resource" ), 0 ); + + self->audio_index = audio_index; + if ( self->video_format && video_index > -1 ) + { + self->video_index = video_index; + video_codec_init( self, video_index, properties ); + } + + pthread_mutex_unlock( &self->audio_mutex ); + mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); +} + +static int seek_video( producer_avformat self, mlt_position position, + int64_t req_position, int must_decode, int use_new_seek, int *ignore ) +{ + mlt_producer producer = self->parent; + int paused = 0; + + if ( self->seekable && ( position != self->video_expected || self->last_position < 0 ) ) + { + mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); + + // Fetch the video format context + AVFormatContext *context = self->video_format; + + // Get the video stream + AVStream *stream = context->streams[ self->video_index ]; + + // Get codec context + AVCodecContext *codec_context = stream->codec; + + // We may want to use the source fps if available + double source_fps = mlt_properties_get_double( properties, "meta.media.frame_rate_num" ) / + mlt_properties_get_double( properties, "meta.media.frame_rate_den" ); + + if ( self->av_frame && position + 1 == self->video_expected ) + { + // We're paused - use last image + paused = 1; + } + else if ( !self->seekable && position > self->video_expected && ( position - self->video_expected ) < 250 ) + { + // Fast forward - seeking is inefficient for small distances - just ignore following frames + *ignore = ( int )( ( position - self->video_expected ) / mlt_producer_get_fps( producer ) * source_fps ); + codec_context->skip_loop_filter = AVDISCARD_NONREF; + } + else if ( self->seekable && ( position < self->video_expected || position - self->video_expected >= 12 || self->last_position < 0 ) ) + { + if ( use_new_seek && self->last_position == POSITION_INITIAL ) { - AVCodecContext *codec_context = context->streams[ video_index ]->codec; - mlt_properties_set_int( properties, "width", codec_context->width ); - mlt_properties_set_int( properties, "height", codec_context->height ); + // find first key frame + int ret = 0; + int toscan = 100; + AVPacket pkt; - if ( codec_context->codec_id == CODEC_ID_DVVIDEO ) + while ( ret >= 0 && toscan-- > 0 ) { - // Fetch the first frame of DV so we can read it directly - AVPacket pkt; - int ret = 0; - while ( ret >= 0 ) + ret = av_read_frame( context, &pkt ); + if ( ret >= 0 && ( pkt.flags & PKT_FLAG_KEY ) && pkt.stream_index == self->video_index ) { - ret = av_read_frame( context, &pkt ); - if ( ret >= 0 && pkt.stream_index == video_index && pkt.size > 0 ) - { - get_aspect_ratio( properties, context->streams[ video_index ], codec_context, &pkt ); - break; - } + mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "first_pts %"PRId64" dts %"PRId64" pts_dts_delta %d\n", pkt.pts, pkt.dts, (int)(pkt.pts - pkt.dts) ); + self->first_pts = pkt.pts; + toscan = 0; } + av_free_packet( &pkt ); } - else - { - get_aspect_ratio( properties, context->streams[ video_index ], codec_context, NULL ); - } -#ifdef SWSCALE - struct SwsContext *context = sws_getContext( codec_context->width, codec_context->height, codec_context->pix_fmt, - codec_context->width, codec_context->height, PIX_FMT_YUYV422, SWS_BILINEAR, NULL, NULL, NULL); - if ( context ) - sws_freeContext( context ); - else - error = 1; -#endif + // Rewind + av_seek_frame( context, -1, 0, AVSEEK_FLAG_BACKWARD ); + } + + // Calculate the timestamp for the requested frame + int64_t timestamp; + if ( use_new_seek ) + { + timestamp = ( req_position - 0.1 / source_fps ) / + ( av_q2d( stream->time_base ) * source_fps ); + mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "pos %"PRId64" pts %"PRId64"\n", req_position, timestamp ); + if ( self->first_pts > 0 ) + timestamp += self->first_pts; + else if ( context->start_time != AV_NOPTS_VALUE ) + timestamp += context->start_time; } - - // We're going to cheat here - for a/v files, we will have two contexts (reasoning will be clear later) - if ( av == 0 && audio_index != -1 && video_index != -1 ) + else { - // We'll use the open one as our video_format - self->video_format = context; - - // And open again for our audio context - av_open_input_file( &context, file, NULL, 0, NULL ); - av_find_stream_info( context ); - - // Audio context - self->audio_format = context; + timestamp = ( int64_t )( ( double )req_position / source_fps * AV_TIME_BASE + 0.5 ); + if ( context->start_time != AV_NOPTS_VALUE ) + timestamp += context->start_time; } - else if ( av != 2 && video_index != -1 ) + if ( must_decode ) + timestamp -= AV_TIME_BASE; + if ( timestamp < 0 ) + timestamp = 0; + mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "seeking timestamp %"PRId64" position %d expected %d last_pos %"PRId64"\n", + timestamp, position, self->video_expected, self->last_position ); + + // Seek to the timestamp + if ( use_new_seek ) { - // We only have a video context - self->video_format = context; + codec_context->skip_loop_filter = AVDISCARD_NONREF; + av_seek_frame( context, self->video_index, timestamp, AVSEEK_FLAG_BACKWARD ); } - else if ( audio_index != -1 ) + else if ( req_position > 0 || self->last_position <= 0 ) { - // We only have an audio context - self->audio_format = context; + av_seek_frame( context, -1, timestamp, AVSEEK_FLAG_BACKWARD ); } else { - // Something has gone wrong - error = -1; + // Re-open video stream when rewinding to beginning from somewhere else. + // This is rather ugly, and I prefer not to do it this way, but ffmpeg is + // not reliably seeking to the first frame across formats. + reopen_video( self, producer ); } - } - } - // Unlock the mutex now - avformat_unlock( ); + // Remove the cached info relating to the previous position + self->current_position = POSITION_INVALID; + self->last_position = POSITION_INVALID; + av_freep( &self->av_frame ); - return error; + if ( use_new_seek ) + { + // flush any pictures still in decode buffer + avcodec_flush_buffers( codec_context ); + } + } + } + return paused; } /** Convert a frame position to a time code. @@ -757,7 +1018,7 @@ static double producer_time_of_frame( mlt_producer producer, mlt_position positi return ( double )position / mlt_producer_get_fps( producer ); } - // Collect information about all audio streams +// Collect information about all audio streams static void get_audio_streams_info( producer_avformat self ) { @@ -801,6 +1062,7 @@ static void set_luma_transfer( struct SwsContext *context, int colorspace, int u { #if defined(SWSCALE) && (LIBSWSCALE_VERSION_INT >= ((0<<16)+(7<<8)+2)) int *coefficients; + const int *new_coefficients; int full_range; int brightness, contrast, saturation; @@ -816,16 +1078,19 @@ static void set_luma_transfer( struct SwsContext *context, int colorspace, int u case 470: case 601: case 624: - coefficients = sws_getCoefficients( SWS_CS_ITU601 ); + new_coefficients = sws_getCoefficients( SWS_CS_ITU601 ); break; case 240: - coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); + new_coefficients = sws_getCoefficients( SWS_CS_SMPTE240M ); break; case 709: - coefficients = sws_getCoefficients( SWS_CS_ITU709 ); + new_coefficients = sws_getCoefficients( SWS_CS_ITU709 ); + break; + default: + new_coefficients = coefficients; break; } - sws_setColorspaceDetails( context, coefficients, full_range, coefficients, full_range, + sws_setColorspaceDetails( context, new_coefficients, full_range, new_coefficients, full_range, brightness, contrast, saturation ); } #endif @@ -853,7 +1118,7 @@ static inline void convert_image( AVFrame *frame, uint8_t *buffer, int pix_fmt, AVPicture output; avpicture_fill( &output, buffer, PIX_FMT_RGBA, width, height ); set_luma_transfer( context, colorspace, full_range ); - sws_scale( context, frame->data, frame->linesize, 0, height, + sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); } @@ -869,7 +1134,7 @@ static inline void convert_image( AVFrame *frame, uint8_t *buffer, int pix_fmt, output.linesize[1] = width >> 1; output.linesize[2] = width >> 1; set_luma_transfer( context, colorspace, full_range ); - sws_scale( context, frame->data, frame->linesize, 0, height, + sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); } @@ -880,7 +1145,7 @@ static inline void convert_image( AVFrame *frame, uint8_t *buffer, int pix_fmt, AVPicture output; avpicture_fill( &output, buffer, PIX_FMT_RGB24, width, height ); set_luma_transfer( context, colorspace, full_range ); - sws_scale( context, frame->data, frame->linesize, 0, height, + sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); } @@ -891,7 +1156,7 @@ static inline void convert_image( AVFrame *frame, uint8_t *buffer, int pix_fmt, AVPicture output; avpicture_fill( &output, buffer, PIX_FMT_RGBA, width, height ); set_luma_transfer( context, colorspace, full_range ); - sws_scale( context, frame->data, frame->linesize, 0, height, + sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); } @@ -902,7 +1167,7 @@ static inline void convert_image( AVFrame *frame, uint8_t *buffer, int pix_fmt, AVPicture output; avpicture_fill( &output, buffer, PIX_FMT_YUYV422, width, height ); set_luma_transfer( context, colorspace, full_range ); - sws_scale( context, frame->data, frame->linesize, 0, height, + sws_scale( context, (const uint8_t* const*) frame->data, frame->linesize, 0, height, output.data, output.linesize); sws_freeContext( context ); } @@ -974,7 +1239,6 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form { // Get the producer producer_avformat self = mlt_frame_pop_service( frame ); - mlt_service_lock( MLT_PRODUCER_SERVICE(self->parent) ); mlt_producer producer = self->parent; // Get the properties from the frame @@ -986,6 +1250,8 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form // Get the producer properties mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer ); + pthread_mutex_lock( &self->video_mutex ); + // Fetch the video format context AVFormatContext *context = self->video_format; @@ -1001,8 +1267,8 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form if ( self->image_cache ) { mlt_cache_item item = mlt_cache_get( self->image_cache, (void*) position ); - *buffer = mlt_cache_item_data( item, (int*) format ); - if ( *buffer ) + uint8_t *original = mlt_cache_item_data( item, (int*) format ); + if ( original ) { // Set the resolution *width = codec_context->width; @@ -1014,9 +1280,19 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form // Cache hit int size = mlt_image_format_size( *format, *width, *height, NULL ); - mlt_properties_set_data( frame_properties, "avformat.image_cache", item, 0, ( mlt_destructor )mlt_cache_item_close, NULL ); - mlt_frame_set_image( frame, *buffer, size, NULL ); - // self->top_field_first = mlt_properties_get_int( frame_properties, "top_field_first" ); + if ( writable ) + { + *buffer = mlt_pool_alloc( size ); + mlt_frame_set_image( frame, *buffer, size, mlt_pool_release ); + memcpy( *buffer, original, size ); + mlt_cache_item_close( item ); + } + else + { + *buffer = original; + mlt_properties_set_data( frame_properties, "avformat.image_cache", item, 0, ( mlt_destructor )mlt_cache_item_close, NULL ); + mlt_frame_set_image( frame, *buffer, size, NULL ); + } self->got_picture = 1; goto exit_get_image; @@ -1025,151 +1301,44 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form // Cache miss int image_size = 0; - avformat_lock(); - // Packet AVPacket pkt; - // Special case pause handling flag - int paused = 0; - // Special case ffwd handling int ignore = 0; // We may want to use the source fps if available double source_fps = mlt_properties_get_double( properties, "meta.media.frame_rate_num" ) / mlt_properties_get_double( properties, "meta.media.frame_rate_den" ); - double fps = mlt_producer_get_fps( producer ); // This is the physical frame position in the source - int req_position = ( int )( position / fps * source_fps + 0.5 ); + int64_t req_position = ( int64_t )( position / mlt_producer_get_fps( producer ) * source_fps + 0.5 ); // Determines if we have to decode all frames in a sequence // Temporary hack to improve intra frame only - int must_decode = strcmp( codec_context->codec->name, "dnxhd" ) && + int must_decode = !( codec_context->codec && codec_context->codec->name ) || ( + strcmp( codec_context->codec->name, "dnxhd" ) && strcmp( codec_context->codec->name, "dvvideo" ) && strcmp( codec_context->codec->name, "huffyuv" ) && strcmp( codec_context->codec->name, "mjpeg" ) && - strcmp( codec_context->codec->name, "rawvideo" ); - - int last_position = self->last_position; + strcmp( codec_context->codec->name, "rawvideo" ) ); // Turn on usage of new seek API and PTS for seeking - int use_new_seek = codec_context->codec_id == CODEC_ID_H264 && !strcmp( context->iformat->name, "mpegts" ); + int use_new_seek = self->seekable && + codec_context->codec_id == CODEC_ID_H264 && !strcmp( context->iformat->name, "mpegts" ); if ( mlt_properties_get( properties, "new_seek" ) ) use_new_seek = mlt_properties_get_int( properties, "new_seek" ); // Seek if necessary - if ( position != self->video_expected || last_position < 0 ) - { - if ( self->av_frame && position + 1 == self->video_expected ) - { - // We're paused - use last image - paused = 1; - } - else if ( !self->seekable && position > self->video_expected && ( position - self->video_expected ) < 250 ) - { - // Fast forward - seeking is inefficient for small distances - just ignore following frames - ignore = ( int )( ( position - self->video_expected ) / fps * source_fps ); - codec_context->skip_loop_filter = AVDISCARD_NONREF; - } - else if ( self->seekable && ( position < self->video_expected || position - self->video_expected >= 12 || last_position < 0 ) ) - { - if ( use_new_seek && last_position == POSITION_INITIAL ) - { - // find first key frame - int ret = 0; - int toscan = 100; - - while ( ret >= 0 && toscan-- > 0 ) - { - ret = av_read_frame( context, &pkt ); - if ( ret >= 0 && ( pkt.flags & PKT_FLAG_KEY ) && pkt.stream_index == self->video_index ) - { - mlt_log_verbose( MLT_PRODUCER_SERVICE(producer), "first_pts %lld dts %lld pts_dts_delta %d\n", pkt.pts, pkt.dts, (int)(pkt.pts - pkt.dts) ); - self->first_pts = pkt.pts; - toscan = 0; - } - av_free_packet( &pkt ); - } - // Rewind - av_seek_frame( context, -1, 0, AVSEEK_FLAG_BACKWARD ); - } - - // Calculate the timestamp for the requested frame - int64_t timestamp; - if ( use_new_seek ) - { - timestamp = ( req_position - 0.1 / source_fps ) / - ( av_q2d( stream->time_base ) * source_fps ); - mlt_log_verbose( MLT_PRODUCER_SERVICE(producer), "pos %d pts %lld ", req_position, timestamp ); - if ( self->first_pts > 0 ) - timestamp += self->first_pts; - else if ( context->start_time != AV_NOPTS_VALUE ) - timestamp += context->start_time; - } - else - { - timestamp = ( int64_t )( ( double )req_position / source_fps * AV_TIME_BASE + 0.5 ); - if ( context->start_time != AV_NOPTS_VALUE ) - timestamp += context->start_time; - } - if ( must_decode ) - timestamp -= AV_TIME_BASE; - if ( timestamp < 0 ) - timestamp = 0; - mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "seeking timestamp %lld position %d expected %d last_pos %d\n", - timestamp, position, self->video_expected, last_position ); - - // Seek to the timestamp - if ( use_new_seek ) - { - codec_context->skip_loop_filter = AVDISCARD_NONREF; - av_seek_frame( context, self->video_index, timestamp, AVSEEK_FLAG_BACKWARD ); - } - else if ( timestamp > 0 || last_position <= 0 ) - { - av_seek_frame( context, -1, timestamp, AVSEEK_FLAG_BACKWARD ); - } - else - { - // Re-open video stream when rewinding to beginning from somewhere else. - // This is rather ugly, and I prefer not to do it this way, but ffmpeg is - // not reliably seeking to the first frame across formats. - if ( self->video_codec ) - avcodec_close( self->video_codec ); - self->video_codec = NULL; - if ( self->dummy_context ) - av_close_input_file( self->dummy_context ); - self->dummy_context = NULL; - if ( self->video_format ) - av_close_input_file( self->video_format ); - self->video_format = NULL; - avformat_unlock(); - producer_set_up_video( self, frame ); - if ( self->video_index < 0 ) - return 1; - avformat_lock(); - context = self->video_format; - stream = context->streams[ self->video_index ]; - codec_context = stream->codec; - } - - // Remove the cached info relating to the previous position - self->current_position = POSITION_INVALID; - self->last_position = POSITION_INVALID; - av_freep( &self->av_frame ); + int paused = seek_video( self, position, req_position, must_decode, use_new_seek, &ignore ); - if ( use_new_seek ) - { - // flush any pictures still in decode buffer - avcodec_flush_buffers( codec_context ); - } - } - } + // Seek might have reopened the file + context = self->video_format; + stream = context->streams[ self->video_index ]; + codec_context = stream->codec; // Duplicate the last image if necessary - if ( self->av_frame && self->av_frame->linesize[0] && self->got_picture && self->seekable + if ( self->av_frame && self->av_frame->linesize[0] && self->got_picture && ( paused || self->current_position == req_position || ( !use_new_seek && self->current_position > req_position ) ) ) @@ -1204,7 +1373,7 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form else { int ret = 0; - int int_position = 0; + int64_t int_position = 0; int decode_errors = 0; int got_picture = 0; @@ -1217,7 +1386,27 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form while( ret >= 0 && !got_picture ) { // Read a packet - ret = av_read_frame( context, &pkt ); + pthread_mutex_lock( &self->packets_mutex ); + if ( mlt_deque_count( self->vpackets ) ) + { + AVPacket *tmp = (AVPacket*) mlt_deque_pop_front( self->vpackets ); + pkt = *tmp; + free( tmp ); + } + else + { + ret = av_read_frame( context, &pkt ); + if ( ret >= 0 && !self->seekable && pkt.stream_index == self->audio_index ) + { + if ( !av_dup_packet( &pkt ) ) + { + AVPacket *tmp = malloc( sizeof(AVPacket) ); + *tmp = pkt; + mlt_deque_push_back( self->apackets, tmp ); + } + } + } + pthread_mutex_unlock( &self->packets_mutex ); // We only deal with video from the selected video_index if ( ret >= 0 && pkt.stream_index == self->video_index && pkt.size > 0 ) @@ -1230,7 +1419,7 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form pts -= self->first_pts; else if ( context->start_time != AV_NOPTS_VALUE ) pts -= context->start_time; - int_position = ( int )( av_q2d( stream->time_base ) * pts * source_fps + 0.1 ); + int_position = ( int64_t )( av_q2d( stream->time_base ) * pts * source_fps + 0.1 ); if ( pkt.pts == AV_NOPTS_VALUE ) { self->invalid_pts_counter++; @@ -1246,25 +1435,21 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form { self->invalid_pts_counter = 0; } - mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "pkt.pts %llu req_pos %d cur_pos %d pkt_pos %d\n", + mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "pkt.pts %"PRId64" req_pos %"PRId64" cur_pos %"PRId64" pkt_pos %"PRId64"\n", pkt.pts, req_position, self->current_position, int_position ); } else { - if ( self->seekable && pkt.dts != AV_NOPTS_VALUE ) + if ( pkt.dts != AV_NOPTS_VALUE ) { - int_position = ( int )( av_q2d( stream->time_base ) * pkt.dts * source_fps + 0.5 ); + double delay = mlt_properties_get_double( properties, "video_delay" ); + int_position = ( int64_t )( ( av_q2d( stream->time_base ) * pkt.dts + delay ) * source_fps + 0.5 ); if ( context->start_time != AV_NOPTS_VALUE ) - int_position -= ( int )( context->start_time * source_fps / AV_TIME_BASE + 0.5 ); - last_position = self->last_position; - if ( int_position == last_position ) - int_position = last_position + 1; - } - else - { - int_position = req_position; + int_position -= ( int64_t )( context->start_time * source_fps / AV_TIME_BASE + 0.5 ); + if ( int_position == self->last_position ) + int_position = self->last_position + 1; } - mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "pkt.dts %llu req_pos %d cur_pos %d pkt_pos %d\n", + mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "pkt.dts %"PRId64" req_pos %"PRId64" cur_pos %"PRId64" pkt_pos %"PRId64"\n", pkt.dts, req_position, self->current_position, int_position ); // Make a dumb assumption on streams that contain wild timestamps if ( abs( req_position - int_position ) > 999 ) @@ -1320,8 +1505,8 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form pts -= self->first_pts; else if ( context->start_time != AV_NOPTS_VALUE ) pts -= context->start_time; - int_position = ( int )( av_q2d( stream->time_base) * pts * source_fps + 0.1 ); - mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "got frame %d, key %d\n", int_position, self->av_frame->key_frame ); + int_position = ( int64_t )( av_q2d( stream->time_base) * pts * source_fps + 0.1 ); + mlt_log_debug( MLT_PRODUCER_SERVICE(producer), "got frame %"PRId64", key %d\n", int_position, self->av_frame->key_frame ); } // Handle ignore if ( int_position < req_position ) @@ -1378,13 +1563,13 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form else { mlt_log_error( MLT_PRODUCER_SERVICE(producer), "VDPAU Error: %s\n", vdp_get_error_string( status ) ); - self->vdpau->is_decoded = 0; + image_size = self->vdpau->is_decoded = 0; } } else { mlt_log_error( MLT_PRODUCER_SERVICE(producer), "VDPAU error in VdpDecoderRender\n" ); - got_picture = 0; + image_size = got_picture = 0; } } else @@ -1400,13 +1585,11 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form got_picture = 0; } } - if ( ret >= 0 ) + if ( self->seekable || pkt.stream_index != self->audio_index ) av_free_packet( &pkt ); } } - avformat_unlock(); - if ( self->got_picture && image_size > 0 && self->image_cache ) { // Copy buffer to image cache @@ -1414,11 +1597,45 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form memcpy( image, *buffer, image_size ); mlt_cache_put( self->image_cache, (void*) position, image, *format, mlt_pool_release ); } + // Try to duplicate last image if there was a decoding failure + else if ( !image_size && self->av_frame && self->av_frame->linesize[0] ) + { + // Duplicate it + if ( ( image_size = allocate_buffer( frame, codec_context, buffer, format, width, height ) ) ) + { + // Workaround 1088 encodings missing cropping info. + if ( *height == 1088 && mlt_profile_dar( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) ) == 16.0/9.0 ) + *height = 1080; +#ifdef VDPAU + if ( self->vdpau && self->vdpau->buffer ) + { + AVPicture picture; + picture.data[0] = self->vdpau->buffer; + picture.data[2] = self->vdpau->buffer + codec_context->width * codec_context->height; + picture.data[1] = self->vdpau->buffer + codec_context->width * codec_context->height * 5 / 4; + picture.linesize[0] = codec_context->width; + picture.linesize[1] = codec_context->width / 2; + picture.linesize[2] = codec_context->width / 2; + convert_image( (AVFrame*) &picture, *buffer, + PIX_FMT_YUV420P, format, *width, *height, self->colorspace ); + } + else +#endif + convert_image( self->av_frame, *buffer, codec_context->pix_fmt, + format, *width, *height, self->colorspace ); + self->got_picture = 1; + } + else + mlt_frame_get_image( frame, buffer, format, width, height, writable ); + } // Regardless of speed, we expect to get the next frame (cos we ain't too bright) self->video_expected = position + 1; exit_get_image: + + pthread_mutex_unlock( &self->video_mutex ); + // Set the progressive flag if ( mlt_properties_get( properties, "force_progressive" ) ) mlt_properties_set_int( frame_properties, "progressive", !!mlt_properties_get_int( properties, "force_progressive" ) ); @@ -1432,10 +1649,10 @@ exit_get_image: mlt_properties_set_int( frame_properties, "top_field_first", self->top_field_first ); // Set immutable properties of the selected track's (or overridden) source attributes. + mlt_service_lock( MLT_PRODUCER_SERVICE( producer ) ); mlt_properties_set_int( properties, "meta.media.top_field_first", self->top_field_first ); mlt_properties_set_int( properties, "meta.media.progressive", mlt_properties_get_int( frame_properties, "progressive" ) ); - - mlt_service_unlock( MLT_PRODUCER_SERVICE(self->parent) ); + mlt_service_unlock( MLT_PRODUCER_SERVICE( producer ) ); return !self->got_picture; } @@ -1503,10 +1720,7 @@ static int video_codec_init( producer_avformat self, int index, mlt_properties p 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; - } // If we don't have a codec and we can't initialise it, we can't do much more... avformat_lock( ); @@ -1519,11 +1733,17 @@ static int video_codec_init( producer_avformat self, int index, mlt_properties p { // Remember that we can't use this later self->video_index = -1; + avformat_unlock( ); + return 0; } avformat_unlock( ); // Process properties as AVOptions apply_properties( codec_context, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM ); +#if LIBAVCODEC_VERSION_MAJOR >= 53 + if ( codec->priv_class && codec_context->priv_data ) + apply_properties( codec_context->priv_data, properties, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM ); +#endif // Reset some image properties mlt_properties_set_int( properties, "width", self->video_codec->width ); @@ -1640,23 +1860,9 @@ static void producer_set_up_video( producer_avformat self, mlt_frame frame ) // Reopen the file if necessary if ( !context && index > -1 ) { - mlt_events_block( properties, producer ); producer_open( self, mlt_service_profile( MLT_PRODUCER_SERVICE(producer) ), - mlt_properties_get( properties, "resource" ) ); + mlt_properties_get( properties, "resource" ), 1 ); context = self->video_format; - if ( self->dummy_context ) - { - avformat_lock(); - av_close_input_file( self->dummy_context ); - avformat_unlock(); - } - self->dummy_context = NULL; - mlt_events_unblock( properties, producer ); - if ( self->audio_format ) - get_audio_streams_info( self ); - - // Process properties as AVOptions - apply_properties( context, properties, AV_OPT_FLAG_DECODING_PARAM ); } // Exception handling for video_index @@ -1680,13 +1886,11 @@ static void producer_set_up_video( producer_avformat self, mlt_frame frame ) { // Reset the video properties if the index changed self->video_index = index; + avformat_lock(); if ( self->video_codec ) - { - avformat_lock(); avcodec_close( self->video_codec ); - avformat_unlock(); - } self->video_codec = NULL; + avformat_unlock(); } // Get the frame properties @@ -1734,7 +1938,7 @@ static int seek_audio( producer_avformat self, mlt_position position, double tim int paused = 0; // Seek if necessary - if ( position != self->audio_expected ) + if ( self->seekable && position != self->audio_expected ) { if ( position + 1 == self->audio_expected ) { @@ -1756,10 +1960,8 @@ static int seek_audio( producer_avformat self, mlt_position position, double tim timestamp = 0; // Set to the real timecode - avformat_lock(); if ( av_seek_frame( context, -1, timestamp, AVSEEK_FLAG_BACKWARD ) != 0 ) paused = 1; - avformat_unlock(); // Clear the usage in the audio buffer int i = MAX_AUDIO_STREAMS + 1; @@ -1770,6 +1972,15 @@ static int seek_audio( producer_avformat self, mlt_position position, double tim return paused; } +static int sample_bytes( AVCodecContext *context ) +{ +#if LIBAVCODEC_VERSION_MAJOR >= 53 + return av_get_bits_per_sample_fmt( context->sample_fmt ) / 8; +#else + return av_get_bits_per_sample_format( context->sample_fmt ) / 8; +#endif +} + static int decode_audio( producer_avformat self, int *ignore, AVPacket pkt, int channels, int samples, double timecode, double fps ) { // Fetch the audio_format @@ -1785,8 +1996,8 @@ static int decode_audio( producer_avformat self, int *ignore, AVPacket pkt, int ReSampleContext *resample = self->audio_resample[ index ]; // Obtain the audio buffers - int16_t *audio_buffer = self->audio_buffer[ index ]; - int16_t *decode_buffer = self->decode_buffer[ index ]; + uint8_t *audio_buffer = self->audio_buffer[ index ]; + uint8_t *decode_buffer = self->decode_buffer[ index ]; int audio_used = self->audio_used[ index ]; uint8_t *ptr = pkt.data; @@ -1795,11 +2006,12 @@ static int decode_audio( producer_avformat self, int *ignore, AVPacket pkt, int while ( ptr && ret >= 0 && len > 0 ) { - int data_size = sizeof( int16_t ) * AVCODEC_MAX_AUDIO_FRAME_SIZE; + int sizeof_sample = resample? sizeof( int16_t ) : sample_bytes( codec_context ); + int data_size = self->audio_buffer_size[ index ]; // Decode the audio #if (LIBAVCODEC_VERSION_INT >= ((52<<16)+(26<<8)+0)) - ret = avcodec_decode_audio3( codec_context, decode_buffer, &data_size, &pkt ); + ret = avcodec_decode_audio3( codec_context, (int16_t*) decode_buffer, &data_size, &pkt ); #elif (LIBAVCODEC_VERSION_INT >= ((51<<16)+(29<<8)+0)) ret = avcodec_decode_audio2( codec_context, decode_buffer, &data_size, ptr, len ); #else @@ -1818,38 +2030,36 @@ static int decode_audio( producer_avformat self, int *ignore, AVPacket pkt, int if ( data_size > 0 ) { // Figure out how many samples will be needed after resampling - int convert_samples = data_size / codec_context->channels / ( av_get_bits_per_sample_format( codec_context->sample_fmt ) / 8 ); - int samples_needed = self->resample_factor * convert_samples + 1; - + int convert_samples = data_size / codec_context->channels / sample_bytes( codec_context ); + int samples_needed = self->resample_factor * convert_samples; + // Resize audio buffer to prevent overflow - if ( audio_used * channels + samples_needed > self->audio_buffer_size[ index ] ) + if ( ( audio_used + samples_needed ) * channels * sizeof_sample > self->audio_buffer_size[ index ] ) { - self->audio_buffer_size[ index ] *= 2; - audio_buffer = self->audio_buffer[ index ] = mlt_pool_realloc( audio_buffer, self->audio_buffer_size[ index ] * sizeof(int16_t) ); + self->audio_buffer_size[ index ] = ( audio_used + samples_needed * 2 ) * channels * sizeof_sample; + audio_buffer = self->audio_buffer[ index ] = mlt_pool_realloc( audio_buffer, self->audio_buffer_size[ index ] ); } if ( resample ) { // Copy to audio buffer while resampling - int16_t *source = decode_buffer; - int16_t *dest = &audio_buffer[ audio_used * channels ]; - avformat_lock(); - audio_used += audio_resample( resample, dest, source, convert_samples ); - avformat_unlock(); + uint8_t *source = decode_buffer; + uint8_t *dest = &audio_buffer[ audio_used * channels * sizeof_sample ]; + audio_used += audio_resample( resample, (short*) dest, (short*) source, convert_samples ); } else { // Straight copy to audio buffer - memcpy( &audio_buffer[ audio_used * codec_context->channels ], decode_buffer, data_size ); + memcpy( &audio_buffer[ audio_used * codec_context->channels * sizeof_sample ], decode_buffer, data_size ); audio_used += convert_samples; } // Handle ignore - while ( *ignore && audio_used > samples ) + while ( *ignore && audio_used ) { *ignore -= 1; - audio_used -= samples; - memmove( audio_buffer, &audio_buffer[ samples * (resample? channels : codec_context->channels) ], - audio_used * sizeof( int16_t ) ); + audio_used -= audio_used > samples ? samples : audio_used; + memmove( audio_buffer, &audio_buffer[ samples * (resample? channels : codec_context->channels) * sizeof_sample ], + audio_used * sizeof_sample ); } } } @@ -1858,17 +2068,21 @@ static int decode_audio( producer_avformat self, int *ignore, AVPacket pkt, int if ( pkt.pts >= 0 ) { double current_pts = av_q2d( context->streams[ index ]->time_base ) * pkt.pts; - int req_position = ( int )( timecode * fps + 0.5 ); - int int_position = ( int )( current_pts * fps + 0.5 ); + int64_t req_position = ( int64_t )( timecode * fps + 0.5 ); + int64_t int_position = ( int64_t )( current_pts * fps + 0.5 ); if ( context->start_time != AV_NOPTS_VALUE ) - int_position -= ( int )( fps * context->start_time / AV_TIME_BASE + 0.5 ); + int_position -= ( int64_t )( fps * context->start_time / AV_TIME_BASE + 0.5 ); - if ( self->seekable && *ignore == 0 ) + if ( *ignore == 0 ) { if ( int_position < req_position ) // We are behind, so skip some - *ignore = 1; - else if ( int_position > req_position + 2 ) + *ignore = req_position - int_position; + + // We use total_channels in this test because the tolerance is dependent + // on the interleaving of all streams esp. when there is more than + // one audio stream. + else if ( int_position > req_position + self->total_channels ) // We are ahead, so seek backwards some more seek_audio( self, req_position, timecode - 1.0, ignore ); } @@ -1881,13 +2095,12 @@ static int decode_audio( producer_avformat self, int *ignore, AVPacket pkt, int /** Get the audio from a frame. */ - static int producer_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples ) { // Get the producer producer_avformat self = mlt_frame_pop_audio( frame ); - mlt_service_lock( MLT_PRODUCER_SERVICE(self->parent) ); + pthread_mutex_lock( &self->audio_mutex ); // Obtain the frame number of this frame mlt_position position = mlt_properties_get_position( MLT_FRAME_PROPERTIES( frame ), "avformat_position" ); @@ -1899,13 +2112,20 @@ static int producer_get_audio( mlt_frame frame, void **buffer, mlt_audio_format double fps = mlt_producer_get_fps( self->parent ); // Number of frames to ignore (for ffwd) - int ignore = 0; + int ignore[ MAX_AUDIO_STREAMS ] = { 0 }; // Flag for paused (silence) - int paused = seek_audio( self, position, real_timecode, &ignore ); + int paused = seek_audio( self, position, real_timecode, &ignore[0] ); + + // Initialize ignore for all streams from the seek return value + int i = MAX_AUDIO_STREAMS; + while ( i-- ) + ignore[i] = ignore[0]; // Fetch the audio_format AVFormatContext *context = self->audio_format; + + int sizeof_sample = sizeof( int16_t ); // Determine the tracks to use int index = self->audio_index; @@ -1951,21 +2171,22 @@ static int producer_get_audio( mlt_frame frame, void **buffer, mlt_audio_format else { codec_context->request_channels = self->audio_index == INT_MAX ? codec_context->channels : *channels; + sizeof_sample = sample_bytes( codec_context ); } // Check for audio buffer and create if necessary - self->audio_buffer_size[ index ] = AVCODEC_MAX_AUDIO_FRAME_SIZE; - self->audio_buffer[ index ] = mlt_pool_alloc( self->audio_buffer_size[ index ] * sizeof( int16_t ) ); + self->audio_buffer_size[ index ] = AVCODEC_MAX_AUDIO_FRAME_SIZE * sizeof_sample; + self->audio_buffer[ index ] = mlt_pool_alloc( self->audio_buffer_size[ index ] ); // Check for decoder buffer and create if necessary - self->decode_buffer[ index ] = av_malloc( AVCODEC_MAX_AUDIO_FRAME_SIZE * sizeof( int16_t ) ); + self->decode_buffer[ index ] = av_malloc( self->audio_buffer_size[ index ] ); } } // Get the audio if required if ( !paused ) { - int ret = 0; + int ret = 0; int got_audio = 0; AVPacket pkt; @@ -1980,48 +2201,97 @@ static int producer_get_audio( mlt_frame frame, void **buffer, mlt_audio_format while ( ret >= 0 && !got_audio ) { // Check if the buffer already contains the samples required - if ( self->audio_index != INT_MAX && self->audio_used[ self->audio_index ] >= *samples && ignore == 0 ) + if ( self->audio_index != INT_MAX && + self->audio_used[ self->audio_index ] >= *samples && + ignore[ self->audio_index ] == 0 ) { got_audio = 1; break; } + else if ( self->audio_index == INT_MAX ) + { + // Check if there is enough audio for all streams + got_audio = 1; + for ( index = 0; got_audio && index < context->nb_streams; index++ ) + if ( ( self->audio_codec[ index ] && self->audio_used[ index ] < *samples ) || ignore[ index ] ) + got_audio = 0; + if ( got_audio ) + break; + } // Read a packet - avformat_lock(); - ret = av_read_frame( context, &pkt ); - avformat_unlock(); + pthread_mutex_lock( &self->packets_mutex ); + if ( mlt_deque_count( self->apackets ) ) + { + AVPacket *tmp = (AVPacket*) mlt_deque_pop_front( self->apackets ); + pkt = *tmp; + free( tmp ); + } + else + { + ret = av_read_frame( context, &pkt ); + if ( ret >= 0 && !self->seekable && pkt.stream_index == self->video_index ) + { + if ( !av_dup_packet( &pkt ) ) + { + AVPacket *tmp = malloc( sizeof(AVPacket) ); + *tmp = pkt; + mlt_deque_push_back( self->vpackets, tmp ); + } + } + } + pthread_mutex_unlock( &self->packets_mutex ); // We only deal with audio from the selected audio index - if ( ret >= 0 && pkt.data && pkt.size > 0 && ( pkt.stream_index == self->audio_index || - ( self->audio_index == INT_MAX && context->streams[ pkt.stream_index ]->codec->codec_type == CODEC_TYPE_AUDIO ) ) ) + index = pkt.stream_index; + if ( ret >= 0 && pkt.data && pkt.size > 0 && ( index == self->audio_index || + ( self->audio_index == INT_MAX && context->streams[ index ]->codec->codec_type == CODEC_TYPE_AUDIO ) ) ) { - int channels2 = self->audio_index == INT_MAX ? self->audio_codec[pkt.stream_index]->channels : *channels; - ret = decode_audio( self, &ignore, pkt, channels2, *samples, real_timecode, fps ); + int channels2 = ( self->audio_index == INT_MAX || !self->audio_resample[index] ) ? + self->audio_codec[index]->channels : *channels; + ret = decode_audio( self, &ignore[index], pkt, channels2, *samples, real_timecode, fps ); } - av_free_packet( &pkt ); - if ( self->audio_index == INT_MAX && ret >= 0 ) - { - // Determine if there is enough audio for all streams - got_audio = 1; - for ( index = 0; index < context->nb_streams; index++ ) + if ( self->seekable || index != self->video_index ) + av_free_packet( &pkt ); + + } + + // Set some additional return values + *format = mlt_audio_s16; + if ( self->audio_index != INT_MAX && !self->audio_resample[ self->audio_index ] ) + { + index = self->audio_index; + *channels = self->audio_codec[ index ]->channels; + *frequency = self->audio_codec[ index ]->sample_rate; + *format = self->audio_codec[ index ]->sample_fmt == SAMPLE_FMT_S32 ? mlt_audio_s32le + : self->audio_codec[ index ]->sample_fmt == SAMPLE_FMT_FLT ? mlt_audio_f32le + : mlt_audio_s16; + sizeof_sample = sample_bytes( self->audio_codec[ index ] ); + } + else if ( self->audio_index == INT_MAX ) + { + // This only works if all audio tracks have the same sample format. + for ( index = 0; index < index_max; index++ ) + if ( self->audio_codec[ index ] && !self->audio_resample[ index ] ) { - if ( self->audio_codec[ index ] && self->audio_used[ index ] < *samples ) - got_audio = 0; + *format = self->audio_codec[ index ]->sample_fmt == SAMPLE_FMT_S32 ? mlt_audio_s32le + : self->audio_codec[ index ]->sample_fmt == SAMPLE_FMT_FLT ? mlt_audio_f32le + : mlt_audio_s16; + sizeof_sample = sample_bytes( self->audio_codec[ index ] ); + break; } - } } - + // Allocate and set the frame's audio buffer - int size = *samples * *channels * sizeof(int16_t); + int size = mlt_audio_format_size( *format, *samples, *channels ); *buffer = mlt_pool_alloc( size ); - *format = mlt_audio_s16; mlt_frame_set_audio( frame, *buffer, *format, size, mlt_pool_release ); // Interleave tracks if audio_index=all if ( self->audio_index == INT_MAX ) { - int16_t *dest = *buffer; + uint8_t *dest = *buffer; int i; for ( i = 0; i < *samples; i++ ) { @@ -2029,18 +2299,18 @@ static int producer_get_audio( mlt_frame frame, void **buffer, mlt_audio_format if ( self->audio_codec[ index ] ) { int current_channels = self->audio_codec[ index ]->channels; - int16_t *src = self->audio_buffer[ index ] + i * current_channels; - memcpy( dest, src, current_channels * sizeof(int16_t) ); - dest += current_channels; + uint8_t *src = self->audio_buffer[ index ] + i * current_channels * sizeof_sample; + memcpy( dest, src, current_channels * sizeof_sample ); + dest += current_channels * sizeof_sample; } } for ( index = 0; index < index_max; index++ ) if ( self->audio_codec[ index ] && self->audio_used[ index ] >= *samples ) { int current_channels = self->audio_codec[ index ]->channels; - int16_t *src = self->audio_buffer[ index ] + *samples * current_channels; + uint8_t *src = self->audio_buffer[ index ] + *samples * current_channels * sizeof_sample; self->audio_used[index] -= *samples; - memmove( self->audio_buffer[ index ], src, self->audio_used[ index ] * current_channels * sizeof(int16_t) ); + memmove( self->audio_buffer[ index ], src, self->audio_used[ index ] * current_channels * sizeof_sample ); } } // Copy a single track to the output buffer @@ -2051,22 +2321,21 @@ static int producer_get_audio( mlt_frame frame, void **buffer, mlt_audio_format // Now handle the audio if we have enough if ( self->audio_used[ index ] > 0 ) { - int16_t *src = self->audio_buffer[ index ]; - *samples = self->audio_used[ index ] < *samples ? self->audio_used[ index ] : *samples; - memcpy( *buffer, src, *samples * *channels * sizeof(int16_t) ); - self->audio_used[ index ] -= *samples; - memmove( src, &src[ *samples * *channels ], self->audio_used[ index ] * *channels * sizeof(int16_t) ); + uint8_t *src = self->audio_buffer[ index ]; + // copy samples from audio_buffer + size = self->audio_used[ index ] < *samples ? self->audio_used[ index ] : *samples; + memcpy( *buffer, src, size * *channels * sizeof_sample ); + // supply the remaining requested samples as silence + if ( *samples > self->audio_used[ index ] ) + memset( *buffer + size * *channels * sizeof_sample, 0, ( *samples - self->audio_used[ index ] ) * *channels * sizeof_sample ); + // reposition the samples within audio_buffer + self->audio_used[ index ] -= size; + memmove( src, src + size * *channels * sizeof_sample, self->audio_used[ index ] * *channels * sizeof_sample ); } else { // Otherwise fill with silence - memset( *buffer, 0, *samples * *channels * sizeof(int16_t) ); - } - if ( !self->audio_resample[ index ] ) - { - // TODO: uncomment and remove following line when full multi-channel support is ready - // *channels = codec_context->channels; - *frequency = self->audio_codec[ index ]->sample_rate; + memset( *buffer, 0, *samples * *channels * sizeof_sample ); } } } @@ -2080,7 +2349,7 @@ static int producer_get_audio( mlt_frame frame, void **buffer, mlt_audio_format if ( !paused ) self->audio_expected = position + 1; - mlt_service_unlock( MLT_PRODUCER_SERVICE(self->parent) ); + pthread_mutex_unlock( &self->audio_mutex ); return 0; } @@ -2117,6 +2386,10 @@ static int audio_codec_init( producer_avformat self, int index, mlt_properties p // Process properties as AVOptions apply_properties( codec_context, properties, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM ); +#if LIBAVCODEC_VERSION_MAJOR >= 53 + if ( codec && codec->priv_class && codec_context->priv_data ) + apply_properties( codec_context->priv_data, properties, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM ); +#endif } return self->audio_codec[ index ] && self->audio_index > -1; } @@ -2149,19 +2422,9 @@ static void producer_set_up_audio( producer_avformat self, mlt_frame frame ) // Reopen the file if necessary if ( !context && self->audio_index > -1 && index > -1 ) { - mlt_events_block( properties, producer ); producer_open( self, mlt_service_profile( MLT_PRODUCER_SERVICE(producer) ), - mlt_properties_get( properties, "resource" ) ); + mlt_properties_get( properties, "resource" ), 1 ); context = self->audio_format; - if ( self->dummy_context ) - { - avformat_lock(); - av_close_input_file( self->dummy_context ); - avformat_unlock(); - } - self->dummy_context = NULL; - mlt_events_unblock( properties, producer ); - get_audio_streams_info( self ); } // Exception handling for audio_index @@ -2182,13 +2445,11 @@ static void producer_set_up_audio( producer_avformat self, mlt_frame frame ) // Update the audio properties if the index changed if ( context && index > -1 && index != self->audio_index ) { + avformat_lock(); if ( self->audio_codec[ self->audio_index ] ) - { - avformat_lock(); avcodec_close( self->audio_codec[ self->audio_index ] ); - avformat_unlock(); - } self->audio_codec[ self->audio_index ] = NULL; + avformat_unlock(); } if ( self->audio_index != -1 ) self->audio_index = index; @@ -2198,8 +2459,8 @@ static void producer_set_up_audio( producer_avformat self, mlt_frame frame ) // Get the codec(s) if ( context && index == INT_MAX ) { - mlt_properties_set_int( frame_properties, "frequency", self->max_frequency ); - mlt_properties_set_int( frame_properties, "channels", self->total_channels ); + mlt_properties_set_int( frame_properties, "audio_frequency", self->max_frequency ); + mlt_properties_set_int( frame_properties, "audio_channels", self->total_channels ); for ( index = 0; index < context->nb_streams; index++ ) { if ( context->streams[ index ]->codec->codec_type == CODEC_TYPE_AUDIO ) @@ -2259,15 +2520,16 @@ static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int i // Update timecode on the frame we're creating mlt_frame_set_position( *frame, mlt_producer_position( producer ) ); - // Set the position of this producer - mlt_properties_set_position( MLT_FRAME_PROPERTIES( *frame ), "avformat_position", mlt_producer_frame( producer ) ); - // Set up the video producer_set_up_video( self, *frame ); // Set up the audio producer_set_up_audio( self, *frame ); + // Set the position of this producer + mlt_position position = self->seekable ? mlt_producer_frame( producer ) : self->nonseek_position++; + mlt_properties_set_position( MLT_FRAME_PROPERTIES( *frame ), "avformat_position", position ); + // Calculate the next timecode mlt_producer_prepare_next( producer ); @@ -2277,7 +2539,8 @@ static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int i static void producer_avformat_close( producer_avformat self ) { mlt_log_debug( NULL, "producer_avformat_close\n" ); - // Close the file + + // Cleanup av contexts av_free( self->av_frame ); avformat_lock(); int i; @@ -2289,12 +2552,15 @@ static void producer_avformat_close( producer_avformat self ) av_free( self->decode_buffer[i] ); if ( self->audio_codec[i] ) avcodec_close( self->audio_codec[i] ); + self->audio_codec[i] = NULL; } if ( self->video_codec ) avcodec_close( self->video_codec ); + self->video_codec = NULL; + // Close the file if ( self->dummy_context ) av_close_input_file( self->dummy_context ); - if ( self->audio_format ) + if ( self->seekable && self->audio_format ) av_close_input_file( self->audio_format ); if ( self->video_format ) av_close_input_file( self->video_format ); @@ -2304,11 +2570,33 @@ static void producer_avformat_close( producer_avformat self ) #endif if ( self->image_cache ) mlt_cache_close( self->image_cache ); + + // Cleanup the mutexes + pthread_mutex_destroy( &self->audio_mutex ); + pthread_mutex_destroy( &self->video_mutex ); + pthread_mutex_destroy( &self->packets_mutex ); + + // Cleanup the packet queues + AVPacket *pkt; + while ( ( pkt = mlt_deque_pop_back( self->apackets ) ) ) + { + av_free_packet( pkt ); + free( pkt ); + } + while ( ( pkt = mlt_deque_pop_back( self->vpackets ) ) ) + { + av_free_packet( pkt ); + free( pkt ); + } + free( self ); } static void producer_close( mlt_producer parent ) { + // Remove this instance from the cache + mlt_service_cache_purge( MLT_PRODUCER_SERVICE(parent) ); + // Close the parent parent->close = NULL; mlt_producer_close( parent );