应用场景:

MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mediaPlayer.release();
mediaPlayer = null;
}
});
mediaPlayer.setDataSource(“abc.mp3”);
mediaPlayer.prepare();
mediaPlayer.start();

一、setDataSource

在MediaPlayer.java 中

public void setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException {
disableProxyListener();
setDataSource(fd, offset, length);
}

setDataSource终于调用了native函数:_setDataSource(fd,offset, length);

我们直接跳到JNI层来看它的详细实现

依据JNI相关的知识,在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);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
} if (fileDescriptor == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
ALOGV("setDataSourceFD: fd %d", fd);
process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}

mp为MediaPlayer类型的对象,在JNI层创建,在MediaPlayer.cpp中。一起来看setDataSource的实现。

status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
status_t err = UNKNOWN_ERROR;
const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(fd, offset, length))) {
player.clear();
}
err = attachNewPlayer(player);
}
return err;
}

getMediaPlayerService()为一个典型的Binder机制向ServiceManager获取服务的方法,Binder这方面的知识能够參考http://blog.csdn.net/super_dc/article/details/37738123http://blog.csdn.net/super_dc/article/details/37764947

service->create(this, mAudioSessionId),先看create方法在IMediaPlayerService.cpp中的实现:

virtual sp<IMediaPlayer> create(
const sp<IMediaPlayerClient>& client, int audioSessionId) {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
data.writeStrongBinder(client->asBinder());
data.writeInt32(audioSessionId); remote()->transact(CREATE, data, &reply);
return interface_cast<IMediaPlayer>(reply.readStrongBinder());
}

这里仅仅是Binder客户端的实现。其终于实现会在MediaPlayerService.cpp中由服务端MediaPlayerService来实现。

sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
int audioSessionId)
{
pid_t pid = IPCThreadState::self()->getCallingPid();
int32_t connId = android_atomic_inc(&mNextConnId); sp<Client> c = new Client(
this, pid, connId, client, audioSessionId,
IPCThreadState::self()->getCallingUid()); ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,
IPCThreadState::self()->getCallingUid());
/* add by Gary. start {{----------------------------------- */
c->setScreen(mScreen);
/* add by Gary. end -----------------------------------}} */
c->setSubGate(mGlobalSubGate); // 2012-03-12, add the global interfaces to control the subtitle gate wp<Client> w = c;
{
Mutex::Autolock lock(mLock);
mClients.add(w);
}
return c;
}

综合上面两点,sp<IMediaPlayer>player(service->create(this, mAudioSessionId));中player实际上是一个Client类型对象的proxy。

其详细实现都在Client中实现。

player->setDataSource(fd, offset, length)就能够直接到MediaPlayerService.cpp中的Client类中来看其详细实现了。

status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
{
struct stat sb;
int ret = fstat(fd, &sb);
if (ret != 0) {
ALOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
return UNKNOWN_ERROR;
} if (offset >= sb.st_size) {
ALOGE("offset error");
::close(fd);
return UNKNOWN_ERROR;
}
if (offset + length > sb.st_size) {
length = sb.st_size - offset;
ALOGV("calculated length = %lld", length);
}
// 关键点1
player_type playerType = MediaPlayerFactory::getPlayerType(this,
fd,
offset,
length);
// 关键点2
sp<MediaPlayerBase> p = setDataSource_pre(playerType);
if (p == NULL) {
return NO_INIT;
} // now set data source
// 关键点3
setDataSource_post(p, p->setDataSource(fd, offset, length));
return mStatus;
}

这里有3个关键点。我们分别破解之,先看getPlayerType

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;

MediaPlayerFactory作为一个工厂类,各种mediaplayer向它注冊。并各自实现scoreFactory和createPlayer用来推断当前多媒体文件是否适合用此mediaplayer来播放和创建mediaplayer。在哪儿注冊mediaplayer呢?在MediaPlayerService的构造函数中,也就是说当向系统注冊MediaPlayerService服务时,就已经注冊了一些mediaplayer了。

播放mp3文件时。会创建STAGEFRIGHT_PLAYER,这也是默认的播放器。

