Android視頻監控實現(四)
時(shi)間:2018-09-26 來源(yuan):未知(zhi)
第五(wu)章 開發指南
5.1 視頻采集安(an)卓(zhuo)端(duan)(spydroid)
作為遠端(duan)采集(ji)端(duan),App啟(qi)動后,連接并(bing)(bing)保(bao)活(huo)至服(fu)務(wu)器(qi)(qi)。采集(ji)安(an)卓攝像頭視頻和mic聲音(yin),進行H264和AAC編碼(這(zhe)里(li)spydroid實現了硬編碼,目前大(da)部(bu)分(fen)Android音(yin)視頻采集(ji)都(dou)支持(chi)硬編碼),再通過RTSP和RTP,將(jiang)實時音(yin)視頻數據(ju)推送到流(liu)(liu)媒(mei)體服(fu)務(wu)器(qi)(qi),并(bing)(bing)由(you)流(liu)(liu)媒(mei)體服(fu)務(wu)器(qi)(qi)進行轉發(fa)和分(fen)發(fa),實現直播(bo)。
這里主要(yao)就(jiu)是(shi)RTSP/RTP的(de)推送(song)過(guo)程,下面章節中DSS的(de)先偵聽后推送(song)式(shi)流媒體轉(zhuan)發詳(xiang)細描述了這個過(guo)程,咱們(men)這里直接修改spydroid中的(de)RTSPClient就(jiu)可以實現ANNOUNCE/SETUP/PLAY/RTP過(guo)程了。
5.1.1 Package

