ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

5. Android MultiMedia框架完全解析 - 再谈Playback框架及一些学习方法的讨论

2021-07-16 13:59:59  阅读:671  来源: 互联网

标签:mSource sp 函数 框架 track MultiMedia NuPlayer Playback GenericSource


前言

经过前面的分析,我们大概知道了一些概念,但是这个StageFright在整个playback的位置是什么样的:
在这里插入图片描述
Android Media这一块的知识杂乱,而且有深度,有广度。深度的话,从app->JNI->C+±>底层的OMX驱动,广度的话,每个播放器在播放的时候,会执行seek操作,快进,快退,暂停等等操作,同时,需要涉及读取源文件(从网络读取,从文件读取),解码(软解,硬解),Render等到一系列的操作,光听我这么说,就感觉很乱。

如果一味的追求深度,从app开始追代码的执行,一路追下去,可能追到最后,我追的初衷是什么,我刚开始想追的是什么都会忘记。所以这里插一章,讲解广度的内容。

Playerback框架

这里可以总结为,需要“一横一竖”的分析,横是指:NuPlayerDriver.cpp中需要实现什么功能,都有哪些接口函数,包括必须调用的接口和可选的调用接口,并包含对这些接口的简单分析。而“竖”指:某一个必须调用的接口函数自上而下的调用过程。
这个博客分析,就是按照“一横一竖”的原则穿插进行。虽然同样可能会引起混乱,但是学习不会是一蹴而就的,更不会简单花两天时间就会理解这么多的东西。
首先,还是以播放一个本地文件为例,我们需要做什么?

本地文件是什么?

这里就需要一些基础知识了,以一个mp4文件为例,它是原始yuv数据,经过MPEG4编码后,再封装成的文件格式。我们想要播放它,就需要反其道而行之:
在这里插入图片描述
在这个播放器流程图中,首先需要将一个mp4文件读取出来,然后通过Parser/Demux来解封装,再通过解码器将压缩过的数据解码成yuv,最后将这些画面Render出来。

这些步骤就是一个完整播放器的需要完成的操作。

我们下面需要接触的NuPlayer,也是一个完整的播放器,它需要做的工作同样也是上图中的工作,只不过,在上面每一个步骤中,再封装提炼出几个Wrapper, 我这里先放出来一个图(或许很多概念和名字都不熟悉),我们以后的分析,就会按照这个图一步一步分析,在分析的过程中,也就好判断,分析到哪个位置了。先来看看NuPlayer所在的位置:
在这里插入图片描述
下面一步一步放大,来看看NuPlayer的框架图:
在这里插入图片描述
看这个图中,出现了很多新概念,现在挑几个重要的解释一下:

(1)Source:数据源,数据的来源不一定都是本地文件,也可能是网上的各种协议如:http,rtsp等等。source的任务就是把数据源抽象出来,为下一个Demux模块提供稳定的数据流,而Demux不用关心数据到底是怎么来的。GenericSource,它是NuPlayer::Source的一个子类,主要功能有:多媒体文件的格式探测,文件读取和解析。

(2)Parser/Demux:解复用,视频文件一般情况下都是把音视频的ES流交织的通过某种规则放在一起。这种规则就是容器规则。现在有很多不同的容器格式。如ts、mp4、flv、mkv、avi、rmvb等等。demux的功能就是把音视频的ES流从容器中剥离出来,然后分别送到不同的解码器中。其实音频和视频本身就是2个独立的系统。容器把它们包在了一起。但是他们都是独立解码的,所以解码之前,需要把它分别 独立出来。demux就是干这活的,他为下一步decoder解码提供了数据流。

(3)Decoder:解码器,播放器的核心模块。分为音频和视频解码器。影像在录制后,原始的音视频都是占用大量空间,而且是冗余度较高的数据。因此,通常会在制作的时候就会进行某种压缩 ( 压缩技术就是将数据中的冗余信息去除数据之间的相关性 )。这就是我们熟知的音视频编码格式,包括MPEG1(VCD)\ MPEG2(DVD)\ MPEG4 \ H.264 等等。音视频解码器的作用就是把这些压缩了的数据还原成原始的音视频数据。当然,编码解码过程基本上都是有损的,解码器的作用就是把编码后的数据还原成原始数据。按照MediaPlayer的框架,一般是调用MediaCodec完成解码,另外还有,解码器初始化,解码以及和其他模块的交互,比如Source,Renderer等。

