gogo专业大尺度亚洲高清人体,美女张开双腿让男生桶,亚洲av无码一区二区三区鸳鸯影院,久久久久国产精品人妻

當(dāng)前位置:首頁(yè) > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > Android視頻監(jiān)控實(shí)現(xiàn)(四)

Android視頻監(jiān)控實(shí)現(xiàn)(四) 時(shí)間:2018-09-26      來(lái)源:未知

第五章 開(kāi)發(fā)指南

5.1 視頻采集安卓端(spydroid)

作為遠(yuǎn)端采集端,App啟動(dòng)后,連接并保活至服務(wù)器。采集安卓攝像頭視頻和mic聲音,進(jìn)行H264和AAC編碼(這里spydroid實(shí)現(xiàn)了硬編碼,目前大部分Android音視頻采集都支持硬編碼),再通過(guò)RTSP和RTP,將實(shí)時(shí)音視頻數(shù)據(jù)推送到流媒體服務(wù)器,并由流媒體服務(wù)器進(jìn)行轉(zhuǎn)發(fā)和分發(fā),實(shí)現(xiàn)直播。

這里主要就是RTSP/RTP的推送過(guò)程,下面章節(jié)中DSS的先偵聽(tīng)后推送式流媒體轉(zhuǎn)發(fā)詳細(xì)描述了這個(gè)過(guò)程,咱們這里直接修改spydroid中的RTSPClient就可以實(shí)現(xiàn)ANNOUNCE/SETUP/PLAY/RTP過(guò)程了。

5.1.1 Package

net.majorkernelpanic.http主要是介紹http server,spydroid自身內(nèi)置http服務(wù)器,客戶(hù)端可以通過(guò)在VLC等播放器中輸入//ip:8080/播放。

net.majorkernelpanic.spydroid主要是Application。

net.majorkernelpanic.spydroid.api主要是幾個(gè)服務(wù)。

net.majorkernelpanic.spydroid.ui主要是activity的界面部分。

net.majorkernelpanic.mp4主要是介紹提取mp4文件的profile,sps,pps等信息。

net.majorkernelpanic.streaming.rtsp 主要是介紹rtsp服務(wù)器部分,spydroid自身內(nèi)置rtsp服務(wù)器,客戶(hù)端可以通過(guò)在VLC等播放器中輸入rtsp://ip:8086/播放。

net.majorkernelpanic.streaming.rtp主要是介紹rtp協(xié)議通信。

net.majorkernelpanic.spydroid主要是activity的界面部分。

net.majorkernelpanic.streaming主要是stream接口和抽象類(lèi)。

net.majorkernelpanic.streaming.audio介紹音頻部分。

net.majorkernelpanic.streaming.video介紹視頻部分。

5.1.2 spydroid運(yùn)行流程

程序運(yùn)行時(shí),進(jìn)入net.majorkernelpanic.spydroid.SpydroidActivity,該activity 運(yùn)行時(shí)候,開(kāi)啟http server ,rtsp server,RTSP Client。這里重點(diǎn)關(guān)注rtsp server服務(wù)與RTSP Client服務(wù)。

RTSP Server服務(wù)

rtspserver開(kāi)啟后,啟動(dòng)一個(gè)線(xiàn)程RequestListenerThread,負(fù)責(zé)監(jiān)聽(tīng)客戶(hù)端(這里用VLC)的請(qǐng)求

public void start() throws IOException

{

if (running) return;

running = true;

listenerThread = new RequestListenerThread(port, handler);

listenerThread.start();

}

當(dāng)有客戶(hù)端請(qǐng)求的時(shí)候,開(kāi)啟一個(gè)workerTread線(xiàn)程。一個(gè)線(xiàn)程session代表一個(gè)請(qǐng)求

new WorkerThread(server.accept(), handler).start();

VLC向rtsp服務(wù)器進(jìn)行交互時(shí),這里就需要用到rtsp協(xié)議的內(nèi)容了,主要分為Options,Describe,Setup,play,teardown這5步驟。關(guān)于RTSP協(xié)議請(qǐng)查看我們提供的《RTSP協(xié)議詳解中文版》。

options請(qǐng)求時(shí),發(fā)送可用的狀態(tài)。describe請(qǐng)求時(shí),發(fā)送流類(lèi)型,在這里是h264視頻流,以及mp4 的profile,sps,pps,在不同手機(jī)上,profile,sps,pps的數(shù)值不一定相同。這個(gè)是通過(guò)提取錄制的該手機(jī)上的mp4文件的內(nèi)容得到的。 除了H264,這里也可以是H263視頻流,或者其他audio音頻流。這里重點(diǎn)查看generateSessionDescriptor()方法,比如在這里,選擇H264,那么就可以看看H264Stream這個(gè)類(lèi)的這個(gè)方法,看看它是如何獲取profile ,sps,pps的setup請(qǐng)求時(shí),主要關(guān)注stream.prepare(),stream.start()方法,prepare()的時(shí)候調(diào)用初始化視頻錄制的參數(shù),比如H264編碼,分辨率,幀數(shù)等相關(guān)信息。而start()方法就開(kāi)始通過(guò)localsocket把錄制的視頻以流的形式發(fā)送到本地,而H264Packetizer通過(guò)獲取其輸入流,然后對(duì)其rtp打包處理,發(fā)送。

mMediaRecorder = new MediaRecorder();

mMediaRecorder.setCamera(mCamera);

mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);

mMediaRecorder.setVideoEncoder(mVideoEncoder);

mMediaRecorder.setPreviewDisplay(mSurfaceView.getHolder().getSurface());

mMediaRecorder.setVideoSize(mRequestedQuality.resX, mRequestedQuality.resY);

//mMediaRecorder.setVideoFrameRate(mRequestedQuality.framerate);

mMediaRecorder.setVideoEncodingBitRate((int)(mRequestedQuality.bitrate * 0.8));

mMediaRecorder.setOutputFile(TESTFILE);

mMediaRecorder.setMaxDuration(3000);

// We wait a little and stop recording

mMediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener()

{

public void onInfo(MediaRecorder mr, int what, int extra)

{

Log.d(TAG, "MediaRecorder callback called !");

if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED)

{

Log.d(TAG, "MediaRecorder: MAX_DURATION_REACHED");

}

else if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED)

{

Log.d(TAG, "MediaRecorder: MAX_FILESIZE_REACHED");

}

else if (what == MediaRecorder.MEDIA_RECORDER_INFO_UNKNOWN)

{

Log.d(TAG, "MediaRecorder: INFO_UNKNOWN");

}

else

{

Log.d(TAG, "WTF ?");

}

mLock.release();

}

});

// Start recording

mMediaRecorder.prepare();

mMediaRecorder.start();

RTSP Client服務(wù)

Spydroid本身并沒(méi)有RTSP Client功能,但是其提供了一個(gè)RTSPClient,這里我們參照RTSPServer直接修改spydroid中的RTSPClient實(shí)現(xiàn)ANNOUNCE/SETUP/PLAY/RTP過(guò)程了。

RTSPClient啟動(dòng)后,執(zhí)行startStream(),首先進(jìn)行Session的設(shè)置。

public void setSession()

{

Session msession = new Session();

msession = null;

try

{

msession = UriParser.parse("rtsp://" + mTmpParameters.host + ":" + mTmpParameters.port);

}

catch (IllegalStateException e)

{

// TODO 自動(dòng)生成的 catch 塊

e.printStackTrace();

}

catch (IOException e)

{

// TODO 自動(dòng)生成的 catch 塊

e.printStackTrace();

}

msession.setOrigin("127.0.0.1");

if (msession.getDestination() == null)

{

msession.setDestination("mTmpParameters.host");

}

try

{

msession.syncConfigure();

}

catch (CameraInUseException e)

{

// TODO 自動(dòng)生成的 catch 塊

e.printStackTrace();

}

catch (StorageUnavailableException e)

{

// TODO 自動(dòng)生成的 catch 塊

e.printStackTrace();

}

catch (ConfNotSupportedException e)

{

// TODO 自動(dòng)生成的 catch 塊

e.printStackTrace();

}

catch (InvalidSurfaceException e)

{

// TODO 自動(dòng)生成的 catch 塊

e.printStackTrace();

}

catch (RuntimeException e)

{

// TODO 自動(dòng)生成的 catch 塊

e.printStackTrace();

}

catch (IOException e)

{

// TODO 自動(dòng)生成的 catch 塊

e.printStackTrace();

}

mTmpParameters.session = msession;

}

當(dāng)然還有一些流被送到的路徑的設(shè)置,RTSP服務(wù)器的目的地址的設(shè)置,如果在服務(wù)器上啟用身份驗(yàn)證,你需要設(shè)置用戶(hù)名/密碼等等。所有準(zhǔn)備工作準(zhǔn)備無(wú)誤后嘗試連接服務(wù)器。

private void tryConnection() throws IOException

{

mCSeq = 0;

mSocket = new Socket(mParameters.host, mParameters.port);

mBufferedReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));

mOutputStream = mSocket.getOutputStream();

sendRequestAnnounce();

sendRequestSetup();

sendRequestRecord();

}

在這個(gè)函數(shù)中可以清楚的看到,RTSPClient連接服務(wù)器推送視頻流所發(fā)送的請(qǐng)求及順序。

ANNOUNCE:該方法方法有兩個(gè)目的:當(dāng)從客戶(hù)端發(fā)往服務(wù)器端,ANNOUNCE向服務(wù)器端上傳請(qǐng)求URL所標(biāo)識(shí)的表示或媒體對(duì)象的描述。當(dāng)從服務(wù)器端發(fā)往客戶(hù)端,ANNOUNCE實(shí)時(shí)更新會(huì)話(huà)描述。當(dāng)一個(gè)新的媒體流加入一個(gè)表示(例如:在一個(gè)現(xiàn)場(chǎng)表示活動(dòng)期間)時(shí),整個(gè)表示而不僅是所增加的部分,應(yīng)該被重發(fā),以便部分刪除。

SETUP:讓服務(wù)器給流分配資源,啟動(dòng)RTSP會(huì)話(huà)。

RECORD(PLAY):?jiǎn)?dòng)SETUP所分配的流的數(shù)據(jù)傳輸。

5.2 流媒體服務(wù)器(Darwin Streaming Server)

5.2.1 DSS的框架

服務(wù)器的作用是充當(dāng)網(wǎng)絡(luò)客戶(hù)和服務(wù)器模塊的接口,其中網(wǎng)絡(luò)客戶(hù)使用RTP和RTSP協(xié)議來(lái)發(fā)送請(qǐng)求和接收響應(yīng),而服務(wù)器模塊則負(fù)責(zé)處理請(qǐng)求和向客戶(hù)端發(fā)送數(shù)據(jù)包。核心服務(wù)器通過(guò)創(chuàng)建四種類(lèi)型的線(xiàn)程來(lái)完成自己的工作,具體如下:

•服務(wù)器自己擁有的主線(xiàn)程(Main Thread)。這個(gè)線(xiàn)程負(fù)責(zé)檢查服務(wù)器是否需要關(guān)閉,記錄狀態(tài)信息,或者打印統(tǒng)計(jì)信息。

•空閑任務(wù)線(xiàn)程(Idle Task Thread)。空閑任務(wù)線(xiàn)程管理一個(gè)周期性的任務(wù)隊(duì)列。該任務(wù)隊(duì)列有兩種類(lèi)型:超時(shí)任務(wù)和套接口任務(wù)。

•事件線(xiàn)程(Event Thread)。事件線(xiàn)程負(fù)責(zé)偵聽(tīng)套接口事件,比如收到RTSP請(qǐng)求和RTP數(shù)據(jù)包,然后把事件傳遞給任務(wù)線(xiàn)程。

•一個(gè)或者多個(gè)任務(wù)(Task)線(xiàn)程。任務(wù)線(xiàn)程從事件線(xiàn)程中接收RTSP和RTP請(qǐng)求,然后把請(qǐng)求傳遞到恰當(dāng)?shù)姆⻊?wù)器模塊進(jìn)行處理,把數(shù)據(jù)包發(fā)送給客戶(hù)端。缺省情況下,核心服務(wù)器為每一個(gè)處理器創(chuàng)建一個(gè)任務(wù)線(xiàn)程。

