由前章节分析可知aout音频输出端对象初始化和vout视频输出端类似,则由第七章分析可知:
aout输出端对象初始化方法为【CreateDecoder】中的【p_dec->pf_aout_format_update = aout_update_format;】方法指针赋值调用的。
// [vlc/src/input/decoder.c]
// vout赋值初始化方法指针赋值
static decoder_t * CreateDecoder( vlc_object_t *p_parent,
input_thread_t *p_input,
const es_format_t *fmt,
input_resource_t *p_resource,
sout_instance_t *p_sout )
{
// ... 省略部分代码
decoder_t *p_dec;
// 此处赋值了aout/vout的初始化方法指针
/* Set buffers allocation callbacks for the decoders */
p_dec->pf_aout_format_update = aout_update_format;
p_dec->pf_vout_format_update = vout_update_format;
// 该方法实现:获取vout端图像队列中的buffer
// 【注:该图像队列池缓冲区其实最终是从display展示层模块获取的】
// 此处暂不展开分析 TODO
p_dec->pf_vout_buffer_new = vout_new_buffer;
p_dec->pf_spu_buffer_new = spu_new_buffer;
// ... 省略部分代码
}
1、【aout_update_format】实现分析:[vlc/src/input/decoder.c]
static int aout_update_format( decoder_t *p_dec )
{
decoder_owner_sys_t *p_owner = p_dec->p_owner;
if( p_owner->p_aout &&
( !AOUT_FMTS_IDENTICAL(&p_dec->fmt_out.audio, &p_owner->fmt.audio) ||
p_dec->fmt_out.i_codec != p_dec->fmt_out.audio.i_format ||
p_dec->fmt_out.i_profile != p_owner->fmt.i_profile ) )
{
audio_output_t *p_aout = p_owner->p_aout;
/* Parameters changed, restart the aout */
vlc_mutex_lock( &p_owner->lock );
p_owner->p_aout = NULL;
vlc_mutex_unlock( &p_owner->lock );
// 若音频格式发生变化,则停止/删除所有涉及音频输出的模块
// 如清空音频过滤器链功能、请求停止播放、去除音频功放模块、卸载增益模块、初始化数据等
aout_DecDelete( p_aout );
// 若p_resource->p_aout存在并和【p_aout】相同,则持续【p_resource->p_aout】的音频输出
input_resource_PutAout( p_owner->p_resource, p_aout );
}
// 若输入音频播放增益参数发生变化则重新初始化输出增益参数
/* Check if only replay gain has changed */
if( aout_replaygain_changed( &p_dec->fmt_in.audio_replay_gain,
&p_owner->fmt.audio_replay_gain ) )
{
p_dec->fmt_out.audio_replay_gain = p_dec->fmt_in.audio_replay_gain;
if( p_owner->p_aout )
{
p_owner->fmt.audio_replay_gain = p_dec->fmt_in.audio_replay_gain;
// 发送增益改变事件回调
var_TriggerCallback( p_owner->p_aout, "audio-replay-gain-mode" );
}
}
if( p_owner->p_aout == NULL )
{// 若当前未有aout模块则进行创建和初始化
p_dec->fmt_out.audio.i_format = p_dec->fmt_out.i_codec;
audio_sample_format_t format = p_dec->fmt_out.audio;
// 根据当前音频采样格式参数,初始化其音频通道(声道)数、量化精度【每个采样点使用的位数bits】、
// 每压缩帧数据占字节数大小、每压缩帧数据包含的采样帧个数
aout_FormatPrepare( &format );
// 获取是否设置了开启杜比音效
const int i_force_dolby = var_InheritInteger( p_dec, "force-dolby-surround" );
if( i_force_dolby &&
format.i_physical_channels == (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT) )
{
// 设置杜比声道模式
if( i_force_dolby == 1 )
format.i_chan_mode |= AOUT_CHANMODE_DOLBYSTEREO;
else /* i_force_dolby == 2 */
format.i_chan_mode &= ~AOUT_CHANMODE_DOLBYSTEREO;
}
aout_request_vout_t request_vout = {
.pf_request_vout = aout_request_vout,
.p_private = p_dec,
};
audio_output_t *p_aout;
// 创建aout输出端对象,并加载vlc中的aout输出端模块,见2小节分析
p_aout = input_resource_GetAout( p_owner->p_resource );
if( p_aout )
{
/* TODO: 3.0 HACK: we need to put i_profile inside audio_format_t
* for 4.0 */
if( p_dec->fmt_out.i_codec == VLC_CODEC_DTS )
var_SetBool( p_aout, "dtshd", p_dec->fmt_out.i_profile > 0 );
// 见3小节分析
if( aout_DecNew( p_aout, &format,
&p_dec->fmt_out.audio_replay_gain,
&request_vout ) )
{// 此处表明该方法执行失败,则释放/清空p_aout对象
input_resource_PutAout( p_owner->p_resource, p_aout );
p_aout = NULL;
}
}
vlc_mutex_lock( &p_owner->lock );
p_owner->p_aout = p_aout;
// 复制decoder层的输出格式信息给【p_owner->fmt】
DecoderUpdateFormatLocked( p_dec );
// 计算当前音频的输出格式信息
aout_FormatPrepare( &p_owner->fmt.audio );
vlc_mutex_unlock( &p_owner->lock );
if( p_owner->p_input != NULL )
// 发送aout变化事件【删除或创建】回调给输入端
input_SendEventAout( p_owner->p_input );
if( p_aout == NULL )
{
msg_Err( p_dec, "failed to create audio output" );
return -1;
}
p_dec->fmt_out.audio.i_bytes_per_frame =
p_owner->fmt.audio.i_bytes_per_frame;
p_dec->fmt_out.audio.i_frame_length =
p_owner->fmt.audio.i_frame_length;
}
return 0;
}
2、input_resource_GetAout实现分析:
// [vlc/src/input/resource.c]
audio_output_t *input_resource_GetAout( input_resource_t *p_resource )
{
audio_output_t *p_aout;
vlc_mutex_lock( &p_resource->lock_hold );
p_aout = p_resource->p_aout;
if( p_aout == NULL || p_resource->b_aout_busy )
{// 若当前没有可用aout输出端对象,则创建它
msg_Dbg( p_resource->p_parent, "creating audio output" );
vlc_mutex_unlock( &p_resource->lock_hold );
// 创建方法,见该小节后续分析
p_aout = aout_New( p_resource->p_parent );
if( p_aout == NULL )
return NULL; /* failed */
vlc_mutex_lock( &p_resource->lock_hold );
// 赋值给resource对象
if( p_resource->p_aout == NULL )
p_resource->p_aout = p_aout;
}
else
msg_Dbg( p_resource->p_parent, "reusing audio output" );
if( p_resource->p_aout == p_aout )
{// 若相同则【b_aout_busy】肯定会为false,将其设置为true
assert( !p_resource->b_aout_busy );
p_resource->b_aout_busy = true;
}
vlc_mutex_unlock( &p_resource->lock_hold );
return p_aout;
}
// [vlc/src/audio_output/output.c]
// 创建一个音频输出端对象并初始化一个对应的输出端模块
audio_output_t *aout_New (vlc_object_t *parent)
{
vlc_value_t val, text;
audio_output_t *aout = vlc_custom_create (parent, sizeof (aout_instance_t),
"audio output");
if (unlikely(aout == NULL))
return NULL;
aout_owner_t *owner = aout_owner (aout);
vlc_mutex_init (&owner->lock);
vlc_mutex_init (&owner->req.lock);
vlc_mutex_init (&owner->dev.lock);
vlc_mutex_init (&owner->vp.lock);
vlc_viewpoint_init (&owner->vp.value);
atomic_init (&owner->vp.update, false);
owner->req.device = (char *)unset_str;
owner->req.volume = -1.f;
owner->req.mute = -1;
vlc_object_set_destructor (aout, aout_Destructor);
// 创建“变量”参数,并添加“变量”变化或设置的回调监听方法
/* Audio output module callbacks */
var_Create (aout, "volume", VLC_VAR_FLOAT);
var_AddCallback (aout, "volume", var_Copy, parent);
var_Create (aout, "mute", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
var_AddCallback (aout, "mute", var_Copy, parent);
var_Create (aout, "device", VLC_VAR_STRING);
var_AddCallback (aout, "device", var_CopyDevice, parent);
/* TODO: 3.0 HACK: only way to signal DTS_HD to aout modules. */
var_Create (aout, "dtshd", VLC_VAR_BOOL);
// 设置音频输出端相关配置信息变化的通知事件即通知java端等
aout->event.volume_report = aout_VolumeNotify;
aout->event.mute_report = aout_MuteNotify;
aout->event.policy_report = aout_PolicyNotify;
aout->event.device_report = aout_DeviceNotify;
aout->event.hotplug_report = aout_HotplugNotify;
aout->event.gain_request = aout_GainNotify;
aout->event.restart_request = aout_RestartNotify;
/* Audio output module initialization */
aout->start = NULL;
aout->stop = NULL;
aout->volume_set = NULL;
aout->mute_set = NULL;
aout->device_select = NULL;
// 加载音频输出模块【"audio output"】,后续章节中分析具体模块实现
owner->module = module_need (aout, "audio output", "$aout", false);
if (owner->module == NULL)
{
msg_Err (aout, "no suitable audio output module");
vlc_object_release (aout);
return NULL;
}
// 以下代码实现均为音频输出相关字段参数创建/更新/初始化:
// 主要有音频过滤器、播放增益模式、声道输出模式、均衡器设置等
/*
* Persistent audio output variables
*/
module_config_t *cfg;
char *str;
/* Visualizations */
var_Create (aout, "visual", VLC_VAR_STRING);
text.psz_string = _("Visualizations");
var_Change (aout, "visual", VLC_VAR_SETTEXT, &text, NULL);
val.psz_string = (char *)"";
text.psz_string = _("Disable");
var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
val.psz_string = (char *)"spectrometer";
text.psz_string = _("Spectrometer");
var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
val.psz_string = (char *)"scope";
text.psz_string = _("Scope");
var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
val.psz_string = (char *)"spectrum";
text.psz_string = _("Spectrum");
var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
val.psz_string = (char *)"vuMeter";
text.psz_string = _("VU meter");
var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
/* Look for goom plugin */
if (module_exists ("goom"))
{
val.psz_string = (char *)"goom";
text.psz_string = (char *)"Goom";
var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
}
/* Look for libprojectM plugin */
if (module_exists ("projectm"))
{
val.psz_string = (char *)"projectm";
text.psz_string = (char*)"projectM";
var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
}
/* Look for VSXu plugin */
if (module_exists ("vsxu"))
{
val.psz_string = (char *)"vsxu";
text.psz_string = (char*)"Vovoid VSXu";
var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
}
/* Look for glspectrum plugin */
if (module_exists ("glspectrum"))
{
val.psz_string = (char *)"glspectrum";
text.psz_string = (char*)"3D spectrum";
var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
}
str = var_GetNonEmptyString (aout, "effect-list");
if (str != NULL)
{
var_SetString (aout, "visual", str);
free (str);
}
var_Create (aout, "audio-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
var_AddCallback (aout, "audio-filter", FilterCallback, NULL);
text.psz_string = _("Audio filters");
var_Change (aout, "audio-filter", VLC_VAR_SETTEXT, &text, NULL);
var_Create (aout, "viewpoint", VLC_VAR_ADDRESS );
var_AddCallback (aout, "viewpoint", ViewpointCallback, NULL);
var_Create (aout, "audio-visual", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
text.psz_string = _("Audio visualizations");
var_Change (aout, "audio-visual", VLC_VAR_SETTEXT, &text, NULL);
/* Replay gain */
var_Create (aout, "audio-replay-gain-mode",
VLC_VAR_STRING | VLC_VAR_DOINHERIT );
text.psz_string = _("Replay gain");
var_Change (aout, "audio-replay-gain-mode", VLC_VAR_SETTEXT, &text, NULL);
cfg = config_FindConfig("audio-replay-gain-mode");
if (likely(cfg != NULL))
for (unsigned i = 0; i < cfg->list_count; i++)
{
val.psz_string = (char *)cfg->list.psz[i];
text.psz_string = vlc_gettext(cfg->list_text[i]);
var_Change (aout, "audio-replay-gain-mode", VLC_VAR_ADDCHOICE,
&val, &text);
}
/* Stereo mode */
var_Create (aout, "stereo-mode", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
owner->initial_stereo_mode = var_GetInteger (aout, "stereo-mode");
var_AddCallback (aout, "stereo-mode", StereoModeCallback, NULL);
vlc_value_t txt;
txt.psz_string = _("Stereo audio mode");
var_Change (aout, "stereo-mode", VLC_VAR_SETTEXT, &txt, NULL);
/* Equalizer */
var_Create (aout, "equalizer-preamp", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
var_Create (aout, "equalizer-bands", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
var_Create (aout, "equalizer-preset", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
return aout;
}
3、aout_DecNew实现分析: [vlc/src/input/decoder.c]
/**
* Creates an audio output
*/
int aout_DecNew( audio_output_t *p_aout,
const audio_sample_format_t *p_format,
const audio_replay_gain_t *p_replay_gain,
const aout_request_vout_t *p_request_vout )
{
if( p_format->i_bitspersample > 0 )
{
// 检查音频格式声道是否有效
/* Sanitize audio format, input need to have a valid physical channels
* layout or a valid number of channels. */
int i_map_channels = aout_FormatNbChannels( p_format );
if( ( i_map_channels == 0 && p_format->i_channels == 0 )
|| i_map_channels > AOUT_CHAN_MAX || p_format->i_channels > INPUT_CHAN_MAX )
{
msg_Err( p_aout, "invalid audio channels count" );
return -1;
}
}
// 音频采样率不能超过384000Hz即最大384KHz
if( p_format->i_rate > 384000 )
{
msg_Err( p_aout, "excessive audio sample frequency (%u)",
p_format->i_rate );
return -1;
}
// 采样率最低不能低于4KHz
if( p_format->i_rate < 4000 )
{
msg_Err( p_aout, "too low audio sample frequency (%u)",
p_format->i_rate );
return -1;
}
aout_owner_t *owner = aout_owner(p_aout);
/* TODO: reduce lock scope depending on decoder's real need */
aout_OutputLock (p_aout);
// 创建音频输出流模块
/* Create the audio output stream */
owner->volume = aout_volume_New (p_aout, p_replay_gain);
atomic_store (&owner->restart, 0);
owner->input_format = *p_format;
owner->mixer_format = owner->input_format;
owner->request_vout = *p_request_vout;
var_Change (p_aout, "stereo-mode", VLC_VAR_SETVALUE,
&(vlc_value_t) { .i_int = owner->initial_stereo_mode }, NULL);
owner->filters_cfg = AOUT_FILTERS_CFG_INIT;
// 加载aout输出端信息,启动aout输出端组件模块,见3.1小节分析
if (aout_OutputNew (p_aout, &owner->mixer_format, &owner->filters_cfg))
goto error;
// 设置当前音频播放格式给功放软件模块,见3.2小节分析
aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
// 创建音频过滤"输入"通道:如重采样设置等
/* Create the audio filtering "input" pipeline */
owner->filters = aout_FiltersNew (p_aout, p_format, &owner->mixer_format,
&owner->request_vout,
&owner->filters_cfg);
if (owner->filters == NULL)
{
aout_OutputDelete (p_aout);
error:
aout_volume_Delete (owner->volume);
owner->volume = NULL;
aout_OutputUnlock (p_aout);
return -1;
}
owner->sync.end = VLC_TS_INVALID;
owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
owner->sync.discontinuity = true;
aout_OutputUnlock (p_aout);
atomic_init (&owner->buffers_lost, 0);
atomic_init (&owner->buffers_played, 0);
atomic_store (&owner->vp.update, true);
return 0;
}
3.1、aout_OutputNew实现分析:
/**
* Starts an audio output stream.
* \param fmt audio output stream format [IN/OUT]
* \warning The caller must hold the audio output lock.
*/
int aout_OutputNew (audio_output_t *aout, audio_sample_format_t *restrict fmt,
aout_filters_cfg_t *filters_cfg)
{
aout_OutputAssertLocked (aout);
// 音频通道类型
audio_channel_type_t input_chan_type = fmt->channel_type;
// 是否强制立体声模式,默认0
int i_forced_stereo_mode = AOUT_VAR_CHAN_UNSET;
// 输入音频格式通道数
unsigned i_nb_input_channels = fmt->i_channels;
/* Ideally, the audio filters would be created before the audio output,
* and the ideal audio format would be the output of the filters chain.
* But that scheme would not really play well with digital pass-through. */
// 该条件代码为宏定义,用于获取并判断音频量化精度是否不为0
if (AOUT_FMT_LINEAR(fmt))
{// 不为0
if (fmt->channel_type == AUDIO_CHANNEL_TYPE_BITMAP
&& aout_FormatNbChannels(fmt) == 0)
{
// 输出格式声道类型未知,则默认使用WAVE声道类型的物理参数
/* The output channel map is unknown, use the WAVE one. */
assert(fmt->i_channels > 0);
aout_SetWavePhysicalChannels(fmt);
}
if (fmt->channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
{
// 立体环绕声道,设置最大的7.1物理声道类型
// 注:输出模块也可以自由选择少些声道来渲染
/* Set the maximum of channels to render ambisonics contents. The
* aout module will still be free to select less channels in order
* to respect the sink setup. */
fmt->i_physical_channels = AOUT_CHANS_7_1;
}
// 根据采样精度来选择设置为vlc标记格式类型
/* Try to stay in integer domain if possible for no/slow FPU. */
fmt->i_format = (fmt->i_bitspersample > 16) ? VLC_CODEC_FL32
: VLC_CODEC_S16N;
// 获取输入端是否设置了立体声道模式
i_forced_stereo_mode = var_GetInteger (aout, "stereo-mode");
if (i_forced_stereo_mode != AOUT_VAR_CHAN_UNSET)
{
// 若设置了立体声道模式,则根据类型来设置物理声道数类型
if (i_forced_stereo_mode == AOUT_VAR_CHAN_LEFT
|| i_forced_stereo_mode == AOUT_VAR_CHAN_RIGHT)
fmt->i_physical_channels = AOUT_CHAN_CENTER;
else
// 双声道
fmt->i_physical_channels = AOUT_CHANS_STEREO;
}
// 初始化准备采样格式的信息:帧数、每帧大小等
aout_FormatPrepare (fmt);
assert (aout_FormatNbChannels(fmt) > 0);
}
// 当前输出端是否启用耳机
aout->current_sink_info.headphones = false;
// 启动aout输出端组件开始方法执行,由第2小节中【aout_New】创建aout对象流程中知晓:
// 该方法是通过加载了aout输出组件模块进行赋值了,
// 因此该分析会在后续aout输出端模块组件章节中分析 TODO
if (aout->start (aout, fmt))
{
msg_Err (aout, "module not functional");
return -1;
}
// 初始化准备立体声模式即声道数的确定并发送通知消息出去
aout_PrepareStereoMode (aout, fmt, filters_cfg, input_chan_type,
i_nb_input_channels, i_forced_stereo_mode);
aout_FormatPrepare (fmt);
assert (fmt->i_bytes_per_frame > 0 && fmt->i_frame_length > 0);
aout_FormatPrint (aout, "output", fmt);
return 0;
}
3.2、aout_volume_SetFormat实现分析:
/**
* Selects the current sample format for software amplification.
*/
int aout_volume_SetFormat(aout_volume_t *vol, vlc_fourcc_t format)
{
if (unlikely(vol == NULL))
return -1;
audio_volume_t *obj = &vol->object;
if (vol->module != NULL)
{// 若当前音频输出模块组件存在并且它持有的音频格式
// 和当前待播放音频格式相同则继续使用当前模块组件。
// 否则卸载当前模块组件
if (obj->format == format)
{
msg_Dbg (obj, "retaining sample format");
return 0;
}
msg_Dbg (obj, "changing sample format");
module_unneed(obj, vol->module);
}
obj->format = format;
// 重新加载新的音频播放组件模块
vol->module = module_need(obj, "audio volume", NULL, false);
if (vol->module == NULL)
return -1;
return 0;
}
由此此前第五七章分析中涉及aout输出端的TODO部分均已基本分析完成了。
分析结束,aout输出端组件模块分析请见后续分析