整章目录:Android------- IjkPlayer 源码学习目录
本篇会有很多源代码,请注意阅读每行代码上面的注释。
本篇介绍的主要内容为上图红框圈起部分:
在前面介绍了如何将一个AvPacket解码为一帧数据,并存入缓存队列中。对于视频帧而言,会有video_refresh_thread线程从缓存队列中取数据,然后渲染到界面上。而缓存队列中的音频数据是由谁取出的????又是如何使用的呢???本篇将聊聊音频数据是如何播放的。
如果你还不知道如何解码,请看:Android --- IjkPlayer 阅读native层源码之如何将AvPacket数据解码出一帧数据(六)
如果你还不知道stream_component_open:请看:Android ---- Ijkplayer阅读native层源码之IjkMediaPlayer_prepareAsync(五)
在stream_component_open中,启动音频解码线程audio_thread的同时,也调用了audio_open函数,去创建音频播放器并开启aout_thread线程(用于处理缓存队列中的帧数据)。下面我们将从audio_open函数开始介绍:
audio_open:
/**
*
* @param opaque FFPlayer
* @param wanted_channel_layout----期望音频存储顺序 eg,AV_CH_LAYOUT_STEREO
* @param wanted_nb_channels----期望声道数,
* @param wanted_sample_rate----期望采样率,
* @param audio_hw_params----记录最终播放的音频参数对象
* @return
*/
static int audio_open(FFPlayer *opaque, int64_t wanted_channel_layout, int wanted_nb_channels, int wanted_sample_rate, struct AudioParams *audio_hw_params)
{
// 省略。。。。。。
// 期望的音频参数
wanted_spec.channels = wanted_nb_channels;
wanted_spec.freq = wanted_sample_rate;
wanted_spec.format = AUDIO_S16SYS;//2byte
wanted_spec.silence = 0;
wanted_spec.samples = FFMAX(SDL_AUDIO_MIN_BUFFER_SIZE, 2 << av_log2(wanted_spec.freq / SDL_AoutGetAudioPerSecondCallBacks(ffp->aout)));
// 注意这个回调函:将需要播放的音频发送给播放器
wanted_spec.callback = sdl_audio_callback;
wanted_spec.userdata = opaque;
// 创建Java层的AudioTrack,
// 开启处理队列中音频的线程
// 最终播方的音频参数存放在spec中
while (SDL_AoutOpenAudio(ffp->aout, &wanted_spec, &spec) < 0) {
//如果创建失败,进入改变期望,然后继续创建
}
// 记录播放的音频参数
audio_hw_params->fmt = AV_SAMPLE_FMT_S16;
audio_hw_params->freq = spec.freq;
audio_hw_params->channel_layout = wanted_channel_layout;
audio_hw_params->channels = spec.channels;
audio_hw_params->frame_size = av_samples_get_buffer_size(NULL, audio_hw_params->channels, 1, audio_hw_params->fmt, 1);
audio_hw_params->bytes_per_sec = av_samples_get_buffer_size(NULL, audio_hw_params->channels, audio_hw_params->freq, audio_hw_params->fmt, 1);
}
上面代码不难理解,准备一些用于创建音频播放器的参数,存储在wanted_spec结构体中。其中的channel_layout、channels、sample_rate三个参数来自于AVCodecContext中。然后将wanted_spec传入SDL_AoutOpenAudio函数,用于创建音频播放器。默认使用的音频格式为AUDIO_S16SYS(AV_SAMPLE_FMT_S16)
SDL_AoutOpenAudio:
这里需要多说两句,对于Android而言:
- IjkPlayer 默认使用的播放器是Android的AudioTrack.java,所以open_audio指向的是ijksdl_aout_android_audiotrack.c中aout_open_audio函数;
- 但是IjkPlayer还提供了另一种播放器:OpenSLES;如果用户想使用该播放器,需要在Java层设置下面的选项:
然后系统会构建不同的播放环境,下图:ffpipeline_android.c中的函数