5.2.2 模塊

媒體服務(wù)器使用模塊來(lái)響應(yīng)各種請(qǐng)求及完成任務(wù)。有三種類(lèi)型的模塊:

1. 內(nèi)容管理模塊

內(nèi)容管理模塊負(fù)責(zé)管理與媒體源相關(guān)的RTSP請(qǐng)求和響應(yīng),比如一個(gè)文件或者一個(gè)廣播。每個(gè)模塊負(fù)責(zé)解釋客戶(hù)的請(qǐng)求,讀取和解析它們的支持文件或者網(wǎng)絡(luò)源,并且以RTSP和RTP的方式進(jìn)行響應(yīng)。在某些情況下,比如流化mp3的模塊,使用的則是HTTP。

QTSSFileModule,QTSSReflectorModule,QTSSRelayModule,和QTSSMP3StreamingModule都是內(nèi)容管理模塊。

2. 服務(wù)器支持模塊

服務(wù)器支持模塊執(zhí)行服務(wù)器數(shù)據(jù)的收集和記錄功能。服務(wù)器模塊包括QTSSErrorLogModule, QTSSAccessLogModule,QTSSWebStatsModule,QTSSWebDebugModule, QTSSAdminModule,和QTSSPOSIXFileSystemModule。

3. 訪(fǎng)問(wèn)控制模塊

訪(fǎng)問(wèn)控制模塊提供鑒權(quán)和授權(quán)功能,以及操作URL路徑提供支持。

訪(fǎng)問(wèn)控制模塊包括QTSSAccessModule,QTSSHomeDirectoryModule,QTSSHttpFileModule,和QTSSSpamDefenseModule。

5.2.3 數(shù)據(jù)

當(dāng)一個(gè)模塊需要訪(fǎng)問(wèn)客戶(hù)請(qǐng)求的RTSP報(bào)頭時(shí),可以通過(guò)QTSS.h這個(gè)API頭文件中定義的請(qǐng)求對(duì)象來(lái)訪(fǎng)問(wèn)相應(yīng)的請(qǐng)求信息。舉例來(lái)說(shuō),RTSPRequestInterface類(lèi)實(shí)現(xiàn)了API字典元素,這些元素可以通過(guò)API來(lái)進(jìn)行訪(fǎng)問(wèn)。名稱(chēng)是以“Interface”結(jié)尾的對(duì)象,比如RTSPRequestInterface,RTSPSessionInterface,和QTSServerInterface,則用于實(shí)現(xiàn)模塊的API。

下面是重要的接口類(lèi):

•QTSServerInterface — 這是內(nèi)部數(shù)據(jù)的存儲(chǔ)對(duì)象,在API中標(biāo)識(shí)為QTSS_ServerObject。在API中的每一個(gè)QTSS_ServerAttributes都在基類(lèi)中聲明和實(shí)現(xiàn)。

•RTSPSessionInterace — 這是內(nèi)部數(shù)據(jù)的存儲(chǔ)對(duì)象,在API中標(biāo)識(shí)為qtssRTSPSessionObjectType。在API中的每一個(gè)QTSS_RTSPSessionAttributes都在基類(lèi)中聲明和實(shí)現(xiàn)。

•RTPSessionInterface — 這是內(nèi)部數(shù)據(jù)的存儲(chǔ)對(duì)象,在API中標(biāo)識(shí)為QTSS_ClientSessionObject。在API中的每一個(gè)QTSS_ClientSessionAttributes都在基類(lèi)中聲明和實(shí)現(xiàn)。

•RTSPRequestInterface — 這是內(nèi)部數(shù)據(jù)的存儲(chǔ)對(duì)象,在API中標(biāo)識(shí)為QTSS_RTSPRequestObject。在API中的每一個(gè)QTSS_RTSPRequestAttributes都在基類(lèi)中聲明和實(shí)現(xiàn)。

5.2.4 源代碼的組織

Server.tproj

這個(gè)目錄包含核心服務(wù)器(core server)的代碼,可以分成三個(gè)子系統(tǒng):

•服務(wù)器內(nèi)核。這個(gè)子系統(tǒng)中的類(lèi)都有一個(gè)QTSS前綴。QTSServer負(fù)責(zé)處理服務(wù)器的啟動(dòng)和關(guān)閉。QTSServerInterface負(fù)責(zé)保存服務(wù)器全局變量,以及收集服務(wù)器的各種統(tǒng)計(jì)信息。QTSSPrefs是存儲(chǔ)服務(wù)器偏好設(shè)定的地方。QTSSModule,QTSSModuleInterface,和QTSSCallbacks類(lèi)的唯一目的就是支持QTSS的模塊API。

•RTSP子系統(tǒng)。這些類(lèi)負(fù)責(zé)解析和處理RTSP請(qǐng)求,以及實(shí)現(xiàn)QTSS模塊API的RTSP部分。其中的幾個(gè)類(lèi)直接對(duì)應(yīng)QTSS API的一些元素(比如,RTSPRequestInterface類(lèi)就是對(duì)應(yīng)于QTSS_RTSPRequestObject對(duì)象)。每個(gè)RTSP TCP連接都有一個(gè)RTSP會(huì)話(huà)對(duì)象與之相對(duì)應(yīng)。RTSPSession對(duì)象是一個(gè)Task對(duì)象,負(fù)責(zé)處理與RTSP相關(guān)的事件。

•RTP子系統(tǒng)。這些類(lèi)處理媒體數(shù)據(jù)的發(fā)送。RTPSession對(duì)象包含與所有RTSP會(huì)話(huà)ID相關(guān)聯(lián)的數(shù)據(jù)。每個(gè)RTPSession都是一個(gè)Task對(duì)象,可以接受核心服務(wù)器的調(diào)度來(lái)進(jìn)行RTP數(shù)據(jù)包的發(fā)送。RTPStream對(duì)象代表一個(gè)單獨(dú)的RTP流,一個(gè)RTPSession對(duì)象可以和任何數(shù)目的RTPStream對(duì)象相關(guān)聯(lián)。這兩個(gè)對(duì)象實(shí)現(xiàn)了QTSS模塊API中的針對(duì)RTP的部分。