net.majorkernelpanic.http主要是介紹(shao)http server,spydroid自身內置(zhi)http服務器(qi)(qi),客(ke)戶端可以通過在VLC等播放器(qi)(qi)中輸(shu)入//ip:8080/播放。
net.majorkernelpanic.spydroid主(zhu)要是(shi)Application。
net.majorkernelpanic.spydroid.api主要是幾個服務。
net.majorkernelpanic.spydroid.ui主要是activity的(de)界面部分。
net.majorkernelpanic.mp4主要是(shi)介紹提(ti)取mp4文件的profile,sps,pps等(deng)信息。
net.majorkernelpanic.streaming.rtsp 主要是介紹rtsp服務器部分,spydroid自身內(nei)置rtsp服務器,客戶端(duan)可(ke)以通過(guo)在VLC等(deng)播(bo)放(fang)器中輸入rtsp://ip:8086/播(bo)放(fang)。
net.majorkernelpanic.streaming.rtp主要(yao)是介紹rtp協議(yi)通信。
net.majorkernelpanic.spydroid主要是activity的界面部分。
net.majorkernelpanic.streaming主要(yao)是stream接口和抽象類。
net.majorkernelpanic.streaming.audio介紹(shao)音(yin)頻(pin)部分(fen)。
net.majorkernelpanic.streaming.video介紹視頻(pin)部分(fen)。
5.1.2 spydroid運行流程
程(cheng)序(xu)運行時,進入net.majorkernelpanic.spydroid.SpydroidActivity,該activity 運行時候,開啟(qi)http server ,rtsp server,RTSP Client。這里重點關注(zhu)rtsp server服務與RTSP Client服務。
RTSP Server服務
rtspserver開(kai)啟后,啟動一個線程RequestListenerThread,負責監聽客戶端(這里用VLC)的請求
public void start() throws IOException
{
if (running) return;
running = true;
listenerThread = new RequestListenerThread(port, handler);
listenerThread.start();
}
當有客(ke)戶端請求的時候,開啟一個workerTread線程。一個線程session代表一個請求
new WorkerThread(server.accept(), handler).start();
VLC向(xiang)rtsp服務器進(jin)行交(jiao)互時,這里就需要用到rtsp協議的(de)內容了,主要分為Options,Describe,Setup,play,teardown這5步(bu)驟(zou)。關于RTSP協議請查看我們提供的(de)《RTSP協議詳(xiang)解中文(wen)版》。
options請求(qiu)時,發(fa)送(song)可(ke)用的(de)狀態(tai)。describe請求(qiu)時,發(fa)送(song)流(liu)(liu)類型,在(zai)這(zhe)(zhe)里(li)(li)是h264視(shi)頻(pin)流(liu)(liu),以及mp4 的(de)profile,sps,pps,在(zai)不(bu)同(tong)(tong)手機上,profile,sps,pps的(de)數值(zhi)不(bu)一定相同(tong)(tong)。這(zhe)(zhe)個(ge)(ge)是通過提取(qu)(qu)錄制(zhi)的(de)該(gai)手機上的(de)mp4文(wen)件的(de)內容得到(dao)的(de)。 除(chu)了H264,這(zhe)(zhe)里(li)(li)也可(ke)以是H263視(shi)頻(pin)流(liu)(liu),或者其(qi)(qi)他(ta)audio音頻(pin)流(liu)(liu)。這(zhe)(zhe)里(li)(li)重(zhong)點查看(kan)generateSessionDescriptor()方(fang)(fang)法(fa),比(bi)如(ru)在(zai)這(zhe)(zhe)里(li)(li),選擇(ze)H264,那么就(jiu)可(ke)以看(kan)看(kan)H264Stream這(zhe)(zhe)個(ge)(ge)類的(de)這(zhe)(zhe)個(ge)(ge)方(fang)(fang)法(fa),看(kan)看(kan)它是如(ru)何獲取(qu)(qu)profile ,sps,pps的(de)setup請求(qiu)時,主要關注stream.prepare(),stream.start()方(fang)(fang)法(fa),prepare()的(de)時候調用初始化(hua)視(shi)頻(pin)錄制(zhi)的(de)參數,比(bi)如(ru)H264編(bian)碼,分辨率,幀(zhen)數等相關信(xin)息。而start()方(fang)(fang)法(fa)就(jiu)開始通過localsocket把錄制(zhi)的(de)視(shi)頻(pin)以流(liu)(liu)的(de)形式發(fa)送(song)到(dao)本地(di),而H264Packetizer通過獲取(qu)(qu)其(qi)(qi)輸入流(liu)(liu),然后對其(qi)(qi)rtp打包處理,發(fa)送(song)。
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服務
Spydroid本(ben)身并沒有RTSP Client功能,但(dan)是(shi)其提(ti)供了一個(ge)RTSPClient,這里我們參照RTSPServer直(zhi)接(jie)修改spydroid中的(de)RTSPClient實現ANNOUNCE/SETUP/PLAY/RTP過程了。
RTSPClient啟動(dong)后(hou),執行startStream(),首先(xian)進行Session的設置。
public void setSession()
{
Session msession = new Session();
msession = null;
try
{
msession = UriParser.parse("rtsp://" + mTmpParameters.host + ":" + mTmpParameters.port);
}
catch (IllegalStateException e)
{
// TODO 自(zi)動生成的 catch 塊(kuai)
e.printStackTrace();
}
catch (IOException e)
{
// TODO 自動生(sheng)成的 catch 塊
e.printStackTrace();
}
msession.setOrigin("127.0.0.1");
if (msession.getDestination() == null)
{
msession.setDestination("mTmpParameters.host");
}
try
{
msession.syncConfigure();
}
catch (CameraInUseException e)
{
// TODO 自(zi)動生成的(de) catch 塊
e.printStackTrace();
}
catch (StorageUnavailableException e)
{
// TODO 自動生(sheng)成的 catch 塊
e.printStackTrace();
}
catch (ConfNotSupportedException e)
{
// TODO 自(zi)動(dong)生成的(de) catch 塊
e.printStackTrace();
}
catch (InvalidSurfaceException e)
{
// TODO 自動生成(cheng)的 catch 塊(kuai)
e.printStackTrace();
}
catch (RuntimeException e)
{
// TODO 自動生成的(de) catch 塊(kuai)
e.printStackTrace();
}
catch (IOException e)
{
// TODO 自(zi)動生(sheng)成(cheng)的 catch 塊
e.printStackTrace();
}
mTmpParameters.session = msession;
}
當然還(huan)有(you)一些流被送(song)到的(de)路徑的(de)設置,RTSP服務(wu)器的(de)目的(de)地址的(de)設置,如果在(zai)服務(wu)器上啟用(yong)(yong)身份(fen)驗證,你需要設置用(yong)(yong)戶名/密碼等(deng)等(deng)。所有(you)準備工作(zuo)準備無誤后嘗試連接服務(wu)器。
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();
}
在這個(ge)函數中可以清楚的看(kan)到,RTSPClient連接服(fu)務器推送視頻流所發送的請求及順序。
ANNOUNCE:該方(fang)(fang)法方(fang)(fang)法有(you)兩個(ge)(ge)目的(de):當(dang)從客戶(hu)端發往(wang)服(fu)務(wu)(wu)器端,ANNOUNCE向(xiang)服(fu)務(wu)(wu)器端上(shang)傳請求URL所(suo)標識的(de)表(biao)(biao)示(shi)或媒(mei)體(ti)對(dui)象的(de)描(miao)述。當(dang)從服(fu)務(wu)(wu)器端發往(wang)客戶(hu)端,ANNOUNCE實時(shi)更新會話描(miao)述。當(dang)一(yi)個(ge)(ge)新的(de)媒(mei)體(ti)流加(jia)入一(yi)個(ge)(ge)表(biao)(biao)示(shi)(例如(ru):在一(yi)個(ge)(ge)現場表(biao)(biao)示(shi)活動期間(jian))時(shi),整個(ge)(ge)表(biao)(biao)示(shi)而(er)不僅(jin)是所(suo)增加(jia)的(de)部分,應該被重發,以(yi)便部分刪除。
SETUP:讓服務器給流分配資源,啟動RTSP會話。
RECORD(PLAY):啟(qi)動SETUP所(suo)分配(pei)的(de)流的(de)數據傳輸。
5.2 流媒體(ti)服務器(Darwin Streaming Server)
5.2.1 DSS的框架
服(fu)(fu)務(wu)(wu)器的作(zuo)用是充當(dang)網絡客(ke)戶(hu)和服(fu)(fu)務(wu)(wu)器模塊(kuai)的接(jie)口,其中網絡客(ke)戶(hu)使用RTP和RTSP協議來發(fa)送請(qing)求和接(jie)收響應,而(er)服(fu)(fu)務(wu)(wu)器模塊(kuai)則負責處理請(qing)求和向客(ke)戶(hu)端發(fa)送數據包。核心服(fu)(fu)務(wu)(wu)器通過創建四種類型的線程(cheng)來完成(cheng)自己的工作(zuo),具體如下:
•服(fu)務器自己擁有的主線程(cheng)(Main Thread)。這個線程(cheng)負責檢查(cha)服(fu)務器是否需要關(guan)閉,記錄狀態信息(xi),或者打印統計信息(xi)。
•空(kong)閑任務(wu)線程(cheng)(Idle Task Thread)。空(kong)閑任務(wu)線程(cheng)管理一個周期(qi)性的任務(wu)隊列(lie)。該任務(wu)隊列(lie)有兩種類(lei)型(xing):超時(shi)任務(wu)和套(tao)接口任務(wu)。
•事(shi)件(jian)線(xian)程(Event Thread)。事(shi)件(jian)線(xian)程負責偵聽套(tao)接(jie)口事(shi)件(jian),比如(ru)收到(dao)RTSP請求和RTP數據包(bao),然后(hou)把事(shi)件(jian)傳遞給(gei)任務線(xian)程。
•一個(ge)或者多個(ge)任務(wu)(wu)(Task)線(xian)程。任務(wu)(wu)線(xian)程從事件線(xian)程中接收(shou)RTSP和RTP請求,然后把(ba)請求傳遞到恰(qia)當的服(fu)務(wu)(wu)器模塊進(jin)行處理(li),把(ba)數(shu)據包發送給客戶端。缺省情況下,核心服(fu)務(wu)(wu)器為(wei)每(mei)一個(ge)處理(li)器創建(jian)一個(ge)任務(wu)(wu)線(xian)程。
5.2.2 模塊
媒體服務(wu)器(qi)使用模塊來響應各種請求及(ji)完成(cheng)任(ren)務(wu)。有三種類型的模塊:
1. 內(nei)容管理模(mo)塊
內容管理(li)模(mo)塊(kuai)負責管理(li)與媒體(ti)源相關的(de)(de)RTSP請(qing)求和(he)響(xiang)應(ying),比如一個(ge)(ge)文件或者(zhe)一個(ge)(ge)廣播(bo)。每個(ge)(ge)模(mo)塊(kuai)負責解釋客戶(hu)的(de)(de)請(qing)求,讀(du)取和(he)解析它們的(de)(de)支(zhi)持文件或者(zhe)網絡(luo)源,并(bing)且以RTSP和(he)RTP的(de)(de)方式進行(xing)響(xiang)應(ying)。在(zai)某(mou)些情況下(xia),比如流化mp3的(de)(de)模(mo)塊(kuai),使用(yong)的(de)(de)則(ze)是(shi)HTTP。
QTSSFileModule,QTSSReflectorModule,QTSSRelayModule,和QTSSMP3StreamingModule都是內容管(guan)理(li)模塊。
2. 服務器支持模塊(kuai)
服務器(qi)(qi)支持模(mo)塊執行服務器(qi)(qi)數據的收集和(he)(he)記錄功能。服務器(qi)(qi)模(mo)塊包括QTSSErrorLogModule, QTSSAccessLogModule,QTSSWebStatsModule,QTSSWebDebugModule, QTSSAdminModule,和(he)(he)QTSSPOSIXFileSystemModule。
3. 訪問(wen)控制模塊(kuai)
訪(fang)問控(kong)制(zhi)模塊提(ti)供鑒權(quan)和授權(quan)功能(neng),以及操作URL路徑提(ti)供支持。
訪問控制模塊包括QTSSAccessModule,QTSSHomeDirectoryModule,QTSSHttpFileModule,和QTSSSpamDefenseModule。
5.2.3 數(shu)據(ju)
當(dang)一個(ge)模塊需要訪問(wen)客戶(hu)請(qing)求(qiu)的(de)RTSP報頭(tou)時,可(ke)以(yi)通過(guo)QTSS.h這個(ge)API頭(tou)文件中定(ding)義的(de)請(qing)求(qiu)對(dui)象(xiang)來訪問(wen)相(xiang)應的(de)請(qing)求(qiu)信息。舉例來說,RTSPRequestInterface類(lei)實(shi)現了API字典(dian)元素(su),這些元素(su)可(ke)以(yi)通過(guo)API來進行訪問(wen)。名稱是(shi)以(yi)“Interface”結尾的(de)對(dui)象(xiang),比如(ru)RTSPRequestInterface,RTSPSessionInterface,和QTSServerInterface,則用于實(shi)現模塊的(de)API。
下(xia)面(mian)是(shi)重(zhong)要的(de)接口類:
•QTSServerInterface — 這是內部數(shu)據的存儲(chu)對象,在API中(zhong)標(biao)識為QTSS_ServerObject。在API中(zhong)的每一個QTSS_ServerAttributes都在基類(lei)中(zhong)聲明和(he)實現。
•RTSPSessionInterace — 這是內部數據的存儲對象,在API中標識(shi)為qtssRTSPSessionObjectType。在API中的每一個(ge)QTSS_RTSPSessionAttributes都(dou)在基類中聲(sheng)明和實現。
•RTPSessionInterface — 這是內部(bu)數據的(de)(de)存儲對象,在(zai)API中(zhong)標識為QTSS_ClientSessionObject。在(zai)API中(zhong)的(de)(de)每一個(ge)QTSS_ClientSessionAttributes都在(zai)基類(lei)中(zhong)聲(sheng)明(ming)和實(shi)現。
•RTSPRequestInterface — 這是內部數據的存儲對象,在API中(zhong)標識為QTSS_RTSPRequestObject。在API中(zhong)的每一個QTSS_RTSPRequestAttributes都在基類中(zhong)聲明和實現。
5.2.4 源代(dai)碼的組織
Server.tproj
這個目錄包含核心服務器(qi)(core server)的代碼,可(ke)以分成三個子系統:
•服(fu)(fu)務(wu)器(qi)內核。這個子系統中的(de)類都有一(yi)個QTSS前綴(zhui)。QTSServer負(fu)責處理服(fu)(fu)務(wu)器(qi)的(de)啟(qi)動和關閉。QTSServerInterface負(fu)責保存服(fu)(fu)務(wu)器(qi)全局變量,以及收(shou)集服(fu)(fu)務(wu)器(qi)的(de)各種統計信息。QTSSPrefs是存儲服(fu)(fu)務(wu)器(qi)偏好設定的(de)地方。QTSSModule,QTSSModuleInterface,和QTSSCallbacks類的(de)唯一(yi)目的(de)就是支持(chi)QTSS的(de)模塊API。
•RTSP子系統(tong)。這些(xie)類負責解析和處(chu)理(li)RTSP請求,以及實現QTSS模塊API的(de)RTSP部分。其中的(de)幾個(ge)類直接(jie)對(dui)應QTSS API的(de)一(yi)些(xie)元素(比(bi)如,RTSPRequestInterface類就是(shi)對(dui)應于QTSS_RTSPRequestObject對(dui)象(xiang))。每個(ge)RTSP TCP連接(jie)都有一(yi)個(ge)RTSP會話對(dui)象(xiang)與(yu)之(zhi)相對(dui)應。RTSPSession對(dui)象(xiang)是(shi)一(yi)個(ge)Task對(dui)象(xiang),負責處(chu)理(li)與(yu)RTSP相關的(de)事件。
•RTP子系(xi)統。這些類(lei)處(chu)理媒體數(shu)(shu)據的(de)發(fa)送。RTPSession對象(xiang)包含與(yu)所有RTSP會話ID相關聯(lian)的(de)數(shu)(shu)據。每個(ge)(ge)RTPSession都是一(yi)個(ge)(ge)Task對象(xiang),可以接受核心服務(wu)器的(de)調度來(lai)進(jin)行RTP數(shu)(shu)據包的(de)發(fa)送。RTPStream對象(xiang)代表(biao)一(yi)個(ge)(ge)單獨(du)的(de)RTP流,一(yi)個(ge)(ge)RTPSession對象(xiang)可以和(he)任(ren)何數(shu)(shu)目的(de)RTPStream對象(xiang)相關聯(lian)。這兩個(ge)(ge)對象(xiang)實現了QTSS模塊API中的(de)針對RTP的(de)部(bu)分。
CommonUtilitiesLib
這(zhe)個目錄含有一個工(gong)具(ju)箱,包括線程管理,數(shu)據結構(gou),網絡,和文(wen)本(ben)解(jie)析工(gong)具(ju)。Darwin流媒體服務器及(ji)其相關(guan)工(gong)具(ju)通過這(zhe)些(xie)類(lei)(lei)(lei)對(dui)類(lei)(lei)(lei)似或者相同(tong)的(de)(de)(de)任務進行抽象(xiang),以減少重復(fu)代碼(ma);這(zhe)些(xie)類(lei)(lei)(lei)的(de)(de)(de)封(feng)裝簡(jian)(jian)化了較高(gao)層次(ci)的(de)(de)(de)代碼(ma);借助這(zhe)些(xie)類(lei)(lei)(lei)還分離了專用(yong)于不同(tong)平(ping)臺的(de)(de)(de)代碼(ma)。下(xia)面是對(dui)目錄下(xia)的(de)(de)(de)各個類(lei)(lei)(lei)的(de)(de)(de)簡(jian)(jian)短描述:
•OS類(lei)。這些類(lei)在(zai)時間,條(tiao)件(jian)變量(liang),互斥鎖(suo),和(he)線程(cheng)方(fang)面提供了專用于不同平臺的(de)代(dai)碼(ma)抽象。這些類(lei)包(bao)括(kuo)OS,OSCond,OSMutex,OSThread,和(he)OSFileSource;數據結構則包(bao)括(kuo)OSQueue,OSHashTable,OSHeap,和(he)OSRef。
•套接口類(lei)(lei)(Sockets)。這些(xie)類(lei)(lei)為TCP和(he)(he)UDP網絡通(tong)訊(xun)方面提供了專(zhuan)用于不同(tong)平臺的(de)(de)代碼抽(chou)象。通(tong)常情況下,套接口類(lei)(lei)是異步的(de)(de)(或者說是非(fei)阻塞(sai)的(de)(de)),可以發送事件給(gei)Task對(dui)象。這些(xie)類(lei)(lei)有(you):EventContext,Socket,UDPSocket,UDPDemuxer,UDPSocketPool,TCPSocket,和(he)(he)TCPListenerSocket。
•解(jie)析(xi)工具。這(zhe)些類(lei)負責解(jie)析(xi)和格式化文(wen)本。包括StringParser,StringFormatter,StrPtrLen,和StringTranslator。
•Task(任務):這些類實現了服務器的異步(bu)事件(jian)機(ji)制。
QTFileLib
流媒體服(fu)務器(qi)的(de)(de)(de)一(yi)個(ge)主要(yao)特性就是它能(neng)夠(gou)將索(suo)引完成(hinted)的(de)(de)(de)QuickTime電(dian)影文(wen)件通(tong)過RTSP和RTP協議(yi)提供給客戶。這個(ge)目錄包(bao)含QTFile庫的(de)(de)(de)源(yuan)代(dai)碼(ma),包(bao)括(kuo)負責(ze)解(jie)析索(suo)引完成的(de)(de)(de)QuickTime文(wen)件的(de)(de)(de)代(dai)碼(ma)。服(fu)務器(qi)的(de)(de)(de)RTPFileModule通(tong)過調用QTFile庫來(lai)從索(suo)引過的(de)(de)(de)QuickTime文(wen)件中取得數據包(bao)和元數據。QTFile庫可(ke)以解(jie)析下面幾種文(wen)件類型:.mov,.mp4(.mov的(de)(de)(de)一(yi)種修改版本(ben)),和.3gpp(.mov的(de)(de)(de)一(yi)種修改版本(ben))。
APICommonCode
這個(ge)目錄(lu)包(bao)含與API相(xiang)關的類(lei)的源(yuan)代碼,比如moduletils,或者諸如記錄(lu)文件的管理這樣(yang)的公共模塊(kuai)函數。
APIModules
這(zhe)個目(mu)錄(lu)包含流媒體服(fu)務器模塊目(mu)錄(lu),每個模塊都(dou)有一個目(mu)錄(lu)。
RTSPClientLib
這(zhe)個目錄包含實現RTSP客(ke)戶端的源(yuan)代碼,這(zhe)些代碼可以用于連接服務器,只要該連接協議被(bei)支持。
RTCPUtilitiesLib
這個目錄包含解析RTCP請求的(de)源代碼。
APIStubLib
這個目錄(lu)包含(han)API的定(ding)義和支持文件。
HTTPUtilitiesLib
這個目錄包含解析HTTP請(qing)求(qiu)的源代碼。
5.2.5 流轉發
引用Darwin開發文檔里(li)面的(de)一段來介紹一下(xia)流(liu)轉(zhuan)發的(de)拉模(mo)式和推模(mo)式:
Darwin支(zhi)持兩種自動播(bo)送的場(chang)景:
•先(xian)(xian)拉后推。為了發(fa)起自動播送,RTSP客戶會發(fa)送標準的RTSP請求來向服(fu)務器請求一(yi)個(ge)流,然后服(fu)務器將該流中繼到(dao)一(yi)個(ge)或(huo)者(zhe)多個(ge)流媒體服(fu)務器。這種場(chang)景在"先(xian)(xian)拉后推"部分中加以描述。
•先偵聽后推(tui)送。在這個(ge)場景中,自動播送在流媒(mei)體服務(wu)器接收到ANNOUNCE請求時被發起。這個(ge)場景在"先偵聽后推(tui)送"部(bu)分中進行描述(shu)。
先拉后推
用戶可以通過發(fa)送標準的(de)(de)DESCRIBE/SETUP/PLAY請(qing)求(qiu)來(lai)向遠(yuan)程的(de)(de)源中(zhong)(zhong)請(qing)求(qiu)一(yi)個(ge)(ge)流,然后將它中(zhong)(zhong)繼轉發(fa)到(dao)一(yi)個(ge)(ge)或者多(duo)個(ge)(ge)目的(de)(de)地(di)。當只希望讓外(wai)部流的(de)(de)一(yi)份(fen)拷(kao)貝(bei)占用其(qi)內部連接的(de)(de)帶寬(kuan)時,這(zhe)個(ge)(ge)功能(neng)可能(neng)有用。中(zhong)(zhong)繼轉發(fa)獲(huo)取(qu)一(yi)份(fen)拷(kao)貝(bei)進行多(duo)份(fen)的(de)(de)復制和轉發(fa)、分發(fa)到(dao)請(qing)求(qiu)的(de)(de)客戶端。圖(tu) 1.提供了一(yi)個(ge)(ge)先拉后推(tui)(pull-then-push)場景的(de)(de)實例(li)。

