承接上一章节分析:【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 10】【02】
本系列文章分析的安卓源码版本:【Android 10.0 版本】
推荐涉及到的知识点:
Binder机制实现原理:Android C++底层Binder通信机制原理分析总结【通俗易懂】
ALooper机制实现原理:Android native层媒体通信架构AHandler/ALooper机制实现源码分析
Binder异常关闭监听:Android native层DeathRecipient对关联进程(如相关Service服务进程)异常关闭通知事件的监听实现源码分析
【此章节小节编号将重新排序】
onOutputFormatChanged()实现分析:
回调输出数据格式改变通知处理流程,此处理过程比较重要,它将会在软音频解码器即非offload音频播放模式时触发AudioSink的open打开处理流程。
它将会回调【CB_OUTPUT_FORMAT_CHANGED】事件消息给NuPlayerDecoder,最后发出【kWhatVideoSizeChanged】事件给NuPlayer通知上层APP。并可能也会更改AudioSink侧的audio配置
// [frameworks/av/media/libstagefright/MediaCodec.cpp]
void MediaCodec::onOutputFormatChanged() {
if (mCallback != NULL) {
// 此前流程分析过,该callback回调监听消息对象是NuPlayerDecoder设置进来的【kWhatCodecNotify】事件消息
sp<AMessage> msg = mCallback->dup();
// 设置回调ID为【CB_OUTPUT_FORMAT_CHANGED】
msg->setInt32("callbackID", CB_OUTPUT_FORMAT_CHANGED);
// 设置更新后输出格式
msg->setMessage("format", mOutputFormat);
msg->post();
}
}
NuPlayerDecoder接收【kWhatCodecNotify】事件消息处理回调ID为【CB_OUTPUT_FORMAT_CHANGED】子事件:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp]
void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
ALOGV("[%s] onMessage: %s", mComponentName.c_str(), msg->debugString().c_str());
switch (msg->what()) {
case kWhatCodecNotify:
{
int32_t cbID;
CHECK(msg->findInt32("callbackID", &cbID));
ALOGV("[%s] kWhatCodecNotify: cbID = %d, paused = %d",
mIsAudio ? "audio" : "video", cbID, mPaused);
if (mPaused) {
// 已暂停时则忽略该事件
break;
}
switch (cbID) {
case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
{
sp<AMessage> format;
CHECK(msg->findMessage("format", &format));
handleOutputFormatChange(format);
break;
}
}
}
}
}
handleOutputFormatChange(format)实现分析:
处理输出数据格式改变事件
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp]
void NuPlayer::Decoder::handleOutputFormatChange(const sp<AMessage> &format) {
if (!mIsAudio) {
// 视频时
int32_t width, height;
if (format->findInt32("width", &width)
&& format->findInt32("height", &height)) {
// 获取输出帧宽高,并加锁设置给全局统计消息对象
Mutex::Autolock autolock(mStatsLock);
mStats->setInt32("width", width);
mStats->setInt32("height", height);
}
// mNotify是此前【NuPlayer::instantiateDecoder】中创建Decoder时
// 传递的【kWhatVideoNotify】视频通知事件消息
sp<AMessage> notify = mNotify->dup();
// 设置子事件【kWhatVideoSizeChanged】和该变化的输出格式
// 见第1小节分析
notify->setInt32("what", kWhatVideoSizeChanged);
notify->setMessage("format", format);
notify->post();
} else if (mRenderer != NULL) {
// 音频(软)解码器并且渲染器不为空时
uint32_t flags;
int64_t durationUs;
// 获取音频数据源输入格式,不为空时表示有音频
// 见此前prepare流程中已有分析
bool hasVideo = (mSource->getFormat(false /* audio */) != NULL);
// getAudioDeepBufferSetting():是否设置了使用音频Deep Buffer模式,见第2小节分析
// 备注:deep buffer模式其实就是offload模式时默认走的音频数据传递模式。
// getDuration 将返回GenericSource此前prepare流程缓存的媒体时长
// AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US表示deep buffer模式最小允许的媒体时长为5秒,小于5秒则不需要offload模式播放
if (getAudioDeepBufferSetting() // override regardless of source duration
|| (mSource->getDuration(&durationUs) == OK
&& durationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US)) {
// getAudioDeepBufferSetting()该配置信息将会覆盖后面的判断。
// deep buffer模式时标记flag
flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
} else {
// 否则标记none
flags = AUDIO_OUTPUT_FLAG_NONE;
}
// 创建【kWhatAudioOutputFormatChanged】音频输出格式改变处理完成事件通知消息,Decoder接收
// 备注:该处理比较简单,判断代数值是否相同,不相同时不会处理,
// 只有相同时处理失败错误码并通过NuPlayer该【kWhatError】事件,OK错误码不处理。
// 因此该流程目前暂不分析 TODO
sp<AMessage> reply = new AMessage(kWhatAudioOutputFormatChanged, this);
// 存储当前Buffer队列代数值
reply->setInt32("generation", mBufferGeneration);
// 音频输出格式改变处理
// 见第3小节分析
mRenderer->changeAudioFormat(
format, false /* offloadOnly */, hasVideo,
flags, mSource->isStreaming(), reply);
}
}
1、NuPlayer接收【kWhatVideoNotify】视频通知事件消息子事件【kWhatVideoSizeChanged】处理:
【kWhatVideoNotify】此事件消息处理流程有很多个子事件,因此目前省略其他子事件实现分析,以后有时间再附上吧,TODO
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
// 以下省略了其他子事件流程代码,后续有时间再完善 TODO
switch (msg->what()) {
// 注意此处是音视频关于各自的Decoder通知NuPlayer的统一处理流程
case kWhatVideoNotify:
case kWhatAudioNotify:
{
// 是否为audio
bool audio = msg->what() == kWhatAudioNotify;
// 获取当前Decoder解码器代数值
int32_t currentDecoderGeneration =
(audio? mAudioDecoderGeneration : mVideoDecoderGeneration);
// 计算当前请求者的代数值
int32_t requesterGeneration = currentDecoderGeneration - 1;
// 获取请求者传递的代数值,并重置requesterGeneration
CHECK(msg->findInt32("generation", &requesterGeneration));
// 判断当前代数值是否一致
if (requesterGeneration != currentDecoderGeneration) {
// 不相同时,表示当前decoder请求者为旧的解码器
ALOGV("got message from old %s decoder, generation(%d:%d)",
audio ? "audio" : "video", requesterGeneration,
currentDecoderGeneration);
sp<AMessage> reply;
if (!(msg->findMessage("reply", &reply))) {
// 没有找到应答消息
return;
}
// 应答该错误信息即非连续性消息错误码
reply->setInt32("err", INFO_DISCONTINUITY);
reply->post();
return;
}
// 相同时
int32_t what;
// 获取子事件
CHECK(msg->findInt32("what", &what));
if (what == DecoderBase::kWhatInputDiscontinuity) {
} else if (what == DecoderBase::kWhatVideoSizeChanged) {
// 视频帧尺寸发生改变时
sp<AMessage> format;
// 获取新输出格式
CHECK(msg->findMessage("format", &format));
// 获取视频数据源输入格式
// 见此前prepare流程中已有分析
sp<AMessage> inputFormat =
mSource->getFormat(false /* audio */);
// 这两个方法此前prepare流程中已有分析
// 设置视频缩放模式
setVideoScalingMode(mVideoScalingMode);
// 更新视频尺寸大小并通知上层APP该事件
updateVideoSize(inputFormat, format);
}
break;
}
}
}
TODO 未完待续 上面流程省略了其他子事件流程分析,后续有时间再完善
2、getAudioDeepBufferSetting()实现:
判断是否设置了使用音频Deep Buffer模式,默认为false
备注:deep buffer模式其实就是offload模式时默认走的音频数据传递模式。
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp]
static inline bool getAudioDeepBufferSetting() {
return property_get_bool("media.stagefright.audio.deep", false /* default_value */);
}
3、mRenderer->changeAudioFormat(format, false /* offloadOnly */, hasVideo, flags, mSource->isStreaming(), reply)实现分析:
音频输出格式改变处理
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp]
void NuPlayer::Renderer::changeAudioFormat(
const sp<AMessage> &format,
bool offloadOnly,
bool hasVideo,
uint32_t flags,
bool isStreaming,
const sp<AMessage> ¬ify) {
// 创建AMessage消息对象,缓存传入的参数值
sp<AMessage> meta = new AMessage;
meta->setMessage("format", format);
// 是否只能offload模式播放
meta->setInt32("offload-only", offloadOnly);
meta->setInt32("has-video", hasVideo);
meta->setInt32("flags", flags);
// 是否为网络流媒体数据源,本地播放时为false
meta->setInt32("isStreaming", isStreaming);
// 创建【kWhatChangeAudioFormat】更改音频输出格式事件消息,Renderer接收处理
sp<AMessage> msg = new AMessage(kWhatChangeAudioFormat, this);
// 设置当前音频Buffer入队列代数值
msg->setInt32("queueGeneration", getQueueGeneration(true /* audio */));
// 设置通知消息即【kWhatAudioOutputFormatChanged】音频输出格式改变处理完成事件通知消息
msg->setMessage("notify", notify);
// 设置元数据即参数消息
msg->setMessage("meta", meta);
msg->post();
}
Renderer接收处理【kWhatChangeAudioFormat】更改音频输出格式事件消息:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp]
void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatChangeAudioFormat:
{
// 获取几个参数
int32_t queueGeneration;
CHECK(msg->findInt32("queueGeneration", &queueGeneration));
sp<AMessage> notify;
CHECK(msg->findMessage("notify", ¬ify));
// 判断是否支持offload音频模式
if (offloadingAudio()) {
// 支持的话则不允许修改该音频输出格式配置信息,通知无效错误码
ALOGW("changeAudioFormat should NOT be called in offload mode");
notify->setInt32("err", INVALID_OPERATION);
notify->post();
break;
}
sp<AMessage> meta;
CHECK(msg->findMessage("meta", &meta));
if (queueGeneration != getQueueGeneration(true /* audio */)
|| mAudioQueue.empty()) {
// 代数值不同【表示当前请求是旧请求了】或音频渲染队列为空时
// 备注:该处理通常在在音频第一帧数据还未到达前执行的,
// 因此队列会为空,也就是说在音频渲染到达前会先执行此处。
// 更改音频输出格式事件处理流程
// 见下面分析
onChangeAudioFormat(meta, notify);
break;
}
// 否则代数值相同并且音频渲染队列不为空时
// 将会执行下面的队列实体添加到音频渲染队列中,然后执行【postDrainAudioQueue_l()】
// 该流程执行将会在后续音频输出数据开始渲染时统一分析,此处暂不先分析
// 但此处非常要注意的是:当前队列实体项结构对象并没有实际的音频负载数据Buffer,
// 而只是拥有了当前新的音频输出格式信息,而从后面分析就知道此处的作用为:
// 由于上面跳过了if中的音频输出格式改变处理流程,
// 因此在此处加入没有Buffer数据的队列实体项对象到队列中后,
// 执行【postDrainAudioQueue_l】方法后,后续流程将会在判断到没有实际负载buffer
// 但有mNotifyConsumed时将会执行上面的【onChangeAudioFormat】来重新打开AudioSink处理流程。
// 也正如【QueueEntry】结构声明上的注释那样的处理。
// 该处理将会在下一章节分析到。
QueueEntry entry;
entry.mNotifyConsumed = notify;
entry.mMeta = meta;
Mutex::Autolock autoLock(mLock);
mAudioQueue.push_back(entry);
postDrainAudioQueue_l();
break;
}
}
}
onChangeAudioFormat(meta, notify)实现分析:
更改音频输出格式事件处理流程
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp]
void NuPlayer::Renderer::onChangeAudioFormat(
const sp<AMessage> &meta, const sp<AMessage> ¬ify) {
// 下面取出之前传的的参数
sp<AMessage> format;
// 获取更新的音频输出格式配置信息
CHECK(meta->findMessage("format", &format));
int32_t offloadOnly;
CHECK(meta->findInt32("offload-only", &offloadOnly));
int32_t hasVideo;
CHECK(meta->findInt32("has-video", &hasVideo));
uint32_t flags;
CHECK(meta->findInt32("flags", (int32_t *)&flags));
uint32_t isStreaming;
CHECK(meta->findInt32("isStreaming", (int32_t *)&isStreaming));
// 打开AudioSink处理流程
// 见下面分析
status_t err = onOpenAudioSink(format, offloadOnly, hasVideo, flags, isStreaming);
if (err != OK) {
// 失败时设置错误码
notify->setInt32("err", err);
}
// 通知Decoder该处理结果,见前面已有分析
notify->post();
}
onOpenAudioSink(format, offloadOnly, hasVideo, flags, isStreaming)实现分析:
打开AudioSink处理流程
备注:关于AudioSink相关功能,不会源码分析,它属于另外的技术领域了,因此只会阐述它的大致工作原理
AudioSink由此前prepare章节可知,它是在MediaPlayerService中实现的,但它其实最终也只是个代理类实现,代理【AudioTrack】该类的实现,而AudioTrack又将会最终和AudioFlinger模块交互,最终AudioFlinger控制和管理所有需要发出声音的音频数据进行混音处理后发送给扬声器等硬件。
注意:默认非offload模式时不使用callback方式。另外注意,软音频解码也可以支持AudioSink通过回调callback方式来主动获取音频渲染队列数据或者渲染器来推送音频数据到AudioSink中去播放。
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp]
status_t NuPlayer::Renderer::onOpenAudioSink(
const sp<AMessage> &format,
bool offloadOnly,
bool hasVideo,
uint32_t flags,
bool isStreaming) {
ALOGV("openAudioSink: offloadOnly(%d) offloadingAudio(%d)",
offloadOnly, offloadingAudio());
// 标记是否音频输出已改变完成
bool audioSinkChanged = false;
int32_t numChannels;
// 获取新音频输出格式中的声道数
CHECK(format->findInt32("channel-count", &numChannels));
int32_t channelMask;
// 获取声道掩码类型
// 关于它的含义此前章节已详细分析和阐述过
if (!format->findInt32("channel-mask", &channelMask)) {
// 没有该字段时
// 通知AudioSink去根据声道数来定位该声道掩码类型,默认值为0
// signal to the AudioSink to derive the mask from count.
channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER;
}
int32_t sampleRate;
// 采样率
CHECK(format->findInt32("sample-rate", &sampleRate));
// 注:若可用的话,从MediaCodec输出格式信息中读取PCM编码格式
// read pcm encoding from MediaCodec output format, if available
int32_t pcmEncoding;
// 读取PCM编码格式成功时执行audioFormatFromEncoding(pcmEncoding),见3.1小节分析
// 【实际就是将PCM编码格式转换为AudioSink能够识别的枚举类型】
// 否则默认音频PCM编码格式的PCM编码位深为16位来表示的
audio_format_t audioFormat =
format->findInt32(KEY_PCM_ENCODING, &pcmEncoding) ?
audioFormatFromEncoding(pcmEncoding) : AUDIO_FORMAT_PCM_16_BIT;
// 判断当前Renderer渲染是否支持音频offload模式播放
if (offloadingAudio()) {
// 支持时
AString mime;
// 获取音频mime格式
CHECK(format->findString("mime", &mime));
// 然后根据mime格式转换映射为AudioSink支持的格式类型
// 备注:该方法在此前onStart()流程中是否支持offload方法中已有分析,见【Part2】部分
// 也就是上面audioFormat音频格式类型这个的可能将被重置,因为不是PCM数据呀,比如返回AUDIO_FORMAT_AAC
status_t err = mapMimeToAudioFormat(audioFormat, mime.c_str());
if (err != OK) {
// 失败时
ALOGE("Couldn't map mime \"%s\" to a valid "
"audio_format", mime.c_str());
// 禁止offload模式,此前有阐述过,这里再分析实现吧
// 见3.2小节分析
onDisableOffloadAudio();
} else {
// 成功时
/// 位宽为16位bit
int32_t bitWidth = 16;
ALOGV("Mime \"%s\" mapped to audio_format 0x%x",
mime.c_str(), audioFormat);
// AVUtils为平台私有实现,我们分析时默认空实现没被改变返回
audioFormat = AVUtils::get()->updateAudioFormat(audioFormat, format);
bitWidth = AVUtils::get()->getAudioSampleBits(format);
int avgBitRate = -1;
// 获取(平均)码率
format->findInt32("bitrate", &avgBitRate);
int32_t aacProfile = -1;
if (audioFormat == AUDIO_FORMAT_AAC
&& format->findInt32("aac-profile", &aacProfile)) {
// AAC音频并且获取AAC档次级别成功时
// 注:根据AAC档次配置重新定义AAC格式
// Redefine AAC format as per aac profile
int32_t isADTSSupported;
// 获取是否支持ADTS数据封装类型的AAC数据
isADTSSupported = AVUtils::get()->mapAACProfileToAudioFormat(format,
audioFormat,
aacProfile);
if (!isADTSSupported) {
// 不支持时,根据AAC的档次级别来转换为具体的AAC变种编码格式
// 备注:该方法在此前onStart()流程中是否支持offload方法中已有分析,见【Part2】部分
// 也就是说AAC类型有很多种子类型,此处将根据档次级别来匹配它最终的具体音频编码格式,比如AUDIO_FORMAT_AAC_MAIN
mapAACProfileToAudioFormat(audioFormat,
aacProfile);
} else {
// 支持时
ALOGV("Format is AAC ADTS\n");
}
}
// 获取offload模式下Buffer大小,AVUtils为私有库实现,因此不分析,默认实现为0
int32_t offloadBufferSize =
AVUtils::get()->getAudioMaxInputBufferSize(
audioFormat,
format);
// 音频offload配置信息
// 【AUDIO_INFO_INITIALIZER】此前章节中已有分析
audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER;
// 获取媒体时长
offloadInfo.duration_us = -1;
format->findInt64(
"durationUs", &offloadInfo.duration_us);
// 采样率
offloadInfo.sample_rate = sampleRate;
// 声道掩码类型
offloadInfo.channel_mask = channelMask;
// 音频编码格式,如AAC、PCM等
offloadInfo.format = audioFormat;
// 位宽【相当于说的位深,也就是一个采样点用多少位来表示】
offloadInfo.bit_width = bitWidth;
// 默认音频流类型为MUSIC
offloadInfo.stream_type = AUDIO_STREAM_MUSIC;
// 码率
offloadInfo.bit_rate = avgBitRate;
// 使用有视频流
offloadInfo.has_video = hasVideo;
// offload Buffer数据大小
offloadInfo.offload_buffer_size = offloadBufferSize;
// 是否为网络流媒体数据源
offloadInfo.is_streaming = isStreaming;
// 比较当前全局offload信息对象和当前新对象中的字节内容是否一致,也就是比较它们是否为一致的配置信息
if (memcmp(&mCurrentOffloadInfo, &offloadInfo, sizeof(offloadInfo)) == 0) {
// 相同配置信息,那么直接返回OK,不再处理
ALOGV("openAudioSink: no change in offload mode");
// no change from previous configuration, everything ok.
return OK;
}
// 【AUDIO_PCMINFO_INITIALIZER】见前面章节已有分析
// 当前PCM信息
mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER;
// 以下在offload模式下尝试打开AudioSink
ALOGV("openAudioSink: try to open AudioSink in offload mode");
// 由前面流程可知,该flagsc传入值为 AUDIO_OUTPUT_FLAG_NONE 或 AUDIO_OUTPUT_FLAG_DEEP_BUFFER
uint32_t offloadFlags = flags;
// 添加另外标记压缩offload模式【AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD】即编码数据直接输出给硬件音频解码器
offloadFlags |= AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
// 去掉【AUDIO_OUTPUT_FLAG_DEEP_BUFFER】标记位
offloadFlags &= ~AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
// 标记AudioSink配置已改变
audioSinkChanged = true;
// 因此关闭它
mAudioSink->close();
// 执行open打开AudioSink方法,它将会最终创建【AudioTrack】,AudioSink将会代理它的功能执行
// 备注:【&NuPlayer::Renderer::AudioSinkCallback】这个参数非常关键,
// 在此前offload模式工作原理讲解中提到过它,它就是offload模式下AudioSink通过
// 该回调callback方式主动获取音频渲染队列Buffer的回调监听。
// TODO 关于AudioSink的open方法后续有时间再附上吧
err = mAudioSink->open(
sampleRate,
numChannels,
(audio_channel_mask_t)channelMask,
audioFormat,
0 /* bufferCount - unused */,
&NuPlayer::Renderer::AudioSinkCallback,
this,
(audio_output_flags_t)offloadFlags,
&offloadInfo);
if (err == OK) {
// 成功时再次设置播放设置参数
err = mAudioSink->setPlaybackRate(mPlaybackSettings);
}
if (err == OK) {
// 成功时,将缓存最新offload配置信息对象
// If the playback is offloaded to h/w, we pass
// the HAL some metadata information.
// We don't want to do this for PCM because it
// will be going through the AudioFlinger mixer
// before reaching the hardware.
// TODO
mCurrentOffloadInfo = offloadInfo;
if (!mPaused) { // for preview mode, don't start if paused
// 若未暂停,则直接启动AudioSink工作,
// 这样AudioSink将会最终执行上面的callback来获取音频渲染队列数据
err = mAudioSink->start();
}
ALOGV_IF(err == OK, "openAudioSink: offload succeeded");
}
if (err != OK) {
// 失败时,关闭它,并退回到非offload模式处理流程
// Clean up, fall back to non offload mode.
mAudioSink->close();
// 见前面已有分析
onDisableOffloadAudio();
// 初始化默认值
mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER;
ALOGV("openAudioSink: offload failed");
if (offloadOnly) {
// 如果设置了只允许offload模式播放音频,则会音频拆卸通知事件
// kForceNonOffload标志强制非offload错误码
// 见3.3小节分析
notifyAudioTearDown(kForceNonOffload);
}
} else {
// 成功时
// 注意此处就记录了AudioSink将会使用callback回调方式来主动获取Renderer中的音频渲染队列数据。
// 为true也就是启用了【NuPlayer::Renderer::AudioSinkCallback】回调功能方法
mUseAudioCallback = true; // offload mode transfers data through callback
// 增加音频消费代数值,将会丢弃旧的事件消息处理
++mAudioDrainGeneration; // discard pending kWhatDrainAudioQueue message.
}
}
}
if (!offloadOnly && !offloadingAudio()) {
// 若没有设置只允许offload模式播放音频模式并且也不是offload播放模式时
// 在非offload模式下打开AudioSink
ALOGV("openAudioSink: open AudioSink in NON-offload mode");
uint32_t pcmFlags = flags;
// 去掉标记位AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD【压缩数据offload播放模式】
pcmFlags &= ~AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
// PCM配置信息结构对象
const PcmInfo info = {
(audio_channel_mask_t)channelMask,
(audio_output_flags_t)pcmFlags,
audioFormat,
numChannels,
sampleRate
};
// 字节内容比较是否相同
if (memcmp(&mCurrentPcmInfo, &info, sizeof(info)) == 0) {
// 相同配置时不再重复打开
ALOGV("openAudioSink: no change in pcm mode");
// no change from previous configuration, everything ok.
return OK;
}
// 标记为true
audioSinkChanged = true;
// 先关闭
mAudioSink->close();
// 初始化
mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER;
// Note: It is possible to set up the callback, but not use it to send audio data.
// This requires a fix in AudioSink to explicitly specify the transfer mode.
// 这是个静态内联函数,实现为:【获取该系统属性配置,默认为false】
// property_get_bool("media.stagefright.audio.cbk", false /* default_value */);
// 也就是是否使用callback回调方法主动来获取音频渲染队列数据
mUseAudioCallback = getUseAudioCallbackSetting();
if (mUseAudioCallback) {
// 若为true时,增加该代数值,丢弃该旧事件消息
++mAudioDrainGeneration; // discard pending kWhatDrainAudioQueue message.
}
// Compute the desired buffer size.
// For callback mode, the amount of time before wakeup is about half the buffer size.
// 对于callback模式,唤醒前的时间大约是Buffer大小的一半。
// 备注:getAudioSinkPcmMsSetting()获取AudioSink PCM数据消耗延迟时长配置值,实现为:【默认值为500毫秒】
// property_get_int32("media.stagefright.audio.sink", 500 /* default_value */);
// 计算推荐帧数:计算方法为采样率乘以二分之一,也就是说callback模式的帧数为帧率的一半
const uint32_t frameCount =
(unsigned long long)sampleRate * getAudioSinkPcmMsSetting() / 1000;
// The doNotReconnect means AudioSink will signal back and let NuPlayer to re-construct
// AudioSink. We don't want this when there's video because it will cause a video seek to
// the previous I frame. But we do want this when there's only audio because it will give
// NuPlayer a chance to switch from non-offload mode to offload mode.
// So we only set doNotReconnect when there's no video.
// 判断是否不需要重连,若有视频时需要重连,否则不需要重连
const bool doNotReconnect = !hasVideo;
// We should always be able to set our playback settings if the sink is closed.
// 设置播放速率配置
LOG_ALWAYS_FATAL_IF(mAudioSink->setPlaybackRate(mPlaybackSettings) != OK,
"onOpenAudioSink: can't set playback rate on closed sink");
// 执行open
status_t err = mAudioSink->open(
sampleRate,
numChannels,
(audio_channel_mask_t)channelMask,
audioFormat,
0 /* bufferCount - unused */,
// 此处判断了是否应该使用callback方式来获取音频渲染队列数据来播放。
// 默认非offload模式时不使用callback方式。
// 备注:也就是说软音频解码也可以支持AudioSink通过回调callback方式来主动获取音频渲染队列数据
// 或者渲染器来推送音频数据到AudioSink中去播放。
mUseAudioCallback ? &NuPlayer::Renderer::AudioSinkCallback : NULL,
mUseAudioCallback ? this : NULL,
(audio_output_flags_t)pcmFlags,
NULL,
doNotReconnect,
frameCount);
if (err != OK) {
// 打开失败时
ALOGW("openAudioSink: non offloaded open failed status: %d", err);
mAudioSink->close();
mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER;
return err;
}
// 缓存
mCurrentPcmInfo = info;
if (!mPaused) { // for preview mode, don't start if paused
// 未暂停时开启AudioSink工作
mAudioSink->start();
}
}
if (audioSinkChanged) {
// AudioSink改变了时
// 回调AudioSink已改变处理流程
// 见3.4小节分析
onAudioSinkChanged();
}
// AudioSink卸载标记为false
mAudioTornDown = false;
return OK;
}
3.1、audioFormatFromEncoding(pcmEncoding)实现分析:
该方法其实就是将PCM编码位深格式转换为AudioSink能够识别的枚举类型:float、16位深、8位深、24、32
// [frameworks/av/media/libstagefright/Utils.cpp]
static audio_format_t constexpr audioFormatFromEncoding(int32_t pcmEncoding) {
switch (pcmEncoding) {
case kAudioEncodingPcmFloat:
return AUDIO_FORMAT_PCM_FLOAT;
case kAudioEncodingPcm16bit:
return AUDIO_FORMAT_PCM_16_BIT;
case kAudioEncodingPcm8bit:
return AUDIO_FORMAT_PCM_8_BIT; // TODO: do we want to support this?
case kAudioEncodingPcm24bitPacked:
return AUDIO_FORMAT_PCM_24_BIT_PACKED;
case kAudioEncodingPcm32bit:
return AUDIO_FORMAT_PCM_32_BIT;
default:
ALOGE("%s: Invalid encoding: %d", __func__, pcmEncoding);
return AUDIO_FORMAT_INVALID;
}
}
3.2、onDisableOffloadAudio()实现分析:
禁止offload模式,也就是主要将全局mFlag标记为去除这个offload标记位,然后将处理多个音频相关代数值
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp]
void NuPlayer::Renderer::onDisableOffloadAudio() {
Mutex::Autolock autoLock(mLock);
// 去除这个offload标记位
mFlags &= ~FLAG_OFFLOAD_AUDIO;
// 增加音频消耗代数值
++mAudioDrainGeneration;
if (mAudioRenderingStartGeneration != -1) {
// 渲染开始代数值不为-1(无效值)时
// 重新为媒体开始渲染做准备
// 见下面分析
prepareForMediaRenderingStart_l();
// PauseTimeout is applied to offload mode only. Cancel pending timer.
// 取消offload模式下的暂停超时卸载AudioSink的未超时操作,不分析它
cancelAudioOffloadPauseTimeout();
}
}
prepareForMediaRenderingStart_l()实现:
重新为媒体开始渲染做准备,也就是将代数值更新,然后标记渲染数据还未交付
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp]
void NuPlayer::Renderer::prepareForMediaRenderingStart_l() {
mAudioRenderingStartGeneration = mAudioDrainGeneration;
mVideoRenderingStartGeneration = mVideoDrainGeneration;
mRenderingDataDelivered = false;
}
3.3、notifyAudioTearDown(kForceNonOffload);实现分析:
如果设置了只允许offload模式播放音频,则会音频拆卸通知处理事件
kForceNonOffload标志强制非offload错误码
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp]
void NuPlayer::Renderer::notifyAudioTearDown(AudioTearDownReason reason) {
sp<AMessage> msg = new AMessage(kWhatAudioTearDown, this);
msg->setInt32("reason", reason);
msg->post();
}
【kWhatAudioTearDown】事件消息处理:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp]
void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatAudioTearDown:
{
int32_t reason;
CHECK(msg->findInt32("reason", &reason));
onAudioTearDown((AudioTearDownReason)reason);
break;
}
}
}
onAudioTearDown((AudioTearDownReason)reason)实现分析:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp]
void NuPlayer::Renderer::onAudioTearDown(AudioTearDownReason reason) {
if (mAudioTornDown) {
// 已卸载则不处理
return;
}
// TimeoutWhenPaused is only for offload mode.
if (reason == kDueToTimeout && !offloadingAudio()) {
// 若原因为暂停时offload超时并且非offload模式时,也不处理
return;
}
// 标记音频已卸载处理流程
mAudioTornDown = true;
int64_t currentPositionUs;
// NuPlayer接收的【kWhatRendererNotify】事件消息
sp<AMessage> notify = mNotify->dup();
// 获取当前播放进度,成功则设置它
// 备注:该方法以后有时间放入获取播放进度的单独章节中分析
if (getCurrentPosition(¤tPositionUs) == OK) {
notify->setInt64("positionUs", currentPositionUs);
}
// 暂停并flush
mAudioSink->stop();
mAudioSink->flush();
// 设置子类型【kWhatAudioTearDown】
// 备注:关于该子事件类型处理流程暂不分析它,以后有时间再说吧。TODO
// 简单说一下它最终将会执行【restartAudio】重启Audio也就是重新创建Audio解码器等工作
notify->setInt32("what", kWhatAudioTearDown);
notify->setInt32("reason", reason);
notify->post();
}
3.4、onAudioSinkChanged()实现分析:
AudioSink改变了时,回调AudioSink已改变处理流程
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp]
void NuPlayer::Renderer::onAudioSinkChanged() {
if (offloadingAudio()) {
// offload模式时不处理
return;
}
// 非offload模式时
CHECK(!mDrainAudioQueuePending);
// 已写入音频帧数初始化为0
mNumFramesWritten = 0;
// 已写入锚点音频帧数初始化为-1
mAnchorNumFramesWritten = -1;
uint32_t written;
// 获取AudioSink中当前已写入音频帧数
if (mAudioSink->getFramesWritten(&written) == OK) {
// 获取成功时缓存记录它
mNumFramesWritten = written;
}
}
本章结束