3 、status_t MediaPlayerService::Client::setVideoSurface(const sp<ISurface>& surface)
{
LOGV("[%d] setVideoSurface(%p)", mConnId, surface.get());
sp<MediaPlayerBase> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
return p->setVideoSurface(surface);
}// 由此可见,其实框架把显示的功能并没有替你做下来,因为这个地方很多p->setVideoSurface(surface);返回值都是空的,这个函数只是给你一个接口,把上层的一个 sp<ISurface>给你,至于你在他上面画什么东西,是你的事情,如果你调用这个函数就有,不调用就没有,很明白简单。所以说,显示的工作还得自己来做。
4、5、 都是设置了源,但是在播放前的一些准备工作,一个是同步,一个是异步。
sp<IMemory> MediaPlayerService::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat)
{
LOGV("decode(%s)", url);
sp<MemoryBase> mem;
sp<MediaPlayerBase> player;
// Protect our precious, precious DRMd ringtones by only allowing
// decoding of http, but not filesystem paths or content Uris.
// If the application wants to decode those, it should open a
// filedescriptor for them and use that.
if (url != NULL && strncmp(url, "http:// ", 7) != 0) {
LOGD("Can't decode %s by path, use filedescriptor instead", url);
return mem;
}
player_type playerType = getPlayerType(url);
LOGV("player type = %d", playerType);
// create the right type of player
sp<AudioCache> cache = new AudioCache(url);
player = Android::createPlayer(playerType, cache.get(), cache->notify);
if (player == NULL) goto Exit;
if (player->hardwareOutput()) goto Exit;
static_cast<MediaPlayerInterface*>(player.get())->setAudioSink(cache);
// set data source
if (player->setDataSource(url) != NO_ERROR) goto Exit;
LOGV("prepare");
player->prepareAsync();
LOGV("wait for prepare");
if (cache->wait() != NO_ERROR) goto Exit;
LOGV("start");
player->start();
LOGV("wait for playback complete");
if (cache->wait() != NO_ERROR) goto Exit;
mem = new MemoryBase(cache->getHeap(), 0, cache->size());
*pSampleRate = cache->sampleRate();
*pNumChannels = cache->channelCount();
*pFormat = cache->format();
LOGV("return memory @ %p, sampleRate=%u, channelCount = %d, format = %d", mem->pointer(), *pSampleRate, *pNumChannels, *pFormat);
Exit:
if (player != 0) player->reset();
return mem;
}
这个工程有这样几步,准备,然后等待准备完成,然后开始,然后等待start完成,完成之后就可以得到解码后的数据。上面的几个接口函数基本上是没有什么东西的,下面我们具体来看这个PVPlayer怎么实现的。主要看他的几个私有函数:
static void do_nothing(status_t s, void *cookie, bool cancelled) { }
static void run_init(status_t s, void *cookie, bool cancelled);
static void run_set_video_surface(status_t s, void *cookie, bool cancelled);
static void run_set_audio_output(status_t s, void *cookie, bool cancelled);
static void run_prepare(status_t s, void *cookie, bool cancelled);
和几个私有成员;
PlayerDriver* mPlayerDriver; //整个pv的播放引擎
char * mDataSourcePath;//数据源
bool mIsDataSourceSet;//一个数据源的标识符
sp<ISurface> mSurface;//显示面
int mSharedFd; //这个估计是文件句柄
status_t mInit; //一个状态标志
int mDuration; //文件播放长度
我们来看实现:
// ----------------------------------------------------------------------------
// implement the Packet Video player
// ----------------------------------------------------------------------------
PVPlayer::PVPlayer()
{
LOGV("PVPlayer constructor");
mDataSourcePath = NULL;
mSharedFd = -1;
mIsDataSourceSet = false;
mDuration = -1;
mPlayerDriver = NULL;
LOGV("construct PlayerDriver");
mPlayerDriver = new PlayerDriver(this);
LOGV("send PLAYER_SETUP");
mInit = mPlayerDriver->enqueueCommand(new PlayerSetup(0,0));//给他一个初始化的命令放在队列里面
}
status_t PVPlayer::initCheck()
{
return mInit;
}没有什么工作,初始化工作其实也可以放在这里的。
PVPlayer::~PVPlayer()
{
LOGV("PVPlayer destructor");
if (mPlayerDriver != NULL) {
PlayerQuit quit = PlayerQuit(0,0);//发送一个退出的命令
mPlayerDriver->enqueueCommand(&quit); // will wait on mSyncSem, signaled by player thread
}
free(mDataSourcePath); //如果文件句柄存在的话,就关闭
if (mSharedFd >= 0) {
close(mSharedFd);
}
}
status_t PVPlayer::setDataSource(const char *url)
{
LOGV("setDataSource(%s)", url);
if (mSharedFd >= 0) {
close(mSharedFd);
mSharedFd = -1;
}
free(mDataSourcePath);
mDataSourcePath = NULL;
// Don't let somebody trick us in to reading some random block of memory
if (strncmp("sharedfd://", url, 11) == 0)
return android::UNKNOWN_ERROR;
mDataSourcePath = strdup(url);
return OK;
} //这个函数只是改变了一下mDataSourcePath这个东东,但是我们的opencore里面如何知道的呢?
status_t PVPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
// This is all a big hack to allow PV to play from a file descriptor.
// Eventually we'll fix PV to use a file descriptor directly instead
// of using mmap().
LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
if (mSharedFd >= 0) {
close(mSharedFd);
mSharedFd = -1;
}
free(mDataSourcePath);
mDataSourcePath = NULL;
char buf[80];
mSharedFd = dup(fd);
sprintf(buf, "sharedfd://%d:%lld:%lld", mSharedFd, offset, length);
mDataSourcePath = strdup(buf);
return OK;
}
然后是:
status_t PVPlayer::setVideoSurface(const sp<ISurface>& surface)
{
LOGV("setVideoSurface(%p)", surface.get());
mSurface = surface;
return OK;
}
然后是prepare,这个函数如果你在setsource里面没有做什么事情的话,这个里面就开始忙了
status_t PVPlayer::prepare()
{
status_t ret;
// We need to differentiate the two valid use cases for prepare():
// 1. new PVPlayer/reset()->setDataSource()->prepare()
// 2. new PVPlayer/reset()->setDataSource()->prepare()/prepareAsync()
// ->start()->...->stop()->prepare()
// If data source has already been set previously, no need to run
// a sequence of commands and only the PLAYER_PREPARE command needs
// to be run.
if (!mIsDataSourceSet) {//首先看我们的源设置了没有,需不需要重新设置
// set data source
LOGV("prepare");
LOGV(" data source = %s", mDataSourcePath);
ret = mPlayerDriver->enqueueCommand(new PlayerSetDataSource(mDataSourcePath,0,0));//如果需要,首先发送设置源的命令
if (ret != OK)
return ret;
// init然后是初始化的命令
LOGV(" init");
ret = mPlayerDriver->enqueueCommand(new PlayerInit(0,0));
if (ret != OK)
return ret;
// set video surface, if there is one然后设置显示面
if (mSurface != NULL) {
LOGV(" set video surface");
ret = mPlayerDriver->enqueueCommand(new PlayerSetVideoSurface(mSurface,0,0));
if (ret != OK)
return ret;
}
// set audio output然后设置音频
// If we ever need to expose selectable audio output setup, this can be broken
// out. In the meantime, however, system audio routing APIs should suffice.
LOGV(" set audio sink");
ret = mPlayerDriver->enqueueCommand(new PlayerSetAudioSink(mAudioSink,0,0));
if (ret != OK)
return ret;
// New data source has been set successfully.
mIsDataSourceSet = true;
}
// prepare 一些列的搞好了之后,才发送准备命令
LOGV(" prepare");
return mPlayerDriver->enqueueCommand(new PlayerPrepare(0,0));
}
如果是异步的话 就涉及到回调
status_t PVPlayer::prepareAsync()
{
LOGV("prepareAsync");
status_t ret = OK;
if (!mIsDataSourceSet) { // If data source has NOT been set.
// Set our data source as cached in setDataSource() above.
LOGV(" data source = %s", mDataSourcePath);
ret = mPlayerDriver->enqueueCommand(new PlayerSetDataSource(mDataSourcePath,run_init,this));
//这里设置了一个回调函数run_init,表明我们的setdatasource完成后会干哈,
mIsDataSourceSet = true;
} else { // If data source has been already set.
// No need to run a sequence of commands.
// The only command needed to run is PLAYER_PREPARE.
ret = mPlayerDriver->enqueueCommand(new PlayerPrepare(do_nothing, NULL));
}
return ret;
}
初始化的函数
void PVPlayer::run_init(status_t s, void *cookie, bool cancelled)
{
LOGV("run_init s=%d, cancelled=%d", s, cancelled);
if (s == NO_ERROR && !cancelled) {
PVPlayer *p = (PVPlayer*)cookie;
p->mPlayerDriver->enqueueCommand(new PlayerInit(run_set_video_surface, cookie));
}//这里发现初始化完成之后还有下步run_set_video_surface
}
void PVPlayer::run_set_video_surface(status_t s, void *cookie, bool cancelled)
{
LOGV("run_set_video_surface s=%d, cancelled=%d", s, cancelled);
if (s == NO_ERROR && !cancelled) {
// If we don't have a video surface, just skip to the next step.
PVPlayer *p = (PVPlayer*)cookie;
if (p->mSurface == NULL) {
run_set_audio_output(s, cookie, false);
} else {
p->mPlayerDriver->enqueueCommand(new PlayerSetVideoSurface(p->mSurface, run_set_audio_output, cookie));
//设置视频之后还要run_set_audio_output
}
}
}
void PVPlayer::run_set_audio_output(status_t s, void *cookie, bool cancelled)
{
LOGV("run_set_audio_output s=%d, cancelled=%d", s, cancelled);
if (s == NO_ERROR && !cancelled) {
PVPlayer *p = (PVPlayer*)cookie;
p->mPlayerDriver->enqueueCommand(new PlayerSetAudioSink(p->mAudioSink, run_prepare, cookie));
}
}
void PVPlayer::run_prepare(status_t s, void *cookie, bool cancelled)
{
LOGV("run_prepare s=%d, cancelled=%d", s, cancelled);
if (s == NO_ERROR && !cancelled) {
PVPlayer *p = (PVPlayer*)cookie;
p->mPlayerDriver->enqueueCommand(new PlayerPrepare(do_nothing,0));
}
}