圖1.先(xian)拉(la)后推(tui)式
以圖1.作為參考,先拉后(hou)推場景(jing)的(de)步驟如下:
1.流媒體服(fu)務(wu)器(qi)A(轉(zhuan)發(fa)服(fu)務(wu)器(qi))發(fa)送標準的RTSP客(ke)戶DESCRIBE/SETUP/PLAY請求(qiu)給遠程服(fu)務(wu)器(qi),即流媒體服(fu)務(wu)器(qi)B。
2.發起請(qing)求的中(zhong)繼“客戶端”(流(liu)媒體服務器(qi)A)開始接受流(liu),然后向該輸(shu)入流(liu)的中(zhong)繼配置中(zhong)列出(chu)的所(suo)有(you)目的地發送ANNOUNCE推送請(qing)求。
在Darwin中,實現拉模式轉(zhuan)發(fa)的(de)模塊為(wei)QTSSRelayModule,每一路轉(zhuan)發(fa)會話為(wei)一個RelaySession對象,轉(zhuan)發(fa)列表存儲于隊列sSessionQueue中
QTSSRelayModule一開(kai)始Initialize()讀取配置文(wen)件(jian)中(zhong)關(guan)于轉(zhuan)發(fa)文(wen)件(jian)路(lu)徑存儲于sRelayPrefs靜態變量(liang)中(zhong)
./relayconfig.xml
在ReadRelayPrefsFile()中(zhong)(zhong)(zhong)讀取sRelayPrefs中(zhong)(zhong)(zhong)配(pei)置并(bing)解析出(chu)分(fen)發(fa)(fa)(fa)(fa)列(lie)表,并(bing)對每一個分(fen)發(fa)(fa)(fa)(fa)配(pei)置中(zhong)(zhong)(zhong)的(de)(de)source配(pei)置創建RelaySessionCreator開(kai)啟分(fen)發(fa)(fa)(fa)(fa),并(bing)加入到(dao)sSessionQueue中(zhong)(zhong)(zhong),RTSPSourceInfo::RelaySessionCreator::Run() 再通過RTSPSourceInfo::RunCreateSession()開(kai)始DESCRIBE/SETUP/PLAY拉(la)取數據(ju),RTSP流程成(cheng)功后,再配(pei)置RTP數據(ju)分(fen)發(fa)(fa)(fa)(fa)的(de)(de)地址(zhi),將RTP數據(ju)推送(song)至分(fen)發(fa)(fa)(fa)(fa)列(lie)表(即destination列(lie)表與source列(lie)表同(tong)一級(ji))中(zhong)(zhong)(zhong),即實現了Darwin文檔中(zhong)(zhong)(zhong)所述(shu)的(de)(de)先(xian)拉(la)后推模式。
先偵聽后推送
流(liu)媒體服務器(qi)可(ke)以(yi)被(bei)配(pei)置(zhi)為將ANNOUNCE請求(qiu)創建的(de)輸入(ru)流(liu)自(zi)(zi)動發送(song)到一(yi)個或者多個目的(de)地。這(zhe)可(ke)能可(ke)以(yi)用于配(pei)制(zhi)自(zi)(zi)動播送(song)網絡。圖 2.提(ti)供了一(yi)個先偵(zhen)聽后推送(song)的(de)場景(jing)的(de)實例(li)。