CommonUtilitiesLib

這個(gè)目錄含有一個(gè)工具箱,包括線(xiàn)程管理,數(shù)據(jù)結(jié)構(gòu),網(wǎng)絡(luò),和文本解析工具。Darwin流媒體服務(wù)器及其相關(guān)工具通過(guò)這些類(lèi)對(duì)類(lèi)似或者相同的任務(wù)進(jìn)行抽象,以減少重復(fù)代碼;這些類(lèi)的封裝簡(jiǎn)化了較高層次的代碼;借助這些類(lèi)還分離了專(zhuān)用于不同平臺(tái)的代碼。下面是對(duì)目錄下的各個(gè)類(lèi)的簡(jiǎn)短描述:

•OS類(lèi)。這些類(lèi)在時(shí)間,條件變量,互斥鎖,和線(xiàn)程方面提供了專(zhuān)用于不同平臺(tái)的代碼抽象。這些類(lèi)包括OS,OSCond,OSMutex,OSThread,和OSFileSource;數(shù)據(jù)結(jié)構(gòu)則包括OSQueue,OSHashTable,OSHeap,和OSRef。

•套接口類(lèi)(Sockets)。這些類(lèi)為T(mén)CP和UDP網(wǎng)絡(luò)通訊方面提供了專(zhuān)用于不同平臺(tái)的代碼抽象。通常情況下,套接口類(lèi)是異步的(或者說(shuō)是非阻塞的),可以發(fā)送事件給Task對(duì)象。這些類(lèi)有:EventContext,Socket,UDPSocket,UDPDemuxer,UDPSocketPool,TCPSocket,和TCPListenerSocket。

•解析工具。這些類(lèi)負(fù)責(zé)解析和格式化文本。包括StringParser,StringFormatter,StrPtrLen,和StringTranslator。

•Task(任務(wù)):這些類(lèi)實(shí)現(xiàn)了服務(wù)器的異步事件機(jī)制。

QTFileLib

流媒體服務(wù)器的一個(gè)主要特性就是它能夠?qū)⑺饕瓿?hinted)的QuickTime電影文件通過(guò)RTSP和RTP協(xié)議提供給客戶(hù)。這個(gè)目錄包含QTFile庫(kù)的源代碼,包括負(fù)責(zé)解析索引完成的QuickTime文件的代碼。服務(wù)器的RTPFileModule通過(guò)調(diào)用QTFile庫(kù)來(lái)從索引過(guò)的QuickTime文件中取得數(shù)據(jù)包和元數(shù)據(jù)。QTFile庫(kù)可以解析下面幾種文件類(lèi)型:.mov,.mp4(.mov的一種修改版本),和.3gpp(.mov的一種修改版本)。

APICommonCode

這個(gè)目錄包含與API相關(guān)的類(lèi)的源代碼,比如moduletils,或者諸如記錄文件的管理這樣的公共模塊函數(shù)。

APIModules

這個(gè)目錄包含流媒體服務(wù)器模塊目錄,每個(gè)模塊都有一個(gè)目錄。

RTSPClientLib

這個(gè)目錄包含實(shí)現(xiàn)RTSP客戶(hù)端的源代碼,這些代碼可以用于連接服務(wù)器,只要該連接協(xié)議被支持。

RTCPUtilitiesLib

這個(gè)目錄包含解析RTCP請(qǐng)求的源代碼。

APIStubLib

這個(gè)目錄包含API的定義和支持文件。

HTTPUtilitiesLib

這個(gè)目錄包含解析HTTP請(qǐng)求的源代碼。

5.2.5 流轉(zhuǎn)發(fā)

引用Darwin開(kāi)發(fā)文檔里面的一段來(lái)介紹一下流轉(zhuǎn)發(fā)的拉模式和推模式:

Darwin支持兩種自動(dòng)播送的場(chǎng)景:

•先拉后推。為了發(fā)起自動(dòng)播送,RTSP客戶(hù)會(huì)發(fā)送標(biāo)準(zhǔn)的RTSP請(qǐng)求來(lái)向服務(wù)器請(qǐng)求一個(gè)流,然后服務(wù)器將該流中繼到一個(gè)或者多個(gè)流媒體服務(wù)器。這種場(chǎng)景在"先拉后推"部分中加以描述。

•先偵聽(tīng)后推送。在這個(gè)場(chǎng)景中,自動(dòng)播送在流媒體服務(wù)器接收到ANNOUNCE請(qǐng)求時(shí)被發(fā)起。這個(gè)場(chǎng)景在"先偵聽(tīng)后推送"部分中進(jìn)行描述。

先拉后推

用戶(hù)可以通過(guò)發(fā)送標(biāo)準(zhǔn)的DESCRIBE/SETUP/PLAY請(qǐng)求來(lái)向遠(yuǎn)程的源中請(qǐng)求一個(gè)流,然后將它中繼轉(zhuǎn)發(fā)到一個(gè)或者多個(gè)目的地。當(dāng)只希望讓外部流的一份拷貝占用其內(nèi)部連接的帶寬時(shí),這個(gè)功能可能有用。中繼轉(zhuǎn)發(fā)獲取一份拷貝進(jìn)行多份的復(fù)制和轉(zhuǎn)發(fā)、分發(fā)到請(qǐng)求的客戶(hù)端。圖 1.提供了一個(gè)先拉后推(pull-then-push)場(chǎng)景的實(shí)例。

圖1.先拉后推式

以圖1.作為參考,先拉后推場(chǎng)景的步驟如下:

1.流媒體服務(wù)器A(轉(zhuǎn)發(fā)服務(wù)器)發(fā)送標(biāo)準(zhǔn)的RTSP客戶(hù)DESCRIBE/SETUP/PLAY請(qǐng)求給遠(yuǎn)程服務(wù)器,即流媒體服務(wù)器B。

2.發(fā)起請(qǐng)求的中繼“客戶(hù)端”(流媒體服務(wù)器A)開(kāi)始接受流,然后向該輸入流的中繼配置中列出的所有目的地發(fā)送ANNOUNCE推送請(qǐng)求。

