]> git.sesse.net Git - mlt/blobdiff - src/modules/avformat/producer_avformat.c
Refactor common code around producer_open into producer_open.
[mlt] / src / modules / avformat / producer_avformat.c
index ed9c403950843f9dc5749c5568986895b12fc77c..d9d626a01f3f6858d6640884cde1120fe93e1ddb 100644 (file)
@@ -111,6 +111,9 @@ struct producer_avformat_s
        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
        {
@@ -138,6 +141,7 @@ 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"
@@ -186,13 +190,10 @@ mlt_producer producer_avformat_init( mlt_profile profile, const char *service, c
                                        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;
@@ -545,7 +546,7 @@ static char* parse_url( mlt_profile profile, const char* URL, AVInputFormat **fo
 {
        if ( !URL ) return NULL;
 
-       char *result = NULL;
+       const char *result = URL;
        char *protocol = strdup( URL );
        char *url = strchr( protocol, ':' );
 
@@ -558,6 +559,7 @@ static char* parse_url( mlt_profile profile, const char* URL, AVInputFormat **fo
        {
                // 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( protocol );
@@ -618,7 +620,7 @@ static char* parse_url( mlt_profile profile, const char* URL, AVInputFormat **fo
                }
        }
        free( protocol );
-       return result ? strdup( result ) : strdup( URL );
+       return strdup( result );
 }
 
 static int get_basic_info( producer_avformat self, mlt_profile profile, const char *filename )
@@ -733,10 +735,13 @@ static int producer_open( producer_avformat self, mlt_profile profile, const cha
 {
        // Return an error code (0 == no error)
        int error = 0;
+       mlt_properties properties = MLT_PRODUCER_PROPERTIES( self->parent );
 
        // Lock the service
        pthread_mutex_init( &self->audio_mutex, NULL );
        pthread_mutex_init( &self->video_mutex, NULL );
+       pthread_mutex_init( &self->packets_mutex, NULL );
+       mlt_events_block( properties, self->parent );
        pthread_mutex_lock( &self->audio_mutex );
        pthread_mutex_lock( &self->video_mutex );
 
@@ -745,8 +750,18 @@ static int producer_open( producer_avformat self, mlt_profile profile, const cha
        AVFormatParameters *params = NULL;
        char *filename = parse_url( profile, URL, &format, &params );
 
-       // Now attempt to open the file
+       // Now attempt to open the file or device with filename
        error = av_open_input_file( &self->video_format, filename, format, 0, params ) < 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, params ) < 0;
+
+       // Set MLT properties onto video AVFormatContext
+       apply_properties( self->video_format, properties, AV_OPT_FLAG_DECODING_PARAM );
+#if LIBAVFORMAT_VERSION_MAJOR > 52
+       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
        if ( params )
@@ -778,9 +793,21 @@ static int producer_open( producer_avformat self, mlt_profile profile, const cha
                        // TODO: Is this really necessary?
                        if ( self->audio_index != -1 && self->video_index != -1 )
                        {
-                               // And open again for our audio context
-                               av_open_input_file( &self->audio_format, filename, NULL, 0, NULL );
-                               av_find_stream_info( self->audio_format );
+                               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 > 52
+                                       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 )
                        {
@@ -793,14 +820,28 @@ static int producer_open( producer_avformat self, mlt_profile profile, const cha
                                // 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 ( self->dummy_context )
+       {
+               av_close_input_file( self->dummy_context );
+               self->dummy_context = NULL;
+       }
 
        // Unlock the service
        pthread_mutex_unlock( &self->audio_mutex );
        pthread_mutex_unlock( &self->video_mutex );
+       mlt_events_unblock( properties, self->parent );
 
        return error;
 }
@@ -828,24 +869,12 @@ static void reopen_video( producer_avformat self, mlt_producer producer )
        int audio_index = self->audio_index;
        int video_index = self->video_index;
 
-       mlt_events_block( properties, producer );
        pthread_mutex_unlock( &self->audio_mutex );
        pthread_mutex_unlock( &self->video_mutex );
        producer_open( self, mlt_service_profile( MLT_PRODUCER_SERVICE(producer) ),
                mlt_properties_get( properties, "resource" ) );
        pthread_mutex_lock( &self->video_mutex );
        pthread_mutex_lock( &self->audio_mutex );
-       if ( self->dummy_context )
-       {
-               av_close_input_file( self->dummy_context );
-               self->dummy_context = NULL;
-       }
-       mlt_events_unblock( properties, producer );
-       apply_properties( self->video_format, properties, AV_OPT_FLAG_DECODING_PARAM );
-#if LIBAVFORMAT_VERSION_MAJOR > 52
-       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
 
        self->audio_index = audio_index;
        if ( self->video_format && video_index > -1 )
@@ -864,7 +893,7 @@ static int seek_video( producer_avformat self, mlt_position position,
        mlt_producer producer = self->parent;
        int paused = 0;
 
-       if ( position != self->video_expected || self->last_position < 0 )
+       if ( self->seekable && ( position != self->video_expected || self->last_position < 0 ) )
        {
                mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
 
@@ -1348,7 +1377,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 )
@@ -1530,7 +1579,7 @@ 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 );
                }
        }
@@ -1805,28 +1854,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" ) );
                context = self->video_format;
-               if ( self->dummy_context )
-               {
-                       av_close_input_file( self->dummy_context );
-                       self->dummy_context = NULL;
-               }
-               mlt_events_unblock( properties, producer );
-               if ( self->audio_format && !self->audio_streams )
-                       get_audio_streams_info( self );
-
-               // Process properties as AVOptions
-               if ( context )
-               {
-                       apply_properties( context, properties, AV_OPT_FLAG_DECODING_PARAM );
-#if LIBAVFORMAT_VERSION_MAJOR > 52
-                       if ( context->iformat && context->iformat->priv_class && context->priv_data )
-                               apply_properties( context->priv_data, properties, AV_OPT_FLAG_DECODING_PARAM );
-#endif
-               }
        }
 
        // Exception handling for video_index
@@ -1904,7 +1934,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 )
                {
@@ -2165,7 +2195,27 @@ static int producer_get_audio( mlt_frame frame, void **buffer, mlt_audio_format
                        }
 
                        // Read a packet
-                       ret = av_read_frame( context, &pkt );
+                       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
                        index = pkt.stream_index;
@@ -2176,7 +2226,9 @@ static int producer_get_audio( mlt_frame frame, void **buffer, mlt_audio_format
                                        self->audio_codec[index]->channels : *channels;
                                ret = decode_audio( self, &ignore, pkt, channels2, *samples, real_timecode, fps );
                        }
-                       av_free_packet( &pkt );
+
+                       if ( self->seekable || index != self->video_index )
+                               av_free_packet( &pkt );
 
                        if ( self->audio_index == INT_MAX && ret >= 0 )
                        {
@@ -2355,18 +2407,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" ) );
                context = self->audio_format;
-               if ( self->dummy_context )
-               {
-                       av_close_input_file( self->dummy_context );
-                       self->dummy_context = NULL;
-               }
-               mlt_events_unblock( properties, producer );
-               if ( self->audio_format )
-                       get_audio_streams_info( self );
        }
 
        // Exception handling for audio_index
@@ -2482,7 +2525,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;
@@ -2497,9 +2541,10 @@ static void producer_avformat_close( producer_avformat self )
        }
        if ( self->video_codec )
                avcodec_close( self->video_codec );
+       // 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 );
@@ -2509,8 +2554,25 @@ 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 );
 }