上一篇MediaPlayer本地播放流程解析(一)讲了MediaPlayer的setDataSource流程,本篇将接着讲MediaPlayer的prepare流程。
Prepare前面的流程一直到AwesomePlayer,和setDataSource都基本上一样,这里直接略掉。下面将从AwesomePlayer开始。
status_t AwesomePlayer::prepare() {
ATRACE_CALL();
Mutex::Autolock autoLock(mLock);
return prepare_l();
}
status_t AwesomePlayer::prepare_l() {
if (mFlags & PREPARED) {
return OK;
}
if (mFlags & PREPARING) {
return UNKNOWN_ERROR;
}
mIsAsyncPrepare = false;
status_t err = prepareAsync_l();
if (err != OK) {
return err;
}
while (mFlags & PREPARING) {
mPreparedCondition.wait(mLock);
}
return mPrepareResult;
}
status_t AwesomePlayer::prepareAsync_l() {
if (mFlags & PREPARING) {
return UNKNOWN_ERROR; // async prepare already pending
}
if (!mQueueStarted) {
mQueue.start();
mQueueStarted = true;
}
modifyFlags(PREPARING, SET);
mAsyncPrepareEvent = new AwesomeEvent(
this, &AwesomePlayer::onPrepareAsyncEvent);
mQueue.postEvent(mAsyncPrepareEvent);
return OK;
}
这里涉及到一个时间事件队列模型,下一篇来详细分析之,这里我们只要知道mQueue.postEvent(mAsyncPrepareEvent)后,onPrepareAsyncEvent方法将会被执行。接着看onPrepareAsyncEvent方法的实现。
void AwesomePlayer::onPrepareAsyncEvent() {
Mutex::Autolock autoLock(mLock);
beginPrepareAsync_l();
}
void AwesomePlayer::beginPrepareAsync_l() {
if (mFlags & PREPARE_CANCELLED) {
ALOGI("prepare was cancelled before doing anything");
abortPrepare(UNKNOWN_ERROR);
return;
}
if (mUri.size() > 0) {
status_t err = finishSetDataSource_l();
if (err != OK) {
abortPrepare(err);
return;
}
}
if (mVideoTrack != NULL && mVideoSource == NULL) {
status_t err = initVideoDecoder();
if (err != OK) {
abortPrepare(err);
return;
}
}
if (mAudioTrack != NULL && mAudioSource == NULL) {
status_t err = initAudioDecoder();
if (err != OK) {
abortPrepare(err);
return;
}
}
modifyFlags(PREPARING_CONNECTED, SET);
if (isStreamingHTTP()) {
postBufferingEvent_l();
} else {
finishAsyncPrepare_l();
}
}
initVideoDecoder和initAudioDecoder实现差不多,这里只讲initVideoDecoder的实现。
status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {
……
mVideoSource = OMXCodec::Create(
mClient.interface(), mVideoTrack->getFormat(),
false, // createEncoder
mVideoTrack,
NULL, flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL);
if (mVideoSource != NULL) {
int64_t durationUs;
if (mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
Mutex::Autolock autoLock(mMiscStateLock);
if (mDurationUs < 0 || durationUs > mDurationUs) {
mDurationUs = durationUs;
}
}
status_t err = mVideoSource->start();
if (err != OK) {
ALOGE("failed to start video source");
mVideoSource.clear();
return err;
}
}
……
return mVideoSource != NULL ? OK : UNKNOWN_ERROR;
}
AwesomePlayer通过OMXCodec与openMax交互。硬件厂家提供.so库,实现openMax的接口,即可被AwesomePlayer调用,实现硬解码,加速性能。
OMXCodec::Create 的第一个参数mClient.interface()返回什么呢?
1、 mClient在AwesomePlayer构造时初始化:mClient.connect()。
2、 mClient.interface()在OMXClient.h中定义,返回mOMX,一个BpOMX类型的对象。
初始化connect看下面的代码:status_t OMXClient::connect() {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.player"));
sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
CHECK(service.get() != NULL);
mOMX = service->getOMX();
CHECK(mOMX.get() != NULL);
if (!mOMX->livesLocally(NULL /* node */, getpid())) {
ALOGI("Using client-side OMX mux.");
mOMX = new MuxOMX(mOMX);
}
return OK;
}
virtual sp<IOMX> getOMX() {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
remote()->transact(GET_OMX, data, &reply);
return interface_cast<IOMX>(reply.readStrongBinder());
}
sp<IOMX> MediaPlayerService::getOMX() {
Mutex::Autolock autoLock(mLock);
if (mOMX.get() == NULL) {
mOMX = new OMX;
}
return mOMX;
}
继续看OMXCodec::Create的实现,在Create中将打开AwesomePlayer和openMax交互的通路。
sp<MediaSource> OMXCodec::Create(
const sp<IOMX> &omx,
const sp<MetaData> &meta, bool createEncoder,
const sp<MediaSource> &source,
const char *matchComponentName,
uint32_t flags,
const sp<ANativeWindow> &nativeWindow) {
……
Vector<CodecNameAndQuirks> matchingCodecs;
findMatchingCodecs(
mime, createEncoder, matchComponentName, flags, &matchingCodecs);
sp<OMXCodecObserver> observer = new OMXCodecObserver;
IOMX::node_id node = 0;
for (size_t i = 0; i < matchingCodecs.size(); ++i) {
const char *componentNameBase = matchingCodecs[i].mName.string();
uint32_t quirks = matchingCodecs[i].mQuirks;
const char *componentName = componentNameBase;
……
status_t err = omx->allocateNode(componentName, observer, &node);
if (err == OK) {
sp<OMXCodec> codec = new OMXCodec(
omx, node, quirks, flags,
createEncoder, mime, componentName,
source, nativeWindow);
observer->setCodec(codec);
err = codec->configureCodec(meta);
if (err == OK) {
if (!strcmp("OMX.Nvidia.mpeg2v.decode", componentName)) {
codec->mFlags |= kOnlySubmitOneInputBufferAtOneTime;
}
return codec;
}
}
}
return NULL;
}
findMatchingCodecs找到系统提供的所有能对当前mimeType解码的codec,并按照软解码优先的顺序排序。Codec通过"/etc/media_codecs.xml"由芯片厂商提供,在full build后自动生成,findMatchingCodecs执行完后,会得到一些codecs的名称等信息,保存在matchingCodecs里面。
allocateNode会根据componentName创建解码库实例,并将observer、node_id等和它建立联系,方便AwesomePlayer根据node_id找到解码器,解码器通过observer的回调函数通知AwesomePlayer。status_t OMX::allocateNode(
const char *name, const sp<IOMXObserver> &observer, node_id *node) {
Mutex::Autolock autoLock(mLock);
*node = 0;
// 创建instance
OMXNodeInstance *instance = new OMXNodeInstance(this, observer);
// 根据name创建解码库实例,并通过参数handle返回component,component结构保存了一些函数指针,实现外部程序对解码库的调用。mMaster在OMX的构造函数中初始化 // ,并加载了外部芯片厂商提供的解码库和google提供的软解码库。
OMX_COMPONENTTYPE *handle;
OMX_ERRORTYPE err = mMaster->makeComponentInstance(
name, &OMXNodeInstance::kCallbacks,
instance, &handle);
……
// 后面的这些就都是建立通路和联系了
*node = makeNodeID(instance);
mDispatchers.add(*node, new CallbackDispatcher(instance));
instance->setHandle(*node, handle);
mLiveNodes.add(observer->asBinder(), instance);
observer->asBinder()->linkToDeath(this);
return OK;
}
在OMX的构造函数中初始化OMXMaster
OMX::OMX()
: mMaster(new OMXMaster),
mNodeCounter(0) {
}
在OMXMaster的构造函数中加载解码库
OMXMaster::OMXMaster()
: mVendorLibHandle(NULL) {
// 芯片厂商提供的解码库
addVendorPlugin();
// 软解码库
addPlugin(new SoftOMXPlugin);
}
下面以softPlugin为例来看makeComponentInstance
OMX_ERRORTYPE OMXMaster::makeComponentInstance(
const char *name,
const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData,
OMX_COMPONENTTYPE **component) {
Mutex::Autolock autoLock(mLock);
*component = NULL;
ssize_t index = mPluginByComponentName.indexOfKey(String8(name));
if (index < 0) {
return OMX_ErrorInvalidComponentName;
}
// 最终会调用解码库实现的makeComponentInstance
OMXPluginBase *plugin = mPluginByComponentName.valueAt(index);
OMX_ERRORTYPE err =
plugin->makeComponentInstance(name, callbacks, appData, component);
if (err != OMX_ErrorNone) {
return err;
}
mPluginByInstance.add(*component, plugin);
return err;
}
OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance(
const char *name,
const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData,
OMX_COMPONENTTYPE **component) {
ALOGV("makeComponentInstance '%s'", name);
for (size_t i = 0; i < kNumComponents; ++i) {
// 根据name找到具体的解码库
if (strcmp(name, kComponents[i].mName)) {
continue;
}
AString libName = "libstagefright_soft_";
libName.append(kComponents[i].mLibNameSuffix);
libName.append(".so");
void *libHandle = dlopen(libName.c_str(), RTLD_NOW);
if (libHandle == NULL) {
ALOGE("unable to dlopen %s", libName.c_str());
return OMX_ErrorComponentNotFound;
}
typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)(
const char *, const OMX_CALLBACKTYPE *,
OMX_PTR, OMX_COMPONENTTYPE **);
// 找到库文件中创建解码库的方法
CreateSoftOMXComponentFunc createSoftOMXComponent =
(CreateSoftOMXComponentFunc)dlsym(
libHandle,
"_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE"
"PvPP17OMX_COMPONENTTYPE");
if (createSoftOMXComponent == NULL) {
dlclose(libHandle);
libHandle = NULL;
return OMX_ErrorComponentNotFound;
}
// 执行该方法,并创建解码库
sp<SoftOMXComponent> codec =
(*createSoftOMXComponent)(name, callbacks, appData, component);
if (codec == NULL) {
dlclose(libHandle);
libHandle = NULL;
return OMX_ErrorInsufficientResources;
}
OMX_ERRORTYPE err = codec->initCheck();
if (err != OMX_ErrorNone) {
dlclose(libHandle);
libHandle = NULL;
return err;
}
codec->incStrong(this);
codec->setLibHandle(libHandle);
return OMX_ErrorNone;
}
return OMX_ErrorInvalidComponentName;
}
//MP3类型的解码器创建方法
android::SoftOMXComponent *createSoftOMXComponent(
const char *name, const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData, OMX_COMPONENTTYPE **component) {
return new android::SoftMP3(name, callbacks, appData, component);
}
create的最后一步configureCodec(meta),主要是设置下输出的宽高和initNativeWindow。
最后来讲status_t err = mVideoSource->start();status_t OMXCodec::start(MetaData *meta) {
……
// Decoder case
if ((err = mSource->start(params.get())) != OK) {
CODEC_LOGE("source failed to start: %d", err);
return err;
}
return init();
}
mSource为mVideoTrack,这里不多讲,直接看init()
status_t OMXCodec::init() {
// mLock is held.
CHECK_EQ((int)mState, (int)LOADED);
status_t err;
if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) {
err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
CHECK_EQ(err, (status_t)OK);
setState(LOADED_TO_IDLE);
}
err = allocateBuffers();
if (err != (status_t)OK) {
return err;
}
if (mQuirks & kRequiresLoadedToIdleAfterAllocation) {
err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
CHECK_EQ(err, (status_t)OK);
setState(LOADED_TO_IDLE);
}
while (mState != EXECUTING && mState != ERROR) {
mAsyncCompletion.wait(mLock);
}
return mState == ERROR ? UNKNOWN_ERROR : OK;
}
主要就是allocateBuffers,然后设置LOADED_TO_IDLE状态。
status_t OMXCodec::allocateBuffers() {
status_t err = allocateBuffersOnPort(kPortIndexInput);
if (err != OK) {
return err;
}
return allocateBuffersOnPort(kPortIndexOutput);
}
到此,prepare的流程就基本分析完成了。