0. 前言
在前面的文章《Android音视频学习(一):MediaPlayer》中,我们对Android系统提供的音视频组件MediaPlayer的应用层接口,主要状态转换和使用流程有了一个大概的了解。今天我们将更进一步,深入Android源码讨论MediaPlayer的主体架构。本文主要介绍MediaPlayer从App到其底层NuPlayer实现的创建过程。
1. MediaPlayer的代码结构
首先介绍一下MediaPlayer
的代码结构。在实现上MediaPlayer
也是类似C/S架构形式,App作为使用者,即客户端,主要使用java的MediaPlayer
接口来控制播放功能,经过JNI,其底层的C++对象通过Binder与MediaServer
进行通信。
而系统的MediaServer
进程则是服务端,其中运行的MediaPlayerService
对象是系统层面的MediaPlayer
的总管对象,系统内当前所有的MediaPlayer
会由其来管理。当收到客户端的Binder请求时,MediaPlayerService
会创建具体的MediaPlayerService::Client
,为当前的App进行服务。在层次图上这里结构有一些代码上的封装,但MediaPlayerService::Client
可以看作具体持有和使用播放器的核心NuPlayer
。
MediaPlayer
的java层API在安卓源码目录的frameworks/base/media/java/android/media/MediaPlayer.java下。
MediaPlayerService
的代码在frameworks/av/media/libmediaplayerservice/MediaPlayerService.h NuPlayer
的代码则在frameworks/av/media/libmediaplayerservice/nuplayer/
2. 创建流程源码探究
2.1 构造方法
首先从MediaPlayer的构造方法看起。
2.1.1 主要流程
MediaPlayer的构造方法主要流程如下,主要是初始化,创建native层的C++对象MediaPlayer(继承自MediaPlayerClient, IMediaPlayerClient Binder)为其设置一个listener,用于java层对象收到回调。
2.1.2 源码走读
java
// MediaPlayer最主要的构造方法
// Context是App的上下文对象
// sessionId是AudioSessionID, 用于音频系统,需要创建一个会话id,如果设置为AudioSystem.AUDIO_SESSION_ALLOCATE (0), 则会由底层自动生成一个唯一id
private MediaPlayer(Context context, int sessionId) {
super(new AudioAttributes.Builder().build(),
AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);
// 这里是创建EventHandler,并将自身引用传入,用于响应时间
// looper是事件系统的事件循环,代表着事件监听者的线程。
Looper looper;
if ((looper = Looper.myLooper()) != null) {
// 如果当前线程有事件循环,则EventHandler交给本线程监听
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
// 否则交由主线程的事件循环监听。
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
// 初始化一个计时器,可以为MediaPlayer提供当前时间,设置定时任务等行管操作
mTimeProvider = new TimeProvider(this);
// 字幕流输入,不是很重要
mOpenSubtitleSources = new Vector<InputStream>();
// AttributionSource用于追踪应用的权限,经常被用于不同应用之间确认权限信息,这里主要是用于native层确认权限信息。
AttributionSource attributionSource =
context == null ? AttributionSource.myAttributionSource()
: context.getAttributionSource();
// set the package name to empty if it was null
if (attributionSource.getPackageName() == null) {
attributionSource = attributionSource.withPackageName("");
}
// 这里其实就是将attributionSource内容,序列化为Parcel结构,用于Binder通信
try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) {
// 主流程!调用native_setup完成native层的创建工作
native_setup(new WeakReference<>(this), attributionSourceState.getParcel(),
resolvePlaybackSessionId(context, sessionId));
}
// 调用基类的方法,实例化完成后向AudioService设置一下audioSessionid和当前player的信息
baseRegisterPlayer(getAudioSessionId());
}
native_setup()
是一个native方法,它会通过JNI调用到frameworks/base/media/jni/android_media_MediaPlayer.cpp下的android_media_MediaPlayer_native_setup()
方法。
C++
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jobject jAttributionSource,
jint jAudioSessionId)
{
ALOGV("native_setup");
// 获取Parcel,反序列化并通过其构造C++层的attributionSource对象
Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
android::content::AttributionSourceState attributionSource;
attributionSource.readFromParcel(parcel);
// 创建C++层的MediaPlayer
sp<MediaPlayer> mp = sp<MediaPlayer>::make(
attributionSource, static_cast<audio_session_t>(jAudioSessionId));
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
// 创建一个JNI层的listener,用于触发MediaPlayer的回调。
// 这里JNIMediaPlayerListener会在env里创建MediaPlayer的全局jclass引用,以及weak_this的全局引用,方便从其他线程触发回调。
// listener会交给C++的mp保留,事件触发时回调给jni层。
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
mp->setListener(listener);
// 将C++的mp对象设置给java对象,会将原始指针保存在java对象的mNativeContext成员中
setMediaPlayer(env, thiz, mp);
}
让我们来看看C++层的MediaPlayer在创建时做了什么,这里就进入了frameworks/av/media/libmedia/mediaplayer.cpp的MediaPlayer::MediaPlayer()
方法。
C++
// 构造方法
MediaPlayer::MediaPlayer(const AttributionSourceState& attributionSource,
const audio_session_t sessionId) : mAttributionSource(attributionSource)
{
// 以下都可以看作是状态的初始化,一开始没办法搞明白每个成员的意义
// 但是不要紧,我们在具体功能中才能去理解,尽量先从大意上有个认识就行
ALOGV("constructor");
mListener = NULL; // JNI监听者
mCookie = NULL;
mStreamType = AUDIO_STREAM_MUSIC;
mAudioAttributesParcel = NULL;
mCurrentPosition = -1;
mCurrentSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;
mSeekPosition = -1;
mSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;
mCurrentState = MEDIA_PLAYER_IDLE; // MediaPlayer的状态,构造后进入idle
mPrepareSync = false;
mPrepareStatus = NO_ERROR;
mLoop = false;
mLeftVolume = mRightVolume = 1.0;
mVideoWidth = mVideoHeight = 0;
mLockThreadId = 0;
// 如果audioSeesionid 传入的是AUDIO_SESSION_ALLOCATE (0), 那么底层具体的id则会自行生成一个唯一id
if (sessionId == AUDIO_SESSION_ALLOCATE) {
mAudioSessionId = static_cast<audio_session_t>(
AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION));
} else {
mAudioSessionId = sessionId;
}
AudioSystem::acquireAudioSessionId(mAudioSessionId, (pid_t)-1, (uid_t)-1); // always in client.
mSendLevel = 0;
mRetransmitEndpointValid = false;
}
// JNI层在构造之后调用的setListener(), 只是对象赋值而已。
status_t MediaPlayer::setListener(const sp<MediaPlayerListener>& listener)
{
ALOGV("setListener");
Mutex::Autolock _l(mLock);
mListener = listener;
return NO_ERROR;
}
2.2 setDataSource()
setDataSource()
方法为MediaPlayer设置需要播放的输入媒体源,在实现上它还包含了创建底层播放器核心NuPlayer的任务。
2.2.1 主要流程
App调用setDataSource()
的主要任务是为了给播放器设置输入参数,如远程文件url、本地文件路径信息等。
而在setDataSource()
这个方法内具体还会通过MediaPlayerService
创建底层播放器实现NuPlayer
。主要的流程如下图所示:
2.2.2 源码走读
2.2.2.1 从Java层到JNI
setDataSource()
方法有很多种参数输入的版本,我们从最简单的版本看起:
java
// 输入是String的版本,也可以是本地文件,也可以是远程url
public void setDataSource(String path)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(path, null, null);
}
继而会调用三参数版本的同名方法。
java
// 注意这里输入的headers和cookies都为null
private void setDataSource(String path, Map<String, String> headers, List<HttpCookie> cookies)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException
{
String[] keys = null;
String[] values = null;
if (headers != null) {
keys = new String[headers.size()];
values = new String[headers.size()];
int i = 0;
for (Map.Entry<String, String> entry: headers.entrySet()) {
keys[i] = entry.getKey();
values[i] = entry.getValue();
++i;
}
}
// 所以这里相当于调用setDataSource(path, null, null, null);
setDataSource(path, keys, values, cookies);
}
然后会调用4参数的版本:
java
private void setDataSource(String path, String[] keys, String[] values,
List<HttpCookie> cookies)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
// 解析url和对应的协议
final Uri uri = Uri.parse(path);
final String scheme = uri.getScheme();
if ("file".equals(scheme)) {
path = uri.getPath();
} else if (scheme != null) {
// 如果是非本地文件资源,会走这里
nativeSetDataSource(
MediaHTTPService.createHttpServiceBinderIfNecessary(path, cookies),
path,
keys,
values);
return;
}
// 如果是本地文件,打开走文件描述符fd版本的输入
final File file = new File(path);
try (FileInputStream is = new FileInputStream(file)) {
setDataSource(is.getFD());
}
}
我们先看看输入是文件的版本:
java
// 直接设置offest为0,length为MAX,调参数为(fd, offset, length)的版本
public void setDataSource(FileDescriptor fd)
throws IOException, IllegalArgumentException, IllegalStateException {
// intentionally less than LONG_MAX
setDataSource(fd, 0, 0x7ffffffffffffffL);
}
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException {
try (ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd)) {
if (modernFd == null) {
_setDataSource(fd, offset, length);
} else {
_setDataSource(modernFd.getFileDescriptor(), offset, length);
}
} catch (IOException e) {
Log.w(TAG, "Ignoring IO error while setting data source", e);
}
}
之后调用_setDataSource()
,也是一个native方法:
java
private native void _setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException;
对应的JNI层的方法:
C++
static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
// 从java对象的mNativeContext成员中拿到C++对象mediaplayer的指针
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;
}
// 从java对象中获取文件描述符fd
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
ALOGV("setDataSourceFD: fd %d", fd);
// 注意,这里套了两层
// 首先是调用C++层的方法,mp->setDataSource()
// 然后根据其返回结果,调用process_media_player_call()对异常进行处理。
process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}
2.2.2.2 通过MediaPlayerService来创建MediaPlayerService::Client
然后来看C++层对象的setDataSource()
:
C++
status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
status_t err = UNKNOWN_ERROR;
// 获得mediaPlayerService(的Binder代理)对象
const sp<IMediaPlayerService> service(getMediaPlayerService());
if (service != 0) {
// 调用service的create方法,创建IMediaPlayer Binder对象(实体在MediaPlayerService进程)
sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mAttributionSource));
// doSetRetransmitEndpoint是有调用setRetransmitEndpoint()后才会生效,功能是"转发XX至对应IP?"(先忽略,不影响主流程,一般也不会生效)
// 具体调用IMediaPlayer的setDataSource方法来完成功能
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(fd, offset, length))) {
player.clear();
}
// 保留新创建的IMediaPlayer引用,会存放在mPlayer字段中
err = attachNewPlayer(player);
}
return err;
}
看一下MediaPlayerService
的create
方法:
C++
sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
audio_session_t audioSessionId, const AttributionSourceState& attributionSource)
{
// -- 创建一个单调递增的connect id
int32_t connId = android_atomic_inc(&mNextConnId);
// -- 创建AttributionSourceState
AttributionSourceState verifiedAttributionSource = attributionSource;
verifiedAttributionSource.pid = VALUE_OR_FATAL(
legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid()));
verifiedAttributionSource.uid = VALUE_OR_FATAL(
legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid()));
// 创建MediaPlayer(实体)
sp<Client> c = new Client(
this, verifiedAttributionSource, connId, client, audioSessionId);
ALOGV("Create new client(%d) from %s, ", connId,
verifiedAttributionSource.toString().c_str());
// 弱引用加入到mClients列表中
wp<Client> w = c;
{
Mutex::Autolock lock(mLock);
mClients.add(w);
}
return c;
}
2.2.2.3 创建具体的NuPlayer
在MediaPlayerService::create()
创建一个MediaPlayerService::Client
后,所调用MediaPlayerService::Client
的setDataSource
方法的具体实现:
c++
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
{
ALOGV("setDataSource fd=%d (%s), offset=%lld, length=%lld",
fd, nameForFd(fd).c_str(), (long long) offset, (long long) length);
// 拿到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 = %llu", static_cast<unsigned long long>(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 = %llu", static_cast<unsigned long long>(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 = %lld", (long long)length);
}
// 根据输入判断Player实现的具体type,这里有Stagefright和NuPlayer,在Androd6.0之后主要使用Nuplayer
// 在最新的Android代码中,这里player_type虽然有STAGEFRIGHT_PLAYER,但实际上的工厂类Factory等,都不再包含Stagefright的代码,所以这里默认创建的是NuPlayer
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
return mStatus = setDataSource_post(p, p->setDataSource(fd, offset, length));
}
具体看看setDataSource_pre()
,里面会创建NuPlayer
示例:
c++
sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
player_type playerType)
{
ALOGV("player type = %d", playerType);
// 这里会创建NuPlayer
sp<MediaPlayerBase> p = createPlayer(playerType);
if (p == NULL) {
return p;
}
// 创建几个Service的状态监听,在进程挂掉后会触发回调
std::vector<DeathNotifier> deathNotifiers;
// Listen to death of media.extractor service
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.extractor"));
if (binder == NULL) {
ALOGE("extractor service not available");
return NULL;
}
deathNotifiers.emplace_back(
binder, [l = wp<MediaPlayerBase>(p)]() {
sp<MediaPlayerBase> listener = l.promote();
if (listener) {
ALOGI("media.extractor died. Sending death notification.");
listener->sendEvent(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED,
MEDIAEXTRACTOR_PROCESS_DEATH);
} else {
ALOGW("media.extractor died without a death handler.");
}
});
{
using ::android::hidl::base::V1_0::IBase;
// Listen to death of OMX service
{
sp<IBase> base = ::android::hardware::media::omx::V1_0::
IOmx::getService();
if (base == nullptr) {
ALOGD("OMX service is not available");
} else {
deathNotifiers.emplace_back(
base, [l = wp<MediaPlayerBase>(p)]() {
sp<MediaPlayerBase> listener = l.promote();
if (listener) {
ALOGI("OMX service died. "
"Sending death notification.");
listener->sendEvent(
MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED,
MEDIACODEC_PROCESS_DEATH);
} else {
ALOGW("OMX service died without a death handler.");
}
});
}
}
// Listen to death of Codec2 services
{
for (std::shared_ptr<Codec2Client> const& client :
Codec2Client::CreateFromAllServices()) {
sp<IBase> hidlBase = client->getHidlBase();
::ndk::SpAIBinder aidlBase = client->getAidlBase();
auto onBinderDied = [l = wp<MediaPlayerBase>(p),
name = std::string(client->getServiceName())]() {
sp<MediaPlayerBase> listener = l.promote();
if (listener) {
ALOGI("Codec2 service \"%s\" died. "
"Sending death notification.",
name.c_str());
listener->sendEvent(
MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED,
MEDIACODEC_PROCESS_DEATH);
} else {
ALOGW("Codec2 service \"%s\" died "
"without a death handler.",
name.c_str());
}
};
if (hidlBase) {
deathNotifiers.emplace_back(hidlBase, onBinderDied);
} else if (aidlBase.get() != nullptr) {
deathNotifiers.emplace_back(aidlBase, onBinderDied);
}
}
}
}
Mutex::Autolock lock(mLock);
mDeathNotifiers.clear();
mDeathNotifiers.swap(deathNotifiers);
mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p);
if (!p->hardwareOutput()) {
mAudioOutput = new AudioOutput(mAudioSessionId, mAttributionSource,
mAudioAttributes, mAudioDeviceUpdatedListener);
static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
}
return p;
}
在createPlayer()
方法里面会调用MediaPlayerFactory
的静态方法来创建具体的播放器NuPlayer
。
C++
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
// 这里getPlayer就是返回的mPlayer,第一次创建时为nullptr
sp<MediaPlayerBase> p = getPlayer();
if ((p != NULL) && (p->playerType() != playerType)) {
ALOGV("delete player");
p.clear();
}
if (p == NULL) {
// 通过MediaPlayerFactory来创建
p = MediaPlayerFactory::createPlayer(playerType, mListener,
VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mAttributionSource.pid)));
}
if (p != NULL) {
p->setUID(VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mAttributionSource.uid)));
}
return p;
}
2.2.2.4 MediaPlayerFactory创建NuPlayer的具体过程
继续上文,具体是调用MediaPlayerFactory::createPlayer()
C++
sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
player_type playerType,
const sp<MediaPlayerBase::Listener> &listener,
pid_t pid) {
sp<MediaPlayerBase> p;
IFactory* factory;
status_t init_result;
Mutex::Autolock lock_(&sLock);
// 根据sFactoryMap中注册的type,这里选到目前只有NuPlayerFactory
// sFactoryMap的注册逻辑可以参考frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.cpp中的内容
if (sFactoryMap.indexOfKey(playerType) < 0) {
ALOGE("Failed to create player object of type %d, no registered"
" factory", playerType);
return p;
}
// 一定是NuPlayerFactory
factory = sFactoryMap.valueFor(playerType);
CHECK(NULL != factory);
// 调用对应的创建方法
p = factory->createPlayer(pid);
if (p == NULL) {
ALOGE("Failed to create player object of type %d, create failed",
playerType);
return p;
}
init_result = p->initCheck();
if (init_result == NO_ERROR) {
p->setNotifyCallback(listener);
} else {
ALOGE("Failed to create player object of type %d, initCheck failed"
" (res = %d)", playerType, init_result);
p.clear();
}
return p;
}
而NuPlayer::createPlayer()
里面则是创建了一个NuPlayerDriver
C++
class NuPlayerFactory : public MediaPlayerFactory::IFactory {
public:
// ...
virtual sp<MediaPlayerBase> createPlayer(pid_t pid) {
ALOGV(" create NuPlayer");
return new NuPlayerDriver(pid);
}
};
2.2.2.5 NuPlayerDriver及NuPlayer的构造过程
这个NuPlayerDriver
继承自MediaPlayerInterface
,它其中包含一个成员mPlayer
,具体指向NuPlayer
,在构造函数中会一起创建。
C++
// struct NuPlayerDriver : public MediaPlayerInterface
NuPlayerDriver::NuPlayerDriver(pid_t pid)
: mState(STATE_IDLE),
mIsAsyncPrepare(false),
mAsyncResult(UNKNOWN_ERROR),
mSetSurfaceInProgress(false),
mDurationUs(-1),
mPositionUs(-1),
mSeekInProgress(false),
mPlayingTimeUs(0),
mRebufferingTimeUs(0),
mRebufferingEvents(0),
mRebufferingAtExit(false),
mLooper(new ALooper), // 会创建一个ALooper, 事件循环
mMediaClock(new MediaClock),
mPlayer(new NuPlayer(pid, mMediaClock)), // 创建新的mPlayer
mPlayerFlags(0),
mCachedPlayerIId(PLAYER_PIID_INVALID),
mMetricsItem(NULL),
mClientUid(-1),
mAtEOS(false),
mLooping(false),
mAutoLoop(false) {
ALOGD("NuPlayerDriver(%p) created, clientPid(%d)", this, pid);
mLooper->setName("NuPlayerDriver Looper");
// 初始化时钟, 这里时钟也是基于单独运行在一个新线程上的新的ALooper,在init()中创建
mMediaClock->init();
// set up an analytics record
mMetricsItem = mediametrics::Item::create(kKeyPlayer);
// 启动事件循环
mLooper->start(
false, /* runOnCallingThread */
true, /* canCallJava */
PRIORITY_AUDIO);
// 事件处理者,mPlayer
mLooper->registerHandler(mPlayer);
// NuPlayer初始化
mPlayer->init(this);
}
时钟在创建时也会创建一个单独的ALooper
:
C++
MediaClock::MediaClock()
: mAnchorTimeMediaUs(-1),
mAnchorTimeRealUs(-1),
mMaxTimeMediaUs(INT64_MAX),
mStartingTimeMediaUs(-1),
mPlaybackRate(1.0),
mGeneration(0) {
mLooper = new ALooper;
mLooper->setName("MediaClock");
mLooper->start(false /* runOnCallingThread */,
false /* canCallJava */,
ANDROID_PRIORITY_AUDIO);
}
void MediaClock::init() {
mLooper->registerHandler(this);
}
2.2.2.6 MediaPlayerService::Client::setDataSource的后续调用
在2.2.2.3节最后,通过setDataSource_pre()
,MediaPlayerService::Client
创建了具体的NuPlayerDriver
实例,之后会调用其setDataSource()
方法,里面会调用NuPlayer
的setDataSourceAsync()
方法。
C++
status_t NuPlayerDriver::setDataSource(
const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers) {
ALOGV("setDataSource(%p) url(%s)", this, uriDebugString(url, false).c_str());
ATRACE_BEGIN(StringPrintf("setDataSource(%p)", this).c_str());
Mutex::Autolock autoLock(mLock);
if (mState != STATE_IDLE) {
ATRACE_END();
return INVALID_OPERATION;
}
mState = STATE_SET_DATASOURCE_PENDING;
// 调用NuPlayer的setDataSourceAsync()方法
mPlayer->setDataSourceAsync(httpService, url, headers);
// 同步等待上面的方法完成
while (mState == STATE_SET_DATASOURCE_PENDING) {
mCondition.wait(mLock);
}
ATRACE_END();
return mAsyncResult;
}
NuPlayer
的SetDataSource
具体做了什么,我们在后续文章中进行解析。
之后MediaPlayerService::Client
会调用setDataSource_post()
方法。
C++
status_t MediaPlayerService::Client::setDataSource_post(
const sp<MediaPlayerBase>& p,
status_t status)
{
ALOGV(" setDataSource");
if (status != OK) {
ALOGE(" error: %d", status);
return status;
}
// 设置转发端口?不是很重要
if (mRetransmitEndpointValid) {
status = p->setRetransmitEndpoint(&mRetransmitEndpoint);
if (status != NO_ERROR) {
ALOGE("setRetransmitEndpoint error: %d", status);
}
}
if (status == OK) {
Mutex::Autolock lock(mLock);
// 将NuPlayerDriver保留引用
mPlayer = p;
}
return status;
}
3. 后记(碎碎念)
说实话,这篇博文鸽了两个多月,除了之前工作上的任务繁忙了一点,其他原因还是得反思一下,就是步子迈大了,把控不住就容易犯懒,降低动力。
我原本预期是想用一篇文章来介绍和讲解MediaPlayer
的主要结构和接口,但是当我这么处理时,我现在回头来想,其实中间过程很难推动。有以下原因:
- 标题开太大,但是自身储备不足,整体结构难以把控。在这种情况下做一个整体的输出,其实耗费心力会很大,慢慢会降低动力。保持动力还是得快速输出。
- 涉及内容太多的话,如果代码走读讲解,文章会很臃肿,容易偏离主题。
- 有些内容单独讲解效果会更好,比如包括
setDisplay()
等方法,涉及到部分Android系统的渲染框架,那么补充一些渲染方面组件的知识,单独再写一篇内容,那样组织起来会更好。
所以写文章也好,干活也好,今后出现这种推进效率很低的情况的时候,我得及时思考一下,是否我当前的任务目标拆解的不够细致。出现这种情况时,我得将目标更进一步拆分,一点一点攻克。