承接上一章节分析:【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 2】
本系列文章分析的安卓源码版本:【Android 10.0 版本】
【此章节小节编号就接着上一章节排列】
6.2、mMediaClock->setPlaybackRate(mPlaybackRate)实现分析:
设置播放速率(默认为1)给MediaClock音视频同步时钟缓存处理。
该播放速率其实是可以用来控制播放速度的即快播或慢播。
// [frameworks/av/media/libstagefright/MediaClock.cpp]
void MediaClock::setPlaybackRate(float rate) {
// 该宏定义适用于判断rate值必现大于等于0
CHECK_GE(rate, 0.0);
// MediaClock会在不同线程中调用,因此必现加锁访问
Mutex::Autolock autoLock(mLock);
// 该值记录的是最近当前音频流播放时的系统真实流逝时长时间戳,
// 但注意,该值是系统开机后才计时的时间戳【开机时长】,
// 而不是从1970-01-01那个起点的现实世界时间戳。【其实作为锚点时间,那种时间应该都行】
// 其实这些字段值的含义在前面的setDataSource第2章节中已分析过了。
if (mAnchorTimeRealUs == -1) {
// 第一次开始播放时,此时该值为-1,会执行此处,缓存
mPlaybackRate = rate;
return;
}
// 播放后进行的改变播放速率时
// 获取当前时间即系统开机时长
// 见下面的分析
int64_t nowUs = ALooper::GetNowUs();
// 此处计算当前(音频)媒体播放时间戳
// mAnchorTimeMediaUs:根据前面setDataSource章节的分析可知该值表示的是媒体同步锚点时间戳
// 即当次连续帧播放的音视频开始时间戳【记录的是媒体开始播放时间戳】。
// nowUs - mAnchorTimeRealUs:该计算得到当前现实时间流逝时长。
// 然后将时间流逝时长乘以播放速率将会计算出真实的媒体本次已播放的时长
// 【默认播放速率为1,但比如为2倍速度播放,那么已播放时长就应该相当于现实时间流程时长的两倍】。
// 然后将媒体开始播放锚点时间戳加上本次已播放时长,最终计算出当前(音频)播放媒体时间戳
int64_t nowMediaUs = mAnchorTimeMediaUs + (nowUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
// 修正值
if (nowMediaUs < 0) {
ALOGW("setRate: anchor time should not be negative, set to 0.");
nowMediaUs = 0;
}
// 然后更新锚点时间和播放速率等值
// 该方法分析放入后续涉及时流程中分析
updateAnchorTimesAndPlaybackRate_l(nowMediaUs, nowUs, rate);
if (rate > 0.0) {
// 若设置的速率大于0
// 改变MediaClock消息事件的代数值,其实通过这么多的代数值作用分析,
// 我们依然知晓它的作用就是在消息被AHander执行处理时判断是否应该执行该消息流程。
// 后续会分析到该判断处。
++mGeneration;
// 该方法分析将会在后续流程中分析
processTimers_l();
}
}
ALooper::GetNowUs()实现:
获取当前时间即系统开机时长。如下调用了【system/core/libutils/include/utils/Timers.h】实现的安卓系统时间头文件的systemTime方法,传入时间类型为SYSTEM_TIME_MONOTONIC。其实际获取的值为:
monotonic time字面意思是单调时间,实际上它指的是系统启动以后流逝的时间,这是由变量jiffies来记录的。系统每次启动时jiffies初始化为0,每来一个timer interrupt,jiffies加1,也就是说它代表系统启动后流逝的tick数。
还有另一种时间即现实世界时间:real time 或 wall time。
wall time字面意思是挂钟时间,实际上就是指的是现实的时间,这是由变量xtime来记录的。系统每次启动时将CMOS上的RTC时间读入xtime,这个值是"自1970-01-01起经历的秒数、本秒中经历的纳秒数",每来一个timer interrupt,也需要去更新xtime。
区别:这两个值都是单调递增的,除了计数起点不一样,还有另外不同之处,wall time时间可能被用户修改,而monotonic time不能修改。
因此此处应该使用的是monotonic time时间来作为音频播放时间锚点,其实就是作为一个音视频同步时钟参考时间点。
再次阐述下关于时间头文件的引入流程是:ALooper.h引入了【system/core/libutils/include/utils/threads.h】,threads.h引入了【system/core/libutils/include/utils/Thread.h】,Thread.h引入了【system/core/libutils/include/utils/Timers.h】。
备注:Timers.h其实最终也是调用Linux的时钟函数,其头文件引入流程为,Timers.h引入了【bionic/libc/include/sys/time.h】,但实际实现是Timers.cpp中引入了【bionic/libc/include/time.h】,而time.h也引入sys/time.h来实现的,最终sys/time.h最后引入了【bionic/libc/kernel/uapi/linux/time.h】来完成了整个头文件引入链。
// [frameworks/av/media/libstagefright/foundation/ALooper.cpp]
// static
int64_t ALooper::GetNowUs() {
return systemTime(SYSTEM_TIME_MONOTONIC) / 1000LL;
}
7、getFrameRate()实现分析:
获取媒体默认视频帧率
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
float NuPlayer::getFrameRate() {
// 获取视频track的元数据信息对象
// 见前面章节中已有相关分析
sp<MetaData> meta = mSource->getFormatMeta(false /* audio */);
if (meta == NULL) {
return 0;
}
// 获取帧率
int32_t rate;
if (!meta->findInt32(kKeyFrameRate, &rate)) {
// 获取失败时,将退回到文件元数据信息来获取该视频帧率
// 见前面章节中已有相关分析
// fall back to try file meta
sp<MetaData> fileMeta = getFileMeta();
if (fileMeta == NULL) {
ALOGW("source has video meta but not file meta");
return -1;
}
int32_t fileMetaRate;
if (!fileMeta->findInt32(kKeyFrameRate, &fileMetaRate)) {
return -1;
}
return fileMetaRate;
}
return rate;
}
8、mRenderer->setVideoFrameRate(rate)实现分析:
设置最佳可支持视频帧率。
Android native层媒体通信架构AHandler/ALooper机制实现源码分析
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp]
void NuPlayer::Renderer::setVideoFrameRate(float fps) {
// 发送【kWhatSetVideoFrameRate】事件消息给Renderer自身AHandler实现处理
// 见下面的分析
sp<AMessage> msg = new AMessage(kWhatSetVideoFrameRate, this);
msg->setFloat("frame-rate", fps);
msg->post();
}
【kWhatSetVideoFrameRate】事件消息处理:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp]
void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatSetVideoFrameRate:
{
// 获取AMessage中视频帧率参数
float fps;
CHECK(msg->findFloat("frame-rate", &fps));
// 见下面分析
onSetVideoFrameRate(fps);
break;
}
}
}
onSetVideoFrameRate(fps)实现分析:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp]
void NuPlayer::Renderer::onSetVideoFrameRate(float fps) {
// 此处创建了视频帧率调度(调整)对象,其主要作用是使视频播放更加流畅。
// 见下面的分析
if (mVideoScheduler == NULL) {
mVideoScheduler = new VideoFrameScheduler();
}
// 初始化其视频帧率
mVideoScheduler->init(fps);
}
VideoFrameScheduler类声明:【省略其他代码】
// [frameworks/av/media/libstagefright/include/media/stagefright/VideoFrameSchedulerBase.h]
struct VideoFrameSchedulerBase : public RefBase {}
// [frameworks/av/media/libstagefright/include/media/stagefright/VideoFrameScheduler.h]
struct VideoFrameScheduler : public VideoFrameSchedulerBase {}
VideoFrameScheduler类构造函数定义实现:
其方法体空实现,直接初始化父类构造函数
// [frameworks/av/media/libstagefright/VideoFrameScheduler.cpp]
VideoFrameScheduler::VideoFrameScheduler() : VideoFrameSchedulerBase() {}
VideoFrameSchedulerBase类构造函数定义:
此处Vsync变量前缀其实表示VSYNC信号的含义即设备显示更新信号。
// [frameworks/av/media/libstagefright/VideoFrameSchedulerBase.cpp]
VideoFrameSchedulerBase::VideoFrameSchedulerBase()
// (屏幕)VSYNC信号显示同步计时时间(单位纳秒ns)
: mVsyncTime(0),
// VSYNC显示同步周期时间(单位纳秒ns),其实际该值表示的是每一帧显示时间
mVsyncPeriod(0),
// 下次刷新计时信息时间(单位纳秒ns)
mVsyncRefreshAt(0),
// 估算(屏幕)最后一帧的同步时间(单位纳秒ns)
mLastVsyncTime(-1),
// 调整(修正)时间(单位纳秒ns)
mTimeCorrection(0) {
}
mVideoScheduler->init(fps)实现:
初始化其视频帧率
// [frameworks/av/media/libstagefright/VideoFrameSchedulerBase.cpp]
void VideoFrameSchedulerBase::init(float videoFps) {
// 更新视频显示同步时间
// 见下面的分析
updateVsync();
// 初始化无效值
mLastVsyncTime = -1;
mTimeCorrection = 0;
// 重置视频帧率
// 该对象为PLL类创建,PLL类为VideoFrameSchedulerBase的内部类,见下面的分析
mPll.reset(videoFps);
}
updateVsync()实现分析:
更新视频显示同步时间。
Android C++底层Binder通信机制原理分析总结【通俗易懂】
// [frameworks/av/media/libstagefright/VideoFrameScheduler.cpp]
void VideoFrameScheduler::updateVsync() {
// kVsyncRefreshPeriod:视频帧同步刷新周期时间,即1秒
// 由前面的分析可知,此处计算的就是下次刷新计时信息时间(单位纳秒ns),也就是每一秒刷新
mVsyncRefreshAt = systemTime(SYSTEM_TIME_MONOTONIC) + kVsyncRefreshPeriod;
// 默认0
mVsyncTime = 0;
mVsyncPeriod = 0;
if (mComposer == NULL) {
// 此处有前面相关分析章节可知,获取一个在服务注册中心ServiceManager中已注册名叫"SurfaceFlinger"的
// 服务Binder对象即Bn实现端对象,并使用interface_cast将其转换为Bp代理端对象即BpSurfaceComposer实现者。
// 该Binder机制实现过程不再分析,可见此前Binder分析章节。
String16 name("SurfaceFlinger");
sp<IServiceManager> sm = defaultServiceManager();
mComposer = interface_cast<ISurfaceComposer>(sm->checkService(name));
}
if (mComposer != NULL) {
// 服务不为空时,获取显示统计值信息
DisplayStatInfo stats;
status_t res = mComposer->getDisplayStats(NULL /* display */, &stats);
if (res == OK) {
// 获取成功时,可以得到Surface即屏幕刷新率相关的VSYNC信号时间值
ALOGV("vsync time:%lld period:%lld",
(long long)stats.vsyncTime, (long long)stats.vsyncPeriod);
// 缓存
mVsyncTime = stats.vsyncTime;
mVsyncPeriod = stats.vsyncPeriod;
} else {
ALOGW("getDisplayStats returned %d", res);
}
} else {
ALOGW("could not get surface mComposer service");
}
}
kVsyncRefreshPeriod定义:
视频显示刷新周期时间,其值为1s。
均在 [frameworks/av/media/libstagefright/include/media/stagefright/VideoFrameSchedulerBase.h] 文件中。
static const nsecs_t kNanosIn1s = 1000000000;
// 默认视频帧显示同步周期时间,其实就是每一帧显示时间。此处计算的帧率为60
static const nsecs_t kDefaultVsyncPeriod = kNanosIn1s / 60; // 60Hz
// 视频帧同步刷新周期时间,即1秒
static const nsecs_t kVsyncRefreshPeriod = kNanosIn1s; // 1 sec
mPll.reset(videoFps)实现分析:
重置视频帧率。mPll该对象为PLL类创建,PLL类为VideoFrameSchedulerBase的内部类。
PLL类声明:
// [frameworks/av/media/libstagefright/include/media/stagefright/VideoFrameSchedulerBase.h]
struct VideoFrameSchedulerBase : public RefBase {
private:
struct PLL {
PLL();
// reset PLL to new PLL
void reset(float fps = -1);
// keep current estimate, but restart phase
void restart();
// returns period or 0 if not yet primed
nsecs_t addSample(nsecs_t time);
nsecs_t getPeriod() const;
private:
// 计算的视频每帧(间)刷新周期时长,也就是1秒除以视频帧率得到
nsecs_t mPeriod;
nsecs_t mPhase;
// 是否准备好?
bool mPrimed; // have an estimate for the period
size_t mSamplesUsedForPriming;
nsecs_t mLastTime; // last input time
nsecs_t mRefitAt; // next input time to fit at
size_t mNumSamples; // can go past kHistorySize
nsecs_t mTimes[kHistorySize];
void test();
// returns whether fit was successful
bool fit(nsecs_t phase, nsecs_t period, size_t numSamples,
int64_t *a, int64_t *b, int64_t *err);
void prime(size_t numSamples);
};
PLL mPll; // PLL for video frame rate based on render time
}
PLL类构造函数实现:
无参构造函数,初始化默认值。
// [frameworks/av/media/libstagefright/VideoFrameSchedulerBase.cpp]
VideoFrameSchedulerBase::PLL::PLL()
: mPeriod(-1),
mPhase(0),
mPrimed(false),
mSamplesUsedForPriming(0),
mLastTime(-1),
mNumSamples(0) {
}
PLL reset()实现分析:
// [frameworks/av/media/libstagefright/VideoFrameSchedulerBase.cpp]
void VideoFrameSchedulerBase::PLL::reset(float fps) {
//test();
// 重置默认值
mSamplesUsedForPriming = 0;
mLastTime = -1;
// set up or reset video PLL
if (fps <= 0.f) {
// 视频帧率小于等于0时
mPeriod = -1;
mPrimed = false;
} else {
ALOGV("reset at %.1f fps", fps);
// 计算视频每帧(间)刷新周期,也就是1秒除以视频帧率得到,并加了一个0.5纳秒
mPeriod = (nsecs_t)(1e9 / fps + 0.5);
// 标记已准备好
mPrimed = true;
}
restart();
}
PLL restart() 实现:
重新启动,其实也是初始化值
// [frameworks/av/media/libstagefright/VideoFrameSchedulerBase.cpp]
// reset PLL but keep previous period estimate
void VideoFrameSchedulerBase::PLL::restart() {
mNumSamples = 0;
mPhase = -1;
}
关于PLL的其他方法和工作流程会在后续涉及到分析流程中再次分析。
9、startPlaybackTimer(“onstart”)实现分析:
启动播放计时器,其实也就是初始化最后一次已启动播放时间戳为当前。
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::startPlaybackTimer(const char *where) {
Mutex::Autolock autoLock(mPlayingTimeLock);
// 该参数值每次播放只赋值一次,where代表哪里调用了该方法。
if (mLastStartedPlayingTimeNs == 0) {
// 注意:此处的systemTime()为无参调用,因此我们必现搞清楚它的时间类型是什么,
// 因此首先寻找NuPlayer.cpp和NuPlayer.h里面是否包含由time字样的头文件名,但可惜没有,
// 但是通过上面的分析我们可以知道NuPlayer实现了ALooper功能,而ALooper其实间接引入时间头文件
//【system/core/libutils/include/utils/Timers.h】。
// 而该方法在C++编译器编译时将会带有默认参数为SYSTEM_TIME_MONOTONIC,也就是上面分析的,获取的是本次开机流逝时间时长。
mLastStartedPlayingTimeNs = systemTime();
ALOGV("startPlaybackTimer() time %20" PRId64 " (%s)", mLastStartedPlayingTimeNs, where);
}
}
10、postScanSources()实现分析:
发送扫描数据源消息事件并处理
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::postScanSources() {
// 该标记作用:记录【kWhatScanSources】消息事件只允许当前只能有一个该消息在消息队列中,
// 若此前已经加入过该消息但还未被处理,那么此处直接return。
if (mScanSourcesPending) {
return;
}
// 发送【kWhatScanSources】消息事件给自身消息线程处理,并携带当前扫描数据源代数值
sp<AMessage> msg = new AMessage(kWhatScanSources, this);
msg->setInt32("generation", mScanSourcesGeneration);
msg->post();
// 标识为true。
// 注意:此处为什么可以安全的放在消息发送post()之后执行?
// 原因是postScanSources()该方法一直被放在NuPlayer的AHandler消息机制线程中执行的,
// 而上面消息放入消息队列中也是消息线程中执行,而当前消息机制正在处理postScanSources()该方法消息处理流程,
// 因此必现在当前postScanSources()方法事件处理完成后才能处理下一个事件消息,也就不会有任何问题的。
// 警告:异步执行即其他线程执行postScanSources()方法,这里就可能出现不同步问题。
mScanSourcesPending = true;
}
【kWhatScanSources】消息事件处理:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatScanSources:
{
// 获取该消息事件代数值参数
int32_t generation;
CHECK(msg->findInt32("generation", &generation));
// 然后判断该值是否被改变了
if (generation != mScanSourcesGeneration) {
// 被改变了则必现丢弃本次事件流程处理,(也就是当前消息正在消息队列中未被处理期间该值发生了变化)
// 原因是该代数值在reset或flush流程中被该了,因此不需要再执行此流程
// Drop obsolete msg.
break;
}
// 相等则将此标志置为false。 其实上面代数值不等时,在reset或flush流程中也会重置该值为false。
mScanSourcesPending = false;
ALOGV("scanning sources haveAudio=%d, haveVideo=%d",
mAudioDecoder != NULL, mVideoDecoder != NULL);
// 当前是否已有音频或(和)视频解码器
bool mHadAnySourcesBefore =
(mAudioDecoder != NULL) || (mVideoDecoder != NULL);
// 是否重新执行该扫描消息事件处理流程
bool rescan = false;
// 英文注释可知,初始化视频解码器在音频解码器之前,因为视频解码器成功初始化过程中可能会改变音频的deep-buffer模式,
// 也就是此前分析过的音频播放模式如offload或deep-buffer等。
// initialize video before audio because successful initialization of
// video may change deep buffer mode of audio.
// 警告:从这里判断可以知道,上层APP在允许视频后台播放时若是第一次后台播放时不设置surface,那么不会创建视频解码器,
// 只有当上层APP执行setSurface接口时则在设置surface处理流程中将会判断若此前视频解码器未创建则立即创建并进行视频解码。
if (mSurface != NULL) {
// Surface不为空时,则初始化创建视频解码器
// 见10.1小节分析
if (instantiateDecoder(false, &mVideoDecoder) == -EWOULDBLOCK) {
// 需重新扫描source标志位置为true
rescan = true;
}
}
// Don't try to re-open audio sink if there's an existing decoder.
// 注:若已存在的一个音频解码器,则不能尝试重新打开AudioSink
if (mAudioSink != NULL && mAudioDecoder == NULL) {
// mAudioSink不为空且音频解码器为空时
// 见前面10.1小节分析
// 注意:instantiateDecoder里面的创建解码器之后的解码器配置等过程的流程都是异步执行的,
// 因此此处的音视频解码器初始化配置等过程也将会是同步进行的。
if (instantiateDecoder(true, &mAudioDecoder) == -EWOULDBLOCK) {
// 需重新扫描source标志位置为true
rescan = true;
}
}
if (!mHadAnySourcesBefore
&& (mAudioDecoder != NULL || mVideoDecoder != NULL)) {
// 此前无音视频编解码器,并且创建了音频或(和)视频解码器时,
// 注意:前面所述初始化解码器过程为异步的,因此此时虽然创建音视频解码器对象,
// 但并不能代表创建的音视频解码器可以胜任工作。
// 还要注意的:对于NuPlayer此处流程和NuPlayerDecoder的流程时异步执行的,
// 但是NuPlayer对于Decoder的大部分请求在NuPlayerDecoder内部执行流程是同步的。
// This is the first time we've found anything playable.
// 第一次可播放时
// 动态获取媒体时长标志位是否存在
if (mSourceFlags & Source::FLAG_DYNAMIC_DURATION) {
// 存在时,则每隔1秒周期性获取媒体时长
// 备注:该方法分析见前面prepare流程已有分析
schedulePollDuration();
}
// 由于前面所述音视频编解码器是异步初始化流程,所以此次处理这种CASE:
// 暂停渲染器直到视频解码器预滚即预拉取到解码数据。
// Pause the renderer till video queue pre-rolls
// 非常重要的注意事项:该if中的判断条件isVideoSampleReceived()方法内部处理其实际控制变量是未加锁的,
// 也就是在(实测)实际运行过程中,Renderer内部的该实际控制变量的状态可能不对时将会造成此处if内部进行了pause后,
// 后面没有流程再来唤醒Renderer进行resume流程了,即极小概率发生视频并未开始播放的黑屏状态现象。
// 当然我们也可以有解决方案,比如在真正执行Renderer的暂停功能时判断是否出现了该问题,若出现了则不再执行pause流程即可。
// isVideoSampleReceived()见10.2小节分析
if (!mPaused && mVideoDecoder != NULL && mAudioDecoder != NULL
&& !mRenderer->isVideoSampleReceived()) {
// 判断当前没有暂停,并且音视频编解码器都存在,
// 并且渲染器中记录视频解码器预滚拉取到解码数据全局变量为false即还没有接受到第一帧解码数据时
// NOTE:在解码器实例化后暂停渲染器,等待视频解码器输出第一帧解码数据
ALOGI("NOTE: Pausing Renderer after decoders instantiated..");
// 暂停Renderer
// 关于暂停流程,见后面pause单独分析流程
mRenderer->pause();
// 设置超时唤醒Renderer事件消息
// wake up renderer if timed out
// kDefaultVideoPrerollMaxUs默认值为2秒,它的作用是:
// 此处pause暂停Renderer等待视频预滚第一帧数据的最大允许超时时长。
// 创建了【kWhatWakeupRendererFromPreroll】该事件消息,并延迟超时时长后进行自动唤醒渲染器Renderer。
// 但注意这个超时时长唤醒事件到达后,在处理时又判断了【isVideoSampleReceived()】这里的条件,
// 因此由于这是两个异步流程,判断未加锁变量,那么将会在这超时事件内,Renderer接收到了第一帧,
// 将会改变该状态变量,那么也就会导致该超时唤醒机制失效,即将会无人唤醒Renderer造成视频不会播放状态。
sp<AMessage> msg = new AMessage(kWhatWakeupRendererFromPreroll, this);
msg->post(kDefaultVideoPrerollMaxUs);
// 【kWhatWakeupRendererFromPreroll】该事件消息处理,见10.3小节分析
}
}
status_t err;
// 由前面分析,若是本地音视频,则GenericSource的该方法其实际为空实现,始终返回true
if ((err = mSource->feedMoreTSData()) != OK) {
// 其它数据流如网络流媒体等,数据源发生错误时
if (mAudioDecoder == NULL && mVideoDecoder == NULL) {
// 都为空则通知该错误码到上层APP
// We're not currently decoding anything (no audio or
// video tracks found) and we just ran out of input data.
if (err == ERROR_END_OF_STREAM) {
notifyListener(MEDIA_PLAYBACK_COMPLETE, 0, 0);
} else {
notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
}
}
break;
}
if (rescan) {
// 上面音视频解码器创建数据不足状态码时,
// 将会在此处等待100毫秒后再次发起该上面流程执行
msg->post(100000LL);
// 并标记该未执行事件状态为true
mScanSourcesPending = true;
}
break;
}
}
}
10.1、instantiateDecoder(false, &mVideoDecoder)实现分析:
根据不同传入参数初始化创建音频或视频解码器,解码器对象传入的是一个指针,因此可以在创建完成后进行赋值。
checkAudioModeChange
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
status_t NuPlayer::instantiateDecoder(
bool audio, sp<DecoderBase> *decoder, bool checkAudioModeChange) {
// The audio decoder could be cleared by tear down. If still in shut down
// process, no need to create a new audio decoder.
// 英文注释意思就是:音频解码器可能在tear down处理流程即offload模式下暂停3秒后会执行该流程中被清除/拆卸了(原因主要是节省电)。
// 因此若音频解码器此时正在shut down流程时将不会创建新的音频解码器。
if (*decoder != NULL || (audio && mFlushingAudio == SHUT_DOWN)) {
// 若当前传入的解码器对象不为空 或 音频解码器此时正在shut down流程时将不会创建新的音频解码器。直接返回成功。
return OK;
}
// 根据前面相关分析,可知此处获取音频或视频track的媒体元数据信息(KV值存储)
sp<AMessage> format = (mSource != NULL) ? mSource->getFormat(audio) : NULL;
if (format == NULL) {
// 失败为空,表示当前track类型的数据源不存在或不支持解复用。
ALOGW("%s: getFormat called when source is gone or not set", __func__);
return UNKNOWN_ERROR;
} else {
// 不为空时获取媒体元数据处理结果错误码,状态为OK即值为0表示成功。否则失败返回错误码。
status_t err;
if (format->findInt32("err", &err) && err) {
return err;
}
}
// 设置优先级(真实时间)
format->setInt32("priority", 0 /* realtime */);
if (!audio) {
// 视频时
// 获取当前video track的"mime"即视频编码格式媒体元数据值
AString mime;
CHECK(format->findString("mime", &mime));
// 创建字幕流解码器,并传入一个和当前NuPlayer进行通信的【kWhatClosedCaptionNotify】事件消息
// 备注:关于字幕流处理目前暂不分析,因为目前很多新视频都是将字幕流嵌入到视频帧里了。 TODO
sp<AMessage> ccNotify = new AMessage(kWhatClosedCaptionNotify, this);
if (mCCDecoder == NULL) {
mCCDecoder = new CCDecoder(ccNotify);
}
// 当前媒体数据源是否加密保护的即DRM数字版权保护,一般我们本地播放的视频都是非加密的。
if (mSourceFlags & Source::FLAG_SECURE) {
format->setInt32("secure", true);
}
// 是否屏幕需要保护(禁用截图)。一般没有该设置
if (mSourceFlags & Source::FLAG_PROTECTED) {
format->setInt32("protected", true);
}
// 如前面的分析,获取视频帧率
float rate = getFrameRate();
if (rate > 0) {
// 获取成功则设置视频帧的操作率,其实就是乘以播放速度(默认为1即正常速度)来得到每秒应该解码的视频帧数
format->setFloat("operating-rate", rate * mPlaybackSettings.mSpeed);
}
if (rate <= 0 || rate > mMaxOutputFrameRate)
// 视频帧率小于等于0 或 帧率大于Surface即屏幕刷新最大帧率,那么直接设置显示输出帧率为屏幕刷新率
format->setInt32("output-frame-rate", mMaxOutputFrameRate);
if (rate <= 0 || rate > mMaxOutputFrameRate)
// 同上条件,设置另一个名称的KV值
format->setFloat("vendor.qti-ext-dec-output-render-frame-rate.value",
mMaxOutputFrameRate);
}
// (解码器锁)加锁创建解码器
Mutex::Autolock autoLock(mDecoderLock);
if (audio) {
// 音频时(先分析下面的视频解码器创建,因此将这里的代码分析流程调换)
// 创建【kWhatAudioNotify】事件通知消息,用于音频解码器通知NuPlayer其解码器状态等
sp<AMessage> notify = new AMessage(kWhatAudioNotify, this);
// 音频解码器代数值加1,然后设置给"generation"参数
++mAudioDecoderGeneration;
notify->setInt32("generation", mAudioDecoderGeneration);
if (checkAudioModeChange) {
// 检查音频模式改变情况【即是否可以支持offload模式播放音频】
// 见10.1.5小节分析
determineAudioModeChange(format);
}
if (mOffloadAudio) {
// 设置offload模式标识
// 备注:该方法是GenericSource父类默认空实现。
mSource->setOffloadAudio(true /* offload */);
// video track不为空时表示有视频
const bool hasVideo = (mSource->getFormat(false /*audio */) != NULL);
format->setInt32("has-video", hasVideo);
// 见10.1.6小节分析
*decoder = AVNuFactory::get()->createPassThruDecoder(notify, mSource, mRenderer);
ALOGV("instantiateDecoder audio DecoderPassThrough hasVideo: %d", hasVideo);
} else {
// 非offload音频播放模式时
// 设置视频解码器输出格式(媒体元数据)信息
// 根据前面相关章节分析可知,AVNuUtils的实现其实是OEM系统芯片厂商自己实现的私有so库。安卓默认cpp为空实现。
AVNuUtils::get()->setCodecOutputFormat(format);
// 设置非offload模式标识
// 备注:该方法是GenericSource父类默认空实现。
mSource->setOffloadAudio(false /* offload */);
// 其实根据前面相关章节分析可知,这里实际也就是直接返回一个NuPlayer::Decoder对象,
// 和视频解码器创建对象是同一个处理流程。
// 见10.1.1小节已有分析
*decoder = AVNuFactory::get()->createDecoder(notify, mSource, mPID, mUID, mRenderer);
ALOGV("instantiateDecoder audio Decoder");
}
// 标记音频解码器无错误状态
mAudioDecoderError = false;
} else {
// 视频时
// 创建【kWhatVideoNotify】事件通知消息,用于视频解码器通知NuPlayer解码器状态等
sp<AMessage> notify = new AMessage(kWhatVideoNotify, this);
// 视频解码器代数值加1,然后设置给"generation"参数
++mVideoDecoderGeneration;
notify->setInt32("generation", mVideoDecoderGeneration);
// 创建新的视频解码器
// 见10.1.1小节分析
*decoder = new Decoder(
notify, mSource, mPID, mUID, mRenderer, mSurface, mCCDecoder);
// 视频解码器是否发生错误,置为false
mVideoDecoderError = false;
// enable FRC if high-quality AV sync is requested, even if not
// directly queuing to display, as this will even improve textureview
// playback.
// 根据注释可知,若需要更高质量AV(视频)同步,则启动FRC功能,即使不是直接入队列渲染显示,
// 因为它将会甚至提高TextureView的播放渲染。
// 备注:FRC(Frame Rate Conversion),FRC动画专家,使LCD画面更加的自然流畅,进一步提高了动态清晰度。
// 具体方法是对相邻的两幅画面的信号进行运算,由处理器生成介于两幅画面中间的过渡画面以达到更加流畅的视觉效果。
// 该功能开启开关为一个系统属性值,该值默认未设置,即不启用。
{
if (property_get_bool("persist.sys.media.avsync", false)) {
format->setInt32("auto-frc", 1);
}
}
}
// 解码器初始化工作
// 见10.1.2小节分析
(*decoder)->init();
// DRM内容暂不分析,通常都是非DRM
// Modular DRM
if (mIsDrmProtected) {
format->setPointer("crypto", mCrypto.get());
ALOGV("instantiateDecoder: mCrypto: %p (%d) isSecure: %d", mCrypto.get(),
(mCrypto != NULL ? mCrypto->getStrongCount() : 0),
(mSourceFlags & Source::FLAG_SECURE) != 0);
}
// 解码器配置工作
// 见10.1.3小节分析
(*decoder)->configure(format);
if (!audio) {
// 视频时
sp<AMessage> params = new AMessage();
// 从视频track媒体数据中获取视频帧率,若大于0则设置该参数
float rate = getFrameRate();
if (rate > 0) {
params->setFloat("frame-rate-total", rate);
}
// 获取文件数据的媒体元数据配置信息
sp<MetaData> fileMeta = getFileMeta();
if (fileMeta != NULL) {
// 获取视频编码的时域层级数,并设置该参数
int32_t videoTemporalLayerCount;
if (fileMeta->findInt32(kKeyTemporalLayerCount, &videoTemporalLayerCount)
&& videoTemporalLayerCount > 0) {
params->setInt32("temporal-layer-count", videoTemporalLayerCount);
}
}
// 设置播放速率,默认为1
params->setFloat("playback-speed", mPlaybackSettings.mSpeed);
// 判断参数个数是否大于0,也就是上面设置的三个参数,正常情况下为3
if (params->countEntries() > 0) {
// 设置此三个参数值
// 见10.1.4小节分析
(*decoder)->setParameters(params);
}
}
// 返回解码器创建成功状态
return OK;
}
10.1.1、new Decoder(notify, mSource, mPID, mUID, mRenderer, mSurface, mCCDecoder)实现分析:
创建新的视频解码器
由于本章节接下来内容篇幅过长,因此必须放入另一章节分析,请查看:
【六】Android MediaPlayer整体架构源码分析 -【start请求播放处理流程】【Part 3】【02】