圖2.先偵聽(ting)后推送(song)式
以(yi)圖2.作(zuo)為參考,先偵聽后推(tui)送場景的(de)步驟如下:
•遠程機器(qi)(IpCamera等前(qian)端設(she)備或者(zhe)中繼(ji)服務(wu)器(qi))向流媒體(ti)服務(wu)器(qi)A發送(song)一個(ge)ANNOUNCE請(qing)求。流媒體(ti)服務(wu)器(qi)可以接受或者(zhe)否(fou)認這個(ge)請(qing)求。如果它接受了請(qing)求,則流媒體(ti)服務(wu)器(qi)會檢(jian)查(cha)其中繼(ji)配置,以確定這個(ge)流是否(fou)應(ying)該被中繼(ji)。
•如果該流應該被中繼,則流媒(mei)體(ti)服務器(qi)將向自身(shen)發送標準的RTSP客戶DESCRIBE/SETUP/PLAY請求。
•發出請求的中(zhong)繼(ji)“客戶(hu)”(流媒體服務器A)開始接收流,然(ran)后向(xiang)相應的輸入流的中(zhong)繼(ji)配(pei)置中(zhong)列出的所有目的地(di)發送一個ANNOUCE請求。
注意:我們在實(shi)際的(de)需(xu)求中(zhong)常(chang)常(chang)遇(yu)到的(de)場景為(wei),前端(duan)(duan)(duan)設備RTSP Announce上線(xian)至中(zhong)繼(ji)服務(wu)器,上報(bao)其流(liu)媒體SDP信息,前端(duan)(duan)(duan)設備再經(jing)過(guo)(guo)'被觸發(fa)',通過(guo)(guo)SETUP/PLAY流(liu)程發(fa)起流(liu)推送,客(ke)戶(hu)端(duan)(duan)(duan)再以拉模式(shi)拉取實(shi)時視頻流(liu),是(shi)一種先推后拉(push-then-pull)模式(shi),如(ru)果客(ke)戶(hu)端(duan)(duan)(duan)請求的(de)視頻流(liu)存在,則直接轉發(fa)已經(jing)獲取的(de)拷貝進行分發(fa)。
具(ju)(ju)體(ti)的(de)(de)RTSP推送(song)流程大致(zhi)為:Announce、Setup、Play、RTP(DSS為RTP over TCP)。這種模(mo)式的(de)(de)轉(zhuan)發(fa)(fa)通(tong)常用于(yu)(yu)類似于(yu)(yu)3G視頻(pin)監(jian)控這種難(nan)以穿透的(de)(de)網絡(luo)類型的(de)(de)數(shu)據的(de)(de)轉(zhuan)發(fa)(fa)。我們就不具(ju)(ju)體(ti)介紹關于(yu)(yu)DSS對(dui)會話(hua)的(de)(de)維護以及各(ge)自自定義的(de)(de)RTSP頭字段(duan)的(de)(de)操作等等,主要就步驟:Announce->Setup->Play->RTP數(shu)據接收與轉(zhuan)發(fa)(fa)進行詳(xiang)細(xi)的(de)(de)分析。在DSS中(zhong),處理推送(song)報文的(de)(de)模(mo)塊為QTSSReflectorModule,其中(zhong)維護了一(yi)個靜(jing)態的(de)(de)轉(zhuan)發(fa)(fa)列表sSessionMap,用于(yu)(yu)存儲各(ge)個轉(zhuan)發(fa)(fa)會話(hua)的(de)(de)信息。下面(mian)就對(dui)具(ju)(ju)體(ti)的(de)(de)報文解析和數(shu)據處理進行分析。
Announce:RTSP Announce命令(ling)為源(yuan)端向服務器端主動發起的(de)上報本(ben)地媒體(ti)sdp信息的(de)命令(ling),處理函(han)數為QTSSReflectorModule模塊的(de)DoAnnounce()函(han)數,這里就只對(dui)該函(han)數的(de)重點部分(fen)進(jin)(jin)行(xing)解析(xi),不全部一(yi)一(yi)描(miao)述了(le)。首先(xian)判斷server配置中(zhong)的(de)enable_broadcast_announce字(zi)段是否為true,開啟了(le)廣播推送轉(zhuan)發,在(zai)通(tong)(tong)過獲取(qu)inParams->inRTSPRequest(在(zai)RTSPSession::Run調用(yong)(yong)前復制(zhi)的(de)當(dang)前請(qing)求的(de)rtspRequest對(dui)象)的(de)字(zi)典中(zhong)的(de)qtssRTSPReqLocalPath鍵(jian)值(zhi)作為標識轉(zhuan)發的(de)唯一(yi)區別(例如:.\Movies/test.sdp,必須以sdp結尾,可以修改sSDPSuffix進(jin)(jin)行(xing)配置),這里的(de)值(zhi)既是一(yi)個標識,又是一(yi)個路徑(jing),用(yong)(yong)于(yu)存儲(chu)(chu)獲取(qu)到的(de)sdp數據,后面此(ci)標識作為存儲(chu)(chu)于(yu)sSessionMap中(zhong)對(dui)象的(de)鍵(jian)值(zhi)。函(han)數中(zhong)通(tong)(tong)過對(dui)頭字(zi)段的(de)解析(xi),獲取(qu)到Content-Length:字(zi)段值(zhi),進(jin)(jin)而去讀取(qu)具體(ti)的(de)spd值(zhi),再存儲(chu)(chu)到qtssRTSPReqLocalPath路徑(jing)中(zhong),返回(hui)200 OK。
Setup:這里的只解(jie)析DoSetup中(zhong)isPush為(wei)true(表(biao)示為(wei)推送(song)的Session)這條(tiao)路路徑,具體isPush值由Setup請求中(zhong)的mode值有關,mode="receive" || mode="record"表(biao)示isPush為(wei)true,
else
{
theSession = DoSessionSetup(inParams, qtssRTSPReqFilePathTrunc, isPush, &foundSession);//根據前(qian)面(mian)Announce中存儲于qtssRTSPReqLocalPath的路徑讀取sdp信(xin)息,創(chuang)建(jian)轉發(fa)會(hui)話ReflectorSession,或者直接引用已(yi)經存在(zai)的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字典(dian)
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));
}
這里需要注意(yi)的是,當(dang)我們前面(mian)已(yi)經有一路(lu)相同(tong)qtssRTSPReqLocalPath路(lu)徑的ReflectorSession存在的時(shi)(shi)候(hou),將不進(jin)行再創建(jian),直接Resolve原有的ReflectorSession,所(suo)以(yi)會出現一種(zhong)情況,當(dang)開(kai)始的推送與(yu)(yu)后(hou)面(mian)進(jin)行的推送音視頻sdp不一致的時(shi)(shi)候(hou),就會出現錯誤,所(suo)以(yi), ReflectorSession的引(yin)用與(yu)(yu)釋放需要注意(yi)!
完成ReflectorSession的(de)(de)創建(jian),下一步(bu)解(jie)析(xi)(xi)(xi)track ID,具體(ti)(ti)的(de)(de)解(jie)析(xi)(xi)(xi)方法可以根據(ju)自(zi)己的(de)(de)實際應用,有(you)的(de)(de)按(an)照(zhao)track%d解(jie)析(xi)(xi)(xi),有(you)的(de)(de)按(an)照(zhao)trackID=%d解(jie)析(xi)(xi)(xi),再根據(ju)trackId獲取具體(ti)(ti)的(de)(de)track sdp信息,AddRTPStream創建(jian)對應于(yu)具體(ti)(ti)track的(de)(de)RTP流
theStreamInfo->fSetupToReceive = true;//標識流轉(zhuan)發的(de)建立(li)
// This is an incoming data session. Set the Reflector Session in the ClientSession
theErr = QTSS_SetValue(inParams->inClientSession, sClientBroadcastSessionAttr, 0, &theSession, sizeof(theSession));//設置(zhi)轉發會話(hua)的RTPSession字典的sClientBroadcastSessionAttr字段
Assert(theErr == QTSS_NoErr);
if (theSession != NULL)
theSession->AddBroadcasterClientSession(inParams);//設置ReflectorSession的fBroadcasterSession屬性為inParams->inClientSession,呵呵比較(jiao)亂噢,相(xiang)當于相(xiang)互(hu)引(yin)用(yong)
Play: 具體到(dao)DoPlay過程,isPush為true的(de)路徑(jing)就比較簡單了,只是(shi)將(jiang)推送的(de)RTSPSession中(zhong)的(de)sRTSPBroadcastSessionAttr屬性設(she)置為前面DoSetup中(zhong)獲取到(dao)的(de)ReflectorSession
theLen = sizeof(inSession);
theErr = QTSS_GetValue(inParams->inClientSession, sClientBroadcastSessionAttr, 0, &inSession, &theLen);//DoSetup()中已經設置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));//設置到inParams->inRTSPSession的sRTSPBroadcastSessionAttr屬性
if (theErr != QTSS_NoErr)
return QTSS_RequestFailed;
RTP數(shu)據處理:ProcessRTPData(),這(zhe)里只處理RTP over TCP的數(shu)據,根據RTP數(shu)據中的channel值,調用特定的ReflectorStream進行處理和(he)轉發(fa),具體函(han)數(shu)為(wei):ProcessRTPData(),先通過前面在DoSetup() & isPush為(wei)true時設置的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;
再根據channelID獲取具體(ti)的ReflectorStream并(bing)進(jin)行數(shu)據推送,給具體(ti)的ReflectorStream進(jin)行處理
UInt32 inIndex = packetChannel / 2; // one stream per every 2 channels rtcp channel handled below
ReflectorStream *theStream = NULL;
if (inIndex < numStreams)
{
theStream = theSession->GetStreamByIndex(inIndex);//獲(huo)取對應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); //推送數據給ReflectorStream并(bing)轉發給分(fen)發列表
}
}
5.2.6 二次開發模塊(kuai)添加的要求
每個DSS模(mo)(mo)(mo)塊必須(xu)實(shi)現兩個函(han)(han)數(shu)(shu):一個是(shi)(shi)Main函(han)(han)數(shu)(shu),服務器在(zai)啟(qi)動時(shi)將(jiang)調用這(zhe)個函(han)(han)數(shu)(shu)進行必要的初始化。另一個是(shi)(shi)Dispatch函(han)(han)數(shu)(shu),通(tong)過實(shi)現此(ci)函(han)(han)數(shu)(shu),服務器可(ke)調用DSS模(mo)(mo)(mo)塊并完成特定處理(li)。對于編譯(yi)到服務器里面的模(mo)(mo)(mo)塊,其主函(han)(han)數(shu)(shu)的地(di)址必須(xu)傳遞到服務器的模(mo)(mo)(mo)塊Main函(han)(han)數(shu)(shu)中。
具體(ti)實(shi)現時,Main函數必須命(ming)名為MyModule_Main,其中MyModule是模塊(kuai)的(de)文件名。此函數的(de)實(shi)現通常如下所示:
QTSS_Error MyModule_Main(void *inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, MyModuleDispatch);
}
每個DSS模(mo)塊都必須提(ti)供一個Dispatch函數。服務器(qi)為了(le)特定的(de)(de)(de)目的(de)(de)(de)需要使用某個模(mo)塊時(shi)(shi),是通過調用該模(mo)塊的(de)(de)(de)Dispatch函數來實現的(de)(de)(de),調用時(shi)(shi)必須將任務的(de)(de)(de)名(ming)稱及(ji)相(xiang)應的(de)(de)(de)參(can)數傳遞給該函數。在DSS中(zhong),使用角(jiao)色(Role)這個術語來描述特定的(de)(de)(de)任務。Dispatch函數的(de)(de)(de)格式如下所示:
void MyModuleDispatch(QTSS_Role inRole,QTSS_RoleParamPtr inParams);
其(qi)中MyModuleDispatch是Dispatch函數的名(ming)稱;MyModule是模塊的文件名(ming);inRole是角(jiao)(jiao)色(se)(se)的名(ming)稱,只有注(zhu)冊了該角(jiao)(jiao)色(se)(se)的模塊才會被(bei)調用(yong);inParams則是一個結構體(ti),可用(yong)于傳遞相應的參數。
5.3 視頻播放器(VLC)
我們提(ti)供了一(yi)個裁(cai)剪過適用于此系(xi)統的VLC,關于原版詳細分(fen)析如下。
5.3.1 eclipse 調試源碼
原版源碼:項目/源碼/vlc_source_android.Zip
其中源(yuan)碼獲取是:git clone git://git.videolan.org/vlc-ports/android.git
源碼介紹
相關(guan)源碼介紹(shao) : VLC 源碼依賴于 另(ling)外四(si)個(ge)工程;
•vlc-android 工程 : VLC 的主要(yao)源碼(ma);
•appcompat 工(gong)程(cheng) : 低版(ban)本兼(jian)容庫, VLC 源碼 vlc-android 需要依賴該工(gong)程(cheng);
•cardview 工(gong)程 : VLC 源碼 vlc-android 需(xu)要依(yi)賴該工(gong)程;
•libvlc 工程(cheng) : VLC 源碼 vlc-android 需要依(yi)賴該工程(cheng);
•WheelView 工程(cheng)(cheng) : VLC 源碼 vlc-android 需要(yao)依(yi)賴該(gai)工程(cheng)(cheng);