(4) MediaCodec 编解码器,Java层会通过JNi层调用Native的方法执行。

(5)Renderer(渲染器),从功能上看,Renderer主要有几个功能:音视频原始数据缓存操作,音频播放(到声卡),视频显示(到显卡),音视频同步,其他辅助播放控制的操作。

(6)NuPlayer是这个播放框架中联系Source,Decoder,Renderer的纽带。

通常我们的调用逻辑是,构造函数->setDataSource->SetVideoSurfaceTexture->prepare/prepareAsync->start->stop->reset->析构函数,按照实际需求还会调用pause、isPlaying、getDuration、getCurrentPosition、setLooping、seekTo等。

在NuPlayerDriver.cpp中,NuPlayerDriver是继承自MediaPlayerInterface,可以看作是NuPlayer的Wrapper,提供了一个状态转换机制。

这个结构体里面比较重要的几个变量是:

State mState; //播放器的状态标志

sp mLooper; //内部消息驱动机制

sp mPlayer; //真正的NuPlayer

先来看NuPlayerDriver的构造函数:

NuPlayerDriver::NuPlayerDriver(pid_t pid)
:mState(STATE_IDLE),
mLooper(new ALooper),
mLooper->start(false, /* runOnCallingThread */
            true,  /* canCallJava */
            PRIORITY_AUDIO);
mPlayer = new NuPlayer(pid);
mLooper->registerHandler(mPlayer);
mPlayer->setDriver(this);

构造函数中最主要的作用是创建ALooper和NuPlayer实例,并将它们关联起来。

1. setDataSource

首先MediaPlayerService调用setDataSource函数,分别执行下面几步操作:

1.1 获取player类型

player_type playerType = MediaPlayerFactory::getPlayerType(this,fd,offset,length);

1.2 创造NuPlayerDriver

sp p = setDataSource_pre(playerType);

通过这两个函数调用,就会获取到需要创建的播放器类型playerType,最终通过MediaPlayerFactory创建出NuPlayerDriver。

1.3 真正setDataSource

setDataSource_post(p, p->setDataSource(fd, offset, length));

这里创建NuPlayerDriver,并调用它的setDataSource函数,由于NuPlayerDriver是NuPlayer的一个Wrapper,在它的构造函数中,已经将mPlayer设置为NuPlayer,在NuPlayerDriver::setDataSource函数中就会去调用mPlayer->setDataSourceAsync(fd, offset, length);最终就跳转到NuPlayer::setDataSourceAsync函数中。

void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
 
    sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
 
    sp<GenericSource> source =
            new GenericSource(notify, mUIDValid, mUID);
 
    status_t err = source->setDataSource(fd, offset, length);
 
    if (err != OK) {
        ALOGE("Failed to set data source!");
        source = NULL;
    }
 
    msg->setObject("source", source);
    msg->post();
}

先解释一个Source的含义,每个需要播放的音视频,都有一个来源,这个来源就称为Source。这个Source位于struct NuPlayer中,他们有以下几种:

  • struct GenericSource; //本地资源
  • struct HTTPLiveSource; //http资源
  • struct StreamingSource; //流媒体资源
  • struct RTSPSource; //RTSP资源

这几个Source都继承自NuPlayer::Source,它同样位于struct NuPlayer中。
struct Source;

在这里是本地播放,所以创建的是GenericSource对象,之后调用它的setDataSource函数。同时发送kWhatSetDataSource消息。在onMessageReceived函数中,可以看到对这个消息的处理,调用了NuPlayerDriver的notifySetDataSourceCompleted函数,来对状态进行了一些处理(将状态设置成STATE_UNPREPARED),并通过mCondition.broadcast();来广播出去,同时NuPlayerDriver::setDataSource函数中通过mCondition.wait(mLock)一直在等待mState的状态发生改变。当接收到这个广播后,这个NuPlayerDriver::setDataSource函数就知道setdatasource已经执行完毕,就继续向下执行。重点是GenericSource的构造函数和source->setDataSource函数。