以下就以STAGEFRIGHT_PLAYER来继续以下的流程。

到眼下为止,我们知道playerType返回了STAGEFRIGHT_PLAYER。接着来看关键点2.

sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
player_type playerType)
{
ALOGV("player type = %d", playerType); // create the right type of player
sp<MediaPlayerBase> p = createPlayer(playerType);
if (p == NULL) {
return p;
} if (!p->hardwareOutput()) {
mAudioOutput = new AudioOutput(mAudioSessionId);
static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
} return p;
}

依据playerType创建播放器,实际上就是创建StagefrightPlayer

再看关键点3,p->setDataSource(fd,offset, length)实际上就是调用了StagefrightPlayer的setDataSource。

看代码:

StagefrightPlayer::StagefrightPlayer()
: mPlayer(new AwesomePlayer) {
ALOGV("StagefrightPlayer"); mPlayer->setListener(this);
}
status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
return mPlayer->setDataSource(dup(fd), offset, length);
}

由代码可知。StagefrightPlayer仅仅是AwesomePlayer的代理类,详细实现还在AwesomePlayer里面。

status_t AwesomePlayer::setDataSource(
int fd, int64_t offset, int64_t length) {
Mutex::Autolock autoLock(mLock); reset_l(); sp<DataSource> dataSource = new FileSource(fd, offset, length); status_t err = dataSource->initCheck(); if (err != OK) {
return err;
} mFileSource = dataSource; {
Mutex::Autolock autoLock(mStatsLock);
mStats.mFd = fd;
mStats.mURI = String8();
} return setDataSource_l(dataSource);
}

FileSource类实现了数据读取,播放器调用dataSource->readAt来获取数据,另外,其基类DataSource提供了一些分离器例如以下。

RegisterDefaultSniffers将在AwesomePlayer的构造函数中被调用。

