]> git.sesse.net Git - vlc/blobdiff - modules/codec/ffmpeg/mux.c
Detect and allow older versions of ffmpeg to be used in conjunction with VLC.
[vlc] / modules / codec / ffmpeg / mux.c
index 40b3b9df4cecc36f7585b520be46ee0052f27de3..975921c10c6afd241f069f322af5981d053ceba3 100644 (file)
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * mux.c: muxer using ffmpeg (libavformat).
  *****************************************************************************
- * Copyright (C) 2006 VideoLAN
+ * Copyright (C) 2006 the VideoLAN team
  * $Id: demux.c 8444 2004-08-17 08:21:07Z gbazin $
  *
  * Authors: Gildas Bazin <gbazin@videolan.org>
 /*****************************************************************************
  * Preamble
  *****************************************************************************/
-#include <stdlib.h>                                      /* malloc(), free() */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
 
 #include <vlc/vlc.h>
-#include <vlc/input.h>
-#include <vlc/sout.h>
+#include <vlc_block.h>
+#include <vlc_sout.h>
 
 /* ffmpeg header */
-#ifdef HAVE_FFMPEG_AVFORMAT_H
-#   include <ffmpeg/avformat.h>
-#else
+#ifdef HAVE_LIBAVFORMAT_AVFORMAT_H
+#   include <libavformat/avformat.h>
+#elif defined(HAVE_LIBAVFORMAT_TREE)
 #   include <avformat.h>
 #endif
 
 //#define AVFORMAT_DEBUG 1
 
 /* Version checking */
-#if (LIBAVFORMAT_BUILD >= 4687) && (defined(HAVE_FFMPEG_AVFORMAT_H) || defined(HAVE_LIBAVFORMAT_TREE))
+#if defined(HAVE_LIBSWSCALE_SWSCALE_H) || defined(HAVE_FFMPEG_SWSCALE_H) || defined(HAVE_LIBSWSCALE_TREE)
+
+static const char *ppsz_mux_options[] = {
+    "mux", NULL
+};
 
 /*****************************************************************************
  * mux_sys_t: mux descriptor
@@ -59,6 +66,8 @@ struct sout_mux_sys_t
 
     vlc_bool_t     b_write_header;
     vlc_bool_t     b_error;
+
+    int64_t        i_initial_dts;
 };
 
 /*****************************************************************************
@@ -77,16 +86,29 @@ static offset_t IOSeek( void *opaque, offset_t offset, int whence );
  *****************************************************************************/
 int E_(OpenMux)( vlc_object_t *p_this )
 {
+    AVOutputFormat *file_oformat;
     sout_mux_t *p_mux = (sout_mux_t*)p_this;
     sout_mux_sys_t *p_sys;
     AVFormatParameters params, *ap = &params;
+    char *psz_mux;
 
     /* Should we call it only once ? */
     av_register_all();
+    av_log_set_callback( E_(LibavcodecCallback) );
+
+    config_ChainParse( p_mux, "ffmpeg-", ppsz_mux_options, p_mux->p_cfg );
 
     /* Find the requested muxer */
-    AVOutputFormat *file_oformat =
-        guess_format(NULL, p_mux->p_access->psz_name, NULL);
+    psz_mux = var_GetNonEmptyString( p_mux, "ffmpeg-mux" );
+    if( psz_mux )
+    {
+        file_oformat = guess_format( psz_mux, NULL, NULL );
+    }
+    else
+    {
+        file_oformat =
+            guess_format(NULL, p_mux->p_access->psz_path, NULL);
+    }
     if (!file_oformat)
     {
       msg_Err( p_mux, "unable for find a suitable output format" );
@@ -130,11 +152,16 @@ int E_(OpenMux)( vlc_object_t *p_this )
         return VLC_EGENERIC;
     }
 
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(0<<8)+0)
+    p_sys->oc->pb = &p_sys->io;
+#else
     p_sys->oc->pb = p_sys->io;
+#endif
     p_sys->oc->nb_streams = 0;
 
     p_sys->b_write_header = VLC_TRUE;
     p_sys->b_error = VLC_FALSE;