在NuPlayer.h中,还有一个sp mSource,这个结构体是保存现在使用的是哪一种Source,也就是说,在执行完GenericSource的setDataSource函数后,通过msg->setObject(“source”, source);将创建的GenericSource通过msg发送过去,然后在接收函数NuPlayer::onMessageReceived中,会将这个source赋值给mSource,通过这个操作,mSource中就保存了当前所使用的Source。

首先看GenericSource的构造函数,它传入了一个notify到Source中,同时在构造函数中通过DataSource::RegisterDefaultSniffers();函数,把那些Sniffer函数注册进去。(Sniffer,中文可以翻译为嗅探器,也叫抓数据包软件)

NuPlayer::GenericSource::GenericSource(
        const sp<AMessage> &notify,
        bool uidValid,
        uid_t uid)
    : Source(notify),
      mAudioTimeUs(0),
      mAudioLastDequeueTimeUs(0),
      mVideoTimeUs(0),
      mVideoLastDequeueTimeUs(0),
      mFetchSubtitleDataGeneration(0),
      mFetchTimedTextDataGeneration(0),
      mDurationUs(-1ll),
      mAudioIsVorbis(false),
      mIsWidevine(false),
      mIsSecure(false),
      mIsStreaming(false),
      mUIDValid(uidValid),
      mUID(uid),
      mFd(-1),
      mDrmManagerClient(NULL),
      mBitrate(-1ll),
      mPollBufferingGeneration(0),
      mPendingReadBufferTypes(0),
      mBuffering(false),
      mPrepareBuffering(false),
      mPrevBufferPercentage(-1) {
    resetDataSource();
    DataSource::RegisterDefaultSniffers();
    mTextTrackType = TextTrackType_3GPP;
}

这里需要注意的是,Source(notify)是NuPlayer::Source类的构造函数,这个构造函数为它的私有成员sp mNotify 赋值。在这个NuPlayer::setDataSourceAsync函数中,这个notify为kWhatSourceNotify。

这个source->setDataSource(fd, offset, length)最终是执行了NuPlayer::GenericSource::setDataSource函数。

这个notify在NuPlayer::onMessageReceived函数中,会去调用onSourceNotify函数,我感觉是为source提供的一个Amessage,source里面有什么情况,都通过这个notify与NuPlayer来沟通。具体的实现可能得到研究source时再具体探究。

2. setVideoSurfaceTexture

同样,是从MediaPlayerService开始调用这个函数,在这个函数中,会去创建Surface,(这里简单解释Surface,SurfaceFlinger,BufferQueue的知识。)

首先会去通过getPlayer()来获取Player,之后调用p->setVideoSurfaceTexture(bufferProducer);函数跳转到NuPlayer::setVideoSurfaceTextureAsync函数中,在这个函数中,只是发送了kWhatSetVideoSurface消息,继续跳转到NuPlayer::onMessageReceived函数中去执行:

NuPlayer::onMessageReceived

—>case kWhatSetVideoSurface:

—>performSetSurface(surface);

—>setVideoScalingMode(mVideoScalingMode)

在执行完setVideoScalingMode(mVideoScalingMode)函数后,NuPlayer::performSetSurface函数会去执行NuPlayerDriver的driver->notifySetSurfaceComplete()函数,然后同样回去通过条件变量去广播,通知NuPlayerDriver setVideoSurfaceTexture操作完成了。

3. prepareAsync

MediaPlayerService::Client::prepareAsync()函数首先会调用到NuPlayerDriver里面的NuPlayerDriver::prepareAsync函数,然后会根据NuPlayer的mState状态,选择去调用NuPlayer的prepareAsync函数还是seekToAsync(0, true /* needNotify */)函数。

先来看NuPlayer::prepareAsync函数,这个函数的实现很简单:

