Android N Audio播放二:setDataSource窥探
来源:互联网 发布:怎么修改远程桌面端口 编辑:程序博客网 时间:2024/05/21 18:33
在上一篇Android如何播放一首音乐中介绍了如何使用MediaPlayer来播放音乐文件。可以看到,步骤非常简单。
player.setDataSource(path);player.prepare();player.start();
但如果你想更深入的了解Audio, 仅仅会调用这个几个API是远远不够的,没有考虑容器格式,文件的来源,协议,编解码等等。
这篇文章就来介绍一下第一步的setDataSource到底都做了些什么。还是以MusicDemo为例,先将play方法中的prepare和start方法注释掉, 这样可以比较清楚的看到setDataSource的都干了些什么事。
private void play(){ try { String path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/Music/Test.mp3"; player.setDataSource(path); Log.d("Jaychou","MusicDemo setDataSource"); //player.prepare(); //Log.d("Jaychou","MusicDemo Prepare"); //player.start(); } catch (IOException e) { e.printStackTrace(); Log.d("Jaychou","err when play"); } }
先来一张流程图:
当点击PLAY_MUSIC的时候,打出如下日志:
02-10 15:45:13.538 V/MediaPlayer( 4748): setDataSource(59, 0, 576460752303423487)02-10 15:45:13.540 V/MediaPlayerService( 647): Client(4) constructor02-10 15:45:13.541 V/MediaPlayerService( 647): Create new client(4) from pid 4748, uid 10140, 02-10 15:45:13.545 V/MediaPlayerService( 647): setDataSource fd=8, offset=0, length=57646075230342348702-10 15:45:13.545 V/MediaPlayerService( 647): st_dev = 2102-10 15:45:13.545 V/MediaPlayerService( 647): st_mode = 3318402-10 15:45:13.546 V/MediaPlayerService( 647): st_uid = 002-10 15:45:13.546 V/MediaPlayerService( 647): st_gid = 999702-10 15:45:13.546 V/MediaPlayerService( 647): st_size = 293271502-10 15:45:13.546 V/MediaPlayerService( 647): calculated length = 293271502-10 15:45:13.546 V/MediaPlayerService( 647): player type = 402-10 15:45:13.546 V/MediaPlayerFactory( 647): create NuPlayer02-10 15:45:13.547 V/NuPlayerDriver( 647): NuPlayerDriver(0xf39ab140)02-10 15:45:13.551 V/AudioSink( 647): AudioOutput(57)02-10 15:45:13.552 V/NuPlayerDriver( 647): setDataSource(0xf39ab140) file(8)02-10 15:45:13.552 V/NuPlayer( 647): kWhatSetAudioSink02-10 15:45:13.552 V/NuPlayer( 647): kWhatSetDataSource02-10 15:45:13.553 I/ExtendedNuUtils( 647): printFileName fd(8) -> /storage/emulated/0/Music/Test.mp302-10 15:45:13.553 V/MediaPlayerService( 647): setDataSource02-10 15:45:13.555 D/Jaychou ( 4748): MusicDemo setDataSource
结合流程图和Log, 我们来看一下setDataSource做的事情。
1、MusicDemo调用framework MediaPlayer.java的接口,继而通过JNI(JNI我这里没有画出来)再调用到Mediaplayer.cpp, 然后再通过Binder(IMediaPlayer)调到MediaPlayerService. 可以看到,MusicDemo, MediaPlayer.java, Mediaplayer.cpp, IMediaPlayer属于一个进程,pid是4748.
2、MediaPlayerService的setDataSource主要有三个动作:
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length){ ALOGV("setDataSource fd=%d, offset=%" PRId64 ", length=%" PRId64 "", fd, offset, 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; } ALOGV("st_dev = %" PRIu64 "", static_cast<uint64_t>(sb.st_dev)); ALOGV("st_mode = %u", sb.st_mode); ALOGV("st_uid = %lu", static_cast<unsigned long>(sb.st_uid)); ALOGV("st_gid = %lu", static_cast<unsigned long>(sb.st_gid)); ALOGV("st_size = %" PRId64 "", sb.st_size); if (offset >= sb.st_size) { ALOGE("offset error"); return UNKNOWN_ERROR; } if (offset + length > sb.st_size) { length = sb.st_size - offset; ALOGV("calculated length = %" PRId64 "\n", length); } player_type playerType = MediaPlayerFactory::getPlayerType(this, fd, offset, length); sp<MediaPlayerBase> p = setDataSource_pre(playerType); if (p == NULL) { return NO_INIT; } // now set data source setDataSource_post(p, p->setDataSource(fd, offset, length)); return mStatus;}
A: 获得播放器类型
MediaPlayerFactory::getPlayerType
从Log看是Nuplayer, Android N上android已经完全抛弃了Asomeplayer, 源代码里面已经看不到相关的代码了。
B: 创建播放器,设置AudioSink等。
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; } sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder = sm->getService(String16("media.extractor")); if (binder == NULL) { ALOGE("Unable to connect to media extractor service"); return NULL; } mExtractorDeathListener = new ServiceDeathNotifier(binder, p, MEDIAEXTRACTOR_PROCESS_DEATH); binder->linkToDeath(mExtractorDeathListener); binder = sm->getService(String16("media.codec")); if (binder == NULL) { ALOGE("Unable to connect to media codec service"); return NULL; } mCodecDeathListener = new ServiceDeathNotifier(binder, p, MEDIACODEC_PROCESS_DEATH); binder->linkToDeath(mCodecDeathListener); if (!p->hardwareOutput()) { Mutex::Autolock l(mLock); mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(), mPid, mAudioAttributes); static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput); } return p;}
C: 调用NuplayerDriver的setDataSource
p->setDataSource(fd, offset, length)
3、setDataSource是一个Blocking的方法, 在NuplayerDriver中可以看出,
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); } AVNuUtils::get()->printFileName(fd); return mAsyncResult;}
设置source之前状态是STATE_SET_DATASOURCE_PENDING, 当设置完成以后,NuplayerDriver的notifySetDataSourceCompleted会被调用, 状态更新成STATE_UNPREPARED。
4、NuplayerDriver调用的是Nuplayer的setDataSourceAsync, 这里通过构造GenericSource将数据源抽成了source。这个source就供下一步的解复用(demux)来使用。
void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) { sp<AMessage> msg = new AMessage(kWhatSetDataSource, this); sp<AMessage> notify = new AMessage(kWhatSourceNotify, this); sp<GenericSource> source = new GenericSource(notify, mUIDValid, mUID); status_t err = source->setDataSource(fd, offset, length); if (err != OK) { ALOGE("Failed to set data source!"); source = NULL; } msg->setObject("source", source); msg->post();}
这样,整个setDataSource的流程就走完了。
- Android N Audio播放二:setDataSource窥探
- Android N Audio播放四:start真面目
- Android N Audio播放一:如何播放一首音乐
- Android N的Audio系统(二)
- video/audio playback:setDataSource
- video/audio playback:setDataSource
- video/audio playback:setDataSource
- video/audio playback:setDataSource
- video/audio playback:setDataSource
- Android N Audio播放三:prepare大揭秘
- Android N Audio播放五:如何选择Extractor
- Audio笔记之MediaPlayerService:setDataSource
- Android Audio 的播放
- Android N Audio: Audio Track play
- Android--Audio播放:竞争Audio之Audio Focus 音频焦点
- [收集]Android中的Audio播放
- MT6737 Android N 平台 Audio系统学习----录音到播放录音流程分析
- MT6737 Android N 平台 Audio系统学习----录音到播放录音流程分析
- Android Studio 出现 Gradle's dependency cache may be corrupt 错误
- 重入锁--ReentrantLock
- 浮动布局所带来的影响以及如何清除浮动
- 变量与函数
- lua实现KMP字符串匹配。
- Android N Audio播放二:setDataSource窥探
- 反思一下
- tomcat远程debug配置详解,Eclipse结合
- mac下安装和使用MySQL-python
- leecode 解题总结:56. Merge Intervals
- ubuntu14.04系统下搭建支持https的apache2服务器
- 前端web开发命名规范
- iOS tabbar设置透明背景
- 蓝桥杯算法训练 开心的金明(01背包,动态规划)