NuPlayer

RTSP流媒体使用的播放器框架是NuPlayer

1.选择播放器

MediaPlayerFactory
用来决定使用什么Player : stagefright , nuplayer, sonivox ...

对于一个URL , mediaplayerservice 调用
setdatasource ,调用getPlayerType

#define GET_PLAYER_TYPE_IMPL(a...)                      \
    Mutex::Autolock lock_(&sLock);                      \
                                                        \
    player_type ret = STAGEFRIGHT_PLAYER;               \
    float bestScore = 0.0;                              \
                                                        \
    for (size_t i = 0; i < sFactoryMap.size(); ++i) {   \
                                                        \
        IFactory* v = sFactoryMap.valueAt(i);           \
        float thisScore;                                \
        CHECK(v != NULL);                               \
        thisScore = v->scoreFactory(a, bestScore);      \
        if (thisScore > bestScore) {                    \
            ret = sFactoryMap.keyAt(i);                 \
            bestScore = thisScore;                      \
        }                                               \
    }                                                   \
                                                        \
    if (0.0 == bestScore) {                             \
        ret = getDefaultPlayerType();                   \
    }                                                   \
                                                        \
    return ret;

player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,
                                              const char* url) {
    GET_PLAYER_TYPE_IMPL(client, url);
}

class NuPlayerFactory : public MediaPlayerFactory::IFactory 

对于每一个派生出来的Factory调用scoreFactory ,看谁的score高,就调用谁的creatPlayer. 对于用NuPlayer调用的是NuPlayerDriver。

2.创建NuplayerDriver

    class Nuplayer : public MediaPlayerInterface
    class stagefright : public MediaPlayerInterface
    class MediaPlayerInterface : public MediaPlayerBase

这里面有 setDatasource, prepare, setAudioSink, start ,stop, notifyListener等接口。

特别的会New 一个mPlayer

mPlayer = new NuPlayer;

对于stagefrightPlayer

mPlayer = new AwesomePlayer
NuPlayerDriver::NuPlayerDriver()
    : mState(STATE_IDLE),
      mIsAsyncPrepare(false),
      mAsyncResult(UNKNOWN_ERROR),
      mSetSurfaceInProgress(false),
      mDurationUs(-1),
      mPositionUs(-1),
      mNumFramesTotal(0),
      mNumFramesDropped(0),
      mLooper(new ALooper),
      mPlayerFlags(0),
      mAtEOS(false),
      mStartupSeekTimeUs(-1) {
    mLooper->setName("NuPlayerDriver Looper");

    mLooper->start(
            false, /* runOnCallingThread */
            true,  /* canCallJava */
            PRIORITY_AUDIO);

    mPlayer = new NuPlayer;
    mLooper->registerHandler(mPlayer);

    mPlayer->setDriver(this);
}
1.1 创建了ALooper对象mLooper,并调用了 mLooper->start函数,优先级为PRIORITY_AUDIO。

在ALooper::start函数中循环执行ALooper::loop()函数,在loop函数中调用gLooperRoster.deliverMessage函数,
最终执行了AHandler的子struct中的onMessageReceived函数。

1.2 创建NuPlayer对象mPlayer,并调用 mLooper->registerHandler(mPlayer)。

把mPlayer注册到全局变量gLooperRoster的mHandlers成员vector中。
gLooperRoster是一个全局变量。
ALooperRoster类:中转类,将消息中转给ALooper 或者 AHandleReflector

1.3NuPlayer NuPlayerDriver 双方都是调用函数接口的方式来相互调用

比如NuPlayerDriver 调用NuPlayer::setDataSourceAsync, 这里面发送一个消息AMessage(kWhatSetDataSource,id())
post()出去, NuPlayer里面onMessageReceived将会处理到。

这里的消息有kWhatSetDataSource, kWhatPrepare,kWhatStart, kWhatPause ....
就是一些播放器动作的消息

2.Nuplayer prepare

成员mSource 是在setDataSource里面创建的,

对于http:// m3u8 创建HTTPLiveSource.

对于rtsp:// 创建RTSPSource

其他的创建GenericSource

把这个对象通过AMessage(kWhatSetDataSource,id())发出去

NuPlayer里面onMessageReceived将会处理到
把创建好的mSource注册,looper()->registerHandle(mSource)

NuPlayer::Source 也是AHandler。

struct NuPlayer::RTSPSource : public NuPlayer::Source {
}
struct NuPlayer::Source : public AHandler {
}

要和谁(AHandle)通信就调用registerHandle,将来可以调用Amessage(kWhatXXX,id())->post()

prepareAsync ,发消息 kWhatPrepare
调用RTSPSource prepareAsync

void NuPlayer::RTSPSource::prepareAsync() {
    if (mLooper == NULL) {
        mLooper = new ALooper;
        mLooper->setName("rtsp");
        mLooper->start();

        mReflector = new AHandlerReflector<RTSPSource>(this);
        mLooper->registerHandler(mReflector);
    }

    CHECK(mHandler == NULL);
    CHECK(mSDPLoader == NULL);

    sp<AMessage> notify = new AMessage(kWhatNotify, mReflector->id());

    CHECK_EQ(mState, (int)DISCONNECTED);
    mState = CONNECTING;

    if (mIsSDP) {
        mSDPLoader = new SDPLoader(notify,
                (mFlags & kFlagIncognito) ? SDPLoader::kFlagIncognito : 0,
                mUIDValid, mUID);

        mSDPLoader->load(
                mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders);
    } else {
        mHandler = new MyHandler(mURL.c_str(), notify, mUIDValid, mUID);
        mLooper->registerHandler(mHandler);

        mHandler->connect();
    }

    sp<AMessage> notifyStart = dupNotify();
    notifyStart->setInt32("what", kWhatBufferingStart);
    notifyStart->post();
}

(1)创建了ALooper对象mLooper,并调用了mLooper->setName("rtsp")和
mLooper->start()函数 启动消息处理(单独的loop线程)

(2)创建了AHandlerReflector<RTSPSource>对象mReflector,并通过
调用mLooper->registerHandler注册
RTSPSource的消息处理循环中的消息,是通过mReflector(反射类)发送,
创建Myhandle类(将这个notify传入),Myhandle里面就可以将消息传到RTSPSource里面来了。

(3)创建消息kWhatNotify,并做为MyHandler的参数,在

notify= new AMessge(kWhatNotify,mReflector->id());

mReflector是反射作用,这个是模板类,mTarget=>RTSPSource , 它的onMessageReceived调用target->onMessageReceived(), 也就是RTSPSource::onMessageReceived,
NuPlayer::RTSPSource::onMessageReceived函数中处理了kWhatNotify消息

(4)执行mHandler->connect(),链接到服务器,并把mState设置为CONNECTING状态
调用ARTSPConnection的connect函数,进行链接服务器的处理。

3.Nuplayer Start

MediaPlayer::start() -> MediaPlayerService::Client::start() -> 
NuPlayerDriver::start()

在NuPlayerDriver::setDataSource中mState被设置为STOPPED状态,所以在start函数中执行了mPlayer->start(),调用到NuPlayer::start()函数

  1. NuPlayer::start()
    发送kWhatStart消息

  2. NuPlayer::onMessageReceived对kWhatStart消息的处理
    接收到kWhatStart消息后,先对一些变量赋初始值,并调用mSource->start()函数,对于rtsp流来说,即调用NuPlayer::RTSPSource::start()函数

  3. 新建Renderer对象mRenderer,传入mAudioSink ,kWhatRendererNotify
    ,把mRenderer注册到全局变量gLooperRoster的mHandlers成员中
    Renderer构造函数只是简单的对变量赋默认值,并没有做其他处理

  4. 调用postScanSources()函数,进行初始化audio/video的解码器decoder
    NuPlayer::postScanSources()函数中发送了kWhatScanSources消息

instantiateDecoder函数执行 根据mSource中的format来初始化video和audio解码器。

new Decoder  ==> NuPlayer::Decoder::Decoder()
    ==>new ACodec

NuPlayer::Decoder::configure
创建消息kWhatCodecNotify,用于ACodec对象mCodec的通知消息

  1. video解码时,创建ALooper对象mCodecLooper,用于释放主消息event队列.

3.MyHandle 处理RTSP消息

MyHandler是核心,其中包含ARTSPConnection和ARTPConnection两大部分。MyHandler负责向Server端发送Request和处理Response。

1) connect()
mConn = new ARTSPConnection   

ARTSPConnection负责维护RTSP socket,发送Request,循环接收Server端数据,响应Server的Request。这里只是接收Response真正的处理在MyHandler侧。

ARTSPConnection用于建立RTSP 的连接, 建立socket, tcp connect ,receive , disconnect, sendrequest, receiveRTSPreponse

mRTPConn = new ARTPConnection

用于RTP RTCP包的接收,分析, parseRTP, parseRTCP. 这里面会创建ARTPSource , 对应于音频视频。
ARTPSource 分析SDP中的信息H264/ MP4A-LATM/ AMR/ MP2T/ ...
生成相对应的解封装的类AAVCAssembler, AMPEG4AudioAssembler, AAMRAssembler, AMPEG2TSAssembler

loop()->registerHandle(mConn);   

消息处理运行在RTSPSource的mLooper中。
mNetLooper()->registerHandle(mRTPConn);消息处理运行在单独启动的线程中。

2) OnMessageReceived()

用来处理RTSP的response,RTSP 发送消息在RTSPConnection里面发送。

当收到conn , 则发送 describe, AMessage('desc')

当收到disc, 则quit or reconnect处理

当收到desc, 分析result和response, 分析SDP, setuptrack(1) setuptrack 里面会创建APacketSource, 构造一个TrackInfo,push到mTracks中
APacketSource主要用来存放Track的一些MetaData, 可以扩展,比如kKeyWidth,kKeyHeigh,kKeySampleRate,kKeyAVCC,....
然后发送setu消息

当收到setu, UDP 需要PokeAHole, 添加一个流准备接收数据,RTPConn ->addStream. 然后发送play消息

当收play, 分析npt时间,记录rtptime