void NuPlayer::prepareAsync() {
    (new AMessage(kWhatPrepare, this))->post();
}

同样在NuPlayer::onMessageReceived函数中的实现也很简单:

case kWhatPrepare:
        {
            mSource->prepareAsync();
            break;
        }

就这样跳转到NuPlayer::GenericSource::prepareAsync()函数中,同样也只是发送了一个kWhatPrepareAsync信息,最终跳转到NuPlayer::GenericSource::onPrepareAsync()函数中。

首先会进入mDataSource = new FileSource(mFd, mOffset, mLength);中,执行FileSource的构造函数,在这个构造函数中,就是通过open函数来打开对应的文件。

继续在NuPlayer::GenericSource::onPrepareAsync()函数中执行,进入initFromDataSource();函数,在这个函数中会有关于Widevine的判断,可以查看网上的资源,这是有关DRM相关的知识,链接如下:
Widevine DRM介绍
对于本地播放,它会依次执行下面的语句:

3.1 创建FileSource

mDataSource = new FileSource(mFd, mOffset, mLength);

会根据mFd等等参数来创建mDataSource,这个mDataSource会在接下面的extractor create函数中使用。

3.2 创建Extractor

这个函数的注释很好:init extractor from data source,也解释了这个函数主要的功能:创建Extractor,并调用Extractor来完成一些对Video/Audio的解析等等。

status_t NuPlayer::GenericSource::initFromDataSource() {
---> extractor = MediaExtractor::Create(mDataSource,
                mimeType.isEmpty() ? NULL : mimeType.string());

在这个函数中会调用source->sniff函数,由于上面创建的mDataSource是FileSource,而FileSource继承自DataSource,FileSource中没有实现sniff函数,所以这里调用的是DataSource里面的sniff函数,即:DataSource::sniff函数,通过这个函数去检测出媒体的类型,检测出媒体类型后,在log上打印出这样的话:

Autodetected media content as ‘video/avi’ with confidence 0.21

继续在MediaExtractor::Create函数中执行,会根据检测出来的媒体类型创建Extractor:

new FslExtractor(source,mime);

通过这个FslExtractor,就能够把这个文件解析出来,比如我们经常说的metadata,就是在extractor里面解析出来的,后面也使用到了这些内容。

继续到NuPlayer::GenericSource::initFromDataSource函数中去执行:

---> mFileMeta = extractor->getMetaData();
    if (mFileMeta != NULL) {
    		    int64_t duration;
    		    if (mFileMeta->findInt64(kKeyDuration, &duration)) {
     		       mDurationUs = duration;
     		   }

获取Metadata和Duration的值。

    int32_t totalBitrate = 0;
 
    size_t numtracks = extractor->countTracks();
 
    for (size_t i = 0; i < numtracks; ++i) {
        sp<MediaSource> track = extractor->getTrack(i);
 
        sp<MetaData> meta = extractor->getTrackMetaData(i);
 
        const char *mime;
        CHECK(meta->findCString(kKeyMIMEType, &mime));
 
        if (!strncasecmp(mime, "audio/", 6)) {
            if (mAudioTrack.mSource == NULL) {
                mAudioTrack.mIndex = i;
                mAudioTrack.mSource = track;
                mAudioTrack.mPackets =
                    new AnotherPacketSource(mAudioTrack.mSource->getFormat());
 
                if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
                    mAudioIsVorbis = true;
                } else {
                    mAudioIsVorbis = false;
                }
            }
        } else if (!strncasecmp(mime, "video/", 6)) {
            if (mVideoTrack.mSource == NULL) {
                mVideoTrack.mIndex = i;
                mVideoTrack.mSource = track;
                mVideoTrack.mPackets =
                    new AnotherPacketSource(mVideoTrack.mSource->getFormat());
 
                // check if the source requires secure buffers
                int32_t secure;
                if (meta->findInt32(kKeyRequiresSecureBuffers, &secure)
                        && secure) {
                    mIsSecure = true;
                    if (mUIDValid) {
                        extractor->setUID(mUID);
                    }
                }
            }
        }
 
        if (track != NULL) {
            mSources.push(track);
            int64_t durationUs;
            if (meta->findInt64(kKeyDuration, &durationUs)) {
                if (durationUs > mDurationUs) {
                    mDurationUs = durationUs;
                }
            }
 
            int32_t bitrate;
            if (totalBitrate >= 0 && meta->findInt32(kKeyBitRate, &bitrate)) {
                totalBitrate += bitrate;
            } else {
                totalBitrate = -1;
            }
        }
    }
 
    mBitrate = totalBitrate;
 
    return OK;
}

