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