源碼導入
將源碼導入 eclipse : 主(zhu)需要重新(xin)設置一(yi)下依賴, 其它(ta)不用修改;

vlc 源(yuan)碼依賴(lai) : vlc 依賴(lai)其余的四(si)個(ge)工程, 下面的是 project.properties 內容;
target=android-21
android.library.reference.1=..\\libvlc
android.library.reference.2=../appcompat
android.library.reference.3=../cardview
android.library.reference.4=../WheelView

執行安裝
執行效果 :

5.3.2 包(bao)結構詳解
VLC Android 源(yuan)碼包結構分析 :
•主包結構截圖 : org.vedio.vlc 包下的內容;

•audio 包 : 音(yin)頻相關的包;

•gui 包 : 界面 UI 相關包;

•interfaces 包 : 定(ding)義各種接口;

•widget 包(bao)(bao) : 自定 義組件相關(guan)的包(bao)(bao);

•utils 包 : 相關工具類;

5.3.3 主要類介紹
注意 : 以下介(jie)請(qing)結(jie)合自己(ji)的理解去閱讀代碼
(1) org.videolan.vlc 下類介紹

org.videolan.vlc 下(xia)類介紹 :
•MediaDatabase 介紹 : 數(shu)據(ju)庫操作相關類(lei), 該類(lei)中定義了 SQLiteOpenHelper 子類(lei), 并(bing)且定義了幾個數(shu)據(ju)庫, 創建了以下(xia)數(shu)據(ju)表 directories_table 路徑表, media_table 媒體信息表, playlist_table 單個播放列表, playlist_media_table 播放列表集合表, searchhistory_table 搜索記(ji)錄表, mrl_table mrl 表;
•MediaGroup 介紹 : 繼承了 Media 類(在 libvlc 中維護(hu), 維護(hu)視頻(pin)音頻(pin)后綴名稱(cheng)或擴展名), 用于維護(hu)一個 Media 集合;
•MediaLibrary 介紹 : Media 相關庫, 該類中維(wei)護了(le)一個條目(mu)列表(biao), 主要(yao)對這個條目(mu)列表(biao)進(jin)行操作 (疑問, 沒看懂);
•PhoneStateReceiver 介紹 : 廣播(bo)接收者(zhe), 一個(ge)監聽手機電話狀態的廣播(bo)接收者(zhe), 如果有電話打入, 或結束通過, 進行對應的操作;
•RemoteControlClientReceiver 介紹 : 廣(guang)播(bo)接收(shou)者, 通過遠程 wifi, 藍牙, 屏(ping)幕鎖定解鎖 等接收(shou)事件, 進行響應的操(cao)作;
•Thumbnailer 介紹 : 是 Runnable 子類, thumbnail 用于代表要執行(xing)的(de)動作, 等(deng)待播放(fang)的(de)流媒(mei)體;
•VLCApplication 介紹 : Application 子類, 進行全(quan)局(ju)的(de)設置;
•VLCCallbackTask 介紹(shao) : AsyncTask 的子類, 這是個回(hui)調的幫助類, 能(neng)夠在線程中更容易實(shi)現(xian)回(hui)調;
•VLCCrashHandler 介(jie)紹(shao) : 用于處理(li)未捕獲的崩潰(kui)信(xin)息(xi), 打印(yin)到日志(zhi) 或者 文(wen)件中;
(2) org.videolan.vlc.audio 下類介(jie)紹