这里应该是将一个码流解析成几个Track,mAudioTrack,mVideoTrack,mSubtitleTrack,mTimedTextTrack,这几个track都是从video码流中解析出来的,每个track都是一个sp。

但是提取出来track后,仅仅用这个结构体还是不能够完成描述一个track的,于是对于每一个track使用struct Track来描述,同时struct Track中包含这个sp指针,指向从video中提取出来的track。

Track结构体的定义如下:

    struct Track {
        size_t mIndex;
        sp<MediaSource> mSource;
        sp<AnotherPacketSource> mPackets;
    };

它包含一个mIndex,这个mIndex就是此track在video中是第几个track,sp就指向从video中提取出来的track,AnotherPacketSource继承自MediaSource,暂时还不知道它的作用。

(之后是 extractor->getMetaData()函数,什么是MetaData,直译过来叫元数据,其实是在Extractor中,它会在extractor->countTracks()等等操作中,获取到的时长,artist等等信息都保存在metadata中,然后才能够获取到。这个函数只是进行了创建MetaData,并设置媒体类型这个简单的操作。

然后就是extractor->countTracks()操作了,这个函数看上去很简单,但是它的实现真的非常复杂。)系统一共提供了这些类型

上面这段话是以前写的,最后这句有点问题,以前认为很复杂的事情,在FSL代码中做了简化,因为以前说的复杂是软件的解码,这些操作确实复杂,但是在FslExtractor中,只是通过

IParser->getNumTracks(parserHandle, &trackCnt);就得到了track的数目,这些操作是lib_avi_parser_arm11_elinux.3.0.so库中做的,只是提供给了外面一些IParser->类的API函数,所以在这里的代码也就不复杂了,都是lib_avi_parser_arm11_elinux.3.0.so库中内部实现的。

3.3 onPrepareAsync后续

    if (mVideoTrack.mSource != NULL) {
        sp<MetaData> meta = doGetFormatMeta(false /* audio */);
        sp<AMessage> msg = new AMessage;
        err = convertMetaDataToMessage(meta, &msg);
        if(err != OK) {
            notifyPreparedAndCleanup(err);
            return;
        }
        notifyVideoSizeChanged(msg);
    }

这一段函数的意思是去设置Video的Size,为什么这样做呢?因为之前已经通过FslExtracor来获取到Video流的framesize,这里就需要重新设置一下,首先是doGetFormatMeta()函数,这个函数的形参是一个bool值,如果为true,就说明MediaSource为audio,同理,如果为false,MediaSource就为video。

在这里就是video了,之后通过convertMetaDataToMessage(meta, &msg);来从metadata里面提取所需的数据,并将某些数据转换正AMessage。这个函数很复杂,定义在Utils.cpp中。

之后调用NuPlayer::Source::notifyVideoSizeChanged函数来通知NuPlayer。(这里GenericSource是NuPlayer结构体中的一项,所以可以直接调用NuPlayer中的函数。)通过发送kWhatVideoSizeChanged,最终调用到NuPlayer::updateVideoSize函数。

4. start函数

同样由MediaPlayerService发送start指令,经过NuPlayerDriver简单的判断处理,在NuPlayer::start() 中只是发送了一个kWhatStart消息,最终成功调用到NuPlayer::onStart函数。

4.1 Source start

首先去做的第一件事就是mSource->start(),在本地播放时,mSource就是GenericSource,所以这里就会调用NuPlayer::GenericSource::start()函数,