在Darwin中,實(shí)現(xiàn)拉模式轉(zhuǎn)發(fā)的模塊為QTSSRelayModule,每一路轉(zhuǎn)發(fā)會(huì)話(huà)為一個(gè)RelaySession對(duì)象,轉(zhuǎn)發(fā)列表存儲(chǔ)于隊(duì)列sSessionQueue中

QTSSRelayModule一開(kāi)始Initialize()讀取配置文件中關(guān)于轉(zhuǎn)發(fā)文件路徑存儲(chǔ)于sRelayPrefs靜態(tài)變量中

./relayconfig.xml

在ReadRelayPrefsFile()中讀取sRelayPrefs中配置并解析出分發(fā)列表,并對(duì)每一個(gè)分發(fā)配置中的source配置創(chuàng)建RelaySessionCreator開(kāi)啟分發(fā),并加入到sSessionQueue中,RTSPSourceInfo::RelaySessionCreator::Run() 再通過(guò)RTSPSourceInfo::RunCreateSession()開(kāi)始DESCRIBE/SETUP/PLAY拉取數(shù)據(jù),RTSP流程成功后,再配置RTP數(shù)據(jù)分發(fā)的地址,將RTP數(shù)據(jù)推送至分發(fā)列表(即destination列表與source列表同一級(jí))中,即實(shí)現(xiàn)了Darwin文檔中所述的先拉后推模式。

先偵聽(tīng)后推送

流媒體服務(wù)器可以被配置為將ANNOUNCE請(qǐng)求創(chuàng)建的輸入流自動(dòng)發(fā)送到一個(gè)或者多個(gè)目的地。這可能可以用于配制自動(dòng)播送網(wǎng)絡(luò)。圖 2.提供了一個(gè)先偵聽(tīng)后推送的場(chǎng)景的實(shí)例。

圖2.先偵聽(tīng)后推送式

以圖2.作為參考,先偵聽(tīng)后推送場(chǎng)景的步驟如下:

•遠(yuǎn)程機(jī)器(IpCamera等前端設(shè)備或者中繼服務(wù)器)向流媒體服務(wù)器A發(fā)送一個(gè)ANNOUNCE請(qǐng)求。流媒體服務(wù)器可以接受或者否認(rèn)這個(gè)請(qǐng)求。如果它接受了請(qǐng)求,則流媒體服務(wù)器會(huì)檢查其中繼配置,以確定這個(gè)流是否應(yīng)該被中繼。

•如果該流應(yīng)該被中繼,則流媒體服務(wù)器將向自身發(fā)送標(biāo)準(zhǔn)的RTSP客戶(hù)DESCRIBE/SETUP/PLAY請(qǐng)求。

•發(fā)出請(qǐng)求的中繼“客戶(hù)”(流媒體服務(wù)器A)開(kāi)始接收流,然后向相應(yīng)的輸入流的中繼配置中列出的所有目的地發(fā)送一個(gè)ANNOUCE請(qǐng)求。

注意:我們?cè)趯?shí)際的需求中常常遇到的場(chǎng)景為,前端設(shè)備RTSP Announce上線(xiàn)至中繼服務(wù)器,上報(bào)其流媒體SDP信息,前端設(shè)備再經(jīng)過(guò)'被觸發(fā)',通過(guò)SETUP/PLAY流程發(fā)起流推送,客戶(hù)端再以拉模式拉取實(shí)時(shí)視頻流,是一種先推后拉(push-then-pull)模式,如果客戶(hù)端請(qǐng)求的視頻流存在,則直接轉(zhuǎn)發(fā)已經(jīng)獲取的拷貝進(jìn)行分發(fā)。

具體的RTSP推送流程大致為:Announce、Setup、Play、RTP(DSS為RTP over TCP)。這種模式的轉(zhuǎn)發(fā)通常用于類(lèi)似于3G視頻監(jiān)控這種難以穿透的網(wǎng)絡(luò)類(lèi)型的數(shù)據(jù)的轉(zhuǎn)發(fā)。我們就不具體介紹關(guān)于DSS對(duì)會(huì)話(huà)的維護(hù)以及各自自定義的RTSP頭字段的操作等等,主要就步驟:Announce->Setup->Play->RTP數(shù)據(jù)接收與轉(zhuǎn)發(fā)進(jìn)行詳細(xì)的分析。在DSS中,處理推送報(bào)文的模塊為QTSSReflectorModule,其中維護(hù)了一個(gè)靜態(tài)的轉(zhuǎn)發(fā)列表sSessionMap,用于存儲(chǔ)各個(gè)轉(zhuǎn)發(fā)會(huì)話(huà)的信息。下面就對(duì)具體的報(bào)文解析和數(shù)據(jù)處理進(jìn)行分析。

Announce:RTSP Announce命令為源端向服務(wù)器端主動(dòng)發(fā)起的上報(bào)本地媒體sdp信息的命令,處理函數(shù)為QTSSReflectorModule模塊的DoAnnounce()函數(shù),這里就只對(duì)該函數(shù)的重點(diǎn)部分進(jìn)行解析,不全部一一描述了。首先判斷server配置中的enable_broadcast_announce字段是否為true,開(kāi)啟了廣播推送轉(zhuǎn)發(fā),在通過(guò)獲取inParams->inRTSPRequest(在RTSPSession::Run調(diào)用前復(fù)制的當(dāng)前請(qǐng)求的rtspRequest對(duì)象)的字典中的qtssRTSPReqLocalPath鍵值作為標(biāo)識(shí)轉(zhuǎn)發(fā)的唯一區(qū)別(例如:.\Movies/test.sdp,必須以sdp結(jié)尾,可以修改sSDPSuffix進(jìn)行配置),這里的值既是一個(gè)標(biāo)識(shí),又是一個(gè)路徑,用于存儲(chǔ)獲取到的sdp數(shù)據(jù),后面此標(biāo)識(shí)作為存儲(chǔ)于sSessionMap中對(duì)象的鍵值。函數(shù)中通過(guò)對(duì)頭字段的解析,獲取到Content-Length:字段值,進(jìn)而去讀取具體的spd值,再存儲(chǔ)到qtssRTSPReqLocalPath路徑中,返回200 OK。