// static
void DataSource::RegisterDefaultSniffers() {
RegisterSniffer(SniffMPEG4);
RegisterSniffer(SniffMatroska);
RegisterSniffer(SniffOgg);
RegisterSniffer(SniffWAV);
RegisterSniffer(SniffFLAC);
RegisterSniffer(SniffAMR);
RegisterSniffer(SniffMPEG2TS);
RegisterSniffer(SniffMP3);
RegisterSniffer(SniffAAC);
RegisterSniffer(SniffMPEG2PS);
RegisterSniffer(SniffWVM); char value[PROPERTY_VALUE_MAX];
if (property_get("drm.service.enabled", value, NULL)
&& (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
RegisterSniffer(SniffDRM);
}
}

接着往下看setDataSource_l(dataSource)

status_t AwesomePlayer::setDataSource_l(
const sp<DataSource> &dataSource) { // 对于不同的文件格式会创建不同的MediaExtractor。MP3文件会创建MP3Extractor
// 文件格式靠source->sniff(&tmp, &confidence, &meta)来区分。这个函数会遍历之前通过RegisterSniffer注冊的分离器,得到最合适的文件格式
sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); if (extractor == NULL) {
return UNKNOWN_ERROR;
} if (extractor->getDrmFlag()) {
checkDrmStatus(dataSource);
} return setDataSource_l(extractor);
} status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
// Attempt to approximate overall stream bitrate by summing all
// tracks' individual bitrates, if not all of them advertise bitrate,
// we have to fail. int64_t totalBitRate = 0; mExtractor = extractor;
for (size_t i = 0; i < extractor->countTracks(); ++i) {
sp<MetaData> meta = extractor->getTrackMetaData(i); int32_t bitrate;
if (!meta->findInt32(kKeyBitRate, &bitrate)) {
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
ALOGV("track of type '%s' does not publish bitrate", mime); totalBitRate = -1;
break;
} totalBitRate += bitrate;
} mBitrate = totalBitRate; ALOGV("mBitrate = %lld bits/sec", mBitrate); {
Mutex::Autolock autoLock(mStatsLock);
mStats.mBitrate = mBitrate;
mStats.mTracks.clear();
mStats.mAudioTrackIndex = -1;
mStats.mVideoTrackIndex = -1;
} bool haveAudio = false;
bool haveVideo = false;
for (size_t i = 0; i < extractor->countTracks(); ++i) {
sp<MetaData> meta = extractor->getTrackMetaData(i); const char *_mime;
CHECK(meta->findCString(kKeyMIMEType, &_mime)); String8 mime = String8(_mime); if (!haveVideo && !strncasecmp(mime.string(), "video/", 6)) {
setVideoSource(extractor->getTrack(i));
haveVideo = true; // Set the presentation/display size
int32_t displayWidth, displayHeight;
bool success = meta->findInt32(kKeyDisplayWidth, &displayWidth);
if (success) {
success = meta->findInt32(kKeyDisplayHeight, &displayHeight);
}
if (success) {
mDisplayWidth = displayWidth;
mDisplayHeight = displayHeight;
} {
Mutex::Autolock autoLock(mStatsLock);
mStats.mVideoTrackIndex = mStats.mTracks.size();
mStats.mTracks.push();
TrackStat *stat =
&mStats.mTracks.editItemAt(mStats.mVideoTrackIndex);
stat->mMIME = mime.string();
}
} else if (!haveAudio && !strncasecmp(mime.string(), "audio/", 6)) {
setAudioSource(extractor->getTrack(i));
haveAudio = true;
mActiveAudioTrackIndex = i; {
Mutex::Autolock autoLock(mStatsLock);
mStats.mAudioTrackIndex = mStats.mTracks.size();
mStats.mTracks.push();
TrackStat *stat =
&mStats.mTracks.editItemAt(mStats.mAudioTrackIndex);
stat->mMIME = mime.string();
} if (!strcasecmp(mime.string(), MEDIA_MIMETYPE_AUDIO_VORBIS)) {
// Only do this for vorbis audio, none of the other audio
// formats even support this ringtone specific hack and
// retrieving the metadata on some extractors may turn out
// to be very expensive.
sp<MetaData> fileMeta = extractor->getMetaData();
int32_t loop;
if (fileMeta != NULL
&& fileMeta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
modifyFlags(AUTO_LOOPING, SET);
}
}
} else if (!strcasecmp(mime.string(), MEDIA_MIMETYPE_TEXT_3GPP)) {
addTextSource_l(i, extractor->getTrack(i));
}
} if (!haveAudio && !haveVideo) {
if (mWVMExtractor != NULL) {
return mWVMExtractor->getError();
} else {
return UNKNOWN_ERROR;
}
} mExtractorFlags = extractor->flags(); return OK;
}

MediaExtractor涉及到媒体文件格式的非常多内容,比方track的构成。有几种track等等。后面再做解说,这里我们播放的是MP3文件,所以countTracks的值为1。sp<MetaData> meta = extractor->getTrackMetaData(i)中meta的kKeyMIMEType值为"audio/",将会运行到setAudioSource(extractor->getTrack(i)),再看代码:

sp<MediaSource> MP3Extractor::getTrack(size_t index) {
if (mInitCheck != OK || index != 0) {
return NULL;
}
// 返回的是一个MP3Source对象
return new MP3Source(
mMeta, mDataSource, mFirstFramePos, mFixedHeader,
mSeeker);
} void AwesomePlayer::setAudioSource(sp<MediaSource> source) {
CHECK(source != NULL); mAudioTrack = source;
}

至此,setdatasource就分析完毕,下一篇将分析prepare的实现过程。

