返回介绍

3.2 设置数据源

发布于 2024-12-23 21:17:29 字数 14322 浏览 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 对应的方法。

发布评论

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