2.3.MediaPlayer的setDataSource流程
上面已经讲了创建流程,已经讲本地的MediaPlayer创建好了,接下来就是给数据。比如给个"/sdcard/test.mp3"或者"/sdcard/test.mp4" 其实在上面来说流程是一样的,想想都知道应该是从:java-》jni-》本地Mediaplayer。
2.3.1JAVA层
[-->MediaPlayer.java]
public void setDataSource(String path)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(path, null, null);
}
private void setDataSource(String path, String[] keys, String[] values)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
...
final File file = new File(path);
if (file.exists()) {
FileInputStream is = new FileInputStream(file);
FileDescriptor fd = is.getFD();
setDataSource(fd);
is.close();
}
}
public void setDataSource(FileDescriptor fd)
throws IOException, IllegalArgumentException, IllegalStateException {
setDataSource(fd, 0, 0x7ffffffffffffffL);
}
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException {
_setDataSource(fd, offset, length);
}
private native void _setDataSource(FileDescriptor fd, long offset, long length)
setDataSource 将path 变成文件描述符fd,最后将fd通过native的_setDataSource设置到下面去。来看下JNI方法,在注册表中看到如下,调用的是android_media_MediaPlayer_setDataSourceFD方法。
2.3.2JNI层
[-->android_media_mediaplayer.cpp]
{"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
[-->android_media_mediaplayer.cpp]
static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
...
process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
...
}
static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
...
if ( opStatus == (status_t) INVALID_OPERATION ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
} else if ( opStatus == (status_t) BAD_VALUE ) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
} else if ( opStatus == (status_t) PERMISSION_DENIED ) {
jniThrowException(env, "java/lang/SecurityException", NULL);
} else if ( opStatus != (status_t) OK ) {
if (strlen(message) > 230) {
// if the message is too long, don't bother displaying the status code
jniThrowException( env, exception, message);
} else {
char msg[256];
// append the status code to the message
sprintf(msg, "%s: status=0x%X", message, opStatus);
jniThrowException( env, exception, msg);
...
}
拿出本地的MedaPlayer对象mp 调用本地的setDataSource,而process_media_player_call,这个方法是检测调用的返回值的,如果返回值有一些其他的异常则抛出异常。
2.3.3本地cpp层
IMediaDeathNotifier::getMediaPlayerService()
{
ALOGV("getMediaPlayerService");
Mutex::Autolock _l(sServiceLock);
if (sMediaPlayerService == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
binder = sm->getService(String16("media.player"));
if (binder != 0) {
break;
}
ALOGW("Media player service not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(sDeathNotifier);
sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
}
ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
return sMediaPlayerService;
}
status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
...
const sp<IMediaPlayerService> service(getMediaPlayerService());
...
player->setDataSource(fd, offset, length);
...
err = attachNewPlayer(player);
...
}
return err;
}
status_t MediaPlayer::attachNewPlayer(const sp<IMediaPlayer>& player)
{
...
p = mPlayer;
mPlayer = player;
...
return err;
}
这里开始和MediaPlayerService打交道了,通过ServiceManager获取到MediaPlayerService,然后setDataSource,最后attachNewPlayer将其保持在变量中。注意在获取到MediaPlayerService 后建立了个DeathNotifier防止断开。
2.3.4 MediaPlayerService层
[-->MediaPlayerService.cpp]
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
{
...
player_type playerType = MediaPlayerFactory::getPlayerType(this,fd, offset,length);
sp<MediaPlayerBase> p = setDataSource_pre(playerType);
...
setDataSource_post(p, p->setDataSource(fd, offset, length));
...
return mStatus;
}
- 通过MediaPlayerFactory工程方法获取播放器的类型
- setDataSource_pre 创建播放器
- 调用创建的播放器设置DataSource
2.3.4.1 通过MediaPlayerFactory工程方法获取播放器的类型
[-->MediaPlayerFactory.cpp]
player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,
int fd,
int64_t offset,
int64_t length) {
GET_PLAYER_TYPE_IMPL(client, fd, offset, length);
}
通过一个宏定义;
#define GET_PLAYER_TYPE_IMPL(a...) \
Mutex::Autolock lock_(&sLock); \
\
player_type ret = STAGEFRIGHT_PLAYER; \
float bestScore = 0.0; \
\
for (size_t i = 0; i < sFactoryMap.size(); ++i) { \
\
IFactory* v = sFactoryMap.valueAt(i); \
float thisScore; \
CHECK(v != NULL); \
thisScore = v->scoreFactory(a, bestScore); \
if (thisScore > bestScore) { \
ret = sFactoryMap.keyAt(i); \
bestScore = thisScore; \
} \
} \
\
if (0.0 == bestScore) { \
ret = getDefaultPlayerType(); \
} \
\
return ret;
static player_type getDefaultPlayerType() {
return NU_PLAYER;
}
这个宏定义和《启动:2.1.1 mediaserver进程的启动》中是对应的,将sFactoryMap中的player类型通过一些比较算法取出来,我们这边将使用最后的getDefaultPlayerType 返回的NU_PLAYER类型进行分析。
2.3.4.2 setDataSource_pre 创建播放器
sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
player_type playerType)
{
...
sp<MediaPlayerBase> p = createPlayer(playerType);
if (p == NULL) {
return p;
}
...
...
mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
mPid, mAudioAttributes);
static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
...
return p;
}
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
...
if (p == NULL) {
p = MediaPlayerFactory::createPlayer(playerType, this, notify, mPid);
}
...
return p;
}
sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
...
factory = sFactoryMap.valueFor(playerType);
CHECK(NULL != factory);
p = factory->createPlayer(pid);
...
init_result = p->initCheck();
if (init_result == NO_ERROR) {
p->setNotifyCallback(cookie, notifyFunc);
}
return p;
}
这里createPlayer 传进入的是NU_PLAYER 类型,并且最后sFactoryMap中取得是NuPlayerDriver—根据《启动:2.1.1 mediaserver进程的启动》
这里还需要注意两点,
- 1.创建时还设置了(p->setNotifyCallback)notify,它是个函数,用于往上传递信息的。
- 2.并且还创建了音频输出对象,static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput)这个将用于音频的输出
2.3.4.3 调用创建的播放器设置DataSource
[-->MediaPlayerFactory.cpp]
class NuPlayerFactory : public MediaPlayerFactory::IFactory {
...
virtual sp<MediaPlayerBase> createPlayer(pid_t pid) {
ALOGV(" create NuPlayer");
return new NuPlayerDriver(pid);
}
...
};
通过NuPlayerFactory 创建出来的是NuPlayerDriver,也就是上面的p ,然后p调用了setDataSource。
2.3.5 NuPlayer层
[-->NuPlayerDriver.cpp]
NuPlayerDriver::NuPlayerDriver(pid_t pid)
: mState(STATE_IDLE),
...
mLooper(new ALooper),
mAutoLoop(false) {
...
mLooper->setName("NuPlayerDriver Looper");
mLooper->start(
false, /* runOnCallingThread */
true, /* canCallJava */
PRIORITY_AUDIO);
mPlayer = new NuPlayer(pid);
mLooper->registerHandler(mPlayer);
mPlayer->setDriver(this);
...
}
status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
ALOGV("setDataSource(%p) file(%d)", this, fd);
...
mPlayer->setDataSourceAsync(fd, offset, length);
...
return mAsyncResult;
}
最终会建立一个底层的Player 叫做Nuplayer ,并调用setDataSourceAsync。将上层的数据交给了Nu;这里需要注意的另一处,有个ALooper 这个loop是用于,消息传递的,这个细节将在后面的章节讲到。
2.3.5.1 最后的归属Nuplayer
在上一节中新建了个NuPlayer并将NuPlayerDriver的this传了进来。
[-->NuPlayer.cpp]
NuPlayer::NuPlayer(pid_t pid)
: mUIDValid(false),
mPID(pid),
...
mToneIsSpeech(0) {
...
init_ext();
}
void NuPlayer::init_ext(){
mVideoDecoder = NULL;//视频解码器变量
mAudioDecoder = NULL;//音频解码器变量
mRenderer = NULL;//
...
}
NuPlayer 构造中没有做什么事情。就一些变量的初始化。
[-->NuPlayer.cpp]
void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
sp<GenericSource> source =
new GenericSource(notify, mUIDValid, mUID);
status_t err = source->setDataSource(fd, offset, length);
msg->post();
}
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatSetDataSource:
{
...
if(mSource == NULL) {
...
status_t err = OK;
sp<RefBase> obj;
CHECK(msg->findObject("source", &obj));
if (obj != NULL) {
Mutex::Autolock autoLock(mSourceLock);
mSource = static_cast<Source *>(obj.get());
} else {
err = UNKNOWN_ERROR;
}
...
break;
}
}
生成一个GenericSource,并调用GenericSource的setDataSource,将fd 传下去。在msg->post()后消息处理将刚才创建的对象保存到mSource。中
[-->GenericSource.cpp]
NuPlayer::GenericSource::GenericSource(
: Source(notify),
{
...
}
status_t NuPlayer::GenericSource::setDataSource(
int fd, int64_t offset, int64_t length) {
...
mFd = dup(fd);
mOffset = offset;
mLength = length;
...
return OK;
}
GenericSource 的构造中没有做啥事情,setDataSource也就是将上面传下来的变量进行了保存。到此整个setDataSource就讲完了,难么回头想想都经历了什么。从上到下我们总结下经过的主要类
-->Mediaplayer.java
-->android_media_MediaPlayer.cpp
-->mediaplayer.cpp
-->MediaPlayerService.cpp
---->MediaPlayerFactory.cpp
-->NuPlayerDirvier.cpp
-->Nuplayer.cpp;
-->GenericSource.cpp
从流程上其实没什么太难的地方,就是将path 转成的fd 最后放到GenericSource中。但是其中间出现的一些技术要点需要好好记住的,
比如
1、关于创建的AudioOutPut ,它是用于音频输出的
2、关于传进去的notify函数,它是用于消息回调的
这些目前还用不到,但到后面的章节中是特别有用过的细节。