+    p_sys->i_initial_dts = 0;
 
     return VLC_SUCCESS;
 }
@@ -146,7 +173,7 @@ void E_(CloseMux)( vlc_object_t *p_this )
 {
     sout_mux_t *p_mux = (sout_mux_t*)p_this;
     sout_mux_sys_t *p_sys = p_mux->p_sys;
-    int i;
+    unsigned int i;
 
     if( av_write_trailer( p_sys->oc ) < 0 )
     {
@@ -196,6 +223,9 @@ static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
     }
     codec = stream->codec;
 
+    /* This is used by LibavcodecCallback (ffmpeg.c) to print messages */
+    codec->opaque = (void*)p_mux;
+
     switch( p_input->p_fmt->i_cat )
     {
     case AUDIO_ES:
@@ -226,10 +256,27 @@ static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
         codec->time_base.den = p_input->p_fmt->video.i_frame_rate;
         codec->time_base.num = p_input->p_fmt->video.i_frame_rate_base;
         break;
+
+    default:
+        msg_Warn( p_mux, "Unhandled ES category" );
     }
 
     codec->bit_rate = p_input->p_fmt->i_bitrate;
+#if LIBAVFORMAT_VERSION_INT >= ((51<<16)+(8<<8)+0)
+    codec->codec_tag = av_codec_get_tag( p_sys->oc->oformat->codec_tag, i_codec_id );
+    if( !codec->codec_tag && i_codec_id == CODEC_ID_MP2 )
+    {
+        i_codec_id = CODEC_ID_MP3;
+        codec->codec_tag = av_codec_get_tag( p_sys->oc->oformat->codec_tag, i_codec_id );
+    }
+#else
+#   warning "WARNING!!!!!!!"
+#   warning "Using libavformat muxing with versions older than 51.8.0 (r7593) might produce broken files."
+    /* This is a hack */
+    if( i_codec_id == CODEC_ID_MP2 )
+        i_codec_id = CODEC_ID_MP3;
     codec->codec_tag = p_input->p_fmt->i_codec;
+#endif
     codec->codec_id = i_codec_id;
 
     if( p_input->p_fmt->i_extra )
@@ -269,9 +316,9 @@ static int MuxGetStream( sout_mux_t *p_mux, int *pi_stream, mtime_t *pi_dts )
 
         /* We don't really need to have anything in the SPU fifo */
         if( p_mux->pp_inputs[i]->p_fmt->i_cat == SPU_ES &&
-            p_fifo->i_depth == 0 ) continue;
+            block_FifoCount( p_fifo ) == 0 ) continue;
 
-        if( p_fifo->i_depth )
+        if( block_FifoCount( p_fifo ) )
         {
             block_t *p_buf;
 
@@ -287,6 +334,7 @@ static int MuxGetStream( sout_mux_t *p_mux, int *pi_stream, mtime_t *pi_dts )
     }
     if( pi_stream ) *pi_stream = i_stream;
     if( pi_dts ) *pi_dts = i_dts;
+    if( !p_mux->p_sys->i_initial_dts ) p_mux->p_sys->i_initial_dts = i_dts;
     return i_stream;
 }
 
@@ -295,25 +343,39 @@ static int MuxBlock( sout_mux_t *p_mux, sout_input_t *p_input )
     sout_mux_sys_t *p_sys = p_mux->p_sys;
     block_t *p_data = block_FifoGet( p_input->p_fifo );
     int i_stream = *((int *)p_input->p_sys);
-    AVPacket pkt = {0};
+    AVStream *p_stream = p_sys->oc->streams[i_stream];
+    AVPacket pkt;
 
+    memset( &pkt, 0, sizeof(AVPacket) );
+
+    av_init_packet(&pkt);
     pkt.data = p_data->p_buffer;
     pkt.size = p_data->i_buffer;
     pkt.stream_index = i_stream;
 
     if( p_data->i_flags & BLOCK_FLAG_TYPE_I ) pkt.flags |= PKT_FLAG_KEY;
