2.4 Android NuPlayer流程—-Nuplayer的工作原理

  • A+
所属分类:安卓 音视频

要解析nuplayer的原理 ,首先要了解播放器的原理,可以先复习下之前的文章

Android 音视频学习基础--1.1 音视频基础知识
Android 音视频学习基础--1.2 需要认识的一些工具
Android 音视频学习基础--1.3 主流的开源项目
Android 音视频学习基础--1.4 ffmpeg pcm输出
Android 音视频学习基础--1.5 ffmpeg yuv输出
Android 音视频学习基础--1.6 ffmpeg 简单视频播放器
Android 音视频学习基础--1.7 Android最简单的音频播放器
Android 音视频学习基础--1.8 Android最简单的音频播放器
Android 音视频学习基础--1.9 Android最简单的视频播放器
Android 音视频学习基础--1.10 Android自制简单音视频播放器

我们按照应用的上层习惯

    MediaPlayer player = new MediaPlayer();
    player.setDataSource();
    player.prepare();
    player.start();

 

来梳理下Nu里面的

    setDataSourceAsync
    prepareAsync
    start
setDataSource
    void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
        sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
    ...
        sp<GenericSource> source =
                new GenericSource(notify, mUIDValid, mUID);
        status_t err = source->setDataSource(fd, offset, length);
    ...
        msg->setObject("source", source);
    ...
        msg->post();
    #endif
    }
    void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
        switch (msg->what()) {
            case kWhatSetDataSource:
            {
    ...
                sp<RefBase> obj;
                CHECK(msg->findObject("source", &obj));
                if (obj != NULL) {
                    Mutex::Autolock autoLock(mSourceLock);
                    mSource = static_cast<Source *>(obj.get());
                } else {
                    err = UNKNOWN_ERROR;
    ...
                break;
            }

 

  • 在Nupayer的setDataSourceAsync,生成了三大部件里面的GenericSource,并把fd交给了他。
  • msg->post(),这个大家把他看成一个handler ,发送消息接收的在onMessageReceived,这里面只是保存mSource;Nuplayer里面很多这样的发送消息的大家把它当成handler就行,
prepareAsync
    void NuPlayer::prepareAsync() {
        (new AMessage(kWhatPrepare, this))->post();
    }
    void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
        switch (msg->what()) {
        ...
            case kWhatPrepare:
            {
    ...
                mSource->prepareAsync();
                break;
            }
    ...

其实Nuplayer 是没什么好准备的,主要是通知数据去准备,

    void NuPlayer::GenericSource::prepareAsync() {
    ...
        sp<AMessage> msg = new AMessage(kWhatPrepareAsync, this);
        msg->post();
    }
    void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) {
        switch (msg->what()) {
          case kWhatPrepareAsync:
          {
              onPrepareAsync();
              break;
          }
          ...
    }
    void NuPlayer::GenericSource::onPrepareAsync() {
    ...
        // init extractor from data source
        status_t err = initFromDataSource();
    ...
    }
    status_t NuPlayer::GenericSource::initFromDataSource() {
    ...
        extractor = MediaExtractor::Create(mDataSource,mimeType.isEmpty() ? NULL : mimeType.string());
        size_t numtracks = extractor->countTracks();
        for (size_t i = 0; i < numtracks; ++i) {
            sp<IMediaSource> track = extractor->getTrack(i);
            if (!strncasecmp(mime, "audio/", 6)) {
                mAudioTrack.mSource = track;
            } else if (!strncasecmp(mime, "video/", 6)) {
                mVideoTrack.mSource = track;
            }
        }
    ...
    }

 

这里面特别有意思,出现了三大组件的另外一个MediaExtractor,分离器。并且将数据分离开来了,将视频的数据分到mVideoTrack,把音频的分到mAudioTrack。所以上层调用prepare的时候耗时是消耗在这里。他需要分离出数据,解析一部分头信息,一些比较复杂的格式,需要较长的时间。

start

最后我们来看下start
void NuPlayer::start() {
(new AMessage(kWhatStart, this))->post();
}
void NuPlayer::onStart(int64_t startPositionUs) {
mRenderer = new Renderer(mAudioSink, notify, flags);
postScanSources();
}
这里面又出现了个组件 Renderer 用于后期数据输出的。

    void NuPlayer::postScanSources() {
    ...
        sp<AMessage> msg = new AMessage(kWhatScanSources, this);
        msg->setInt32("generation", mScanSourcesGeneration);
        msg->post();
    ...
    }
    void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
    ...
            case kWhatScanSources:
            {
                if (mSurface != NULL) {
                    if (instantiateDecoder(false, &mVideoDecoder) == -EWOULDBLOCK) {
                        rescan = true;
                    }
                }
                if (mAudioSink != NULL && mAudioDecoder == NULL) {
                    if (instantiateDecoder(true, &mAudioDecoder) == -EWOULDBLOCK) {
                        rescan = true;
                    }
                }
            }
    ...
    }

这里已经是最后一步了,在instantiateDecoder中分别创建 音频和视频的分离器

    status_t NuPlayer::instantiateDecoder(
    *decoder = new Decoder(notify, mSource, mPID, mRenderer);
    }

至此 Nuplayer里面的四大组件都准备好了 有GenericSource负责输入数据,MediaExtractor负责分离数据,MediaCodec负责解析数据,Renderer负责输出数据。
2.0 Android NuPlayer流程简介
2.1 Android NuPlayer流程 —进程启动
2.2 Android NuPlayer流程—-MediaPlayer的创建
2.3 Android NuPlayer流程 -MediaPlayer的setDataSource流程
2.4 Android NuPlayer流程—-Nuplayer的工作原理
2.5 Android NuPlayer流程—-视频和音频的输出

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: