[vlc-devel] [PATCH] codec: avcodec: adapt audio code to new API

Francois Cartegnie fcvlcdev at free.fr
Wed Oct 12 22:06:29 CEST 2016


adapting to the new API requires more than local
patches.

The block is always passed in full, and we can
expect more than a single frame when push returns
EAGAIN.
Requires then more interpolation for timestamps before
using next input packet pts.
Should also be correct when dequeing end of stream.
Implements decoder draining
---
 modules/codec/avcodec/audio.c   | 295 +++++++++++++++++++++++++---------------
 modules/codec/avcodec/avcodec.c |  16 ++-
 modules/codec/avcodec/avcodec.h |   1 +
 3 files changed, 198 insertions(+), 114 deletions(-)

diff --git a/modules/codec/avcodec/audio.c b/modules/codec/avcodec/audio.c
index a0d5c03..3c9d71a 100644
--- a/modules/codec/avcodec/audio.c
+++ b/modules/codec/avcodec/audio.c
@@ -51,6 +51,9 @@ struct decoder_sys_t
 {
     AVCODEC_COMMON_MEMBERS
 
+    block_t *p_decoded;
+    block_t **pp_decoded_last;
+
     /*
      * Output properties
      */
@@ -70,6 +73,7 @@ struct decoder_sys_t
 #define BLOCK_FLAG_PRIVATE_REALLOCATED (1 << BLOCK_FLAG_PRIVATE_SHIFT)
 
 static void SetupOutputFormat( decoder_t *p_dec, bool b_trust );
+static block_t * ConvertAVFrame( decoder_t *p_dec, AVFrame *frame );
 static block_t *DecodeAudio( decoder_t *, block_t ** );
 static void Flush( decoder_t * );
 
@@ -188,6 +192,34 @@ static block_t *vlc_av_frame_Wrap(AVFrame *frame)
     return block;
 }
 
+static block_t * DequeueOneDecodedFrame( decoder_sys_t *p_sys )
+{
+    block_t *p_decoded = p_sys->p_decoded;
+    p_sys->p_decoded = p_sys->p_decoded->p_next;
+    if( p_sys->p_decoded == NULL )
+        p_sys->pp_decoded_last = &p_sys->p_decoded;
+    p_decoded->p_next = NULL;
+    return p_decoded;
+}
+
+/*****************************************************************************
+ * EndAudio: decoder destruction
+ *****************************************************************************
+ * This function is called when the thread ends after a successful
+ * initialization.
+ *****************************************************************************/
+void EndAudioDec( decoder_t *p_dec )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    if( p_sys->p_decoded )
+    {
+        block_ChainRelease( p_sys->p_decoded );
+        p_sys->p_decoded = NULL;
+        p_sys->pp_decoded_last = &p_sys->p_decoded;
+    }
+    ffmpeg_CloseCodec( p_dec );
+}
+
 /*****************************************************************************
  * InitAudioDec: initialize audio decoder
  *****************************************************************************
@@ -204,6 +236,9 @@ int InitAudioDec( decoder_t *p_dec, AVCodecContext *p_context,
         return VLC_ENOMEM;
     }
 
+    p_sys->p_decoded = NULL;
+    p_sys->pp_decoded_last = &p_sys->p_decoded;
+
     p_context->refcounted_frames = true;
     p_sys->p_context = p_context;
     p_sys->p_codec = p_codec;
@@ -254,6 +289,13 @@ static void Flush( decoder_t *p_dec )
     if( ctx->codec_id == AV_CODEC_ID_MP2 ||
         ctx->codec_id == AV_CODEC_ID_MP3 )
         p_sys->i_reject_count = 3;
+
+    if( p_sys->p_decoded )
+    {
+        block_ChainRelease( p_sys->p_decoded );
+        p_sys->p_decoded = NULL;
+        p_sys->pp_decoded_last = &p_sys->p_decoded;
+    }
 }
 
 /*****************************************************************************
@@ -264,11 +306,7 @@ static block_t *DecodeAudio( decoder_t *p_dec, block_t **pp_block )
     decoder_sys_t *p_sys = p_dec->p_sys;
     AVCodecContext *ctx = p_sys->p_context;
     AVFrame *frame = NULL;
-
-    if( !pp_block || !*pp_block )
-        return NULL;
-
-    block_t *p_block = *pp_block;
+    block_t *p_block = NULL;
 
     if( !ctx->extradata_size && p_dec->fmt_in.i_extra && p_sys->b_delayed_open)
     {
@@ -277,162 +315,199 @@ static block_t *DecodeAudio( decoder_t *p_dec, block_t **pp_block )
     }
 
     if( p_sys->b_delayed_open )
-        goto end;
-
-    if( p_block->i_flags & BLOCK_FLAG_CORRUPTED )
     {
-        Flush( p_dec );
+        if( pp_block )
+            p_block = *pp_block;
         goto end;
     }
 
-    if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY )
+    /* Flushing or decoding, we return any block ready from multiple frames output */
+    if( p_sys->p_decoded )
+        return DequeueOneDecodedFrame( p_sys );
+
+    if( pp_block == NULL ) /* Drain request */
     {
-        date_Set( &p_sys->end_date, VLC_TS_INVALID );
+        /* we don't need to care about return val */
+        (void) avcodec_send_packet( ctx, NULL );
+    }
+    else
+    {
+        p_block = *pp_block;
     }
 