void NuPlayer::GenericSource::start() {
    ALOGI("start");
 
    mStopRead = false;
    if (mAudioTrack.mSource != NULL) {
        postReadBuffer(MEDIA_TRACK_TYPE_AUDIO);
    }
 
    if (mVideoTrack.mSource != NULL) {
        postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
    }
 
    setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
    mStarted = true;
 
    (new AMessage(kWhatStart, this))->post();
}

在这个函数中,首先通过postReadBuffer函数来启动音视频的buffer流动,然后发送kWhatStart消息。先来看看NuPlayer::GenericSource::postReadBuffer函数中做了什么,发现它内部发送了一个kWhatReadBuffer消息,继续追踪,只调用了一个函数onReadBuffer(msg),继续调用到readBuffer(trackType)函数

void NuPlayer::GenericSource::readBuffer(
        media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) {
    // Do not read data if Widevine source is stopped
    if (mStopRead) {
        return;
    }
    Track *track;
    size_t maxBuffers = 1;
    switch (trackType) { //一共就是4种Track
        case MEDIA_TRACK_TYPE_VIDEO:
            track = &mVideoTrack;
            if (mIsWidevine) {
                maxBuffers = 2;
            } else {
                maxBuffers = 8;  // too large of a number may influence seeks
            }
            break;
        case MEDIA_TRACK_TYPE_AUDIO:
            track = &mAudioTrack;
            if (mIsWidevine) {
                maxBuffers = 8;
            } else {
                maxBuffers = 64;
            }
            break;
        case MEDIA_TRACK_TYPE_SUBTITLE:
            track = &mSubtitleTrack;
            break;
        case MEDIA_TRACK_TYPE_TIMEDTEXT:
            track = &mTimedTextTrack;
            break;
        default:
            TRESPASS();
    }

    if (track->mSource == NULL) {
        return;
    }

    if (actualTimeUs) { //第一次进来为null
        *actualTimeUs = seekTimeUs;
    }

    MediaSource::ReadOptions options;

    bool seeking = false;
    if (seekTimeUs >= 0) { //seekTimeUs也是null
        options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
        seeking = true;
    }

    const bool couldReadMultiple = (!mIsWidevine && track->mSource->supportReadMultiple());

    if (mIsWidevine || couldReadMultiple) {
        options.setNonBlocking();
    }

    for (size_t numBuffers = 0; numBuffers < maxBuffers; ) {
        Vector<MediaBuffer *> mediaBuffers;
        status_t err = NO_ERROR;

        if (couldReadMultiple) {
            err = track->mSource->readMultiple(
                    &mediaBuffers, maxBuffers - numBuffers, &options);
        } else {
            MediaBuffer *mbuf = NULL;
            err = track->mSource->read(&mbuf, &options);  //会走到这里来
            if (err == OK && mbuf != NULL) {
                mediaBuffers.push_back(mbuf);
            }
        }

        options.clearNonPersistent();

        size_t id = 0;
        size_t count = mediaBuffers.size();
        for (; id < count; ++id) {
            int64_t timeUs;
            MediaBuffer *mbuf = mediaBuffers[id];
            if (!mbuf->meta_data()->findInt64(kKeyTime, &timeUs)) {
                mbuf->meta_data()->dumpToLog();
                track->mPackets->signalEOS(ERROR_MALFORMED);
                break;
            }
            if (trackType == MEDIA_TRACK_TYPE_AUDIO) {
                mAudioTimeUs = timeUs;
                mBufferingMonitor->updateQueuedTime(true /* isAudio */, timeUs);
            } else if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
                mVideoTimeUs = timeUs;
                mBufferingMonitor->updateQueuedTime(false /* isAudio */, timeUs);
            }

            queueDiscontinuityIfNeeded(seeking, formatChange, trackType, track);

            sp<ABuffer> buffer = mediaBufferToABuffer(
                    mbuf, trackType, seekTimeUs,
                    numBuffers == 0 ? actualTimeUs : NULL);
            track->mPackets->queueAccessUnit(buffer);
            formatChange = false;
            seeking = false;
            ++numBuffers;
        }
        if (id < count) {
            // Error, some mediaBuffer doesn't have kKeyTime.
            for (; id < count; ++id) {
                mediaBuffers[id]->release();
            }
            break;
        }

       ...
    }
}