org.videolan.vlc.audio 包(bao)類介紹 :
•AudioService 介紹 : 集成(cheng) Service, 播放音頻(pin)的后臺服務;
•AudioServiceController 介紹 : 音頻服務控(kong)制類;
•RepeatType 介紹 : 重復類型枚(mei)舉定義, 不重復, 重復一(yi)次, 循(xun)環;
(3) org.videolan.vlc.widget 下(xia)類介紹

org.videolan.vlc.widget 類介紹 :
•AnimatedCoverView 介紹 : 繼承 View 組(zu)件, 自(zi)定(ding)義組(zu)件, 動畫切換相(xiang)關(guan)的(de) 自(zi)定(ding)義 View;
•AudioMediaSwitcher 介紹 : 繼(ji)承(cheng)結構 AudioMediaSwitcher -> FlingViewGroup -> ViewGroup, 音(yin)頻(pin)媒(mei)體切換(huan)相關類;
•AudioPlaylistItemViewGroup 介(jie)紹 : 繼(ji)承結構(gou) AudioPlaylistItemViewGroup -> FlingViewGroup, 音頻播放(fang)列(lie)表相關類;
•ContentLinearLayout 介紹 : 繼承 LinearLayout, 重寫了 onInterceptTouchEvent 方法, 用(yong)于攔(lan)截(jie)觸摸(mo)事(shi)件(jian)(jian)(jian), 當(dang)媒(mei)體正在播放的時(shi)候, 如(ru)果觸摸(mo)子組(zu)件(jian)(jian)(jian), 觸發(fa)事(shi)件(jian)(jian)(jian), 會影響播放, 此(ci)時(shi)我們需要攔(lan)截(jie)這些觸摸(mo)事(shi)件(jian)(jian)(jian);
•EqualizerBar 介(jie)紹 : 繼承 LinearLayout, 均衡器調(diao)節(jie)條;
•ExpandableLayout 介紹(shao) : 主要(yao)內容;

