[vlc-devel] [PATCH 3/4] avformat mux: Mark keyframe blocks as such.

Steinar H. Gunderson sgunderson at bigfoot.com
Thu Aug 15 00:12:37 CEST 2013


Some browsers, such as Firefox, are very picky about WebM streams needing to
start with a keyframe. To be able to handle this correctly when streaming,
the avformat mux needs to mark keyframe-containing blocks (or clusters, in
Matroska terminology) as such even after they have been muxed. The next patch
in the series will make httpd actually care about this flag.

Unfortunately, as avformat does not actually propagate this status, we need
to use some heuristics to figure out which blocks contain keyframes. The natural
thing to do would be to say that when we write a keyframe, the block that comes
back has to be a keyframe block, but the WebM/Matroska muxer thwarts this by
having its own internal buffering of clusters, flushing the _previous_ cluster
when we send it a keyframe. In this case, we need to look at the _next_ cluster
after the one that comes back when we mux a keyframe.

I couldn't find a way to reliably autodetect this behavior (think for instance
about the audio packet caching that the WebM mux does), short of simply making
a list of such muxes and checking for them.
---
 modules/demux/avformat/mux.c |   34 +++++++++++++++++++++++++++++++++-
 1 file changed, 33 insertions(+), 1 deletion(-)

diff --git a/modules/demux/avformat/mux.c b/modules/demux/avformat/mux.c
index f8208a6..d266c85 100644
--- a/modules/demux/avformat/mux.c
+++ b/modules/demux/avformat/mux.c
@@ -58,7 +58,9 @@ struct sout_mux_sys_t
     AVFormatContext *oc;
 
     bool     b_write_header;
+    bool     b_write_keyframe;
     bool     b_error;
+    bool     b_buffers_frames;
 };
 
 /*****************************************************************************
@@ -129,8 +131,15 @@ int OpenMux( vlc_object_t *p_this )
     p_sys->oc->nb_streams = 0;
 
     p_sys->b_write_header = true;
+    p_sys->b_write_keyframe = false;
     p_sys->b_error = false;
 
+    p_sys->b_buffers_frames = false;
+    if( !strcmp( file_oformat->name, "webm" ) )
+        p_sys->b_buffers_frames = true;
+    else if( !strcmp( file_oformat->name, "mkv" ) )
+        p_sys->b_buffers_frames = true;
+
     /* Fill p_mux fields */
     p_mux->pf_control   = Control;
     p_mux->pf_addstream = AddStream;
@@ -265,6 +274,7 @@ 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);
+    bool b_keyframe = p_data->i_flags & BLOCK_FLAG_TYPE_I;
     AVStream *p_stream = p_sys->oc->streams[i_stream];
     AVPacket pkt;
 
@@ -275,7 +285,12 @@ static int MuxBlock( sout_mux_t *p_mux, sout_input_t *p_input )
     pkt.size = p_data->i_buffer;
     pkt.stream_index = i_stream;
 
-    if( p_data->i_flags & BLOCK_FLAG_TYPE_I ) pkt.flags |= AV_PKT_FLAG_KEY;
+    if( b_keyframe )
+    {
+        if( !p_sys->b_buffers_frames )
+            p_sys->b_write_keyframe = true;
+        pkt.flags |= AV_PKT_FLAG_KEY;
+    }
 
     if( p_data->i_pts > 0 )
         pkt.pts = p_data->i_pts * p_stream->time_base.den /
@@ -297,6 +312,17 @@ static int MuxBlock( sout_mux_t *p_mux, sout_input_t *p_input )
         return VLC_EGENERIC;
     }
 
+    if( b_keyframe )
+    {
+        /* If this mux buffers frames, the _next_ block we're getting out is going
+         * to be a keyframe. There are situations where this doesn't hold, unfortunately
+         * (e.g., a WebM cluster can start with a non-keyframe if there was less than
+         * 4 kB spillover from the previous cluster), but they should be rare.
+         */
+        if( p_sys->b_buffers_frames )
+            p_sys->b_write_keyframe = true;
+    }
+
     block_Release( p_data );
     return VLC_SUCCESS;
 }
@@ -402,6 +428,12 @@ static int IOWrite( void *opaque, uint8_t *buf, int buf_size )
     if( p_mux->p_sys->b_write_header )
         p_buf->i_flags |= BLOCK_FLAG_HEADER;
 
+    if( p_mux->p_sys->b_write_keyframe )
+    {
+        p_buf->i_flags |= BLOCK_FLAG_TYPE_I;
+        p_mux->p_sys->b_write_keyframe = false;
+    }
+
     i_ret = sout_AccessOutWrite( p_mux->p_access, p_buf );
     return i_ret ? i_ret : -1;
 }
-- 
1.7.10.4




More information about the vlc-devel mailing list