[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