+
+    /* avformat expects pts/dts which start from 0 */
+    p_data->i_dts -= p_mux->p_sys->i_initial_dts;
+    p_data->i_pts -= p_mux->p_sys->i_initial_dts;
+
     if( p_data->i_pts > 0 )
-        pkt.pts = p_data->i_pts * p_sys->oc->streams[i_stream]->time_base.den /
-            I64C(1000000) / p_sys->oc->streams[i_stream]->time_base.num;
+        pkt.pts = p_data->i_pts * p_stream->time_base.den /
+            I64C(1000000) / p_stream->time_base.num;
     if( p_data->i_dts > 0 )
-        pkt.dts = p_data->i_dts * p_sys->oc->streams[i_stream]->time_base.den /
-            I64C(1000000) / p_sys->oc->streams[i_stream]->time_base.num;
+        pkt.dts = p_data->i_dts * p_stream->time_base.den /
+            I64C(1000000) / p_stream->time_base.num;
+
+    /* this is another hack to prevent libavformat from triggering the "non monotone timestamps" check in avformat/utils.c */
+    p_stream->cur_dts = ( p_data->i_dts * p_stream->time_base.den /
+            I64C(1000000) / p_stream->time_base.num ) - 1;
 
     if( av_write_frame( p_sys->oc, &pkt ) < 0 )
     {
         msg_Err( p_mux, "could not write frame (pts: "I64Fd", dts: "I64Fd") "
                  "(pkt pts: "I64Fd", dts: "I64Fd")",
                  p_data->i_pts, p_data->i_dts, pkt.pts, pkt.dts );
+        block_Release( p_data );
         return VLC_EGENERIC;
     }
 
@@ -335,14 +397,20 @@ static int Mux( sout_mux_t *p_mux )
     {
         msg_Dbg( p_mux, "writing header" );
 
-        p_sys->b_write_header = VLC_FALSE;
-
         if( av_write_header( p_sys->oc ) < 0 )
         {
             msg_Err( p_mux, "could not write header" );
+            p_sys->b_write_header = VLC_FALSE;
             p_sys->b_error = VLC_TRUE;
             return VLC_EGENERIC;
         }
+
+#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(0<<8)+0)
+        put_flush_packet( p_sys->oc->pb );
+#else
+        put_flush_packet( &p_sys->oc->pb );
+#endif
+        p_sys->b_write_header = VLC_FALSE;
     }
 
     for( ;; )
@@ -374,6 +442,12 @@ static int Control( sout_mux_t *p_mux, int i_query, va_list args )
         return VLC_SUCCESS;
 
     case MUX_GET_MIME:
+    {
+        char **ppsz = (char**)va_arg( args, char ** );
+        *ppsz = strdup( p_mux->p_sys->oc->oformat->mime_type );
+        return VLC_SUCCESS;
+    }
+
     default:
         return VLC_EGENERIC;
     }
@@ -395,6 +469,9 @@ static int IOWrite( void *opaque, uint8_t *buf, int buf_size )
     block_t *p_buf = block_New( p_mux->p_sout, buf_size );
     if( buf_size > 0 ) memcpy( p_buf->p_buffer, buf, buf_size );
 
+    if( p_mux->p_sys->b_write_header )
+        p_buf->i_flags |= BLOCK_FLAG_HEADER;
+
     i_ret = sout_AccessOutWrite( p_mux->p_access, p_buf );
     return i_ret ? i_ret : -1;
 }
@@ -428,15 +505,4 @@ static offset_t IOSeek( void *opaque, offset_t offset, int whence )
     return 0;
 }
 
-#else /* LIBAVFORMAT_BUILD >= 4687 */
-
-int E_(OpenMux)( vlc_object_t *p_this )
-{
-    return VLC_EGENERIC;
-}
-
-void E_(CloseMux)( vlc_object_t *p_this )
-{
-}
-
-#endif /* LIBAVFORMAT_BUILD >= 4687 */
+#endif /* HAVE_LIBAVFORMAT_AVFORMAT_H */