MediaPlayer本地播放流程解析(一)的更多相关文章

  1. 【Android 多媒体开发】 MediaPlayer 状态机 接口 方法 解析

    作者 : 韩曙亮 转载请著名出处 :  http://blog.csdn.net/shulianghan/article/details/38487967 一. MediaPlayer 状态机 介绍 ...

  2. Android 本地播放器

    发布时间:2018-09-06   技术:Glide+pinyin4j+SwipeDelMenuLayout   概述 这是一款Android 端的本地音乐播放器,界面风格有模仿网易云音乐.bilib ...

  3. 运用surfaceView与MediaPlayer实现播放视频的功能

    该程序运用了surfaceView与MediaPlayer结合,实现播放视频,surfaceView详情请见 SurfaceView的使用 使用了第三方包Volly里面的方法StringQueue下载 ...

  4. MediaPlayer 音频播放 示例

    状态机.流程图.生命周期 对播放音频/视频文件和流的控制是通过一个状态机来管理的.下图显示一个MediaPlayer对象被支持的播放控制操作驱动的生命周期和状态. 椭圆代表MediaPlayer对象可 ...

  5. MediaPlayer音乐播放器、上一首、下一首、播放、停止、自动下一首、进度条

    本文介绍MediaPlayer的使用.MediaPlayer可以播放音频和视频,另外也可以通过VideoView来播放视频,虽然VideoView比MediaPlayer简单易用,但定制性不如用Med ...

  6. HBase - 数据写入流程解析

    本文由  网易云发布. 作者:范欣欣 本篇文章仅限内部分享,如需转载,请联系网易获取授权. 众所周知,HBase默认适用于写多读少的应用,正是依赖于它相当出色的写入性能:一个100台RS的集群可以轻松 ...

  7. Session (简介、、相关方法、流程解析、登录验证)

    Session简介 Session的由来 Cookie虽然在一定程度上解决了"保持状态"的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能 ...

  8. TCP/IP协议三次握手与四次握手流程解析

    原文链接地址:http://www.2cto.com/net/201310/251896.html TCP/IP协议三次握手与四次握手流程解析 TCP/IP协议的详细信息参看<TCP/IP协议详 ...

  9. SSL/TLS算法流程解析

    SSL/TLS 早已不是陌生的词汇,然而其原理及细则却不是太容易记住.本文将试图通过一些简单图示呈现其流程原理,希望读者有所收获. 一.相关版本 Version Source Description ...

随机推荐

  1. Android开源库集锦

    一.兼容类库 ActionBarSherlock : Action Bar是Android 3.0后才开始支持的,ActionBarSherlock是让Action Bar功能支持2.X后的所有平台, ...

  2. Android 环境配置:git开启多颜色模式

    git config --global color.status autogit config --global color.diff autogit config --global color.br ...

  3. hibernate jpa 2.0 报错Hibernate cannot unwrap interface java.sql.Connection

    今天在做报表的时候,利用Hibernate JPA 2.0需要获取数据库连接com.sql.Connection的时候获取不到,网上说用这种方式解决: entityManager.getTransac ...

  4. C++ 报错 R6030 CRT not initialized

    昨天,在写一个算法的时候,报错R6030 CRT not initialized. 认真检查发现,是出了比较低级的错误. 一. 会出错的代码,编译的时候不会报错,执行过程中报R6030 CRT not ...

  5. Unable to run mksdcard SDK tool.

    Ubuntu 14.04,安装android studio后运行出错,sdk manager不能正常运行 Unable to run mksdcard SDK tool. 原因,缺少运行需要的库:li ...

  6. WEB服务器5--IIS中ISAPI扩展、ISAPI筛选器

    在IIS的文档中经常会提到两个术语:ISAPI扩展和ISAPI筛选器. ISAPI扩展 “ISAPI扩展(ISAPI Extension)”是一种可以添加到IIS中以增强Web服务器功能的程序,其载体 ...

  7. SpringMvc学习-增删改查

    本节主要介绍SpringMVC简单的增删改查功能. 1.查询 dao中的代码 public List<WeatherPojo> getAllWeather(){ String sql=&q ...

  8. NPOI通过DataTable导出和读取Excel

    Excel导入及导出问题产生: 从接触.net到现在一直在维护一个DataTable导出到Excel的类,时不时还会维护一个导入类.以下是时不时就会出现的问题: 导出问题: 如果是asp.net,你得 ...

  9. Django 实战 之 搭项目(正在更新)

    系统:win10 python版本:python 3.5 工具: pyCharm 3.4 professional 源码来源:https://github.com/ouzhigang/django-o ...

  10. (原)Matlab的svmtrain和svmclassify

    转载请注明出处: http://www.cnblogs.com/darkknightzh/p/5554551.html 参考网址: http://www.cnblogs.com/zhangchaoya ...