-    /* We've just started the stream, wait for the first PTS. */
-    if( !date_Get( &p_sys->end_date ) && p_block->i_pts <= VLC_TS_INVALID )
-        goto end;
+    if( p_block )
+    {
+        if( p_block->i_flags & BLOCK_FLAG_CORRUPTED )
+        {
+            Flush( p_dec );
+            goto end;
+        }
 
-    if( p_block->i_buffer <= 0 )
-        goto end;
+        if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY )
+        {
+            date_Set( &p_sys->end_date, VLC_TS_INVALID );
+        }
 
-    if( (p_block->i_flags & BLOCK_FLAG_PRIVATE_REALLOCATED) == 0 )
-    {
-        p_block = block_Realloc( p_block, 0, p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE );
-        if( !p_block )
-            return NULL;
-        *pp_block = p_block;
-        p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE;
-        memset( &p_block->p_buffer[p_block->i_buffer], 0, FF_INPUT_BUFFER_PADDING_SIZE );
-
-        p_block->i_flags |= BLOCK_FLAG_PRIVATE_REALLOCATED;
+        /* We've just started the stream, wait for the first PTS. */
+        if( !date_Get( &p_sys->end_date ) && p_block->i_pts <= VLC_TS_INVALID )
+            goto end;
+
+        if( p_block->i_buffer <= 0 )
+            goto end;
+
+        if( (p_block->i_flags & BLOCK_FLAG_PRIVATE_REALLOCATED) == 0 )
+        {
+            p_block = block_Realloc( p_block, 0, p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE );
+            if( !p_block )
+                return NULL;
+            *pp_block = p_block;
+            p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE;
+            memset( &p_block->p_buffer[p_block->i_buffer], 0, FF_INPUT_BUFFER_PADDING_SIZE );
+
+            p_block->i_flags |= BLOCK_FLAG_PRIVATE_REALLOCATED;
+        }
     }
 
     frame = av_frame_alloc();
     if (unlikely(frame == NULL))
         goto end;
 
