【以mp4文件格式和AAC、H264编码的本地文件为例展开分析】
由第六章节可知音频解码器的加载方式与视频解码器也是类似的。
通过此前分析过的模块加载方式,可找到ffmpeg解码和编码模块的初始化加载如下:
// 位于[vlc/modules/codec/avcodec/avcodec.c]
vlc_module_begin ()
set_shortname( "FFmpeg")
set_category( CAT_INPUT )
set_subcategory( SUBCAT_INPUT_VCODEC )
/* decoder main module */
set_description( N_("FFmpeg audio/video decoder") )
set_help( MODULE_DESCRIPTION )
set_section( N_("Decoding") , NULL )
// video
add_shortcut("ffmpeg")
set_capability("video decoder", 70)
set_callbacks(InitVideoDec, EndVideoDec)
// audio
add_submodule()
add_shortcut("ffmpeg")
set_capability("audio decoder", 70)
set_callbacks(InitAudioDec, EndAudioDec)
// subtitle
add_submodule()
add_shortcut("ffmpeg")
set_capability("spu decoder", 70)
set_callbacks(InitSubtitleDec, EndSubtitleDec)
// ...省略其他代码
vlc_module_end ()
有上面可知三个解码模块加载的初始化入口方法。
该章节分析audio解码:
1、InitVideoDec实现分析:【vlc/modules/codec/avcodec/audio.c】
/*****************************************************************************
* InitAudioDec: initialize audio decoder
*****************************************************************************
* The avcodec codec will be opened, some memory allocated.
*****************************************************************************/
int InitAudioDec( vlc_object_t *obj )
{
decoder_t *p_dec = (decoder_t *)obj;
// 具体的解码器对象
const AVCodec *codec;
// 初始化libavcocdec模块,根据track类型【audio/video/spu】和avcodec的解码ID类型,
// 来初始化对应解码器,并设置debug等级参数等,此处为分析audio解码器
AVCodecContext *avctx = ffmpeg_AllocContext( p_dec, &codec );
if( avctx == NULL )
return VLC_EGENERIC;
/* Allocate the memory needed to store the decoder's structure */
decoder_sys_t *p_sys = malloc(sizeof(*p_sys));
if( unlikely(p_sys == NULL) )
{
avcodec_free_context( &avctx );
return VLC_ENOMEM;
}
p_dec->p_sys = p_sys;
p_sys->p_context = avctx;
p_sys->p_codec = codec;
// 初始化解码器额外附带信息【有的话】
// Initialize decoder extradata
InitDecoderConfig( p_dec, avctx );
// 打开ffmpeg对应音频格式的编码器
// 见2小节分析
/* ***** Open the codec ***** */
if( OpenAudioCodec( p_dec ) < 0 )
{
free( p_sys );
avcodec_free_context( &avctx );
return VLC_EGENERIC;
}
p_sys->i_reject_count = 0;
p_sys->b_extract = false;
p_sys->i_previous_channels = 0;
p_sys->i_previous_layout = 0;
// 根据输入的音频采样格式信息来初始化音频输出格式:
// 音频编解码格式、声道类型、采样率、声道布局【单声道、立体声道、4/5.1/7.1声道等】
//
/* */
/* Try to set as much information as possible but do not trust it */
SetupOutputFormat( p_dec, false );
// 初始化PTS时间,若有采样率值则根据采样率值来重新初始化PTS时间
date_Set( &p_sys->end_date, VLC_TS_INVALID );
if( !p_dec->fmt_out.audio.i_rate )
p_dec->fmt_out.audio.i_rate = p_dec->fmt_in.audio.i_rate;
if( p_dec->fmt_out.audio.i_rate )
date_Init( &p_sys->end_date, p_dec->fmt_out.audio.i_rate, 1 );
p_dec->fmt_out.audio.i_chan_mode = p_dec->fmt_in.audio.i_chan_mode;
// 赋值解码方法指针,该方法被调用是在此前第五章分析中:
// 【2.3.2、DecoderDecode解码请求实现分析:【vlc/src/input/decoder.c】】
// 见第3小节分析
p_dec->pf_decode = DecodeAudio;
// 赋值刷新解码器缓冲区buffer的方法指针,该方法被调用也在此前第五章分析中刷新流程
// 见第4小节分析
p_dec->pf_flush = Flush;
/* XXX: Writing input format makes little sense. */
if( avctx->profile != FF_PROFILE_UNKNOWN )
p_dec->fmt_in.i_profile = avctx->profile;
if( avctx->level != FF_LEVEL_UNKNOWN )
p_dec->fmt_in.i_level = avctx->level;
return VLC_SUCCESS;
}
2、OpenAudioCodec实现分析:
// 【vlc/modules/codec/avcodec/audio.c】
static int OpenAudioCodec( decoder_t *p_dec )
{
decoder_sys_t *p_sys = p_dec->p_sys;
AVCodecContext *ctx = p_sys->p_context;
const AVCodec *codec = p_sys->p_codec;
if( ctx->extradata_size <= 0 )
{
if( codec->id == AV_CODEC_ID_VORBIS ||
( codec->id == AV_CODEC_ID_AAC &&
!p_dec->fmt_in.b_packetized ) )
{
// AAC编码若开启不分包功能则解码器失败
msg_Warn( p_dec, "waiting for extra data for codec %s",
codec->name );
return 1;
}
}
// 音频输入格式信息等传递给ffmpeg解码器上下文信息中
ctx->sample_rate = p_dec->fmt_in.audio.i_rate;
ctx->channels = p_dec->fmt_in.audio.i_channels;
ctx->block_align = p_dec->fmt_in.audio.i_blockalign;
ctx->bit_rate = p_dec->fmt_in.i_bitrate;
ctx->bits_per_coded_sample = p_dec->fmt_in.audio.i_bitspersample;
// 若是自适应差分脉冲编码调制格式并且码率和采样率都不为空时重新初始化量化精度
if( codec->id == AV_CODEC_ID_ADPCM_G726 &&
ctx->bit_rate > 0 &&
ctx->sample_rate > 0)
ctx->bits_per_coded_sample = ctx->bit_rate / ctx->sample_rate;
// 根据全局设置的播放器配置参数【"avcodec-options"】,打开并初始化ffmpeg的一些结构体数据,
// 并且加锁只会初始化一次,设置解码器非常多的数据初始化
// 后续分析ffmpeg模块章节 TODO
return ffmpeg_OpenCodec( p_dec, ctx, codec );
}
3、DecodeAudio音频解码实现分析:
// 【vlc/modules/codec/avcodec/audio.c】
static int DecodeAudio( decoder_t *p_dec, block_t *p_block )
{
block_t **pp_block = p_block ? &p_block : NULL;
int i_ret;
// 若解码成功则循环继续解码剩余数据
do
{
i_ret = DecodeBlock( p_dec, pp_block );
}
while( i_ret == VLCDEC_SUCCESS && pp_block && *pp_block );
return i_ret;
}
// 【vlc/modules/codec/avcodec/audio.c】
/*****************************************************************************
* DecodeBlock: Called to decode one frame
*****************************************************************************/
static int DecodeBlock( 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;
block_t *p_block = NULL;
bool b_error = false;
if( !ctx->extradata_size && p_dec->fmt_in.i_extra
&& !avcodec_is_open( ctx ) )
{
InitDecoderConfig( p_dec, ctx );
OpenAudioCodec( p_dec );
}
if( !avcodec_is_open( ctx ) )
{
// 打开失败则丢弃当前音频块block数据
if( pp_block )
p_block = *pp_block;
goto drop;
}
if( pp_block == NULL ) /* Drain request */
{
// 若输入音频块数据为空则表示需要解码器端的输出全部已解码缓冲buffer数据并清空buffer
/* we don't need to care about return val */
(void) avcodec_send_packet( ctx, NULL );
}
else
{
p_block = *pp_block;
}
if( p_block )
{
if( p_block->i_flags & BLOCK_FLAG_CORRUPTED )
{
// 若当前block块数据有损坏则直接丢弃并刷新解码器缓冲区buffer
// 见4小节分析
Flush( p_dec );
goto drop;
}
if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY )
{
// 若当前block数据不是连续帧数据,则重置PTS时间为0
date_Set( &p_sys->end_date, VLC_TS_INVALID );
}
// 若当前保存的最后的PTS时间为0并且当前block块数据的PTS也无效,则丢弃该数据
/* 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 drop;
// 当前block块数据的负载数据【即编码数据】为空时,丢弃该数据
if( p_block->i_buffer <= 0 )
goto drop;
// 若未重分配block内存,则需要重分配
if( (p_block->i_flags & BLOCK_FLAG_PRIVATE_REALLOCATED) == 0 )
{
// 此处理:重分配内存使其大小符合字节对齐【32位或64位】
*pp_block = p_block = block_Realloc( p_block, 0, p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE );
if( !p_block )
goto end;
p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE;
// 此处实现重要:如上面的重分配存储后此处
// 将最后的【FF_INPUT_BUFFER_PADDING_SIZE】32位值全初始化为0
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 ret = 0; ret == 0; )
{
/* Feed in the loop as buffer could have been full on first iterations */
if( p_block )
{
// ffmpeg使用的已编码数据结构对象
AVPacket pkt;
av_init_packet( &pkt );
pkt.data = p_block->p_buffer;
pkt.size = p_block->i_buffer;
// 发送给ffmpeg模块进行解码
// 见后续ffmpeg模块章节分析 TODO
ret = avcodec_send_packet( ctx, &pkt );
if( ret == 0 ) /* Block has been consumed */
{
// 返回0代码解码成功
// 注译:如果输入块已使用【已解码成功】,只能从输入块中设置新的pts,否则通过插值
/* 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;
}
else if ( ret != AVERROR(EAGAIN) ) /* Errors other than buffer full */
{
if( ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL) )
goto end;
else
goto drop;
}
}
// 从ctx中接收解码器端已解码的原始帧数据(音视频原始数据)存入frame
// 注:音频帧的定义和编码格式有关,不同编码标准不同
// 见后续ffmpeg模块章节分析 TODO
/* Try to read one or multiple frames */
ret = avcodec_receive_frame( ctx, frame );
if( ret == 0 )
{
// 解码成功
/* checks and init from first decoded frame */
if( ctx->channels <= 0 || ctx->channels > INPUT_CHAN_MAX
|| ctx->sample_rate <= 0 )
{
msg_Warn( p_dec, "invalid audio properties channels count %d, sample rate %d",
ctx->channels, ctx->sample_rate );
goto drop;
}
else if( p_dec->fmt_out.audio.i_rate != (unsigned int)ctx->sample_rate )
{
// 若解码器实际输出的采样率和输入解码器的采样率不同,则更新其PTS时间为0并记录新值
date_Init( &p_sys->end_date, ctx->sample_rate, 1 );
}
// 更新音频输出格式相关信息:采样率、声道类型、声道数等
SetupOutputFormat( p_dec, true );
// 注:此处内部会调用vlc的decoder_t对象的【dec->pf_aout_format_update( dec );】该方法
// 该方法赋值实现是在vlc的decoder初始化时赋值的。
// 实现分析见后续aout音频输出端章节分析 TODO
if( decoder_UpdateAudioFormat( p_dec ) )
goto drop;
// 根据当前音频样本格式判断是否需要进行样本格式的转换
// 即若音频样本是平面布局方式则需要转成交错布局方法的样本数据,
// 最后转成block块数据来保存已解码的数据
block_t *p_converted = ConvertAVFrame( p_dec, frame ); /* Consumes frame */
if( p_converted )
{
/* Silent unwanted samples */
if( p_sys->i_reject_count > 0 )
{
// 通过分析可知,大于0的清空是flush执行时若是MP2或Mp3编码格式音频
// 则需要重置其开头buffer大小的数据
memset( p_converted->p_buffer, 0, p_converted->i_buffer );
p_sys->i_reject_count--;
}
// 重新计算buffer大小 = 【音频样本数量】*【每帧(样本)占用字节数】
p_converted->i_buffer = p_converted->i_nb_samples
* p_dec->fmt_out.audio.i_bytes_per_frame;
// 获取当前最新的PTS时间
p_converted->i_pts = date_Get( &p_sys->end_date );
// 计算当前block数据中样本总时长
p_converted->i_length = date_Increment( &p_sys->end_date,
p_converted->i_nb_samples ) - p_converted->i_pts;
// 将解码的block块数据入栈到aout输出端队列缓冲区中
// 见3.1分析
decoder_QueueAudio( p_dec, p_converted );
}
/* Prepare new frame */
frame = av_frame_alloc();
if (unlikely(frame == NULL))
break;
}
else
{
/* After draining, we need to reset decoder with a flush */
if( ret == AVERROR_EOF )
// EOF数据读取结束后进行清空当前解码器缓冲区buffer
// 实现分析见后续aout音频输出端章节分析 TODO
avcodec_flush_buffers( p_sys->p_context );
av_frame_free( &frame );
}
};
return VLCDEC_SUCCESS;
end:
b_error = true;
drop:
if( pp_block )
{
assert( *pp_block == p_block );
*pp_block = NULL;
}
if( p_block != NULL )
block_Release(p_block);
if( frame != NULL )
av_frame_free( &frame );
// 若仅仅是drop事件则也返回成功
return (b_error) ? VLCDEC_ECRITICAL : VLCDEC_SUCCESS;
}
3.1、decoder_QueueAudio实现分析:【此方法的调用流程和视频的调用流程基本一致】
// 【vlc/modules/codec/avcodec/audio.c】
/**
* This function queues a single audio block to the audio output.
*
* \note
* The caller doesn't own the audio block anymore after this call (even in case
* of error).
*
* \return 0 if the block is queued, -1 on error
*/
static inline int decoder_QueueAudio( decoder_t *dec, block_t *p_aout_buf )
{
assert( p_aout_buf->p_next == NULL );
assert( dec->pf_queue_audio != NULL );
// 此处可知最终调用了vlc的decoder控制对象的[pf_queue_audio]方法执行的
// 该方法赋值在【vlc/src/input/decoder.c】的CreateDecoder方法中
// [p_dec->pf_queue_audio = DecoderQueueAudio;]
return dec->pf_queue_audio( dec, p_aout_buf );
}
// 【vlc/src/input/decoder.c】
static int DecoderQueueAudio( decoder_t *p_dec, block_t *p_aout_buf )
{
unsigned lost = 0;
decoder_owner_sys_t *p_owner = p_dec->p_owner;
// 请求播放音频
// 见3.1.1小节分析
int ret = DecoderPlayAudio( p_dec, p_aout_buf, &lost );
// 音频播放请求完成后进行相关状态更新
// 该方法赋值在【vlc/src/input/decoder.c】的CreateDecoder方法中
// [p_owner->pf_update_stat = DecoderUpdateStatAudio;]
// 【DecoderUpdateStatAudio】该方法见3.1.2小节分析
p_owner->pf_update_stat( p_owner, 1, lost );
return ret;
}
3.1.1、DecoderPlayAudio实现分析:【vlc/src/input/decoder.c】
static int DecoderPlayAudio( decoder_t *p_dec, block_t *p_audio,
unsigned *restrict pi_lost_sum )
{
decoder_owner_sys_t *p_owner = p_dec->p_owner;
bool prerolled;
assert( p_audio != NULL );
vlc_mutex_lock( &p_owner->lock );
// 检查当前待播放音频的显示时间点PTS是否已过期或无效,则丢弃
if( p_owner->i_preroll_end > p_audio->i_pts )
{
vlc_mutex_unlock( &p_owner->lock );
block_Release( p_audio );
return -1;
}
prerolled = p_owner->i_preroll_end > INT64_MIN;
p_owner->i_preroll_end = INT64_MIN;
vlc_mutex_unlock( &p_owner->lock );
if( unlikely(prerolled) )
{
msg_Dbg( p_dec, "end of audio preroll" );
// 音频预解码完成则刷新视频输出端数据buffer队列,
// 不需要输出,直接清空,该方法内部调用了音频解码模块的Flush功能方法
// 【Flush功能】见第4小节分析
if( p_owner->p_aout )
aout_DecFlush( p_owner->p_aout, false );
}
/* */
if( p_audio->i_pts <= VLC_TS_INVALID ) // FIXME --VLC_TS_INVALID verify audio_output/*
{
// 当前待输出音频的PTS时间无效则drop,并记录下来【统计状态值】
msg_Warn( p_dec, "non-dated audio buffer received" );
*pi_lost_sum += 1;
block_Release( p_audio );
return 0;
}
/* */
vlc_mutex_lock( &p_owner->lock );
if( p_owner->b_waiting )
{
// 该情况此前分析过,若demuxer线程当前已是暂停状态,则唤醒其继续解复用数据
p_owner->b_has_data = true;
vlc_cond_signal( &p_owner->wait_acknowledge );
}
/* */
int i_rate = INPUT_RATE_DEFAULT;
// 检查vlc的decoder控制层是否应该wait即暂停向解码模块传递编码数据,
// 若是暂停了则会被上述demuxer层[wait_acknowledge]事件执行后的处理中进行唤醒
DecoderWaitUnblock( p_dec );
// 根据jitter buffer时钟策略,更新当前PTS时间,当前采样率等
DecoderFixTs( p_dec, &p_audio->i_pts, NULL, &p_audio->i_length,
&i_rate, AOUT_MAX_ADVANCE_TIME );
vlc_mutex_unlock( &p_owner->lock );
audio_output_t *p_aout = p_owner->p_aout;
// 1、PTS和采样率数据的合理性判断。
// 2、【AOUT_MAX_PREPARE_TIME】该值2秒用于限制该音频数据能够达到audio输出端的最大值即默认2秒
// 若大于2秒会造成当前线程进入睡眠【因为解码太快】。
// 3、【DecoderTimedWait】该方法实现为:若当前decoder层正在进行flush请求则返回失败【非0】,
// 否则判断当前PTS与当前时间差值,决定当前vlc的decoder控制层线程是否应该wait等待一段时间【差值】,
// 若wait时间结束后再次判断当前decoder层是否正在请求flush,若flush则返回失败,否则成功进入if
if( p_aout != NULL && p_audio->i_pts > VLC_TS_INVALID
&& i_rate >= INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE
&& i_rate <= INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE
&& !DecoderTimedWait( p_dec, p_audio->i_pts - AOUT_MAX_PREPARE_TIME ) )
{
// 【可能需要】过滤并混淆当前音频block数据,
// 并将结果数据入栈aout输出端队列用于播放
// 见3.1.1.1小节分析
int status = aout_DecPlay( p_aout, p_audio, i_rate );
if( status == AOUT_DEC_CHANGED )
{
// 若当前音频编码格式发送变化则标记需要重新加载对应音频解码器的flag
/* Only reload the decoder */
RequestReload( p_dec );
}
else if( status == AOUT_DEC_FAILED )
{
// 此处表明aout播放失败即不能播放,此处标记应该停止aout并重新加载解码器模块组件
/* If we reload because the aout failed, we should release it. That
* way, a next call to aout_update_format() won't re-use the
* previous (failing) aout but will try to create a new one. */
atomic_store( &p_owner->reload, RELOAD_DECODER_AOUT );
}
}
else
{
msg_Dbg( p_dec, "discarded audio buffer" );
*pi_lost_sum += 1;
block_Release( p_audio );
}
return 0;
}
3.1.1.1、aout_DecPlay实现分析:【vlc/src/audio_output/dec.c】
int aout_DecPlay (audio_output_t *aout, block_t *block, int input_rate)
{
aout_owner_t *owner = aout_owner (aout);
assert (input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE);
assert (input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE);
assert (block->i_pts >= VLC_TS_0);
// 计算当前block音频数据时长 = 样本数目/采样率
block->i_length = CLOCK_FREQ * block->i_nb_samples
/ owner->input_format.i_rate;
aout_OutputLock (aout);
// 检查aout音频输出端是否已准备好
// 若未启动则重启动aout端,并重设置音频过滤器filters相关对象
int ret = aout_CheckReady (aout);
if (unlikely(ret == AOUT_DEC_FAILED))
goto drop; /* Pipeline is unrecoverably broken :-( */
// 判断当前数据帧
const mtime_t now = mdate (), advance = block->i_pts - now;
// [AOUT_MAX_PTS_DELAY]该值为60毫秒即定义的音频播放最大延迟或过早时间限制,超出则丢弃
if (advance < -AOUT_MAX_PTS_DELAY)
{
/* Late buffer can be caused by bugs in the decoder, by scheduling
* latency spikes (excessive load, SIGSTOP, etc.) or if buffering is
* insufficient. We assume the PTS is wrong and play the buffer anyway:
* Hopefully video has encountered a similar PTS problem as audio. */
msg_Warn (aout, "buffer too late (%"PRId64" us): dropped", advance);
goto drop;
}
if (advance > AOUT_MAX_ADVANCE_TIME)
{
/* Early buffers can only be caused by bugs in the decoder. */
msg_Err (aout, "buffer too early (%"PRId64" us): dropped", advance);
goto drop;
}
// 标记同步信息字段是否为不连续音频数据
if (block->i_flags & BLOCK_FLAG_DISCONTINUITY)
owner->sync.discontinuity = true;
// 此处判断是否应该更新欧拉角过滤器【用于立体声渲染】
if (atomic_exchange(&owner->vp.update, false))
{
vlc_mutex_lock (&owner->vp.lock);
aout_FiltersChangeViewpoint (owner->filters, &owner->vp.value);
vlc_mutex_unlock (&owner->vp.lock);
}
// 使用设置的过滤器处理输入的block数据中的音频样本数据,
// 过滤处理后的音频数据成为需要播放的目标音频数据。
// 过滤器有均衡器、转换、重采样率等
block = aout_FiltersPlay (owner->filters, block, input_rate);
if (block == NULL)
goto lost;
// 音频播放增益等设置【功放等三方module组件模块加载方式实现】
/* Software volume */
aout_volume_Amplify (owner->volume, block);
// PTS同步处理:PTS漂移修正。【重采样过滤器处理漂移问题等】
// 注译:根据实际播放时间和预期播放时间之间的漂移,音频核心可能会忽略漂移,
// 触发上采样或下采样,插入静音或甚至丢弃采样数据。未来的VLC版本可能会调整输入速率。
// 音频输出插件负责估计它的实际播放时间,或者更确切地说,下一个样本将被播放的估计时间。
// (实际的播放时间总是当前时间,也就是说mdate()。这不是一个有用的统计数据。)
// 大多数音频输出插件可以估计延迟,直到播放的下一个样本被写入缓冲区,或相等的时间,
// 直到所有样本在缓冲区将被播放。然后:pts = mdate() + delay
/* Drift correction */
aout_DecSynchronize (aout, block->i_pts, input_rate);
/* Output */
// 更新同步信息
owner->sync.end = block->i_pts + block->i_length + 1;
owner->sync.discontinuity = false;
// 通过aout音频输出端请求播放
// 见本小节后续分析
aout_OutputPlay (aout, block);
// 统计状态值:已播放的buffer数据个数加1
atomic_fetch_add(&owner->buffers_played, 1);
out:
aout_OutputUnlock (aout);
return ret;
drop:
owner->sync.discontinuity = true;
block_Release (block);
lost:
// 统计状态值:已丢失的buffer数据个数加1
atomic_fetch_add(&owner->buffers_lost, 1);
goto out;
}
// 【vlc/src/audio_ouput/output.c】
/**
* Plays a decoded audio buffer.
* \note This can only be called after a successful aout_OutputNew().
* \warning The caller must hold the audio output lock.
*/
void aout_OutputPlay (audio_output_t *aout, block_t *block)
{
aout_OutputAssertLocked (aout);
#ifndef NDEBUG
// debug模式时进入
aout_owner_t *owner = aout_owner (aout);
assert (owner->mixer_format.i_frame_length > 0);
assert (block->i_buffer == 0 || block->i_buffer / block->i_nb_samples ==
owner->mixer_format.i_bytes_per_frame /
owner->mixer_format.i_frame_length);
#endif
// 播放已解码音频buffer数据
// 调用了aout输出端的play方法
// 见aout输出端章节分析 TODO
aout->play (aout, block);
}
3.1.2、DecoderUpdateStatAudio实现分析:
// 【vlc/src/input/decoder.c】
static void DecoderUpdateStatAudio( decoder_owner_sys_t *p_owner,
unsigned decoded, unsigned lost )
{
input_thread_t *p_input = p_owner->p_input;
unsigned played = 0;
/* Update ugly stat */
if( p_input == NULL )
return;
if( p_owner->p_aout != NULL )
{
unsigned aout_lost;
// 获取aout输出端播放当前block音频数据执行的结果状态:已播放个数、已丢失个数
aout_DecGetResetStats( p_owner->p_aout, &aout_lost, &played );
lost += aout_lost;
}
// 然后统计当前音频播放结果统计器状态值:已丢失数、已播放数、已解码数
vlc_mutex_lock( &input_priv(p_input)->counters.counters_lock);
stats_Update( input_priv(p_input)->counters.p_lost_abuffers, lost, NULL );
stats_Update( input_priv(p_input)->counters.p_played_abuffers, played, NULL );
stats_Update( input_priv(p_input)->counters.p_decoded_audio, decoded, NULL );
vlc_mutex_unlock( &input_priv(p_input)->counters.counters_lock);
}
//【vlc/src/audio_output/dec.c】
void aout_DecGetResetStats(audio_output_t *aout, unsigned *restrict lost,
unsigned *restrict played)
{
aout_owner_t *owner = aout_owner (aout);
*lost = atomic_exchange(&owner->buffers_lost, 0);
*played = atomic_exchange(&owner->buffers_played, 0);
}
4、Flush解码器缓冲区刷新实现分析:【vlc/modules/codec/avcodec/audio.c】
static void Flush( decoder_t *p_dec )
{
decoder_sys_t *p_sys = p_dec->p_sys;
AVCodecContext *ctx = p_sys->p_context;
if( avcodec_is_open( ctx ) )
// EOF数据读取结束后进行清空当前解码器缓冲区buffer
// 实现分析见后续aout音频输出端章节分析 TODO
avcodec_flush_buffers( ctx );
// 重置最近一次音频数据的PTS时间为0
date_Set( &p_sys->end_date, VLC_TS_INVALID );
if( ctx->codec_id == AV_CODEC_ID_MP2 ||
ctx->codec_id == AV_CODEC_ID_MP3 )
// 对应上面解码流程执行过程中的对该编码格式的数据进行重置操作
p_sys->i_reject_count = 3;
}
此章分析结束,aout输出端分析请见后续章节