承接上一章节分析:【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 7】【03】
本系列文章分析的安卓源码版本:【Android 10.0 版本】
推荐涉及到的知识点:
Binder机制实现原理:Android C++底层Binder通信机制原理分析总结【通俗易懂】
ALooper机制实现原理:Android native层媒体通信架构AHandler/ALooper机制实现源码分析
Binder异常关闭监听:Android native层DeathRecipient对关联进程(如相关Service服务进程)异常关闭通知事件的监听实现源码分析
【此章节小节编号将重新排序】
doRequestBuffers()实现分析:
返回true时表示本次Buffer数据填充不足,因此必须请求填充更多数据
备注:通过下面的分析可知,该方法会在fetchInputData执行时,若无解析数据Buffer时会进行线程wait,等待解析数据生产者端添加缓冲区队列后进行唤醒继续获取Buffer数据。也就是说对于NuPlayerDecoder中的线程可能会被此流程处理而阻塞。
接下来关于GenericSource模块中的处理流程涉及早前流程已有分析时后续分析只会提醒一下,具体请参见前面prepare流程中已有分析过。
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp]
/*
* returns true if we should request more data
*/
bool NuPlayer::Decoder::doRequestBuffers() {
// 前面已分析,true时表示输入格式配置信息改变了
if (isDiscontinuityPending()) {
return false;
}
status_t err = OK;
// while循环执行已出队列输入Buffer的索引队列
while (err == OK && !mDequeuedInputBuffers.empty()) {
// err为OK且待填充输入索引队列不为空时
// 获取第一个item即索引
size_t bufferIx = *mDequeuedInputBuffers.begin();
// 创建AMessage消息并缓存该输入Buffer索引
sp<AMessage> msg = new AMessage();
msg->setSize("buffer-ix", bufferIx);
// 获取输入数据
// 见下面分析
err = fetchInputData(msg);
if (err != OK && err != ERROR_END_OF_STREAM) {
// 非OK并且非EOS的其他错误码时,停止该流程处理
// if EOS, need to queue EOS buffer
// EOS时还是需要获取EOS数据的。
break;
}
// 获取成功时,把当前输入Buffer索引,从待填充输入索引队列中移除
mDequeuedInputBuffers.erase(mDequeuedInputBuffers.begin());
if (!mPendingInputMessages.empty()
|| !onInputBufferFetched(msg)) {
// 若待执行输入消息事件队列不为空,或者onInputBufferFetched()即输入Buffer已获取完成处理流程失败时
// 也就是说这个判断的处理效果将是:若mPendingInputMessages队列为空即本次第一次获取输入Buffer成功时,
// 或此前该队列已都执行完成时,此时将直接执行onInputBufferFetched()流程处理,
// 否则将会通过前面流程对mPendingInputMessages队列的循环处理来返回该已获取输入Buffer的消息给到编解码器。
// 将会添加当前需要被执行onInputBufferFetched()方法的待执行输入消息事件到其队列中。
mPendingInputMessages.push_back(msg);
}
}
// EWOULDBLOCK错误码表示:需要更多数据块
// mSource为早期分析的GenericSource数据源实现对象,
// 它的feedMoreTSData()方法实现实际上是空实现,始终返回OK。
// 因此若err错误码为EWOULDBLOCK时,返回值为true,表示数据源提供当前Buffer数据不足。
return err == -EWOULDBLOCK
&& mSource->feedMoreTSData() == OK;
}
fetchInputData(msg)实现分析:
获取输入数据
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp]
status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) {
// 创建Buffer数据访问单元sp对象
sp<ABuffer> accessUnit;
// 是否丢弃访问单元
bool dropAccessUnit = true;
// while循环获取
do {
// 获取(即出队列)Buffer数据访问单元,简单说就是获取数据源解复用后的音视频数据
// 调用GenericSource的该方法
// 见第1小节分析
// 注意第二个参数为sp对象地址,因此不会为空,用于接收已解析数据Buffer的
status_t err = mSource->dequeueAccessUnit(mIsAudio, &accessUnit);
if (err == -EWOULDBLOCK) {
// 返回数据不足错误码
return err;
} else if (err != OK) {
// 其他错误码时
if (err == INFO_DISCONTINUITY) {
// INFO_DISCONTINUITY即Buffer非连续性错误码时
int32_t type;
// 获取非连续性类型
CHECK(accessUnit->meta()->findInt32("discontinuity", &type));
// 判断音频或视频Track数据源格式是否发生变化
bool formatChange =
(mIsAudio &&
(type & ATSParser::DISCONTINUITY_AUDIO_FORMAT))
|| (!mIsAudio &&
(type & ATSParser::DISCONTINUITY_VIDEO_FORMAT));
// 判断媒体播放时间戳发生变化
bool timeChange = (type & ATSParser::DISCONTINUITY_TIME) != 0;
// 该log打印在系统运行时默认会打印
ALOGI("%s discontinuity (format=%d, time=%d)",
mIsAudio ? "audio" : "video", formatChange, timeChange);
// 表示是否为“无缝”格式改变
// 备注:“无缝”的意思就是可以继续使用当前解码器并不用flush数据,即当前格式改变不影响此前的数据格式配置
bool seamlessFormatChange = false;
// 获取新的即已改变的格式信息,见早前相关已分析流程
sp<AMessage> newFormat = mSource->getFormat(mIsAudio);
if (formatChange) {
// 数据源Track格式变化时
// 判断是否支持“无缝”格式改变
// 见第2小节分析
seamlessFormatChange =
supportsSeamlessFormatChange(newFormat);
// treat seamless format change separately
// 单独处理“无缝”格式更改
// 非“无缝”格式更改时就代表数据格式改变了
formatChange = !seamlessFormatChange;
}
// 检查格式改变
// 备注:根据早前我们的分析可知,AVNuUtils类的实现其实际是高通私有库实现的。
// 也就是说上面的判断只是系统内部原生判断处理,
// 厂商还可以根据该输入Buffer数据来自定义修改该格式改变标识
AVNuUtils::get()->checkFormatChange(&formatChange, accessUnit);
// 对于格式或时间数据变化,返回EOS到(入队列)EOS输入队列中,然后等待输出端口EOS
// For format or time change, return EOS to queue EOS input,
// then wait for EOS on output.
if (formatChange /* not seamless */) {
// 格式改变时
// 标记格式改变等待为true
mFormatChangePending = true;
// 返回EOS错误码
err = ERROR_END_OF_STREAM;
} else if (timeChange) {
// 时间改变时
// 记录新格式数据中的CSD数据(到mCSDsForCurrentFormat列表中)
// 该方法见此前已有分析
rememberCodecSpecificData(newFormat);
// 标记时间改变等待
mTimeChangePending = true;
// 返回EOS错误码
err = ERROR_END_OF_STREAM;
} else if (seamlessFormatChange) {
// “无缝”格式改变时【重用已存在解码器并且不用flush】
// 记录新格式数据中的CSD数据(到mCSDsForCurrentFormat列表中)
// 该方法见此前已有分析
// reuse existing decoder and don't flush
rememberCodecSpecificData(newFormat);
// 继续上面的【mSource->dequeueAccessUnit】执行,重新获取新的输入Buffer,也就是说刚获取的数据丢弃了
continue;
} else {
// 其他情况时,表示该数据流不受非连续性的影响,返回EWOULDBLOCK即数据不足错误码
// This stream is unaffected by the discontinuity
return -EWOULDBLOCK;
}
}
// reply should only be returned without a buffer set
// when there is an error (including EOS)
// 发生异常错误(包括EOS)时,reply传入参数应答消息设置该错误码返回,不能设置buffer
CHECK(err != OK);
reply->setInt32("err", err);
// 返回EOS
return ERROR_END_OF_STREAM;
}
// 获取输入Buffer成功时
// 判断是否drop丢弃访问单元Buffer
dropAccessUnit = false;
if (!mIsAudio && !mIsEncrypted) {
// 非音频并且非加密数据源时,即非加密视频源时
// 下面这段注释实际上就是解释当前if判断里面的处理CASE问题,此处不展开翻译,自行阅读吧。
// 主要就是做一些异步并发操作同步判断等额外保护措施。
// Extra safeguard if higher-level behavior changes. Otherwise, not required now.
// Preventing the buffer from being processed (and sent to codec) if this is a later
// round of playback but this time without prepareDrm. Or if there is a race between
// stop (which is not blocking) and releaseDrm allowing buffers being processed after
// Crypto has been released (GenericSource currently prevents this race though).
// Particularly doing this check before IsAVCReferenceFrame call to prevent parsing
// of encrypted data.
if (mIsEncryptedObservedEarlier) {
// 处理不当,理论上此值不应该为true,返回无效错误码
ALOGE("fetchInputData: mismatched mIsEncrypted/mIsEncryptedObservedEarlier (0/1)");
return INVALID_OPERATION;
}
// 获取当前获取到的输入Buffer的元数据中的视频时域层级