Setup:這里的只解析DoSetup中isPush為true(表示為推送的Session)這條路路徑,具體isPush值由Setup請(qǐng)求中的mode值有關(guān),mode="receive" || mode="record"表示isPush為true,

else

{

theSession = DoSessionSetup(inParams, qtssRTSPReqFilePathTrunc, isPush, &foundSession);//根據(jù)前面Announce中存儲(chǔ)于qtssRTSPReqLocalPath的路徑讀取sdp信息,創(chuàng)建轉(zhuǎn)發(fā)會(huì)話(huà)ReflectorSession,或者直接引用已經(jīng)存在的Session

if (theSession == NULL)

return QTSS_RequestFailed;

// This is an incoming data session. Set the Reflector Session in the ClientSession

theErr = QTSS_SetValue(inParams->inClientSession, sClientBroadcastSessionAttr, 0, &theSession, sizeof(theSession));//ReflectorSession附屬于RTPSession中的sClientBroadcastSessionAttr字典

Assert(theErr == QTSS_NoErr);

//qtss_printf("QTSSReflectorModule.cpp:SETsession sClientBroadcastSessionAttr=%"_U32BITARG_" theSession=%"_U32BITARG_" err=%"_S32BITARG_" \n",(UInt32)sClientBroadcastSessionAttr, (UInt32) theSession,theErr);

(void) QTSS_SetValue(inParams->inClientSession, qtssCliSesTimeoutMsec, 0, &sBroadcasterSessionTimeoutMilliSecs, sizeof(sBroadcasterSessionTimeoutMilliSecs));

}

這里需要注意的是,當(dāng)我們前面已經(jīng)有一路相同qtssRTSPReqLocalPath路徑的ReflectorSession存在的時(shí)候,將不進(jìn)行再創(chuàng)建,直接Resolve原有的ReflectorSession,所以會(huì)出現(xiàn)一種情況,當(dāng)開(kāi)始的推送與后面進(jìn)行的推送音視頻sdp不一致的時(shí)候,就會(huì)出現(xiàn)錯(cuò)誤,所以, ReflectorSession的引用與釋放需要注意!

完成ReflectorSession的創(chuàng)建,下一步解析track ID,具體的解析方法可以根據(jù)自己的實(shí)際應(yīng)用,有的按照track%d解析,有的按照trackID=%d解析,再根據(jù)trackId獲取具體的track sdp信息,AddRTPStream創(chuàng)建對(duì)應(yīng)于具體track的RTP流

theStreamInfo->fSetupToReceive = true;//標(biāo)識(shí)流轉(zhuǎn)發(fā)的建立

// This is an incoming data session. Set the Reflector Session in the ClientSession

theErr = QTSS_SetValue(inParams->inClientSession, sClientBroadcastSessionAttr, 0, &theSession, sizeof(theSession));//設(shè)置轉(zhuǎn)發(fā)會(huì)話(huà)的RTPSession字典的sClientBroadcastSessionAttr字段

Assert(theErr == QTSS_NoErr);

if (theSession != NULL)

theSession->AddBroadcasterClientSession(inParams);//設(shè)置ReflectorSession的fBroadcasterSession屬性為inParams->inClientSession,呵呵比較亂噢,相當(dāng)于相互引用

Play: 具體到DoPlay過(guò)程,isPush為true的路徑就比較簡(jiǎn)單了,只是將推送的RTSPSession中的sRTSPBroadcastSessionAttr屬性設(shè)置為前面DoSetup中獲取到的ReflectorSession

theLen = sizeof(inSession);

theErr = QTSS_GetValue(inParams->inClientSession, sClientBroadcastSessionAttr, 0, &inSession, &theLen);//DoSetup()中已經(jīng)設(shè)置sClientBroadcastSessionAttr屬性

if (theErr != QTSS_NoErr)

return QTSS_RequestFailed;

theErr = QTSS_SetValue(inParams->inClientSession, sKillClientsEnabledAttr, 0, &sTearDownClientsOnDisconnect, sizeof(sTearDownClientsOnDisconnect));

if (theErr != QTSS_NoErr)

return QTSS_RequestFailed;

Assert(inSession != NULL);

theErr = QTSS_SetValue(inParams->inRTSPSession, sRTSPBroadcastSessionAttr, 0, &inSession, sizeof(inSession));//設(shè)置到inParams->inRTSPSession的sRTSPBroadcastSessionAttr屬性

if (theErr != QTSS_NoErr)

return QTSS_RequestFailed;

RTP數(shù)據(jù)處理:ProcessRTPData(),這里只處理RTP over TCP的數(shù)據(jù),根據(jù)RTP數(shù)據(jù)中的channel值,調(diào)用特定的ReflectorStream進(jìn)行處理和轉(zhuǎn)發(fā),具體函數(shù)為:ProcessRTPData(),先通過(guò)前面在DoSetup() & isPush為true時(shí)設(shè)置的sRTSPBroadcastSessionAttr屬性,獲取ReflectorSession

ReflectorSession *theSession = NULL;

UInt32 theLen = sizeof(theSession);

QTSS_Error theErr = QTSS_GetValue(inParams->inRTSPSession, sRTSPBroadcastSessionAttr, 0, &theSession, &theLen);

if (theSession == NULL || theErr != QTSS_NoErr)

return QTSS_NoErr;

再根據(jù)channelID獲取具體的ReflectorStream并進(jìn)行數(shù)據(jù)推送,給具體的ReflectorStream進(jìn)行處理

UInt32 inIndex = packetChannel / 2; // one stream per every 2 channels rtcp channel handled below

ReflectorStream *theStream = NULL;

if (inIndex < numStreams)

{

theStream = theSession->GetStreamByIndex(inIndex);//獲取對(duì)應(yīng)track的ReflectorStream

SourceInfo::StreamInfo *theStreamInfo = theStream->GetStreamInfo();

UInt16 serverReceivePort = theStreamInfo->fPort;

Bool16 isRTCP = false;

if (theStream != NULL)

{

if (packetChannel & 1)

{

serverReceivePort ++;

isRTCP = true;

}

theStream->PushPacket(rtpPacket, packetDataLen, isRTCP); //推送數(shù)據(jù)給ReflectorStream并轉(zhuǎn)發(fā)給分發(fā)列表

}

}

