返回介绍

3.2 设置数据源

发布于 2024-12-23 21:17:29 字数 14322 浏览 0 评论 0 收藏 0

setDataSource 对应的是 jni 中的这个函数:

static void android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
  sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
  // mediaplayer 为空
  if (mp == NULL ) {
    jniThrowException(env, "java/lang/IllegalStateException", NULL);
    return;
  }
  // 文件描述符为空
  if (fileDescriptor == NULL) {
    jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    return;
  }
  int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
  ALOGV("setDataSourceFD: fd %d", fd);
  // 调用 mp->setDataSource(fd, offset, length) 为 mediaplayer 设置数据源
  process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}

可以看到函数开始先做了空指针的判断,之后调用了 mp->setDataSource(fd, offset, length) 方法来给 mediaplayer 设置数据源,同时 process_media_player_call 函数对 setDataSource 返回值做判断是否设置成功以及失败后抛出异常。

mediaplayer 中的 setDataSource 函数分为三种,分别对应播放文件、网络和流三种情况,现在只看播放文件的情况:

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 接口
  const sp<IMediaPlayerService>& service(getMediaPlayerService());
  if (service != 0) {
  	// 获取 MediaPlayer 接口
    sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
    // 设置数据源
    if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
      (NO_ERROR != player->setDataSource(fd, offset, length))) {
       player.clear();
    }
    err = attachNewPlayer(player);
  }
  return err;
}

这个函数分为三个部分,逐个分析。

3.2.1 获取 MediaPlayerService 接口

这里先是调用了 getMediaPlayerService 方法获取到一个 IMediaPlayerService

/*static*/const sp<IMediaPlayerService> &IMediaDeathNotifier::getMediaPlayerService()
{
  ALOGV("getMediaPlayerService");
  Mutex::Autolock _l(sServiceLock);
  if (sMediaPlayerService == 0) {
  	// 获取 servicemanager
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> binder;
    do {
      // 获取对应的 binder
      binder = sm->getService(String16("media.player"));
      if (binder != 0) {
        break;
      }
      ALOGW("Media player service not published, waiting...");
      usleep(500000); // 0.5 s
    } while (true);
    if (sDeathNotifier == NULL) {
      sDeathNotifier = new DeathNotifier();
    }
    binder->linkToDeath(sDeathNotifier);
    // asInterface 获取 binder 中的 remote 对象
    sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
  }
  ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
  return sMediaPlayerService;
}

IServiceManager 是 IInterface 类型,和用 AIDL 生成的接口相同,IInterface 中包含了 binder 和 remote 两个东西。在 ndk 中对应的是 BnInterface 和 BpInterface。在 Java 中,本地 Client 通过 ServiceConnection 中的 onServiceConnected(ComponentName name, IBinder service) 方法获得 Service 中在 onBind 时候返回的 binder,同理,这里通过 ServiceManager 的 getService 获得和 media.player 这个 Service 通信用的 binder。

到这里为止我们只拿到了 media.player Service 的 binder,要想调用接口中的方法还需要通过 asInterface 方法来获得与之对应的 IInterface 接口。这里的 interface_cast(binder) 方法就可以获得对应的接口,那么 interface_cast 是怎么做到的呢,其实很简单,利用了模板封装了 asInterface 的操作:

template<typename INTERFACE> inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
  return INTERFACE::asInterface(obj);
}

现在我们就拿到了 media.player Service 的接口。

看到这里会有一个疑问,这个 media.player 的 Service 是什么时候启动的呢。我们根据 media.player 这个线索找到了下面的代码:

void MediaPlayerService::instantiate() {
  defaultServiceManager()->addService(String16("media.player"), new MediaPlayerService());
}

int main(int argc __unused, char** argv)
{
  ...
  sp<ProcessState> proc(ProcessState::self());
  sp<IServiceManager> sm = defaultServiceManager();
  ALOGI("ServiceManager: %p", sm.get());
  AudioFlinger::instantiate();
  // 就是这里啦
  MediaPlayerService::instantiate();
  // 照相机
  CameraService::instantiate();
  // 音频
  AudioPolicyService::instantiate();
  // 语音识别
  SoundTriggerHwService::instantiate();
  registerExtensions();
  ProcessState::self()->startThreadPool();
  IPCThreadState::self()->joinThreadPool();
}

其中 MediaPlayerService 位于 MediaPlayerService.cpp 中,而 main 函数位于 main_mediaserver.cpp 。还记得在本文最开始的那张图么,其中的 mediaserver 对应的就是这个。它是在开机的时候启动的,被写在了启动脚本中:

这样在系统启动的时候这个进程就开启了,同时里面的 Service 也就启动了。