当收到accu ,表示得到一个视频或音频帧
这是ARTPConnection发上来的消息
可以处理timeupdate ,first-rtp ..
如果10s timeout收不到任何rtp包 (postAccessunitTimeout) eos
将第一个收到的包rtptime 作为这个Track时间基准 setAnchor。

mRTPAnchor/mRTPBaseTIme,/mLastRTPTime,都是 rtp-time作为单位

onTimeUpdate更新rtptime npttime
调用OnAccessUnitComplete()

addMediaTimeStamp() 计算出PTS 将视频音频泳衣时间单位毫秒,

postQueueAccessunit() , 将消息kWhatAccessUnit发送到RTSPSource 处理

4. NuPlayer::feedDecoderInputData

从RTSPSource里面dequeue accessunit

mSource->dequeueAccessUnit(audio,&accessunit)

5. RTSPSource里的消息

RTSPSource的与播放框架接口,是播放框架的数据源。

NuPlayer通过RTSPSource接口得到数据流的信息和解码数据本身,RTSPSource的接口有start、getFormat、getDuration、seekTo、dequeueAccessUnit、feedMoreTSData和stop。

AnotherPacketSource在RTSPSource中作为mAudioTrack和mVideoTrack,他虽然继承了MediaSource接口,但是并没有使用read来读数据,而是通过dequeueAccessUnit接
口,Server端的压缩流通过queueAccessUnit保存到这里

kWhatNotify => 表示MyHandle发过来的消

Myhandle::kWhatConnected , 接收到第一个Accu,则认为连接上,调用OnConnected()

notify 上层VideoSizeChanged, Prepared.
创建Tracks, 放入mTracks. ()
mAudioTrack, mVideoTrack ,info.mSource = new AnotherPacketSource

AnotherPacketSource 继承MediaSource
将AccessUnit存放于mBuffers的list里面, queueAccessUnit,dequeueAccessUnit

Myhandle::kWhatDisconnected
Myhandle::kWhatAccessunit 消息中携带accessunit的buffer

6. RTPConnection

通过消息的方式不停的onPollStreams ( select ,read RTP 包)

然后parseRTP ,调用RTPSource 的ProcessRTPPacket,对应queuePacket,并且调用相应的ARTPAssembler. 解封装,
AAVCAssembler, AMPEG4AudioAssembler, AAMRAssembler,

AMPEG2TSAssembler继承ARTPAssembler
这里会解析出accessunit并通过notify发送出去。

new Abuffer, 扩展任意meta,这里携带了rtp-time.

AMessage如何携带ABuffer ? 调用msg->setBuffer("access-unit",buf);
接受者可以通过msg->findBuffer("access-unit",&buf)可以提取出来

7. ACodec

相当于stagefrightplayer里面的OMXCodec,就是实例化OMX的Client,调用OMX组件实现解码

状态机
mUninitializedState, mLoaded , mLoadedtoIdleState, mIdelToExecutingState, mExecutingState, mExecutingtoIdleState, mIdletoLoadedState , mFlushingState.

当有消息来的时候,如果派生类有重写,则会调到重写的方法里,如果没有,则会调到BaseState.

ACodec收到的消息分两种,一种是MediaCodec传过来的,一种是OMX Component传过来的。分别对应onMessageReceived和onOMXMessage.

onOMXMessage里面又分4种:EVENT,EMPTY_BUFFER_DONE,FILL_BUFFER_DONE,FRAME_RENDERED.

ACodec是如何实现同OMXNodeInstance实现消息传递:

ACodec将CodecObserver observer对象通过omx->allocateNode()传递到OMXNodeInstance.

OMXNodeInstance将kCallbacks(OnEvent,OnEmptyBufferDone,OnFillBufferDone)传递给OMX Component.

当OMX Component有消息notify上来时,OMXNodeInstance最先收到,然后调用OMX.cpp。将消息在OMX.cpp里面将OMX Component thread转换到CallbackDispatcher线程中处理。CallbackDispatcher又将消息反调到OMXNodeInstance. 最后调用mObserver->onMessage()回到ACodec中

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352

推荐阅读更多精彩内容

  • RTSP SDP RTP/RTCP 介绍应用层 RTSP、SDP; 传输层 RTP、TCP、UDP; 网络层 IP...
    Atom_Woo阅读 3,829评论 0 7
  • gihub:https://github.com/wangdxh/Desert-Eagle/只实现了视频的处理。r...
    little_wang阅读 9,733评论 0 21
  • 在这个千变万化的世界中,人生一词是人们一直都在争辩的一个话题,很多人都追求有一个完美的人生,但是,以我的角度来思考...
    夜雪无缘阅读 935评论 3 9
  • 挣扎 一路携手并肩,用汗和泪写永远。 夜夜梦里回首,壮志任在胸。 等候岁月,波涛汹涌的自由。 灯塔守候,...
    夜未眠V良人阅读 360评论 0 0
  • 韦盖利 为了让妻子高兴,让她看起来“很美”,男人往往会对自己的妻子撒谎,以下是他们常用的七大谎言。 1、“不,你看...
    N师学院N4058阅读 615评论 0 0