5.2.6 二次開(kāi)發(fā)模塊添加的要求

每個(gè)DSS模塊必須實(shí)現(xiàn)兩個(gè)函數(shù):一個(gè)是Main函數(shù),服務(wù)器在啟動(dòng)時(shí)將調(diào)用這個(gè)函數(shù)進(jìn)行必要的初始化。另一個(gè)是Dispatch函數(shù),通過(guò)實(shí)現(xiàn)此函數(shù),服務(wù)器可調(diào)用DSS模塊并完成特定處理。對(duì)于編譯到服務(wù)器里面的模塊,其主函數(shù)的地址必須傳遞到服務(wù)器的模塊Main函數(shù)中。

具體實(shí)現(xiàn)時(shí),Main函數(shù)必須命名為MyModule_Main,其中MyModule是模塊的文件名。此函數(shù)的實(shí)現(xiàn)通常如下所示:

QTSS_Error MyModule_Main(void *inPrivateArgs)

{

return _stublibrary_main(inPrivateArgs, MyModuleDispatch);

}

每個(gè)DSS模塊都必須提供一個(gè)Dispatch函數(shù)。服務(wù)器為了特定的目的需要使用某個(gè)模塊時(shí),是通過(guò)調(diào)用該模塊的Dispatch函數(shù)來(lái)實(shí)現(xiàn)的,調(diào)用時(shí)必須將任務(wù)的名稱(chēng)及相應(yīng)的參數(shù)傳遞給該函數(shù)。在DSS中,使用角色(Role)這個(gè)術(shù)語(yǔ)來(lái)描述特定的任務(wù)。Dispatch函數(shù)的格式如下所示:

void MyModuleDispatch(QTSS_Role inRole,QTSS_RoleParamPtr inParams);

其中MyModuleDispatch是Dispatch函數(shù)的名稱(chēng);MyModule是模塊的文件名;inRole是角色的名稱(chēng),只有注冊(cè)了該角色的模塊才會(huì)被調(diào)用;inParams則是一個(gè)結(jié)構(gòu)體,可用于傳遞相應(yīng)的參數(shù)。

5.3 視頻播放器(VLC)

我們提供了一個(gè)裁剪過(guò)適用于此系統(tǒng)的VLC,關(guān)于原版詳細(xì)分析如下。

5.3.1 eclipse 調(diào)試源碼

原版源碼:項(xiàng)目/源碼/vlc_source_android.Zip

其中源碼獲取是:git clone git://git.videolan.org/vlc-ports/android.git

源碼介紹

相關(guān)源碼介紹 : VLC 源碼依賴(lài)于 另外四個(gè)工程;

•vlc-android 工程 : VLC 的主要源碼;

•appcompat 工程 : 低版本兼容庫(kù), VLC 源碼 vlc-android 需要依賴(lài)該工程;

•cardview 工程 : VLC 源碼 vlc-android 需要依賴(lài)該工程;

•libvlc 工程 : VLC 源碼 vlc-android 需要依賴(lài)該工程;

•WheelView 工程 : VLC 源碼 vlc-android 需要依賴(lài)該工程;

源碼導(dǎo)入

將源碼導(dǎo)入 eclipse : 主需要重新設(shè)置一下依賴(lài), 其它不用修改;

vlc 源碼依賴(lài) : vlc 依賴(lài)其余的四個(gè)工程, 下面的是 project.properties 內(nèi)容;

target=android-21

android.library.reference.1=..\\libvlc

android.library.reference.2=../appcompat

android.library.reference.3=../cardview

android.library.reference.4=../WheelView

執(zhí)行安裝

執(zhí)行效果 : 

5.3.2 包結(jié)構(gòu)詳解

VLC Android 源碼包結(jié)構(gòu)分析 :

•主包結(jié)構(gòu)截圖 : org.vedio.vlc 包下的內(nèi)容;

•audio 包 : 音頻相關(guān)的包;

•gui 包 : 界面 UI 相關(guān)包;

•interfaces 包 : 定義各種接口;

•widget 包 : 自定 義組件相關(guān)的包;

•utils 包 : 相關(guān)工具類(lèi);

5.3.3 主要類(lèi)介紹

注意 : 以下介請(qǐng)結(jié)合自己的理解去閱讀代碼

(1) org.videolan.vlc 下類(lèi)介紹

org.videolan.vlc 下類(lèi)介紹 :

•MediaDatabase 介紹 : 數(shù)據(jù)庫(kù)操作相關(guān)類(lèi), 該類(lèi)中定義了 SQLiteOpenHelper 子類(lèi), 并且定義了幾個(gè)數(shù)據(jù)庫(kù), 創(chuàng)建了以下數(shù)據(jù)表 directories_table 路徑表, media_table 媒體信息表, playlist_table 單個(gè)播放列表, playlist_media_table 播放列表集合表, searchhistory_table 搜索記錄表, mrl_table mrl 表;

•MediaGroup 介紹 : 繼承了 Media 類(lèi)(在 libvlc 中維護(hù), 維護(hù)視頻音頻后綴名稱(chēng)或擴(kuò)展名), 用于維護(hù)一個(gè) Media 集合;

•MediaLibrary 介紹 : Media 相關(guān)庫(kù), 該類(lèi)中維護(hù)了一個(gè)條目列表, 主要對(duì)這個(gè)條目列表進(jìn)行操作 (疑問(wèn), 沒(méi)看懂);

•PhoneStateReceiver 介紹 : 廣播接收者, 一個(gè)監(jiān)聽(tīng)手機(jī)電話(huà)狀態(tài)的廣播接收者, 如果有電話(huà)打入, 或結(jié)束通過(guò), 進(jìn)行對(duì)應(yīng)的操作;

•RemoteControlClientReceiver 介紹 : 廣播接收者, 通過(guò)遠(yuǎn)程 wifi, 藍(lán)牙, 屏幕鎖定解鎖 等接收事件, 進(jìn)行響應(yīng)的操作;

•Thumbnailer 介紹 : 是 Runnable 子類(lèi), thumbnail 用于代表要執(zhí)行的動(dòng)作, 等待播放的流媒體;

•VLCApplication 介紹 : Application 子類(lèi), 進(jìn)行全局的設(shè)置;

