承接上一章节分析:
【三】Android MediaPlayer整体架构源码分析 -【设置数据源】【Part 2】
本系列文章分析的安卓源码版本:【Android 10.0 版本】
关于彻底理解掌握AMessage消息循环机制实现原理,推荐先看下系列内容:
Android native层媒体通信架构AHandler/ALooper机制实现源码分析
分析的代码来源如下:
// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
{
ALOGV("setDataSource fd=%d, offset=%" PRId64 ", length=%" PRId64 "", fd, offset, length);
// 省略其他上一章节已分析过的代码
// 见下面的分析
// now set data source
return mStatus = setDataSource_post(p, p->setDataSource(fd, offset, length));
}
setDataSource_post(p, p->setDataSource(fd, offset, length))实现分析:
该调用分两部分执行,先执行【p->setDataSource】后执行【setDataSource_post】。
根据前面章节的分析可知,p对象是NuPlayerDriver对象。
p->setDataSource的实现分析:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp]
status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
ALOGV("setDataSource(%p) file(%d)", this, fd);
Mutex::Autolock autoLock(mLock);
// setDataSource必须【STATE_IDLE】空闲状态才能执行,否则其他状态都无效
if (mState != STATE_IDLE) {
return INVALID_OPERATION;
}
// 更新播放器状态
mState = STATE_SET_DATASOURCE_PENDING;
// 根据前面章节分析,可知mPlayer其实就是NuPlayer对象
// 见下面的分析
mPlayer->setDataSourceAsync(fd, offset, length);
while (mState == STATE_SET_DATASOURCE_PENDING) {
// C++实现的条件锁,等待上面【setDataSourceAsync】处理流程完成后才唤醒此处
// 注意:每次都会执行此处,因此可以知晓上层APP进行的setDataSource操作其实可能会耗时,
// 当然一般情况下会非常快的,因为NuPlayer中的处理其实很简单不耗时的,
// 除非NuPlayer自身的ALooper消息循环线程本身已阻塞,否则将非常快返回。
mCondition.wait(mLock);
}
// NuPlayer设置数据源完成时,唤醒继续执行此处代码
// 此处就是打印当前文件名【其实是文件全路径名,可从系统log中看到】
AVNuUtils::get()->printFileName(fd);
// 返回设置数据源处理结果状态
return mAsyncResult;
}
上一章节1.1小节createPlayer(playerType)中遗留了一个内容未分析,p->setUID(mUid) 实现分析(p为NuPlayerDriver对象):
设置调用者用户id
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp]
status_t NuPlayerDriver::setUID(uid_t uid) {
// 调用了NuPlayer的该方法
// 见下面的分析
mPlayer->setUID(uid);
mClientUid = uid;
// mAnalyticsItem 初始化为空,因此暂不分析
Mutex::Autolock autoLock(mMetricsLock);
if (mAnalyticsItem) {
mAnalyticsItem->setUid(mClientUid);
}
return OK;
}
mPlayer->setUID(uid)实现分析:
设置调用者用户id,并将mUIDValid标志为true
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::setUID(uid_t uid) {
mUIDValid = true;
mUID = uid;
}
NuPlayer的setDataSourceAsync(fd, offset, length)实现分析:
关于AMessage的实现原理和使用,请见文章开头提到的另一章节内容分析。
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
// 创建【kWhatSetDataSource】设置数据源事件消息,NuPlayer自身为AHandler子类接收处理
sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
// 创建【kWhatSourceNotify】数据源通知事件消息,NuPlayer自身为AHandler子类接收处理
sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
// 创建一个通用(统一)数据源处理对象
// GenericSource类声明和构造函数实现分析,见第1小节分析
sp<GenericSource> source =
new GenericSource(notify, mUIDValid, mUID, mMediaClock);
ALOGV("setDataSourceAsync fd %d/%lld/%lld source: %p",
fd, (long long)offset, (long long)length, source.get());
// 设置数据源
// 见第2小节分析
status_t err = source->setDataSource(fd, offset, length);
// 若设置失败,则清除数据源对象
if (err != OK) {
ALOGE("Failed to set data source!");
source = NULL;
}
// 设置数据源对象source参数
msg->setObject("source", source);
// 立即发送该消息给NuPlayer的onMessageReceived()方法接收处理
// 该消息事件【kWhatSetDataSource】的处理流程,见第3小节分析
msg->post();
// 设置数据源类型为DATA_SOURCE_TYPE_GENERIC_FD
mDataSourceType = DATA_SOURCE_TYPE_GENERIC_FD;
}
1、GenericSource类声明和构造函数实现分析
GenericSource类声明:【省略其他代码】
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.h]
struct NuPlayer::GenericSource : public NuPlayer::Source,
public MediaBufferObserver // Modular DRM
{}
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerSource.h]
struct NuPlayer::Source : public AHandler {}
// [frameworks/av/media/libstagefright/include/media/stagefright/MediaBufferBase.h]
class MediaBufferObserver {}
GenericSource构造函数:
初始化默认值,这些参数具体作用将在后续分析流程中使用到的地方进行详细分析。
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
NuPlayer::GenericSource::GenericSource(
const sp<AMessage> ¬ify,
bool uidValid,
uid_t uid,
const sp<MediaClock> &mediaClock)
// 父类Source构造方法,见下面的分析
: Source(notify),
mAudioTimeUs(0),
mAudioLastDequeueTimeUs(0),
mVideoTimeUs(0),
mVideoLastDequeueTimeUs(0),
mPrevBufferPercentage(-1),
mPollBufferingGeneration(0),
mSentPauseOnBuffering(false),
mAudioDataGeneration(0),
mVideoDataGeneration(0),
mFetchSubtitleDataGeneration(0),
mFetchTimedTextDataGeneration(0),
mDurationUs(-1LL),
mAudioIsVorbis(false),
mIsSecure(false),
mIsStreaming(false),
mUIDValid(uidValid),
mUID(uid),
mMediaClock(mediaClock),
mFd(-1),
// 码率
mBitrate(-1LL),
mPendingReadBufferTypes(0) {
ALOGV("GenericSource");
CHECK(mediaClock != NULL);
// 数据缓冲设置信息的初始化标记时间【单位为毫秒ms】,初始化值5秒
mBufferingSettings.mInitialMarkMs = kInitialMarkMs;
// 数据缓冲设置信息的底缓冲数据时指定的可恢复播放标记时间【单位为毫秒ms】,
// 初始化值15秒即默认需要缓冲15秒的数据。
// 该参数作用:即若是由于底缓冲数据需要缓冲数据时造成的暂停播放,
// 则会在缓冲数据达到该标记时间值时自动恢复播放。
mBufferingSettings.mResumePlaybackMarkMs = kResumePlaybackMarkMs;
// 重置(初始化)数据源信息
// 见下面的分析
resetDataSource();
}
Source(notify)实现分析:
其实就是缓存该通知NuPlayer接收【kWhatSourceNotify】数据源通知事件消息,即用于通知NuPlayer
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerSource.h]
struct NuPlayer::Source : public AHandler {
// The provides message is used to notify the player about various
// events.
explicit Source(const sp<AMessage> ¬ify)
: mNotify(notify) {
}
}
resetDataSource() 实现分析:
重置清除(初始化)数据源缓存信息,主要就是做一些清除数据工作,比较简单此处就不一一展开分析了
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
void NuPlayer::GenericSource::resetDataSource() {
ALOGV("resetDataSource");
mHTTPService.clear();
{
Mutex::Autolock _l_d(mDisconnectLock);
mHttpSource.clear();
mDisconnected = false;
}
mUri.clear();
mUriHeaders.clear();
mSources.clear();
if (mFd >= 0) {
// 关闭打开的文件
close(mFd);
mFd = -1;
}
mOffset = 0;
mLength = 0;
mStarted = false;
mPreparing = false;
mIsDrmProtected = false;
mIsDrmReleased = false;
mIsSecure = false;
mMimes.clear();
}
2、source->setDataSource(fd, offset, length) 实现分析
向通用数据源处理对象设置数据源
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
status_t NuPlayer::GenericSource::setDataSource(
int fd, int64_t offset, int64_t length) {
// 加锁处理
Mutex::Autolock _l(mLock);
ALOGV("setDataSource %d/%lld/%lld", fd, (long long)offset, (long long)length);
// 重置清除(初始化)数据源缓存信息
resetDataSource();
// dup()复制参数旧参数fd 所指的文件描述符值
// 返回值:当复制成功时, 则返回最小及尚未使用的文件描述词,
// 若有错误则返回-1, errno 会存放错误代码。
// 错误代码:EBADF 参数fd 非有效的文件描述词, 或该文件已关闭。
mFd = dup(fd);
// 缓存文件偏移量即文件开始读取位置
mOffset = offset;
// 缓存文件总大小
mLength = length;
// 返回成功OK状态
// 注意此处英文注释意思:即当前方法只是记录了打开的文件描述符和文件信息,
// 但没有执行文件数据源的读取处理,而将该处理移到prepareSync()方法中执行,
// 原因是避免阻塞方法调用端线程。
// delay data source creation to prepareAsync() to avoid blocking
// the calling thread in setDataSource for any significant time.
return OK;
}
3、【kWhatSetDataSource】设置数据源事件消息接收处理:
由前面的ALooper消息循环实现机制分析可知,NuPlayer是允许在自己新创建的ALooper消息循环线程中进行接收消息处理的。
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatSetDataSource:
{
ALOGV("kWhatSetDataSource");
CHECK(mSource == NULL);
status_t err = OK;
sp<RefBase> obj;
// 获取传入的数据源对象source参数
CHECK(msg->findObject("source", &obj));
if (obj != NULL) {
// 若不为空,则加锁,强转为子类sp<Source>类型数据来缓存
Mutex::Autolock autoLock(mSourceLock);
mSource = static_cast<Source *>(obj.get());
} else {
// 未知错误
err = UNKNOWN_ERROR;
}
CHECK(mDriver != NULL);
// 尝试提升NuPlayerDriver为强引用指针对象
sp<NuPlayerDriver> driver = mDriver.promote();
if (driver != NULL) {
// 通知设置数据源完成事件
// 见下面的分析
driver->notifySetDataSourceCompleted(err);
}
break;
}
// ... 省略其他代码
}
}
driver->notifySetDataSourceCompleted(err) 实现分析:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp]
void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
// 加锁
Mutex::Autolock autoLock(mLock);
// 检查当前状态必须是该状态
CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);
// 设置数据源完成状态码【成功或失败】
mAsyncResult = err;
// 成功则扭转状态为 STATE_UNPREPARED 即已设置数据源但未prepare状态,
// 否则设置为空闲状态【STATE_IDLE】
mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;
// 然后激活等待在该条件变量上所有线程【基本上只会有一个线程等待,因为做了状态判断】
// 即会激活本文开头NuPlayerDriver.setDataSource()中的等待事件被唤醒继续执行,
// 也就完成了整个设置数据源处理流程
mCondition.broadcast();
}
本章节【设置数据源处理流程】系列内容分析结束。
后续 prepare 流程请查看后续章节