在上一篇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) constructor
02-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=576460752303423487
02-10 15:45:13.545 V/MediaPlayerService( 647): st_dev = 21
02-10 15:45:13.545 V/MediaPlayerService( 647): st_mode = 33184
02-10 15:45:13.546 V/MediaPlayerService( 647): st_uid = 0
02-10 15:45:13.546 V/MediaPlayerService( 647): st_gid = 9997
02-10 15:45:13.546 V/MediaPlayerService( 647): st_size = 2932715
02-10 15:45:13.546 V/MediaPlayerService( 647): calculated length = 2932715
02-10 15:45:13.546 V/MediaPlayerService( 647): player type = 4
02-10 15:45:13.546 V/MediaPlayerFactory( 647): create NuPlayer
02-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): kWhatSetAudioSink
02-10 15:45:13.552 V/NuPlayer( 647): kWhatSetDataSource
02-10 15:45:13.553 I/ExtendedNuUtils( 647): printFileName fd(8) -> /storage/emulated/0/Music/Test.mp3
02-10 15:45:13.553 V/MediaPlayerService( 647): setDataSource
02-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的流程就走完了。