-    for( int got_frame = 0; !got_frame; )
+    for( int ret = 0; ret == 0; )
     {
-        if( p_block->i_buffer == 0 )
-            goto end;
-
-        AVPacket pkt;
-        av_init_packet( &pkt );
-        pkt.data = p_block->p_buffer;
-        pkt.size = p_block->i_buffer;
-
-        int ret = avcodec_send_packet( ctx, &pkt );
-        if( ret != 0 && ret != AVERROR(EAGAIN) )
+        /* Feed in the loop as buffer could have been full on first iterations */
+        if( p_block )
         {
-            msg_Warn( p_dec, "cannot decode one frame (%zu bytes)",
-                      p_block->i_buffer );
-            goto end;
+            AVPacket pkt;
+            av_init_packet( &pkt );
+            pkt.data = p_block->p_buffer;
+            pkt.size = p_block->i_buffer;
+            ret = avcodec_send_packet( ctx, &pkt );
+            if( ret == 0 ) /* Block has been consumed */
+            {
+                /* Only set new pts from input block if it has been used,
+                 * otherwise let it be through interpolation */
+                if( p_block->i_pts > date_Get( &p_sys->end_date ) )
+                {
+                    date_Set( &p_sys->end_date, p_block->i_pts );
+                }
+
+                block_Release( p_block );
+                *pp_block = p_block = NULL;
+
+                /* FIXME: Should those init be really done after block and not frame ? */
+                SetupOutputFormat( p_dec, true );
+                if( decoder_UpdateAudioFormat( p_dec ) )
+                    goto drop;
+            }
+            else if ( ret != AVERROR(EAGAIN) ) /* Errors other than buffer full */
+            {
+                goto end;
+            }
         }
-        int used = ret != AVERROR(EAGAIN) ? pkt.size : 0;
 
+        /* Try to read one or multiple frames */
         ret = avcodec_receive_frame( ctx, frame );
-        if( ret != 0 && ret != AVERROR(EAGAIN) )
+        if( ret == 0 )
         {
-            msg_Warn( p_dec, "cannot decode one frame (%zu bytes)",
-                      p_block->i_buffer );
-            goto end;
-        }
-        got_frame = ret == 0;
+            /* checks and init from first decoded frame */
+            if( ctx->channels <= 0 || ctx->channels > 8 || ctx->sample_rate <= 0 )
+            {
+                msg_Warn( p_dec, "invalid audio properties channels count %d, sample rate %d",
+                          ctx->channels, ctx->sample_rate );
+                goto end;
+            }
+            else if( p_dec->fmt_out.audio.i_rate != (unsigned int)ctx->sample_rate )
+            {
+                date_Init( &p_sys->end_date, ctx->sample_rate, 1 );
+            }
 
-        p_block->p_buffer += used;
-        p_block->i_buffer -= used;
-    }
+            block_t *p_converted = ConvertAVFrame( p_dec, frame ); /* Consumes frame */
+            if( p_converted )
+            {
+                /* Silent unwanted samples */
+                if( p_sys->i_reject_count > 0 )
+                {
+                    memset( p_converted->p_buffer, 0, p_converted->i_buffer );
+                    p_sys->i_reject_count--;
+                }
+                p_converted->i_buffer = p_converted->i_nb_samples
+                                      * p_dec->fmt_out.audio.i_bytes_per_frame;
+                p_converted->i_pts = date_Get( &p_sys->end_date );
+                p_converted->i_length = date_Increment( &p_sys->end_date,
+                                                      p_converted->i_nb_samples ) - p_converted->i_pts;
+
+                block_ChainLastAppend( &p_sys->pp_decoded_last, p_converted );
+            }
 
-    if( ctx->channels <= 0 || ctx->channels > 8 || ctx->sample_rate <= 0 )
-    {
-        msg_Warn( p_dec, "invalid audio properties channels count %d, sample rate %d",
-                  ctx->channels, ctx->sample_rate );
-        goto end;
-    }
+            /* Prepare new frame */
+            frame = av_frame_alloc();
+            if (unlikely(frame == NULL))
+                break;
+        }
+        else av_frame_free( &frame );
+    };
 
-    if( p_dec->fmt_out.audio.i_rate != (unsigned int)ctx->sample_rate )
-        date_Init( &p_sys->end_date, ctx->sample_rate, 1 );
+    return ( p_sys->p_decoded ) ? DequeueOneDecodedFrame( p_sys ) : NULL;
 
-    if( p_block->i_pts > date_Get( &p_sys->end_date ) )
+end:
+    p_dec->b_error = true;
+    if( pp_block )
     {
-        date_Set( &p_sys->end_date, p_block->i_pts );
-    }
-
-    if( p_block->i_buffer == 0 )
-    {   /* Done with this buffer */
-        block_Release( p_block );
-        p_block = NULL;
+        assert( *pp_block == p_block );
         *pp_block = NULL;
     }
+drop:
+    if( p_block != NULL )
+        block_Release(p_block);
+    return NULL;
+}
 
-    /* NOTE WELL: Beyond this point, p_block refers to the DECODED block! */
-    SetupOutputFormat( p_dec, true );
-    if( decoder_UpdateAudioFormat( p_dec ) )
-        goto drop;
+static block_t * ConvertAVFrame( decoder_t *p_dec, AVFrame *frame )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    AVCodecContext *ctx = p_sys->p_context;
+    block_t *p_block;
 
     /* Interleave audio if required */
     if( av_sample_fmt_is_planar( ctx->sample_fmt ) )
     {
         p_block = block_Alloc(frame->linesize[0] * ctx->channels);
-        if (unlikely(p_block == NULL))
-            goto drop;
-
-        const void *planes[ctx->channels];
-        for (int i = 0; i < ctx->channels; i++)
-            planes[i] = frame->extended_data[i];
+        if ( likely(p_block) )
+        {
+            const void *planes[ctx->channels];
+            for (int i = 0; i < ctx->channels; i++)
+                planes[i] = frame->extended_data[i];
 
-        aout_Interleave(p_block->p_buffer, planes, frame->nb_samples,
-                        ctx->channels, p_dec->fmt_out.audio.i_format);
-        p_block->i_nb_samples = frame->nb_samples;
+            aout_Interleave(p_block->p_buffer, planes, frame->nb_samples,
+                            ctx->channels, p_dec->fmt_out.audio.i_format);
+            p_block->i_nb_samples = frame->nb_samples;
+        }
         av_frame_free(&frame);
     }
     else
     {
         p_block = vlc_av_frame_Wrap(frame);
-        if (unlikely(p_block == NULL))
-            goto drop;
         frame = NULL;
     }
 
