
[时间: 2016-09] [状态: 打开]
[关键字: android,nuplayer,开源播放器,播放框架,nuplayerdriver]
0 NuPlayer简介
流媒体框架是Android 2.3中引入的,流媒体框架的核心是NuPlayer. 在以前的版本中,通常认为本地播放使用Stagefrightplayer + Awesomeplayer和NuPlayer进行流式传输. 在Android4.0之后,HttpLive和RTSP协议开始使用NuPlayer播放器,而在Android5.0(L版本)之后,本地播放器也开始使用NuPlayer播放器. Android 7.0(N版)完全删除了Awesomeplayer.
用外行的话来说,NuPlayer是AOSP中提供的多媒体播放框架,它可以支持本地文件,HTTP(HLS),RTSP和其他协议的播放,通常支持H.264android 音频播放框架,H.265 / HEVC,AAC编码格式,支持MP4,MPEG-TS包.
在实现方面,NuPlayer与Awesomeplayer不同. NuPlayer基于StagefrightPlayer的基本类. 它使用较低级别的ALooper / AHandler机制来异步处理请求. ALooper将消息请求排队. AHandler处理它们,因此NuPlayer中的Mutex / Lock更少. Awesomeplayer使用omxcodec,NuPlayer使用Acodec.
1个NuPlayer框架
下图是NuPlayer的总体框架

或以下图片

Android层的多媒体框架具有多层实现,甚至跨进程调用. 在这里,我们重点介绍NuPlayerDriver之后的实现和相关逻辑. 至于上层的调用逻辑,建议参考其他材料.

每个部分的功能如下:
2如何通过NuPlayer播放多媒体文件
在AOSP中,多媒体文件或URL通常称为数据源. 通常,多媒体文件包含至少一个音频流,视频流或字幕流. NuPlayer将这三个称为Track,并且在细分下有AudioTrack,VideoTrack和SubtitleTrack. 解析多媒体文件后,可以将其还原为原始数据,然后进行渲染. 具体过程参考下图:

DataSource有两个概念:
VideoTrack和AudioTrack是指提取器(多路分配器)的两个通道,此处的输出只是简单地多路分解的视频和音频流. 之后的输出是音频和视频的输出:
关于Android应用程序层如何调用MediaPlayerandroid 音频播放框架,建议参考我以前的文章MediaPlayer Interface&State.
3我个人的AOSP源代码分析方法
鉴于AOSP是一个操作系统,整个过程更加复杂. 从实际的角度来看,您可以专注于某个点. 例如,我在这里主要关注NuPlayer框架及其内部实现逻辑. 最后,如何从类中提取所需的框架和知识点. 然后,类的外部接口部分通常包括:
在多媒体播放中,关注点是:
4 NuPlayer接口实现分析(NuPlayerDriver)
NuPlayer框架中最顶层的类是NuPlayerDriver,它继承自MediaPlayerInterface,并且主要提供状态转换机制作为NuPlayer类的包装器. NuPlayerDriver类的最重要的成员如下:

首先解释一下,我参考的是Android 7的源代码,即NuPlayerDriver.cpp(目录: ./frameworks/av/media/libmediaplayerservice/nuplayer /)
4.1构造函数和析构函数
从代码中可以看到,构造函数的主要功能是创建ALooper和NuPlayer实例并将它们关联.
mLooper = (new ALooper);
mLooper->start(
false, /* runOnCallingThread */
true, /* canCallJava */
PRIORITY_AUDIO);
mPlayer = new NuPlayer(pid);
mLooper->registerHandler(mPlayer);
mPlayer->setDriver(this);
析构函数主要是销毁创建的ALooper和NuPlayer. 由于它是一个智能指针,因此只需停止即可.
mLooper->stop();
4.2 SetDataSource
此接口的实现非常简单,检查当前的播放状态,然后直接调用NuPlayer :: setDataSourceAsync函数.
status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
ALOGV("setDataSource(%p) file(%d)", this, fd);
Mutex::Autolock autoLock(mLock);
if (mState != STATE_IDLE) {
return INVALID_OPERATION;
}
mState = STATE_SET_DATASOURCE_PENDING;
mPlayer->setDataSourceAsync(fd, offset, length);
while (mState == STATE_SET_DATASOURCE_PENDING) {
mCondition.wait(mLock);
}
return mAsyncResult;
}
最后等待函数返回并调用NuPlayerDriver :: notifySetDataSourceCompleted接口以更改播放器状态.
4.3 setVideoSurfaceTexture
此接口的实现思路与SetDataSource相同,但调用了NuPlayer :: setVideoSurfaceTextureAsync,并在处理请求后调用了NuPlayerDriver :: notifySetSurfaceComplete接口.