readBuffer这个函数根据传入的track类型来选择最大的buffer数目,然后执行

err = track->mSource->read(&mbuf, &options);

这里有点晕,这里的track->mSource是什么?与这里的GenericSource有什么关系?再缕缕。
在前面initFromDataSource说过每一个解析出来的track都会添加到对应的Track的mSource里面。例如如果是VIdeoTrack就会添加到mVideoTrack.mSource里面去。

mVideoTrack.mSource = track;

所以这里的track->mSource就是指解析出来的这些track,然后调用的是read函数,那么这个read函数是在哪里实现的?
首先来看track->mSource,他是struct MediaSource类型的变量,但是在这个类型的头文件中,只看到了read函数是一个虚函数,也就是说,这个read函数是需要struct MediaSource的子类自己实现的函数。
/frameworks/av/include/media/stagefright/MediaSource.h

struct MediaSource : public BnMediaSource {
    MediaSource();
    
    virtual status_t start(MetaData *params = NULL) = 0;
    virtual status_t stop() = 0;
    virtual sp<MetaData> getFormat() = 0;
    virtual status_t read(
            MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;
    virtual status_t pause() {
        return ERROR_UNSUPPORTED;
    }
    virtual status_t setBuffers(const Vector<MediaBuffer *> & /* buffers */) {
        return ERROR_UNSUPPORTED;
    }

在FslExtractor.cpp文件中,核心结构体是FslExtractor,它继承自class FslExtractor : public MediaExtractor,这时候思考到,既然FSL打算自己去实现这个Extractor,那么理应也有这个MediaSource对应的子类,所以在FslExtractor.cpp文件中找到了 FslMediaSource这个结构体,它继承自MediaSource结构体,实现了FslMediaSource::read()函数。

我们就以MPEG4Extractor.cpp为例子,在MPEG4Extractor.cpp文件中也找到了MPEG4Source 结构体,它继承自MediaSource结构体,并实现MediaSource::read()函数。

class MPEG4Source : public MediaSource {
public:
    // Caller retains ownership of both "dataSource" and "sampleTable".
    MPEG4Source(const sp<MPEG4Extractor> &owner,
                const sp<MetaData> &format,
                const sp<DataSource> &dataSource,
                int32_t timeScale,
                const sp<SampleTable> &sampleTable,
                Vector<SidxEntry> &sidx,
                const Trex *trex,
                off64_t firstMoofOffset);

    virtual status_t start(MetaData *params = NULL);
    virtual status_t stop();

    virtual sp<MetaData> getFormat();

    virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL);
    virtual bool supportNonblockingRead() { return true; }
    virtual status_t fragmentedRead(MediaBuffer **buffer, const ReadOptions *options = NULL);
status_t MPEG4Source::read(
        MediaBuffer **out, const ReadOptions *options) {
    ...
}

4.2 postScanSources

继续看postScanSources()函数,在这个函数中有对解码器相关的操作。

instantiateDecoder(false, &mVideoDecoder);
instantiateDecoder(true, &mAudioDecoder);

通过这两个函数,就分别初始化了音频和视频的解码器,稍微简单看看函数内部做了什么:

NuPlayer::instantiateDecoder(bool audio, sp<DecoderBase> *decoder) {
	sp<AMessage> format = mSource->getFormat(audio);
	//根据参数audio设置format参数
	*decoder = new Decoder(
	                notify, mSource, mPID, mRenderer, mSurface, mCCDecoder);
	 
	(*decoder)->init();
	(*decoder)->configure(format);
}

这是大致整个NuPlayer的播放流程,对他们先有一个理性的认识,后面再具体分析分析他们。

标签:mSource,sp,函数,框架,track,MultiMedia,NuPlayer,Playback,GenericSource
来源: https://blog.csdn.net/dfvbrtdhy/article/details/118785731

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有