]> git.sesse.net Git - mlt/blobdiff - src/modules/avformat/consumer_avformat.c
Add support for FFmpeg AVMetadata API.
[mlt] / src / modules / avformat / consumer_avformat.c
index 29d730da725700ae541a60e2afffd2500531b7e3..3d1bc18546b2dfa80e7a7ca8967cdcdebe0cc8db 100644 (file)
@@ -24,6 +24,7 @@
 #include <framework/mlt_frame.h>
 #include <framework/mlt_profile.h>
 #include <framework/mlt_log.h>
+#include <framework/mlt_events.h>
 
 // System header files
 #include <stdio.h>
@@ -51,6 +52,8 @@
 
 #define MAX_AUDIO_STREAMS (8)
 #define AUDIO_ENCODE_BUFFER_SIZE (48000 * 2 * MAX_AUDIO_STREAMS)
+#define AUDIO_BUFFER_SIZE (1024 * 42)
+#define VIDEO_BUFFER_SIZE (2048 * 1024)
 
 void avformat_lock( );
 void avformat_unlock( );
@@ -195,6 +198,8 @@ mlt_consumer consumer_avformat_init( mlt_profile profile, char *arg )
                this->start = consumer_start;
                this->stop = consumer_stop;
                this->is_stopped = consumer_is_stopped;
+               
+               mlt_events_register( properties, "consumer-fatal-error", NULL );
        }
 
        // Return this
@@ -214,33 +219,60 @@ static int consumer_start( mlt_consumer this )
        char *s = mlt_properties_get( properties, "f" );
        if ( s && strcmp( s, "list" ) == 0 )
        {
-               fprintf( stderr, "---\nformats:\n" );
+               mlt_properties doc = mlt_properties_new();
+               mlt_properties formats = mlt_properties_new();
+               char key[20];
                AVOutputFormat *format = NULL;
+               
+               mlt_properties_set_data( properties, "f", formats, 0, (mlt_destructor) mlt_properties_close, NULL );
+               mlt_properties_set_data( doc, "formats", formats, 0, NULL, NULL );
                while ( ( format = av_oformat_next( format ) ) )
-                       fprintf( stderr, "  - %s\n", format->name );
-               fprintf( stderr, "...\n" );
+               {
+                       snprintf( key, sizeof(key), "%d", mlt_properties_count( formats ) );
+                       mlt_properties_set( formats, key, format->name );
+               }
+               fprintf( stderr, "%s", mlt_properties_serialise_yaml( doc ) );
+               mlt_properties_close( doc );
                error = 1;
        }
        s = mlt_properties_get( properties, "acodec" );
        if ( s && strcmp( s, "list" ) == 0 )
        {
-               fprintf( stderr, "---\naudio_codecs:\n" );
+               mlt_properties doc = mlt_properties_new();
+               mlt_properties codecs = mlt_properties_new();
+               char key[20];
                AVCodec *codec = NULL;
+
+               mlt_properties_set_data( properties, "acodec", codecs, 0, (mlt_destructor) mlt_properties_close, NULL );
+               mlt_properties_set_data( doc, "audio_codecs", codecs, 0, NULL, NULL );
                while ( ( codec = av_codec_next( codec ) ) )
                        if ( codec->encode && codec->type == CODEC_TYPE_AUDIO )
-                               fprintf( stderr, "  - %s\n", codec->name );
-               fprintf( stderr, "...\n" );
+                       {
+                               snprintf( key, sizeof(key), "%d", mlt_properties_count( codecs ) );
+                               mlt_properties_set( codecs, key, codec->name );
+                       }
+               fprintf( stderr, "%s", mlt_properties_serialise_yaml( doc ) );
+               mlt_properties_close( doc );
                error = 1;
        }
        s = mlt_properties_get( properties, "vcodec" );
        if ( s && strcmp( s, "list" ) == 0 )
        {
-               fprintf( stderr, "---\nvideo_codecs:\n" );
+               mlt_properties doc = mlt_properties_new();
+               mlt_properties codecs = mlt_properties_new();
+               char key[20];
                AVCodec *codec = NULL;
+
+               mlt_properties_set_data( properties, "vcodec", codecs, 0, (mlt_destructor) mlt_properties_close, NULL );
+               mlt_properties_set_data( doc, "video_codecs", codecs, 0, NULL, NULL );
                while ( ( codec = av_codec_next( codec ) ) )
                        if ( codec->encode && codec->type == CODEC_TYPE_VIDEO )
-                               fprintf( stderr, "  - %s\n", codec->name );
-               fprintf( stderr, "...\n" );
+                       {
+                               snprintf( key, sizeof(key), "%d", mlt_properties_count( codecs ) );
+                               mlt_properties_set( codecs, key, codec->name );
+                       }
+               fprintf( stderr, "%s", mlt_properties_serialise_yaml( doc ) );
+               mlt_properties_close( doc );
                error = 1;
        }
 