3.2.2 获取 MediaPlayer 接口

service->create(this, mAudioSessionId) 方法通过 IPC 的方式调用 MediaPlayerService 中的 create 方法获得 IMediaPlayer ,IMediaPlayer 从名字就可以看出是一个 IInterface,所以 MediaPlayer 也是通过 IPC 来调用的。先看这个方法做了什么:

sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client, int audioSessionId)
{
  pid_t pid = IPCThreadState::self()->getCallingPid();
  int32_t connId = android_atomic_inc(&mNextConnId);
  // MediaPlayerClient
  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());
  wp<Client> w = c;
  {
    Mutex::Autolock lock(mLock);
    // 管理 Client
    mClients.add(w);
  }
  return c;
}

首先调用的时候传的参数是 MediaPlayer 本身,MediaPlayer 除了继承 IMediaDeathNotifier 同时还继承了 BnMediaPlayerClient,而 BnMediaPlayerClient 又继承了 BnInterface,所以这里的参数列表是一个 client 引用。其中 Client 的实现也在 MediaPlayerService.cpp 这个文件中,他的构造函数用来保存这些对象:

MediaPlayerService::Client::Client(
    const sp<MediaPlayerService>& service, pid_t pid,
    int32_t connId, const sp<IMediaPlayerClient>& client,
    int 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;
#if CALLBACK_ANTAGONIZER
  ALOGD("create Antagonizer");
  mAntagonizer = new Antagonizer(notify, this);
#endif
}

最后 create 方法返回一个 MediaPlayer 的接口,这个接口通过 IPC 用来调用 Client 中的函数。

3.2.3 设置数据源

经过了之前的折腾,我们先拿到了 MediaPlayerService 的接口,通过 MediaPlayerService 的接口又拿到了 MediaPlayer 的接口,接下来就要进行这个函数的最终目的 设置数据源

同样是通过 IPC 调用了 MediaPlayer 的 setDataSource,定义如下:

status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
{
  // 前面是一些输出要设置的数据源的信息的 log
	...
  // 获取播放器类型
  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
  setDataSource_post(p, p->setDataSource(fd, offset, length));
  return mStatus;
}

首先是获取播放器类型,播放器类型定义在 MediaPlayerInterface.h 中:

enum player_type {
  PV_PLAYER = 1,
  SONIVOX_PLAYER = 2,
  STAGEFRIGHT_PLAYER = 3,
  NU_PLAYER = 4,
  // Test players are available only in the 'test' and 'eng' builds.
  // The shared library with the test player is passed passed as an
  // argument to the 'test:' url in the setDataSource call.
  TEST_PLAYER = 5,
};

下面说下这几种类型是做什么的,其中的每一个都对应一个工厂来创建对应的 Player,由于 PV_PLAYER 已经被抛弃了,所以在 5.1 的源码里并没有出现它

  1. PV_PLAYER 这个类型是 Android 最初采用的 OpenCore,由于太臃肿已经被抛弃
  2. SONIVOX_PLAYER 用来处理 midi 相关
  3. NU_PLAYER 全能型,在 5.x 上处于可选
  4. STAGEFRIGHT_PLAYER 5.x 之前的主力即 awesome player ,可以胜任除 midi 外全部的工作
  5. TEST_PLAYER 测试用

这些 player 都是由对应的 factory 创建的,对应的实现在 MediaPlayerFactory.cpp 中,其中的代码比较简单,这里就不分析了,主要是匹配不同类型对应不同的分数,然后选取分高的 player 创建。当前的主力是 STAGEFRIGHT_PLAYER 也就是 awesome player,而 NU_PLAYER 是未来的主力,从 Android M 目前的 源码 中也可以看出代码中只剩下了 NU_PLAYER 和 STAGEFRIGHT_PLAYER,其中 NU_PLAYER 负责网络和流的播放,STAGEFRIGHT_PLAYER 负责有 DRM 和文件的播放。

这里我们就以目前的主力 STAGEFRIGHT_PLAYER 播放器继续往下分析。获取到播放器类型后就到了 创建播放器

sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(player_type playerType)
{
  ...
  // create the right type of player
  sp<MediaPlayerBase> p = createPlayer(playerType);
  if (p == NULL) {
    return p;
  }
	...
  return p;
}
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
  // mPlayer 是当前已经有的 player,如果是刚创建这个对象,
  // 那么 player 是 null 需要创建新的 player
  // 如果创建过了了则对比下需要用到的 player 类型,避免重复创建
  sp<MediaPlayerBase> p = mPlayer;
  if ((p != NULL) && (p->playerType() != playerType)) {
    ALOGV("delete player");
    p.clear();
  }
  if (p == NULL) {
    p = MediaPlayerFactory::createPlayer(playerType, this, notify);
  }
	...
  return p;
}

