MediaPlayerService是android的多媒体框架的核心服务之一,该服务存储有android平台所支援的编解码器信息,管理所有通过MediaPlayer创建的播放器实例,起着承上启下的作用,这一节我们将了解MediaPlayerService中的内容。
上一篇笔记中我们了解了 MediaPlayer native 实现,这个实现在源码中位于 frameworks/av/media/libmedia,libmedia也是多媒体框架的核心库之一,这里我们来看看libmedia到底包含哪些东西。
libmedia底下的内容大致可以分为四类:
mediaplayer.cpp
mediarecorder.cpp
MediaScanner.cpp
,分别对应MediaPlayer.java
MediaRecorder.java
MediaScanner.java
;IMediaExtractorService.aidl
;IMediaPlayerService.cpp
IMediaPlayer.cpp
IMediaExtractor.cpp
IDataSource.cpp
IMediaCodecList.cpp
,这些对象将在service进程中创建,通过binder传输给client进行,供client使用;MediaCodecInfo.cpp
MediaCodecBuffer.cpp
OMXBuffer.cpp
为什么有的 binder service 用 aidl 生成相关 bp/bn 文件,有的却手动实现呢?大致浏览 aidl 文件可以发现,需要传递的类型已经使用 aidl 定义过了或者是基础类型;手动编写的文件例如 IMediaPlayer.cpp
需要传递比较复杂的类型,例如 KeyedVector 等,如果要用 aidl 编写会很麻烦。
我们这一篇要了解的 MediaPlayerService 部分源码就在 libmedia 之中。
MediaPlayerService 启动文件位于 frameworks/av/media/mediaserver/main_mediaserver.cpp,这个进程名为 mediaserver
,除了有 MediaPlayerService 这个服务外,还有包含有一个资源管理服务 ResourceManagerService,这里我们不做研究。
MediaPlayerService 在 ServiceManager 中注册的名字是 media.player
,平时我会将 MediaPlayerService
mediaserver
media.player
看作是一个东西。
接下来从 IMediaPlayerService.h 中来看 MediaPlayerService 会为我们提供什么服务:
class IMediaPlayerService: public IInterface
{
public:
virtual sp<IMediaRecorder> createMediaRecorder(
const android::content::AttributionSourceState &attributionSource) = 0;
virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client,
audio_session_t audioSessionId = AUDIO_SESSION_ALLOCATE,
const android::content::AttributionSourceState &attributionSource =
android::content::AttributionSourceState()) = 0;
virtual sp<IMediaCodecList> getCodecList() const = 0;
};
MediaPlayerService 主要提供了三个服务createMediaRecorder
create
和 getCodecList
,我们主要了解后面两个服务。
sp<IMediaCodecList> MediaPlayerService::getCodecList() const {
return MediaCodecList::getLocalInstance();
}
第一次调用getCodecList 会去加载 Codec 信息,信息来自于两个地方:
media_codecs.xml
media_codecs_c2.xml
media_codecs_performance.xml
,前两个文件一般包含有所有的 code type,最后一个文件包含 codec 对应的性能信息。所有加载到的信息以 MediaCodecInfo 的形式存储在 MediaCodecList 中,我们可以调用 MediaCodecList 的 findCodecByType
findCodecByName
findMatchingCodecs
来查找想要的 codec 组件。
以下是从虚拟设备中的 vendor/etc/media_codecs_google_video.xml
截取到的 h265 decoder的信息
<MediaCodec name="OMX.google.hevc.decoder" type="video/hevc">
<Limit name="size" min="2x2" max="2048x2048" />
<Limit name="alignment" value="2x2" />
<Limit name="block-size" value="8x8" />
<Limit name="block-count" range="1-139264" />
<Limit name="blocks-per-second" range="1-2000000" />
<Limit name="bitrate" range="1-10000000" />
<Limit name="performance-point-1920x1080" value="30" />
<Feature name="adaptive-playback" />
MediaCodec>
以下是从 vendor/etc/media_codecs_performance.xml
截取到的 performance 信息
<MediaCodec name="OMX.google.hevc.decoder" type="video/hevc" update="true">
<Limit name="measured-frame-rate-352x288" range="754-817" />
<Limit name="measured-frame-rate-640x360" range="323-394" />
<Limit name="measured-frame-rate-720x480" range="349-372" />
<Limit name="measured-frame-rate-1280x720" range="144-157" />
<Limit name="measured-frame-rate-1920x1080" range="74-85" />
MediaCodec>
以下是 dumpsys media.player
获取到的部分 codec 信息:
Media type 'video/hevc':
Decoder "OMX.google.hevc.decoder" supports
aliases: []
attributes: 0: [
encoder: 0,
vendor: 0,
software-only: 0,
hw-accelerated: 0 ]
owner: "default"
rank: 528
profile/levels: [
1/65536 (Main/Main 5.1),
4/65536 (MainStill/Main 5.1) ]
colors: [
0x7f420888 (YUV420Flexible),
0x13 (YUV420Planar) ]
details: AMessage(what = 0x00000000) = {
string alignment = "2x2"
string bitrate-range = "1-10000000"
string block-count-range = "1-139264"
string block-size = "8x8"
string blocks-per-second-range = "1-2000000"
int32_t feature-adaptive-playback = 0
string measured-frame-rate-1280x720-range = "144-157"
string measured-frame-rate-1920x1080-range = "74-85"
string measured-frame-rate-352x288-range = "754-817"
string measured-frame-rate-640x360-range = "323-394"
string measured-frame-rate-720x480-range = "349-372"
string performance-point-1920x1080-range = "30-30"
string size-range = "2x2-2048x2048"
}
我们可以从信息中看到mime type,codec name,是否为 encoder, 是否是vendor实现,是否是硬件加速,owner 为 default
表示是OMX组件,c2 software codec 的 owner 为 codec2::software
。
MediaCodecInfo 中还可以包含其他信息,比如 FEATURE_TUNNELED_PLAYBACK
等,我们可以调用 getCapabilitiesFor 获取指定 codec 的编解码能力。
status_t MediaPlayer::setDataSource(
const sp<IMediaHTTPService> &httpService,
const char *url, const KeyedVector<String8, String8> *headers)
{
ALOGV("setDataSource(%s)", url);
status_t err = BAD_VALUE;
if (url != NULL) {
const sp<IMediaPlayerService> service(getMediaPlayerService());
if (service != 0) {
sp<IMediaPlayer> player(service->create(this, mAudioSessionId, mAttributionSource));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(httpService, url, headers))) {
player.clear();
}
err = attachNewPlayer(player);
}
}
return err;
}
MediaPlayer native 对象会在 setDataSource
时获取 MediaPlayerService 并且调用 create
方法创建一个远程的 player 实例,接着调用该实例的 setDataSource
方法。
先看 create:
sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
audio_session_t audioSessionId, const AttributionSourceState& attributionSource)
{
// 给创建的 client 进行编号
int32_t connId = android_atomic_inc(&mNextConnId);
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()));
// 创建 client
sp<Client> c = new Client(
this, verifiedAttributionSource, connId, client, audioSessionId);
// 添加 client 到 mediaplayerservice
wp<Client> w = c;
{
Mutex::Autolock lock(mLock);
mClients.add(w);
}
// 返回 client 到 mediaPlayer native
return c;
}
create 方法会传入 MediaPlayer Native 对象的远程代理用于 Callback
,这里要注意,传进来的是MediaPlayer Native 对象的一个强引用,MediaPlayerService 回传给 MediaPlayer Native 的也是一个强引用,这里出现循环引用,所以我们要注意释放的流程是怎么样的。
create 方法还会将创建的 Client 对象的弱引用传递给 MediaPlayerService,这里的传递仅仅供 dumpsys media.player
使用,我们来看看会 dump 出什么:
generic_x86:/ # dumpsys media.player
Client
pid(4130), connId(7323), status(0), looping(false)
NuPlayer
state(5), atEOS(0), looping(0), autoLoop(0)
mime(video/avc)
decoder(OMX.android.goldfish.h264.decoder)
resolution(1920 x 1080)
numFramesTotal(388), numFramesDropped(0), percentageDropped(0.00%)
mime(audio/mp4a-latm)
decoder(c2.android.aac.decoder)
AudioOutput
stream type(3), left - right volume(1.000000, 1.000000)
msec per frame(0.022676), latency (531)
aux effect id(0), send level (0.000000)
AudioTrack::dump
id(279) status(0), state(0), session Id(2793), flags(0)
stream type(3), left - right volume(1.000000, 1.000000)
format(0x1), channel mask(0x3), channel count(2)
sample rate(44100), original sample rate(44100), speed(1.000000)
frame count(22448), req. frame count(22448)
notif. frame count(11224), req. notif. frame count(0), req. notif. per buff(0)
latency (531), selected device Id(0), routed device Id(2)
output(13) AF latency (22) AF frame count(976) AF SampleRate(44100)
17 registered handlers:
34: CCodecWatchdog: 2124 messages processed
37487: MediaClock: 0 messages processed
37488: NuPlayerDriver Looper: 3 messages processed
37489: http live: 1 messages processed
37490: http live: 3 messages processed
37997: MediaClock: 395 messages processed
37998: NuPlayerDriver Looper: 36 messages processed
37999: http live: 1 messages processed
38000: http live: 24 messages processed
38002: Fetcher: 25 messages processed
38003: NuPlayerRenderer: 934 messages processed
38004: NPDecoder: 288 messages processed
38005: NPDecoder-CL: 106 messages processed
38006: NPDecoder-CL: 396 messages processed
38007: NPDecoder: 1174 messages processed
38008: CodecLooper: 1240 messages processed
38009: NPDecoder-CL: 1969 messages processed
可以看到有调用 MediaPlayerService 的进程 id,使用的 Player 类型,audio/video mime 和 codec type,一些播放器信息(当前的状态,解出的帧数,丢弃的帧数等), audio的相关信息,还有所有的Handler,其注册的Looper以及处理消息的数量。以后如果播放器出现异常可以将这些信息dump下来看看。
MediaPlayerService 的构造函数会初始化 MediaPlayerFactory
,里面注册有平台上所有的播放器,如果我们要添加一个新的播放器,那么除了要继承于 MediaPlayerInterface/MediaPlayerHWInterface
实现MediaPlayerBase
接口外,还要实现一个新的 IFactory
并且注册到 MediaPlayerFactory 中。
MediaPlayerService::MediaPlayerService()
{
ALOGV("MediaPlayerService created");
mNextConnId = 1;
MediaPlayerFactory::registerBuiltinFactories();
}
使用是先调用 getPlayerType 获取合适的 player 类型:
static player_type getPlayerType(const sp<IMediaPlayer>& client, const char* url);
再调用 createPlayer,用获取到的 player 类型创建一个对应的实例:
static sp<MediaPlayerBase> createPlayer(player_type playerType, const sp<MediaPlayerBase::Listener> &listener, pid_t pid);
MediaPlayerService::Client
的接口可以在 IMediaPlayer.h 中查询。
我们继续来看 setDataSource :
status_t MediaPlayerService::Client::setDataSource(
const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers)
{
ALOGV("setDataSource(%s)", url);
if (url == NULL)
return UNKNOWN_ERROR;
// 检查网络权限
if ((strncmp(url, "http://", 7) == 0) ||
(strncmp(url, "https://", 8) == 0) ||
(strncmp(url, "rtsp://", 7) == 0)) {
if (!checkPermission("android.permission.INTERNET")) {
return PERMISSION_DENIED;
}
}
// 获取player类型
player_type playerType = MediaPlayerFactory::getPlayerType(this, url);
// create player and setAudioSink
sp<MediaPlayerBase> p = setDataSource_pre(playerType);
if (p == NULL) {
return NO_INIT;
}
// 调用 player 的 setDataSource 并且将 player 存储到 Client 中
return mStatus =
setDataSource_post(
p, p->setDataSource(httpService, url, headers));
}
到这里播放器的创建就完成了,由于涉及的类比较多,这里贴一张图来表现他们的层次关系:
图中黑色箭头表示调用关系,红色的箭头表示消息上抛过程,一连串的调用之后,最后到达 NuPlayer
,后面我们将了解它的结构与实现;
reset 的目的是销毁用 MediaPlayerService 创建的 IMediaPlayer
本地对象,上一篇笔记中我们了解 reset
过程分为3步,这里我们再回顾一下:
IMediaPlayer
的 reset 方法;IMediaPlayer
的 disconnect 方法;IMediaPlayer
。IMediaPlayer reset 核心是调用 MediaPlayerBase 的 reset 方法:
status_t MediaPlayerService::Client::reset()
{
ALOGV("[%d] reset", mConnId);
mRetransmitEndpointValid = false;
sp<MediaPlayerBase> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
return p->reset();
}
IMediaPlayer disconnect 做的事情会比较多:
void MediaPlayerService::Client::disconnect()
{
ALOGV("disconnect(%d) from AttributionSource %s", mConnId,
mAttributionSource.toString().c_str());
// grab local reference and clear main reference to prevent future
// access to object
sp<MediaPlayerBase> p;
{
Mutex::Autolock l(mLock);
p = mPlayer;
// 1 清除上层传下来的 MediaPlayer native对象
mClient.clear();
// 2 清除 Client mPlayer中的内容
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) {
// 3 清除 MediaPlayerBase 中的 callback
p->setNotifyCallback(0);
#if CALLBACK_ANTAGONIZER
ALOGD("kill Antagonizer");
mAntagonizer->kill();
#endif
// 4 调用 MediaPlayerBase 中的 reset
p->reset();
}
{
Mutex::Autolock l(mLock);
disconnectNativeWindow_l();
}
IPCThreadState::self()->flushCommands();
// 5 销毁 MediaPlayerBase
}
析构函数会再次尝试调用 disconnect 销毁内部的 MediaPlayerBase 对象,然后移除 MediaPlayerService 记录的 Client 实例。
MediaPlayerService::Client::~Client()
{
ALOGV("Client(%d) destructor AttributionSource = %s", mConnId,
mAttributionSource.toString().c_str());
mAudioOutput.clear();
wp<Client> client(this);
disconnect();
mService->removeClient(client);
if (mAudioAttributes != NULL) {
free(mAudioAttributes);
}
mAudioDeviceUpdatedListener.clear();
}
至此 MediaPlayer Native 对象中的 mPlayer 就释放完成了,如果还要继续使用该MediaPlayer实例,需要重新调用 setDataSource 让 MediaPlayerService 重新创建本地实例。
Java 有一个api release
,用意是释放MediaPlayer Native对象,JNI结构实现如下:
static void
android_media_MediaPlayer_release(JNIEnv *env, jobject thiz)
{
ALOGV("release");
decVideoSurfaceRef(env, thiz);
// 1 强引用计数减一
sp<MediaPlayer> mp = setMediaPlayer(env, thiz, 0);
if (mp != NULL) {
// this prevents native callbacks after the object is released
mp->setListener(0);
// 2 调用disconnect
mp->disconnect();
MediaPlayer 析构函数仍会尝试调用一次 disconnect 去释放远程资源:
MediaPlayer::~MediaPlayer()
{
ALOGV("destructor");
if (mAudioAttributesParcel != NULL) {
delete mAudioAttributesParcel;
mAudioAttributesParcel = NULL;
}
AudioSystem::releaseAudioSessionId(mAudioSessionId, (pid_t)-1);
disconnect();
IPCThreadState::self()->flushCommands();
}
到这里MediaPlayer Native对象资源释放完毕,MediaPlayer Java对象就无法再使用了。如果还想使用MediaPlayer 需要重新 new 一个对象,它的构造函数会重新创建一个 native 对象。