@@ -894,13 +926,13 @@ static void *consumer_thread( void *arg )
        int samples = 0;
 
        // AVFormat audio buffer and frame size
-       int audio_outbuf_size = ( 1024 * 42 );
+       int audio_outbuf_size = AUDIO_BUFFER_SIZE;
        uint8_t *audio_outbuf = av_malloc( audio_outbuf_size );
        int audio_input_frame_size = 0;
 
        // AVFormat video buffer and frame count
        int frame_count = 0;
-       int video_outbuf_size = ( 1024 * 1024 );
+       int video_outbuf_size = VIDEO_BUFFER_SIZE;
        uint8_t *video_outbuf = av_malloc( video_outbuf_size );
 
        // Used for the frame properties
@@ -1020,6 +1052,28 @@ static void *consumer_thread( void *arg )
        }
 
        // Write metadata
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
+       for ( i = 0; i < mlt_properties_count( properties ); i++ )
+       {
+               char *name = mlt_properties_get_name( properties, i );
+               if ( name && !strncmp( name, "meta.attr.", 10 ) )
+               {
+                       char *key = strdup( name + 10 );
+                       char *markup = strrchr( key, '.' );
+                       if ( markup && !strcmp( markup, ".markup") )
+                       {
+                               markup[0] = '\0';
+                               if ( !strstr( key, ".stream." ) )
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(43<<8)+0)
+                                       av_metadata_set2( &oc->metadata, key, mlt_properties_get_value( properties, i ), 0 );
+#else
+                                       av_metadata_set( &oc->metadata, key, mlt_properties_get_value( properties, i ) );
+#endif
+                       }
+                       free( key );
+               }
+       }
+#else
        char *tmp = NULL;
        int metavalue;
 
@@ -1043,6 +1097,7 @@ static void *consumer_thread( void *arg )
 
        metavalue = mlt_properties_get_int( properties, "meta.attr.track.markup");
        if (metavalue != 0) oc->track = metavalue;
+#endif
 
        oc->oformat = fmt;
        snprintf( oc->filename, sizeof(oc->filename), "%s", filename );
@@ -1195,9 +1250,20 @@ static void *consumer_thread( void *arg )
 
                                        // Get the audio samples
                                        if ( n > 0 )
+                                       {
                                                sample_fifo_fetch( fifo, audio_buf_1, n );
+                                       }
+                                       else if ( audio_codec_id == CODEC_ID_VORBIS && terminated )
+                                       {
+                                               // This prevents an infinite loop when some versions of vorbis do not
+                                               // increment pts when encoding silence.
+                                               audio_pts = video_pts;
+                                               break;
+                                       }
                                        else
+                                       {
                                                memset( audio_buf_1, 0, AUDIO_ENCODE_BUFFER_SIZE );
+                                       }
                                        samples = n / channels;
 
                                        // For each output stream