•VLCCallbackTask 介紹 : AsyncTask 的子類(lèi), 這是個(gè)回調(diào)的幫助類(lèi), 能夠在線(xiàn)程中更容易實(shí)現(xiàn)回調(diào);

•VLCCrashHandler 介紹 : 用于處理未捕獲的崩潰信息, 打印到日志 或者 文件中;

(2) org.videolan.vlc.audio 下類(lèi)介紹

org.videolan.vlc.audio 包類(lèi)介紹 :

•AudioService 介紹 : 集成 Service, 播放音頻的后臺(tái)服務(wù);

•AudioServiceController 介紹 : 音頻服務(wù)控制類(lèi);

•RepeatType 介紹 : 重復(fù)類(lèi)型枚舉定義, 不重復(fù), 重復(fù)一次, 循環(huán);

(3) org.videolan.vlc.widget 下類(lèi)介紹

org.videolan.vlc.widget 類(lèi)介紹 :

•AnimatedCoverView 介紹 : 繼承 View 組件, 自定義組件, 動(dòng)畫(huà)切換相關(guān)的 自定義 View;

•AudioMediaSwitcher 介紹 : 繼承結(jié)構(gòu) AudioMediaSwitcher -> FlingViewGroup -> ViewGroup, 音頻媒體切換相關(guān)類(lèi);

•AudioPlaylistItemViewGroup 介紹 : 繼承結(jié)構(gòu) AudioPlaylistItemViewGroup -> FlingViewGroup, 音頻播放列表相關(guān)類(lèi);

•ContentLinearLayout 介紹 : 繼承 LinearLayout, 重寫(xiě)了 onInterceptTouchEvent 方法, 用于攔截觸摸事件, 當(dāng)媒體正在播放的時(shí)候, 如果觸摸子組件, 觸發(fā)事件, 會(huì)影響播放, 此時(shí)我們需要攔截這些觸摸事件;

•EqualizerBar 介紹 : 繼承 LinearLayout, 均衡器調(diào)節(jié)條;

•ExpandableLayout 介紹 : 主要內(nèi)容;

•FlingViewGroup 介紹 : 繼承 ViewGroup, 主要是修改了一些手勢(shì)操作, 覆蓋重寫(xiě)了 onScrollChanged, onTouchEvent, onInterceptTouchEvent 方法;

•HeaderScrollView 介紹 : 繼承 HorizontalScrollView, 橫向滑動(dòng)的 View 組件;

•SlidingPaneLayout 介紹 : 繼承 ViewGroup, 這個(gè)類(lèi)是從 Android 中剝離出來(lái)的, 屬于 support-v4 中的一個(gè)類(lèi), 如果想要上下滑動(dòng), 不是左右側(cè)劃, 需要修改一些地方;

•VerticalSeekBar 介紹 : 繼承 SeekBar, 這個(gè)組件是一個(gè)垂直的拖動(dòng)條;

•VLCAppWidgetProvider 介紹 : 集成 AppWidgetProvider 類(lèi), App 組件提供者, 相當(dāng)與一個(gè)廣播接收者;

(4) org.videolan.vlc.util 下類(lèi)介紹

org.videolan.vlc.util 包類(lèi)介紹 :

•AndroidDevices 類(lèi) : 獲取手機(jī)相關(guān)信息, 是否有內(nèi)存卡, 手機(jī)型號(hào), 獲取存儲(chǔ)路徑, 獲取媒體目錄;

•BitmapCache 類(lèi) : 圖片緩存相關(guān)類(lèi), 使用 LruCache 實(shí)現(xiàn)圖片的流暢緩存;

•BitmapUtil 類(lèi) : 處理位圖相關(guān)類(lèi), 提供 邊緣切割, 縮放, 從緩存中獲取圖片;

•CustomDirectories 類(lèi) : 管理用戶(hù)信息存放路徑;

•Logcat 類(lèi) : 獲取日志, 將日志輸出到文件中;

•MurmurHash : MurmurHash算法:高運(yùn)算性能,低碰撞率,由Austin Appleby創(chuàng)建于2008年;

•Preferences : SharedPreferences 操作相關(guān);

•Strings : 用于處理字符串相關(guān)的工具類(lèi);

•Util : 一些小公共方法;

•VLCInstance : libvlc 相關(guān)的類(lèi), 與 libvlc 工程相關(guān);

•VLCRunnable : 繼承 Runnable, 線(xiàn)程相關(guān)類(lèi);

•WeakHandler : 繼承 Handler;

(5) org.videolan.vlc.interfaces 下類(lèi)介紹

org.videolan.vlc.interfaces 包介紹 :

•IAudioPlayer 介紹 : 音頻播放接口, 提供了更新 和 更新進(jìn)度條方法;

•IAudioPlayerControl 介紹 : 音頻播放控制接口, 提供了一系列的音頻控制方法;

(4) org.videolan.vlc.audio 下類(lèi)介紹

org.videolan.vlc.gui.audio.widget 包介紹 :

•CoverMediaSwitcher 介紹 : 繼承結(jié)構(gòu) CoverMediaSwitcher -> AudioMediaSwitcher -> FlingViewGroup -> ViewGroup;

•HeaderMediaSwitcher 介紹 : 繼承結(jié)構(gòu) HeaderMediaSwitcher -> AudioMediaSwitcher -> FlingViewGroup -> ViewGroup;

上一篇:Android視頻監(jiān)控實(shí)現(xiàn)(三)

下一篇:理解js中this的指向問(wèn)題

熱點(diǎn)文章推薦
華清學(xué)員就業(yè)榜單
高薪學(xué)員經(jīng)驗(yàn)分享
熱點(diǎn)新聞推薦
前臺(tái)專(zhuān)線(xiàn):010-82525158 企業(yè)培訓(xùn)洽談專(zhuān)線(xiàn):010-82525379 院校合作洽談專(zhuān)線(xiàn):010-82525379 Copyright © 2004-2022 北京華清遠(yuǎn)見(jiàn)科技集團(tuán)有限公司 版權(quán)所有 ,京ICP備16055225號(hào)-5,京公海網(wǎng)安備11010802025203號(hào)

回到頂部