4.4 prepare / prepareAsync
这两个接口的基本功能是相同的,但是第二个是异步调用过程. 第一个是通过prepare_l接口实现的,两个接口最终都调用NuPlayer :: prepareAsync,并在请求处理完成后调用NuPlayerDriver :: notifyPrepareCompleted接口. 但是,有很多代码可用来判断播放状态. 例如,prepareAsync
中的代码
ALOGV("prepareAsync(%p)", this);
Mutex::Autolock autoLock(mLock);
switch (mState) {
case STATE_UNPREPARED:
mState = STATE_PREPARING;
mIsAsyncPrepare = true;
mPlayer->prepareAsync();
return OK;
case STATE_STOPPED:
// this is really just paused. handle as seek to start
mAtEOS = false;
mState = STATE_STOPPED_AND_PREPARING;
mIsAsyncPrepare = true;
mPlayer->seekToAsync(0, true /* needNotify */);
return OK;
default:
return INVALID_OPERATION;
};
4.5开始/停止
这两个功能用作开始和停止播放的界面,主要涉及播放器内部状态的切换和判断. 最终功能是通过调用NuPlayer :: start和NuPlayer :: pause接口来实现的.
以下是启动函数实现代码(start_l),主要需要判断处于不同状态的调用逻辑:
switch (mState) {
case STATE_UNPREPARED:
{
status_t err = prepare_l();
if (err != OK) {
return err;
}
CHECK_EQ(mState, STATE_PREPARED);
// fall through
}
case STATE_PAUSED:
case STATE_STOPPED_AND_PREPARED:
case STATE_PREPARED:
{
mPlayer->start();
// fall through
}
case STATE_RUNNING:
{
if (mAtEOS) {
mPlayer->seekToAsync(0);
mAtEOS = false;
mPositionUs = -1;
}
break;
}
default:
return INVALID_OPERATION;
}
mState = STATE_RUNNING;
stop接口的实现相对简单,主要是确定可以调用stop接口的状态并报告MEDIA_STOPPED状态. 代码如下:
switch (mState) {
case STATE_RUNNING:
mPlayer->pause();
// fall through
case STATE_PAUSED:
mState = STATE_STOPPED;
notifyListener_l(MEDIA_STOPPED);
break;
case STATE_PREPARED:
case STATE_STOPPED:
case STATE_STOPPED_AND_PREPARING:
case STATE_STOPPED_AND_PREPARED:
mState = STATE_STOPPED;
break;
default:
return INVALID_OPERATION;
}
4.6暂停/重置
pause用于实现暂停,其实现比较简单,直接调用NuPlayer :: puase即可实现,代码如下:

switch (mState) {
case STATE_PAUSED:
case STATE_PREPARED:
return OK;
case STATE_RUNNING:
mState = STATE_PAUSED;
notifyListener_l(MEDIA_PAUSED);
mPlayer->pause();
break;
default:
return INVALID_OPERATION;
}
reset重置播放器,这是一个同步调用界面. 通过调用NuPlayer :: resetAsync接口,然后调用NuPlayerDriver :: notifyResetComplete来通知最终的实现.
4.7 isPlaying / getDuration / getCurrentPosition
这些界面主要用于获取播放器的状态.
isPlaying由玩家状态直接判断,其实现如下:
bool NuPlayerDriver::isPlaying() {
return mState == STATE_RUNNING && !mAtEOS;
}
getDuration的实现相对简单,代码如下. 但是,具体获得的mDurationUs需要通过NuPlayer报告或定期更新.
status_t NuPlayerDriver::getDuration(int *msec) {
Mutex::Autolock autoLock(mLock);
if (mDurationUs < 0) {
return UNKNOWN_ERROR;
}
*msec = (mDurationUs + 500ll) / 1000;
return OK;
}
getCurrentPosition是通过调用NuPlayer :: getCurrentPosition获得的.
4.8 getMetadata
此界面主要用于判断播放器的属性,例如是否支持暂停,搜索,向前搜索,向后搜索等.
5后续详细分析
本文主要分析NuPlayerDriver的接口实现. 以下分析部分包括:
总而言之,NuPlayerDriver主要是接口层的连接,并在播放器内部记录状态数据以确保其符合Android MediaPlayer状态调用逻辑. 代码相对简单.
参考NuPlayer引入了用于HTTP实时流传输的NuPlayerStagefright视频播放过程
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/bofangqi/article-268667-1.html
男女比例失调正常因为重男轻女