Android 13 - Media框架(4)- MediaPlayerService

MediaPlayerService是android的多媒体框架的核心服务之一,该服务存储有android平台所支援的编解码器信息,管理所有通过MediaPlayer创建的播放器实例,起着承上启下的作用,这一节我们将了解MediaPlayerService中的内容。

1、libmedia

上一篇笔记中我们了解了 MediaPlayer native 实现,这个实现在源码中位于 frameworks/av/media/libmedia,libmedia也是多媒体框架的核心库之一,这里我们来看看libmedia到底包含哪些东西。

libmedia底下的内容大致可以分为四类:

  1. Java类的native实现,例如:mediaplayer.cpp mediarecorder.cpp MediaScanner.cpp,分别对应MediaPlayer.java MediaRecorder.java MediaScanner.java
  2. binder service所需要的文件,这类可以分为两小类:
    • aidl文件,编译时直接生成bp/bn文件,例如IMediaExtractorService.aidl
    • 手动实现的bp/bn文件,例如IMediaPlayerService.cpp
  3. binder service中传输对象所需的文件,例如IMediaPlayer.cpp IMediaExtractor.cpp IDataSource.cpp IMediaCodecList.cpp,这些对象将在service进程中创建,通过binder传输给client进行,供client使用;
  4. 其他工具类,例如MediaCodecInfo.cpp MediaCodecBuffer.cpp OMXBuffer.cpp

为什么有的 binder service 用 aidl 生成相关 bp/bn 文件,有的却手动实现呢?大致浏览 aidl 文件可以发现,需要传递的类型已经使用 aidl 定义过了或者是基础类型;手动编写的文件例如 IMediaPlayer.cpp 需要传递比较复杂的类型,例如 KeyedVector 等,如果要用 aidl 编写会很麻烦。

我们这一篇要了解的 MediaPlayerService 部分源码就在 libmedia 之中。

2、MediaPlayerService

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 creategetCodecList,我们主要了解后面两个服务。

2.1、getCodecList

sp<IMediaCodecList> MediaPlayerService::getCodecList() const {
    return MediaCodecList::getLocalInstance();
}

第一次调用getCodecList 会去加载 Codec 信息,信息来自于两个地方:

  • OmxInfoBuilder.cpp :从OMX service获取硬件编解码组件信息
  • Codec2InfoBuilder.cpp :从注册文件获取软件编解码组件信息,文件包括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">
            <!-- profiles and levels:  ProfileMain : MainTierLevel51 -->
            <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">
            <!-- 4 runs, min 754 max 817 gmean 775 -->
            <Limit name="measured-frame-rate-352x288" range="754-817" />
            <!-- 4 runs, min 323 max 394 gmean 373 -->
            <Limit name="measured-frame-rate-640x360" range="323-394" />
            <!-- 4 runs, min 349 max 372 gmean 358 -->
            <Limit name="measured-frame-rate-720x480" range="349-372" />
            <!-- 4 runs, min 144 max 157 gmean 151 -->
            <Limit name="measured-frame-rate-1280x720" range="144-157" />
            <!-- 4 runs, min 74 max 85 gmean 80 -->
            <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 的编解码能力。

2.2、create

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下来看看。

2.3、MediaPlayerFactory

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);

3、MediaPlayerService::Client

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));
}
  1. 根据 url 判断是否需要网络权限;
  2. 使用 MediaPlayerFactory 判断要创建哪一个 player;
  3. 根据获取到的播放器类型来创建播放器;
    a. 检查 media.extractor 服务并监听死亡通知;
    b. 检查 IOmx 服务并监听死亡通知;
    c. 监听 Codec2 服务的死亡通知;
    d. 用 audioSessionId 创建 AudioOutput,调用 player 的 setAudioSink;
  4. 调用 player 的 setDataSource 方法;

到这里播放器的创建就完成了,由于涉及的类比较多,这里贴一张图来表现他们的层次关系:
请添加图片描述
图中黑色箭头表示调用关系,红色的箭头表示消息上抛过程,一连串的调用之后,最后到达 NuPlayer,后面我们将了解它的结构与实现;


阅读全文:Android Media Framework(五)MediaPlayerService

关注公众号《青山渺渺》阅读完整内容; 如有问题可在公众号后台私信,也可进入音视频开发技术分享群一起讨论!

在这里插入图片描述

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青山渺渺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值