这里从 MediaPlayerFactory 创建了对应的播放器,之后使用创建好的 player 设置数据源:

status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
  ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
  return mPlayer->setDataSource(dup(fd), offset, length);
}

这里的 mPlayer 就是 AwesomePlayer,AwesomePlayer 在 StagefrightPlayer 类的构造函数中创建:

StagefrightPlayer::StagefrightPlayer(): mPlayer(new AwesomePlayer) {
  ALOGV("StagefrightPlayer");
  mPlayer->setListener(this);
}

StagefrightPlayer 的源码可以看到对象中的 AwesomePlayer 来完成。代码如下:

status_t AwesomePlayer::setDataSource(
    int fd, int64_t offset, int64_t length) {
  Mutex::Autolock autoLock(mLock);
	// 重置播放器状态
  reset_l();
	// 封装成 DataSource
  sp<DataSource> dataSource = new FileSource(fd, offset, length);
	...
  return setDataSource_l(dataSource);
}

status_t AwesomePlayer::setDataSource_l(const sp<DataSource> &dataSource) {
  // 通过 datasource 创建分离器
  sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
  if (extractor == NULL) {
    return UNKNOWN_ERROR;
  }
  if (extractor->getDrmFlag()) {
    checkDrmStatus(dataSource);
  }
	// 为分离器设置数据源
  return setDataSource_l(extractor);
}

这个分离器其实和 ffpmeg 中的 demuxer 一样,作用是将数据中的音频部分和视频部分分开,然后获取到对应的类型,调用对应的解码器进行解码:

status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
  // Attempt to approximate overall stream bitrate by summing all
  // tracks' individual bitrates, if not all of them advertise bitrate,
  // we have to fail.
  int64_t totalBitRate = 0;
  mExtractor = extractor;
  for (size_t i = 0; i < extractor->countTracks(); ++i) {
    sp<MetaData> meta = extractor->getTrackMetaData(i);
    int32_t bitrate;
    if (!meta->findInt32(kKeyBitRate, &bitrate)) {
      const char *mime;
      CHECK(meta->findCString(kKeyMIMEType, &mime));
      ALOGV("track of type '%s' does not publish bitrate", mime);
      totalBitRate = -1;
      break;
    }
    // 总的码率
    totalBitRate += bitrate;
  }
  // metadata 就是数据中的元信息
  sp<MetaData> fileMeta = mExtractor->getMetaData();
  if (fileMeta != NULL) {
    int64_t duration;
    if (fileMeta->findInt64(kKeyDuration, &duration)) {
      // 长度
      mDurationUs = duration;
    }
  }
  mBitrate = totalBitRate;
  bool haveAudio = false;
  bool haveVideo = false;
  for (size_t i = 0; i < extractor->countTracks(); ++i) {
    sp<MetaData> meta = extractor->getTrackMetaData(i);
    const char *_mime;
    CHECK(meta->findCString(kKeyMIMEType, &_mime));
    String8 mime = String8(_mime);
    // 视频
    if (!haveVideo && !strncasecmp(mime.string(), "video/", 6)) {
      setVideoSource(extractor->getTrack(i));
      haveVideo = true;
			...
    // 音频
    } else if (!haveAudio && !strncasecmp(mime.string(), "audio/", 6)) {
      setAudioSource(extractor->getTrack(i));
      haveAudio = true;
			...
      // ogg
      if (!strcasecmp(mime.string(), MEDIA_MIMETYPE_AUDIO_VORBIS)) {
        // Only do this for vorbis audio, none of the other audio
        // formats even support this ringtone specific hack and
        // retrieving the metadata on some extractors may turn out
        // to be very expensive.
        sp<MetaData> fileMeta = extractor->getMetaData();
        int32_t loop;
        if (fileMeta != NULL
            && fileMeta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
          modifyFlags(AUTO_LOOPING, SET);
        }
      }
    } else if (!strcasecmp(mime.string(), MEDIA_MIMETYPE_TEXT_3GPP)) {
      addTextSource_l(i, extractor->getTrack(i));
    }
  }
	...
  return OK;
}

这里通过分离器来确定数据源中是音频还是视频,然后对 player 的 videotrack 和 audiotrack 进行相对应的设置。

到此为止 setDataSource 的流程就算是走完了,如果继续往下分析的话就到了视频的编解码的知识了。Client 是 MediaPlayerService 的内部类。MediaPlayerService 通过 IPC 和 MediaPlayerService 通信获得 Client,Client 中包含了 StagefrightPlayer,而 StagefrightPlayer 最终通过调用 AwesomePlayer 对应的方法。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文