X-Git-Url: http://research.m1stereo.tv/gitweb?a=blobdiff_plain;f=src%2Fmodules%2Favformat%2Fproducer_avformat.c;h=bf3654f311c80224acb69fec463595b7b7a991ff;hb=7067858fa9cf333f072778d30d31947ca095a308;hp=d0ff408caec8298d38152e0f3ce1b72bedbdbbcd;hpb=6269c02322276247410e28545a1a4e07af6e5e5b;p=melted diff --git a/src/modules/avformat/producer_avformat.c b/src/modules/avformat/producer_avformat.c index d0ff408..bf3654f 100644 --- a/src/modules/avformat/producer_avformat.c +++ b/src/modules/avformat/producer_avformat.c @@ -2,30 +2,32 @@ * 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 program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * 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 #include +#ifdef SWSCALE +#include +#endif // System header files #include @@ -37,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; @@ -66,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" ) ); + } } } @@ -89,10 +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 ) @@ -101,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; } } } @@ -147,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; @@ -159,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_properties_get_double( properties, "fps" ); + double fps = mlt_profile_fps( profile ); // Lock the mutex now avformat_lock( ); @@ -197,11 +213,13 @@ static int producer_open( mlt_producer this, char *file ) params->width = 640; params->height = 480; params->time_base= (AVRational){1,25}; - params->device = file; + // params->device = file; params->channels = 2; params->sample_rate = 48000; } + // XXX: this does not work anymore since avdevice + // TODO: make producer_avddevice? // Parse out params mrl = strchr( file, '?' ); while ( mrl ) @@ -266,26 +284,32 @@ static int producer_open( mlt_producer this, char *file ) if ( context->duration != AV_NOPTS_VALUE ) { // This isn't going to be accurate for all formats - mlt_position frames = ( mlt_position )( ( ( double )context->duration / ( double )AV_TIME_BASE ) * fps ); - mlt_properties_set_position( properties, "out", frames - 2 ); - mlt_properties_set_position( properties, "length", frames - 1 ); + mlt_position frames = ( mlt_position )( ( ( double )context->duration / ( double )AV_TIME_BASE ) * fps + 0.5 ); + mlt_properties_set_position( properties, "out", frames - 1 ); + mlt_properties_set_position( properties, "length", frames ); } // 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 ); + av_open_input_file( &context, file, NULL, 0, NULL ); + av_find_stream_info( context ); + } else 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 if ( video_index != -1 ) @@ -295,9 +319,25 @@ static int producer_open( mlt_producer this, char *file ) mlt_properties_set_int( properties, "height", codec_context->height ); mlt_properties_set_double( properties, "aspect_ratio", av_q2d( codec_context->sample_aspect_ratio ) ); } + + // Read Metadata + if (context->title != NULL) + mlt_properties_set(properties, "meta.attr.title.markup", context->title ); + if (context->author != NULL) + mlt_properties_set(properties, "meta.attr.author.markup", context->author ); + if (context->copyright != NULL) + mlt_properties_set(properties, "meta.attr.copyright.markup", context->copyright ); + if (context->comment != NULL) + mlt_properties_set(properties, "meta.attr.comment.markup", context->comment ); + if (context->album != NULL) + mlt_properties_set(properties, "meta.attr.album.markup", context->album ); + if (context->year != 0) + mlt_properties_set_int(properties, "meta.attr.year.markup", context->year ); + if (context->track != 0) + 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 ); @@ -340,18 +380,48 @@ static int producer_open( mlt_producer this, char *file ) static double producer_time_of_frame( mlt_producer this, mlt_position position ) { - // Get the properties - mlt_properties properties = MLT_PRODUCER_PROPERTIES( this ); - - // Obtain the fps - double fps = mlt_properties_get_double( properties, "fps" ); - - // Do the calc - return ( double )position / fps; + return ( double )position / mlt_producer_get_fps( this ); } static inline void convert_image( AVFrame *frame, uint8_t *buffer, int pix_fmt, mlt_image_format format, int width, int height ) { +#ifdef SWSCALE + if ( format == mlt_image_yuv420p ) + { + struct SwsContext *context = sws_getContext( width, height, pix_fmt, + width, height, PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL); + AVPicture output; + output.data[0] = buffer; + output.data[1] = buffer + width * height; + output.data[2] = buffer + ( 3 * width * height ) / 2; + output.linesize[0] = width; + output.linesize[1] = width >> 1; + output.linesize[2] = width >> 1; + sws_scale( context, frame->data, frame->linesize, 0, height, + output.data, output.linesize); + sws_freeContext( context ); + } + else if ( format == mlt_image_rgb24 ) + { + struct SwsContext *context = sws_getContext( width, height, pix_fmt, + width, height, PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL); + AVPicture output; + avpicture_fill( &output, buffer, PIX_FMT_RGB24, width, height ); + sws_scale( context, frame->data, frame->linesize, 0, height, + output.data, output.linesize); + sws_freeContext( context ); + } + else + { + struct SwsContext *context = sws_getContext( width, height, pix_fmt, + width, height, PIX_FMT_YUYV422, SWS_FAST_BILINEAR, NULL, NULL, NULL); + AVPicture output; + avpicture_fill( &output, buffer, PIX_FMT_YUYV422, width, height ); + sws_scale( context, frame->data, frame->linesize, 0, height, + output.data, output.linesize); + sws_freeContext( context ); + } +#else if ( format == mlt_image_yuv420p ) { AVPicture pict; @@ -375,6 +445,7 @@ static inline void convert_image( AVFrame *frame, uint8_t *buffer, int pix_fmt, avpicture_fill( &output, buffer, PIX_FMT_YUV422, width, height ); img_convert( &output, PIX_FMT_YUV422, (AVPicture *)frame, pix_fmt, width, height ); } +#endif } /** Get an image from a frame. @@ -403,9 +474,6 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form // Obtain the expected frame numer mlt_position expected = mlt_properties_get_position( properties, "_video_expected" ); - // Calculate the real time code - double real_timecode = producer_time_of_frame( this, position ); - // Get the video stream AVStream *stream = context->streams[ index ]; @@ -424,11 +492,12 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form // Special case ffwd handling int ignore = 0; - // Current time calcs - double current_time = mlt_properties_get_double( properties, "_current_time" ); - // We may want to use the source fps if available double source_fps = mlt_properties_get_double( properties, "source_fps" ); + double fps = mlt_producer_get_fps( this ); + + // This is the physical frame position in the source + int req_position = ( int )( position / fps * source_fps + 0.5 ); // Get the seekable status int seekable = mlt_properties_get_int( properties, "seekable" ); @@ -439,6 +508,9 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form // Hopefully provide better support for streams... int av_bypass = mlt_properties_get_int( properties, "av_bypass" ); + // Determines if we have to decode all frames in a sequence + int must_decode = 1; + // Set the result arguments that we know here (only *buffer is now required) *width = codec_context->width; *height = codec_context->height; @@ -464,6 +536,11 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form // Construct the output image *buffer = mlt_pool_alloc( size ); + // Temporary hack to improve intra frame only + must_decode = strcmp( codec_context->codec->name, "mjpeg" ) && + strcmp( codec_context->codec->name, "rawvideo" ) && + strcmp( codec_context->codec->name, "dvvideo" ); + // Seek if necessary if ( position != expected ) { @@ -475,23 +552,34 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form else if ( !seekable && position > expected && ( position - expected ) < 250 ) { // Fast forward - seeking is inefficient for small distances - just ignore following frames - ignore = position - expected; + ignore = ( int )( ( position - expected ) / fps * source_fps ); } else if ( seekable && ( position < expected || position - expected >= 12 ) ) { - // Set to the real timecode - av_seek_frame( context, -1, mlt_properties_get_double( properties, "_start_time" ) + real_timecode * 1000000.0, AVSEEK_FLAG_BACKWARD ); + // Calculate the timestamp for the requested frame + int64_t timestamp = ( int64_t )( ( double )req_position / source_fps * AV_TIME_BASE + 0.5 ); + if ( ( uint64_t )context->start_time != AV_NOPTS_VALUE ) + timestamp += context->start_time; + if ( must_decode ) + timestamp -= AV_TIME_BASE; + if ( timestamp < 0 ) + timestamp = 0; + + // Set to the timestamp + av_seek_frame( context, -1, timestamp, AVSEEK_FLAG_BACKWARD ); // Remove the cached info relating to the previous position - mlt_properties_set_double( properties, "_current_time", real_timecode ); - + mlt_properties_set_int( properties, "_current_position", -1 ); + mlt_properties_set_int( properties, "_last_position", -1 ); mlt_properties_set_data( properties, "av_frame", NULL, 0, NULL, NULL ); av_frame = NULL; } } - + // Duplicate the last image if necessary (see comment on rawvideo below) - if ( av_frame != NULL && ( paused || mlt_properties_get_double( properties, "_current_time" ) >= real_timecode ) && av_bypass == 0 ) + int current_position = mlt_properties_get_int( properties, "_current_position" ); + int got_picture = mlt_properties_get_int( properties, "_got_picture" ); + if ( av_frame != NULL && got_picture && ( paused || current_position >= req_position ) && av_bypass == 0 ) { // Duplicate it convert_image( av_frame, *buffer, codec_context->pix_fmt, *format, *width, *height ); @@ -502,12 +590,8 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form else { int ret = 0; - int got_picture = 0; - int must_decode = 1; - - // Temporary hack to improve intra frame only - if ( !strcmp( codec_context->codec->name, "mjpeg" ) || !strcmp( codec_context->codec->name, "rawvideo" ) ) - must_decode = 0; + int int_position = 0; + got_picture = 0; av_init_packet( &pkt ); @@ -527,24 +611,27 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form if ( ret >= 0 && pkt.stream_index == index && pkt.size > 0 ) { // Determine time code of the packet - if ( pkt.dts != AV_NOPTS_VALUE ) - current_time = av_q2d( stream->time_base ) * pkt.dts; - else - current_time = real_timecode; + int_position = ( int )( av_q2d( stream->time_base ) * pkt.dts * source_fps + 0.5 ); + if ( context->start_time != AV_NOPTS_VALUE ) + int_position -= ( int )( context->start_time * source_fps / AV_TIME_BASE + 0.5 ); + int last_position = mlt_properties_get_int( properties, "_last_position" ); + if ( int_position == last_position ) + int_position = last_position + 1; + mlt_properties_set_int( properties, "_last_position", int_position ); // Decode the image - if ( must_decode || current_time >= real_timecode ) + if ( must_decode || int_position >= req_position ) ret = avcodec_decode_video( codec_context, av_frame, &got_picture, pkt.data, pkt.size ); if ( got_picture ) { // Handle ignore - if ( ( int )( current_time * 100 ) < ( int )( real_timecode * 100 ) - 7 ) + if ( int_position < req_position ) { ignore = 0; got_picture = 0; } - else if ( current_time >= real_timecode ) + else if ( int_position >= req_position ) { ignore = 0; } @@ -553,33 +640,26 @@ static int producer_get_image( mlt_frame frame, uint8_t **buffer, mlt_image_form got_picture = 0; } } + av_free_packet( &pkt ); + } + else if ( ret >= 0 ) + { + av_free_packet( &pkt ); } // Now handle the picture if we have one 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 ); - - if ( current_time == 0 && source_fps != 0 ) - { - double fps = mlt_properties_get_double( properties, "fps" ); - current_time = ceil( source_fps * ( double )position / fps ) * ( 1 / source_fps ); - mlt_properties_set_double( properties, "_current_time", current_time ); - } - else - { - mlt_properties_set_double( properties, "_current_time", current_time ); - } + mlt_properties_set_int( properties, "_current_position", int_position ); + mlt_properties_set_int( properties, "_got_picture", 1 ); } - - // We're finished with this packet regardless - av_free_packet( &pkt ); } + 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 @@ -610,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 ]; @@ -627,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 @@ -641,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... @@ -651,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 @@ -664,16 +812,17 @@ static void producer_set_up_video( mlt_producer this, mlt_frame frame ) } else { - int is_pal = mlt_properties_get_double( properties, "fps" ) == 25.0; - aspect_ratio = is_pal ? 59.0/54.0 : 10.0/11.0; + aspect_ratio = 1.0; } // Determine the fps source_fps = ( double )codec_context->time_base.den / ( codec_context->time_base.num == 0 ? 1 : codec_context->time_base.num ); // We'll use fps if it's available - if ( source_fps > 0 && source_fps < 30 ) + if ( source_fps > 0 ) mlt_properties_set_double( properties, "source_fps", source_fps ); + else + mlt_properties_set_double( properties, "source_fps", mlt_producer_get_fps( this ) ); mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio ); // Set the width and height @@ -805,7 +954,7 @@ static int producer_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_form { int ret = 0; int got_audio = 0; - int16_t *temp = mlt_pool_alloc( sizeof( int16_t ) * AVCODEC_MAX_AUDIO_FRAME_SIZE ); + int16_t *temp = av_malloc( sizeof( int16_t ) * AVCODEC_MAX_AUDIO_FRAME_SIZE ); av_init_packet( &pkt ); @@ -821,16 +970,20 @@ static int producer_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_form // Read a packet ret = av_read_frame( context, &pkt ); - int len = pkt.size; - uint8_t *ptr = pkt.data; - int data_size; + int len = pkt.size; + uint8_t *ptr = pkt.data; // We only deal with audio from the selected audio_index while ( ptr != NULL && ret >= 0 && pkt.stream_index == index && len > 0 ) { + int data_size = sizeof( int16_t ) * AVCODEC_MAX_AUDIO_FRAME_SIZE; + // Decode the audio +#if (LIBAVCODEC_VERSION_INT >= ((51<<16)+(29<<8)+0)) + ret = avcodec_decode_audio2( codec_context, temp, &data_size, ptr, len ); +#else ret = avcodec_decode_audio( codec_context, temp, &data_size, ptr, len ); - +#endif if ( ret < 0 ) { ret = 0; @@ -862,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 @@ -890,7 +1046,7 @@ static int producer_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_form mlt_properties_set_int( properties, "_audio_used", audio_used ); // Release the temporary audio - mlt_pool_release( temp ); + av_free( temp ); } else { @@ -919,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 ); @@ -941,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 @@ -952,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... @@ -971,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 ) );