-    if (p_sys->b_extract)
+    if (p_sys->b_extract && p_block)
     {   /* TODO: do not drop channels... at least not here */
         block_t *p_buffer = block_Alloc( p_dec->fmt_out.audio.i_bytes_per_frame
                                          * p_block->i_nb_samples );
-        if( unlikely(p_buffer == NULL) )
-            goto drop;
-        aout_ChannelExtract( p_buffer->p_buffer,
-                             p_dec->fmt_out.audio.i_channels,
-                             p_block->p_buffer, ctx->channels,
-                             p_block->i_nb_samples, p_sys->pi_extraction,
-                             p_dec->fmt_out.audio.i_bitspersample );
-        p_buffer->i_nb_samples = p_block->i_nb_samples;
+        if( likely(p_buffer) )
+        {
+            aout_ChannelExtract( p_buffer->p_buffer,
+                                 p_dec->fmt_out.audio.i_channels,
+                                 p_block->p_buffer, ctx->channels,
+                                 p_block->i_nb_samples, p_sys->pi_extraction,
+                                 p_dec->fmt_out.audio.i_bitspersample );
+            p_buffer->i_nb_samples = p_block->i_nb_samples;
+        }
         block_Release( p_block );
         p_block = p_buffer;
     }
 
-    /* Silent unwanted samples */
-    if( p_sys->i_reject_count > 0 )
-    {
-        memset( p_block->p_buffer, 0, p_block->i_buffer );
-        p_sys->i_reject_count--;
-    }
-
-    p_block->i_buffer = p_block->i_nb_samples
-                        * p_dec->fmt_out.audio.i_bytes_per_frame;
-    p_block->i_pts = date_Get( &p_sys->end_date );
-    p_block->i_length = date_Increment( &p_sys->end_date,
-                                      p_block->i_nb_samples ) - p_block->i_pts;
     return p_block;
-
-end:
-    *pp_block = NULL;
-drop:
-    av_frame_free(&frame);
-    if( p_block != NULL )
-        block_Release(p_block);
-    return NULL;
 }
 
 /*****************************************************************************
diff --git a/modules/codec/avcodec/avcodec.c b/modules/codec/avcodec/avcodec.c
index d471077..f491dc5 100644
--- a/modules/codec/avcodec/avcodec.c
+++ b/modules/codec/avcodec/avcodec.c
@@ -336,10 +336,18 @@ static void CloseDecoder( vlc_object_t *p_this )
 {
     decoder_t *p_dec = (decoder_t *)p_this;
 
-    if( p_dec->fmt_out.i_cat == VIDEO_ES )
-        EndVideoDec( p_dec );
-    else
-        ffmpeg_CloseCodec( p_dec );
+    switch( p_dec->fmt_out.i_cat )
+    {
+        case VIDEO_ES:
+            EndVideoDec( p_dec );
+            break;
+        case AUDIO_ES:
+            EndAudioDec( p_dec );
+            break;
+        default:
+            ffmpeg_CloseCodec( p_dec );
+            break;
+    }
 
     decoder_sys_t *p_sys = p_dec->p_sys;
 
diff --git a/modules/codec/avcodec/avcodec.h b/modules/codec/avcodec/avcodec.h
index de1a4ac..c9dfbc9 100644
--- a/modules/codec/avcodec/avcodec.h
+++ b/modules/codec/avcodec/avcodec.h
@@ -41,6 +41,7 @@ void EndVideoDec( decoder_t *p_dec );
 
 /* Audio Decoder */
 int InitAudioDec( decoder_t *, AVCodecContext *, const AVCodec * );
+void EndAudioDec( decoder_t *p_dec );
 
 /* Subtitle Decoder */
 int InitSubtitleDec( decoder_t *, AVCodecContext *, const AVCodec * );
-- 
2.7.4



More information about the vlc-devel mailing list