@@ -1296,7 +1362,11 @@ static void *consumer_thread( void *arg )
                                                if ( pkt.size > 0 )
                                                {
                                                        if ( av_interleaved_write_frame( oc, &pkt ) )
-                                                               mlt_log_error( MLT_CONSUMER_SERVICE( this ), "error writing audio frame %d\n", frames - 1 );
+                                                       {
+                                                               mlt_log_fatal( MLT_CONSUMER_SERVICE( this ), "error writing audio frame\n" );
+                                                               mlt_events_fire( properties, "consumer-fatal-error", NULL );
+                                                               goto on_fatal_error;
+                                                       }
                                                }
 
                                                mlt_log_debug( MLT_CONSUMER_SERVICE( this ), " frame_size %d\n", codec->frame_size );
@@ -1316,7 +1386,7 @@ static void *consumer_thread( void *arg )
                                // Write video
                                if ( mlt_deque_count( queue ) )
                                {
-                                       int out_size, ret;
+                                       int out_size, ret = 0;
                                        AVCodecContext *c;
 
                                        frame = mlt_deque_pop_front( queue );
@@ -1327,7 +1397,6 @@ static void *consumer_thread( void *arg )
                                        if ( mlt_properties_get_int( frame_properties, "rendered" ) )
                                        {
                                                int i = 0;
-                                               int j = 0;
                                                uint8_t *p;
                                                uint8_t *q;
 
@@ -1339,12 +1408,8 @@ static void *consumer_thread( void *arg )
                                                for ( i = 0; i < height; i ++ )
                                                {
                                                        p = input->data[ 0 ] + i * input->linesize[ 0 ];
-                                                       j = width;
-                                                       while( j -- )
-                                                       {
-                                                               *p ++ = *q ++;
-                                                               *p ++ = *q ++;
-                                                       }
+                                                       memcpy( p, q, width * 2 );
+                                                       q += width * 2;
                                                }
 
                                                // Do the colour space conversion
@@ -1452,6 +1517,12 @@ static void *consumer_thread( void *arg )
                                                }
                                        }
                                        frame_count++;
+                                       if ( ret )
+                                       {
+                                               mlt_log_fatal( MLT_CONSUMER_SERVICE( this ), "error writing video frame\n" );
+                                               mlt_events_fire( properties, "consumer-fatal-error", NULL );
+                                               goto on_fatal_error;
+                                       }
                                        mlt_frame_close( frame );
                                }
                                else
@@ -1515,8 +1586,9 @@ static void *consumer_thread( void *arg )
                        pkt.data = audio_outbuf;
                        if ( av_interleaved_write_frame( oc, &pkt ) != 0 )
                        {
-                               mlt_log_error( MLT_CONSUMER_SERVICE( this ), "%s: error writing flushed audio frame\n", __FILE__ );
-                               break;
+                               mlt_log_fatal( MLT_CONSUMER_SERVICE( this ), "error writing flushed audio frame\n" );
+                               mlt_events_fire( properties, "consumer-fatal-error", NULL );
+                               goto on_fatal_error;
                        }
                }
 
@@ -1543,8 +1615,9 @@ static void *consumer_thread( void *arg )
                        // write the compressed frame in the media file
                        if ( av_interleaved_write_frame( oc, &pkt ) != 0 )
                        {
-                               mlt_log_error( MLT_CONSUMER_SERVICE(this), "error writing flushed video frame\n" );
-                               break;
+                               mlt_log_fatal( MLT_CONSUMER_SERVICE(this), "error writing flushed video frame\n" );
+                               mlt_events_fire( properties, "consumer-fatal-error", NULL );
+                               goto on_fatal_error;
                        }
                        // Dual pass logging
                        if ( mlt_properties_get_data( properties, "_logfile", NULL ) && c->stats_out )
@@ -1552,6 +1625,8 @@ static void *consumer_thread( void *arg )
                }
        }
 
+on_fatal_error:
+       
        // Write the trailer, if any
        av_write_trailer( oc );