承接上一章节分析:【一】Android MediaPlayer整体架构源码分析 -【初始化和创建】
本系列文章分析的安卓源码版本:【Android 10.0 版本】
setDataSource方法实现流程分析
设置数据源示例:
mMediaPlayer.setDataSource(mPath);
通过java层MediaPlayer的源码分析可知,setDataSource有多个重载方法,但最终只对应有3个native方法,如下:
多个java重载方法,但目前暂时只先分析传递string URL参数的方法(数据流可为本地文件或网络传输)处理流程:【后续有时间再分析其他实现】
由注释可知,该path可传入http/rtsp URL或文件路径均可。
// [android.media.MediaPlayer.java]
/**
* Sets the data source (file-path or http/rtsp URL) to use.
*
* <p>When <code>path</code> refers to a local file, the file may actually be opened by a
* process other than the calling application. This implies that the pathname
* should be an absolute path (as any other process runs with unspecified current working
* directory), and that the pathname should reference a world-readable file.
* As an alternative, the application could first open the file for reading,
* and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
*
* @param path the path of the file, or the http/rtsp URL of the stream you want to play
* @throws IllegalStateException if it is called in an invalid state
*/
public void setDataSource(String path)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(path, null, null);
}
@UnsupportedAppUsage
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, keys, values, cookies);
}
@UnsupportedAppUsage
private void setDataSource(String path, String[] keys, String[] values,
List<HttpCookie> cookies)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
// 将其转换为URI类型
final Uri uri = Uri.parse(path);
// URL(网络)协议头
final String scheme = uri.getScheme();
if ("file".equals(scheme)) {
// 若是file文件协议则
path = uri.getPath();
} else if (scheme != null) {
// 否则协议头就是非文件流即网络流
// handle non-file sources
nativeSetDataSource(
MediaHTTPService.createHttpServiceBinderIfNecessary(path, cookies),
path,
keys,
values);
return;
}
final File file = new File(path);
try (FileInputStream is = new FileInputStream(file)) {
// 读取文件输入流并且设置文件描述符
setDataSource(is.getFD());
}
}
public void setDataSource(FileDescriptor fd)
throws IOException, IllegalArgumentException, IllegalStateException {
// 默认设置文件长度为long类型最大,从头开始读取,一般情况下不会有比这个长
// intentionally less than LONG_MAX
setDataSource(fd, 0, 0x7ffffffffffffffL);
}
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException {
// 最后调用该native层方法
_setDataSource(fd, offset, length);
}
3个native方法:
// [android.media.MediaPlayer.java]
// 有参数名可知,该方法是给网络播放源使用的
private native void nativeSetDataSource(
IBinder httpServiceBinder, String path, String[] keys, String[] values)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
// 文件播放源进行播放
private native void _setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException;
// 根据MediaDataSource类的功能声明来看,其实就是给了应用层进行控制数据读取位置来指定数据播放
private native void _setDataSource(MediaDataSource dataSource)
throws IllegalArgumentException, IllegalStateException;
这三个native方法在jni层的调用声明如下:
即它们是通过JNI的方法映射对应别名方法的调用,JNI层这种做法比较主流。
// [frameworks/base/media/jni/android_media_MediaPlayer.cpp]
static const JNINativeMethod gMethods[] = {
// 见第1小节分析
{"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
// 见第2小节分析
{
"nativeSetDataSource",
"(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
"[Ljava/lang/String;)V",
(void *)android_media_MediaPlayer_setDataSourceAndHeaders
},
// 见第3小节分析
{"_setDataSource", "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
// ... 省略其他代码
}
1、android_media_MediaPlayer_setDataSourceFD实现分析:
通过文件描述符来设置数据源
// [frameworks/base/media/jni/android_media_MediaPlayer.cpp]
static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
// 获取native层此前创建缓存的MediaPlayer对象
// 见1.1小节分析
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;
}
// jni层从文件描述符对象中获取int型文件描述符值,用于标记该访问文件
// 此处不展开分析【比如路径文件打不开则会返回-1等错误】
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
ALOGV("setDataSourceFD: fd %d", fd);
// 然后调用了native层MediaPlayer的setDataSource方法
// 见1.2小节分析
process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}
1.1、getMediaPlayer(env, thiz)实现分析:
此为static方法,注意在C++中,static修饰的方法不是静态方法,而是只在当前cpp文件中被访问的方法,其他文件访问不了,可以称为本地方法
// [frameworks/base/media/jni/android_media_MediaPlayer.cpp]
static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(sLock);
// 由前面章节分析可知,该字段即为此前创建缓存的native层MediaPlayer对象指针值,
// 将其强制转换为该对象指针,然后进行返回sp智能指针对象,
// 以便在使用过程中用于控制指针的引用计数
MediaPlayer* const p = (MediaPlayer*)env->GetLongField(thiz, fields.context);
return sp<MediaPlayer>(p);
}
1.2、mp->setDataSource(fd, offset, length)实现分析:
// [frameworks/av/media/libmedia/mediaplayer.cpp]
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;
// 通过Binder机制获取BpMediaPlayerService媒体播放器服务代理实现
// 见1.2.1小节分析
const sp<IMediaPlayerService> service(getMediaPlayerService());
if (service != 0) {
// create实现,见1.2.2小节分析
sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
// doSetRetransmitEndpoint 处理为:判断player是否有效并且判断是否需要设置UDP重传端点URL设置信息,
// 该功能是java层MediaPlayer会进行的设置,但是目前没有开放给应用层使用,
// 其功能主要就是:若设置了该UDP端口重传端点信息,则MediaPlayer将不会进行解码和播放,
// 只会进行网络传输数据(如使用RTP格式封装数据后进行传输)。
// player->setDataSource:调用了服务端MediaPlayer的该方法
// 见1.2.3小节分析
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(fd, offset, length))) {
// 若有异常失败,则释放当前持有的服务端MediaPlayer的Bp代理对象内存
// clear方法是智能指针相关的实现,关于智能指针实现原理后续章节分析下
player.clear();
}
// 进行关联新的服务端Client播放器代理对象
// 见1.2.4小节分析
err = attachNewPlayer(player);
}
return err;
}
1.2.1、getMediaPlayerService()实现分析:【获取BpMediaPlayerService】
此方法实现原理为:Android C++层Binder机制实现,即向注册服务管理中心获取自己的服务即Bp实现对象,也是Bn对象的在客户端的代理操作对象。
关于Binder机制实现原理可参考我另一章节分析:Android C++底层Binder通信机制原理分析总结【通俗易懂】
// [frameworks/av/media/libmedia/IMediaDeathNotifier.cpp]
// establish binder interface to MediaPlayerService
/*static*/const sp<IMediaPlayerService>
IMediaDeathNotifier::getMediaPlayerService()
{
ALOGV("getMediaPlayerService");
Mutex::Autolock _l(sServiceLock);
if (sMediaPlayerService == 0) {
// 获取注册服务管理中心即BpServiceManager,单列模式
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
// 通过响应服务注册时的服务名称【"media.player"】获取注册过的服务
binder = sm->getService(String16("media.player"));
if (binder != 0) {
break;
}
// 服务未注册,则每隔0.5毫秒重新获取一次
ALOGW("Media player service not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
// 若为空则创建一个全局的Binder异常关闭的监听,监听接收到对端Binder进程异常的通知
if (sDeathNotifier == NULL) {
sDeathNotifier = new DeathNotifier();
}
binder->linkToDeath(sDeathNotifier);
// 最后使用interface_cast转换成对应服务取得Bp代理对象,
// 即获取到了BpMediaPlayerService实现,此处转换成它的父类类型返回
sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
}
ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
return sMediaPlayerService;
}
1.2.2、service->create(this, mAudioSessionId)实现分析:
由前面的分析,我们直接查找BpMediaPlayerService对应的实现,如下
// [frameworks/av/media/libmedia/IMediaPlayerService.cpp]
virtual sp<IMediaPlayer> create(
const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId) {
// 此处即为Binder机制实现的代理调用发送端实现,发送和等待返回的数据类型均为Parcel
// 将请求参数封装到data中,创建等待返回数据的replay
Parcel data, reply;
// 通过Binder机制分析可知,getInterfaceDescriptor()该方法为宏定义中实现的,
// 其获取的值其实是如下宏定义赋值的字符串即接口描述符【"android.media.IMediaPlayerService"】
// IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.media.IMediaPlayerService");
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
// 将client即当前native层的MediaPlayer对象,
// 即由Binder机制实现可知,该对象为Bn实现端,将其强转为IBinder类型传递
data.writeStrongBinder(IInterface::asBinder(client));
data.writeInt32(audioSessionId);
// 发起【CREATE】事务码的Binder请求,
// 写入请求数据到Binder驱动,等待replay读取返回数据,
// BpRefBase中的remote()方法返回的mRemote即BpBinder对象实例用来和Binder驱动交互使用。
// 注意:此处的【CREATE】非常重要,其是枚举类型,代表着Binder通信的事务请求事件标识码,
// 即服务端在Bn实现端的onTransact()需要根据该请求类型标识,
// 来执行对应的任务,此处为创建MediaPlayer的事务请求事件
remote()->transact(CREATE, data, &reply);
// 远程服务端响应处理后返回创建的IMediaPlayer对象。
// 最后使用interface_cast转换成对应的Bp代理对象,
// 即获取到了BpMediaPlayer实现,此处转换成它的父类类型返回
return interface_cast<IMediaPlayer>(reply.readStrongBinder());
}
根据Binder实现机制,直接定位到服务端的Bn实现端即BnMediaPlayerService的onTransact()方法处理【CREATE】事务事件流程:
// [frameworks/av/media/libmedia/IMediaPlayerService.cpp]
tatus_t BnMediaPlayerService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
// 接收到Binder通信的事务请求事件标识码[CREATE]
case CREATE: {
// 宏定义代码,检查接口调用端是否有权限,内部主要就是对参数中的接口描述符字符串
// 和当前进程中对应值进行校验【binder->getInterfaceDescriptor()】
// 若校验失败则直接return返回PERMISSION_DENIED无权限。
// 校验方式主要是调用传入参数Parcel对象的【checkInterface】方法
CHECK_INTERFACE(IMediaPlayerService, data, reply);
// 由上面的分析,此处读取的就是传入参数中的IMediaPlayerClient的Bn实现对象,因此服务端使用时需要使用interface_cast转换成对应的Bp代理对象
sp<IMediaPlayerClient> client =
interface_cast<IMediaPlayerClient>(data.readStrongBinder());
// 再获取session id信息
// 【备注:Parcel读取必须按顺序读取】
audio_session_t audioSessionId = (audio_session_t) data.readInt32();
// 然后调用了BnMediaPlayerService派生类的create方法
// 见下面的分析
sp<IMediaPlayer> player = create(client, audioSessionId);
// 创建执行完毕后,响应【返回】创建服务端IMediaPlayer的Bn实现对象给调用端【客户端】
// 由上可知,客户端最后也使用了interface_cast转换成对应的Bp代理对象进行使用
reply->writeStrongBinder(IInterface::asBinder(player));
return NO_ERROR;
} break;
// ... 省略其他代码
}
}
create(client, audioSessionId)实现分析:
通过分析可知,BnMediaPlayerService的子类实现了该方法,即MediaPlayerService实现类,其声明如下
// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.h]
class MediaPlayerService : public BnMediaPlayerService {}
// [frameworks/av/media/libmedia/include/media/IMediaPlayerServive.h]
class BnMediaPlayerService: public BnInterface<IMediaPlayerService> {}
// [frameworks/av/media/libmedia/include/media/IMediaPlayerServive.h]
class IMediaPlayerService: public IInterface {}
// [frameworks/native/libs/binder/include/binder/IInterface.h]
class IInterface : public virtual RefBase {}
// [frameworks/native/libs/binder/include/binder/IInterface.h]
template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder {}
// [frameworks/native/libs/binder/include/binder/Binder.h]
class BBinder : public IBinder {}
// [frameworks/native/libs/binder/include/binder/IBinder.h]
class [[clang::lto_visibility_public]] IBinder : public virtual RefBase {}
create该方法的实现:
// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
audio_session_t audioSessionId)
{
// 此为获取到当前调用进程的进程ID【如何获取此处暂不展开分析】
// 【注:该值其实就是调用者进程ID,即上层APP的调用进程】
pid_t pid = IPCThreadState::self()->getCallingPid();
// 【mNextConnId】该字段值记录的是:
// 当前MediaPlayerService创建连接过的Client播放器的计数值,作为一个连接ID
// 【包括已释放的对象,因为该变量计数没有调用减法操作】。
// android_atomic_inc方法为原子性自增操作,其内部调用了android_atomic_add函数(内嵌汇编实现)
int32_t connId = android_atomic_inc(&mNextConnId);
// 创建服务端Bn实现端(BnMediaPlayer)子类Client播放器对象
// Client类的声明是[/frameworks/av/media/libmediaplayerservice/MediaPlayerService.h]文件中MediaPlayerService类的内部类
// 参数【getCallingUid()】获取的是调用者用户ID,
// 在 Android 上,一个应用程序只有一个UID,当然多个应用程序也可以共享(android:sharedUserId)一个UID。
// 该ID主要用于数据共享或权限检查(Linux上是多用户ID)
// Client类对象初始化创建,见下面的分析
sp<Client> c = new Client(
this, pid, connId, client, audioSessionId,
IPCThreadState::self()->getCallingUid());
ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,
IPCThreadState::self()->getCallingUid());
// 注意此处将Client新对象强引用转换给弱引用指针对象,然后进行缓存
wp<Client> w = c;
{
// 加锁缓存的当前新创建Client弱引用对象在【mClients】集合列表中,
// 其声明为【SortedVector< wp<Client> > mClients;】
Mutex::Autolock lock(mLock);
mClients.add(w);
}
// 然后Binder机制返回给请求的客户端
return c;
}
Client类声明和构造函数实现:
// [/frameworks/av/media/libmediaplayerservice/MediaPlayerService.h]
class Client : public BnMediaPlayer {
// IMediaPlayer interface
// 省略其他代码
}
// [/frameworks/av/media/libmedia/include/media/IMediaPlayer.h]
class BnMediaPlayer: public BnInterface<IMediaPlayer> {}
// [/frameworks/av/media/libmedia/include/media/IMediaPlayer.h]
class IMediaPlayer: public IInterface {}
// 构造函数
// [/frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
MediaPlayerService::Client::Client(
const sp<MediaPlayerService>& service, pid_t pid,
int32_t connId, const sp<IMediaPlayerClient>& client,
audio_session_t audioSessionId, uid_t uid)
{// 初始化操作
ALOGV("Client(%d) constructor", connId);
mPid = pid;
mConnId = connId;
mService = service;
mClient = client;
mLoop = false;
mStatus = NO_INIT;
mAudioSessionId = audioSessionId;
mUid = uid;
mRetransmitEndpointValid = false;
mAudioAttributes = NULL;
// 该类是当前Client类的内部类,其声明和实现见下面的分析
// 创建一个监听事件对象,其实际就是代理调用Client自己的notify()方法
mListener = new Listener(this);
// 该宏定义默认为0即false,因此暂不分析
#if CALLBACK_ANTAGONIZER
ALOGD("create Antagonizer");
mAntagonizer = new Antagonizer(mListener);
#endif
}
Listener的声明和实现:
Client类的内部类,其实现很简单,就是缓存当前Client对象的弱引用,并调用client对象的notify方法,就相当于一个代理类实现作用,主要就是简化调用即若直接调用则需要每次都判断Client弱引用对象并将其提升为强引用对象,然后判断是否提升成功,若当前Client对象没有被释放内存,则提升成功,否则为null
// [/frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
class Listener : public MediaPlayerBase::Listener {
public:
Listener(const wp<Client> &client) : mClient(client) {}
virtual ~Listener() {}
virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) {
// 提升弱指针代理调用处理
sp<Client> client = mClient.promote();
if (client != NULL) {
client->notify(msg, ext1, ext2, obj);
}
}
private:
wp<Client> mClient;
};
1.2.3、player->setDataSource(fd, offset, length)实现分析:
由上面的分析,可知调用的Client代理对象即BpMediaPlayer代理类的该方法实现:
// [frameworks/av/media/libmedia/IMediaPlayer.cpp]
status_t setDataSource(int fd, int64_t offset, int64_t length) {
// 数据打包并进行Binder事务请求
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeFileDescriptor(fd);
data.writeInt64(offset);
data.writeInt64(length);
// 发起【SET_DATA_SOURCE_FD】事务码的Binder请求,
// 写入请求数据到Binder驱动,等待replay读取返回数据,
// BpRefBase中的remote()方法返回的mRemote即BpBinder对象实例用来和Binder驱动交互使用。
remote()->transact(SET_DATA_SOURCE_FD, data, &reply);
// 服务端处理完成后在reply变量中存储了状态值,进行读取
return reply.readInt32();
}
然后通过Binder机制请求服务端BnMediaPlayer的onTransact()方法对应事务码处理流程实现:
// [frameworks/av/media/libmedia/IMediaPlayer.cpp]
status_t BnMediaPlayer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{// ... 省略其他代码
switch (code) {
case SET_DATA_SOURCE_FD: {
// 检查客户端调用接口是否有权限
CHECK_INTERFACE(IMediaPlayer, data, reply);
// 依次读取参数变量Parcel中的参数值
int fd = data.readFileDescriptor();
int64_t offset = data.readInt64();
int64_t length = data.readInt64();
// 调用BnMediaPlayer实现者子类该方法【setDataSource】实现即Client类的该方法实现,见下面的分析
// 然后将该方法的返回状态码写入应答reply对象中,以供调用端读取该值
reply->writeInt32(setDataSource(fd, offset, length));
return NO_ERROR;
}
}
}
setDataSource(fd, offset, length)实现分析:
【由于当前文章篇幅较长,因此将其放入当前章节的第二部分接着分析】
请查看:
【三】Android MediaPlayer整体架构源码分析 -【设置数据源】【Part 2】
1.2.4、attachNewPlayer(player)实现分析:
关联新的服务端Client播放器代理对象
// [frameworks/av/media/libmedia/mediaplayer.cpp]
status_t MediaPlayer::attachNewPlayer(const sp<IMediaPlayer>& player)
{
status_t err = UNKNOWN_ERROR;
sp<IMediaPlayer> p;
// 加锁代码块处理
{ // scope for the lock
Mutex::Autolock _l(mLock);
// 此处检查当前native层播放器类状态,必须为包含IDLE或等于ERROR状态,才能关联(缓存)新的播放器对象。
// 注意此处的处理上的不同,IDLE使用的&与运算符,若包含该状态则结果为true,
// 而ERROR是 == 相等运算符即必须当前状态不包含其他状态并且等于该状态则为true。
if ( !( (mCurrentState & MEDIA_PLAYER_IDLE) ||
(mCurrentState == MEDIA_PLAYER_STATE_ERROR ) ) ) {
ALOGE("attachNewPlayer called in state %d", mCurrentState);
return INVALID_OPERATION;
}
// 清空相关变量值操作
// 见1.2.4.1小节分析
clear_l();
// 记录旧对象,并缓存新对象到类全局变量中
p = mPlayer;
mPlayer = player;
if (player != 0) {
// 新对象成功,则改为初始化状态
mCurrentState = MEDIA_PLAYER_INITIALIZED;
err = NO_ERROR;
} else {
ALOGE("Unable to create media player");
}
}
if (p != 0) {
// 若有旧对象,则执行其断开连接操作方法
// // 见1.2.4.2小节分析【根据前面分析,可知该对象为服务端Client播放器对象】
p->disconnect();
}
// 并且注意,在该方法返回时旧对象p即智能指针对象将会执行智能指针对象自身析构函数,
// 即减少Client对象指针引用到0,释放Client对象内存【delete】,
// 因此即会导致Client对象的析构函数也会被调用。
// 见1.2.4.3小节分析
return err;
}
1.2.4.1、clear_l()实现分析:
清空相关变量值操作
// [frameworks/av/media/libmedia/mediaplayer.cpp]
// always call with lock held
void MediaPlayer::clear_l()
{
mCurrentPosition = -1;
mCurrentSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;
mSeekPosition = -1;
mSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;
mVideoWidth = mVideoHeight = 0;
mRetransmitEndpointValid = false;
}
1.2.4.2、p->disconnect()实现分析:
// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
void MediaPlayerService::Client::disconnect()
{
ALOGV("disconnect(%d) from pid %d", mConnId, mPid);
// grab local reference and clear main reference to prevent future
// access to object
sp<MediaPlayerBase> p;
{
Mutex::Autolock l(mLock);
// 由前面的分析可知,mPlayer是前面创建的NuPlayerDriver播放器对象
// 获取缓存的NuPlayerDriver类对象
// 注意:由智能指针sp的实现原理可知,赋值=运算符会导致其内部缓存的NuPlayerDriver对象指针引用计数再加1
p = mPlayer;
// 执行智能指针clear方法即减少mClient指针对象的引用计数,
// 该对象是native层MediaPlayer对象类型,
// 但注意:此处执行之后却不会执行该对象的析构函数,因此此处的处理
// 只为清空Client的该缓冲字段值即将其指针引用计数减少,
// 但其引用计数不会为0【因为JNI层还有对该对象的引用计数】,
// 所以不会执行其析构函数,只是不在引用它
mClient.clear();
// 执行智能指针clear方法,减小NuPlayerDriver类对象指针引用计数
// 注意:有上面分析,此处执行智能指针sp的clear方法只是减少引用计数到1,还不会执行释放内存操作【即delete内存,会由此处的局部变量p来自动完成】
// 并且此处执行clear也是为了清空类全局智能指针变量mPlayer内部的实际对象指针赋值为空即0
mPlayer.clear();
}
// clear the notification to prevent callbacks to dead client
// and reset the player. We assume the player will serialize
// access to itself if necessary.
if (p != 0) {
// 清空旧NuPlayerDriver对象的通知回调监听,前面已分析过
p->setNotifyCallback(0);
#if CALLBACK_ANTAGONIZER
ALOGD("kill Antagonizer");
mAntagonizer->kill();
#endif
// 然后执行该对象的reset操作
// 见第2小节分析【由于后续篇幅长,因此小节计数从2开始】
p->reset();
}
{// 加锁代码块,执行断开native层window的连接操作
Mutex::Autolock l(mLock);
// 见第3小节分析
disconnectNativeWindow_l();
}
// IPC线程执行刷新清空指令【即清空此时的Binder请求操作,
// 因为当前Client已经执行断开连接了,不需要处理后续请求操作】
// 不展开分析
IPCThreadState::self()->flushCommands();
// 然后在该函数结束返回时,会执行智能指针p对象的析构函数,
// 也就如上类似分析的结论,将会减少NuPlayerDriver对象指针引用计数到0,
// 随即delete释放其内存,然后就会执行NuPlayerDriver的析构函数
// 见第4小节分析
}
1.2.4.3、Client对象的析构函数实现分析:
// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
MediaPlayerService::Client::~Client()
{
ALOGV("Client(%d) destructor pid = %d", mConnId, mPid);
// 清空该对象【实际是智能指针对象,相当于字段变量赋值为null,
// 并且减去当前对象指针的引用计数,若减去之后为0,
// 那么则会执行delete释放其内存,然后还会执行该实际指针对象的析构函数,这就是智能指针的clear()实现原理处理,不展开分析,可自行分析】
mAudioOutput.clear();
wp<Client> client(this);
// 又调用了上面分析的方法
disconnect();
// 执行移除当前Client对象,mService如前面分析就是MediaPlayerService对象
// 见下面的分析
mService->removeClient(client);
if (mAudioAttributes != NULL) {
// 释放该内存
free(mAudioAttributes);
}
// 清空该对象
mAudioDeviceUpdatedListener.clear();
}
mService->removeClient(client)实现分析:
// [frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp]
void MediaPlayerService::removeClient(const wp<Client>& client)
{
Mutex::Autolock lock(mLock);
// 其实就是从此前创建Client缓存的集合变量中移除当前需要被移除的Client对象
mClients.remove(client);
}
2、p->reset()实现分析:
分析NuPlayerDriver的reset方法
由于篇幅较长,该部分放入靠后章节即MediaPlayer的reset处理流程章节进行统一分析。
请查看章节:
【XX】Android MediaPlayer整体架构源码分析 -【reset处理流程】
TODO 【后续完成后补上链接】
本章节结束,其它相关内容请查看其他章节分析。