•FlingViewGroup 介(jie)紹 : 繼承(cheng) ViewGroup, 主要(yao)是修改(gai)了一些手勢操(cao)作(zuo), 覆蓋重(zhong)寫了 onScrollChanged, onTouchEvent, onInterceptTouchEvent 方法;
•HeaderScrollView 介紹 : 繼承(cheng) HorizontalScrollView, 橫(heng)向滑動的 View 組件;
•SlidingPaneLayout 介紹(shao) : 繼承 ViewGroup, 這個(ge)(ge)類是(shi)從 Android 中(zhong)剝離出來的, 屬于 support-v4 中(zhong)的一個(ge)(ge)類, 如果想要上(shang)下滑動, 不是(shi)左右側(ce)劃, 需要修(xiu)改一些地(di)方;
•VerticalSeekBar 介紹 : 繼(ji)承 SeekBar, 這個(ge)組件(jian)是一個(ge)垂直的拖動條(tiao);
•VLCAppWidgetProvider 介紹 : 集(ji)成(cheng) AppWidgetProvider 類, App 組件(jian)提供者, 相當與(yu)一個(ge)廣播接收者;
(4) org.videolan.vlc.util 下(xia)類介紹(shao)

org.videolan.vlc.util 包類(lei)介紹 :
•AndroidDevices 類 : 獲取(qu)(qu)手機相(xiang)關信息, 是否有內存(cun)卡, 手機型(xing)號, 獲取(qu)(qu)存(cun)儲(chu)路徑(jing), 獲取(qu)(qu)媒體目(mu)錄;
•BitmapCache 類 : 圖片緩存相(xiang)關類, 使用(yong) LruCache 實現圖片的流暢緩存;
•BitmapUtil 類 : 處理位圖(tu)相(xiang)關類, 提供 邊緣切割, 縮(suo)放, 從緩存中獲(huo)取圖(tu)片;
•CustomDirectories 類 : 管理用戶信息存放路(lu)徑(jing);
•Logcat 類 : 獲(huo)取日(ri)志, 將日(ri)志輸出到文件中;
•MurmurHash : MurmurHash算法:高運(yun)算性能,低碰撞(zhuang)率,由Austin Appleby創建于2008年;
•Preferences : SharedPreferences 操作相關;
•Strings : 用(yong)于處理字符(fu)串相關(guan)的工具類;
•Util : 一些(xie)小公共方法;
•VLCInstance : libvlc 相(xiang)關的類, 與 libvlc 工程(cheng)相(xiang)關;
•VLCRunnable : 繼(ji)承 Runnable, 線程相關類;
•WeakHandler : 繼承 Handler;
(5) org.videolan.vlc.interfaces 下類介紹
org.videolan.vlc.interfaces 包介紹 :
•IAudioPlayer 介紹 : 音(yin)頻播放接口, 提供了更新 和(he) 更新進度條方法;
•IAudioPlayerControl 介紹(shao) : 音頻播放(fang)控制(zhi)(zhi)接口, 提供了一系列(lie)的音頻控制(zhi)(zhi)方法;
(4) org.videolan.vlc.audio 下類介紹
org.videolan.vlc.gui.audio.widget 包介紹(shao) :
•CoverMediaSwitcher 介紹 : 繼(ji)承(cheng)結(jie)構 CoverMediaSwitcher -> AudioMediaSwitcher -> FlingViewGroup -> ViewGroup;
•HeaderMediaSwitcher 介(jie)紹 : 繼承結構 HeaderMediaSwitcher -> AudioMediaSwitcher -> FlingViewGroup -> ViewGroup;

