JMF中可以實現RTP媒體流的回放(playback)和傳輸(transmission),主要由javax.media.rtp, javax.media.rtp.event,和javax.media.rtp.rtcp包中定義的API完成。JMF可以通過標准的JMF plug-in機制來實現支持特定的RTP格式和動態負載。
你可以在本地播放RTP數據流,或將其存儲到本地文件。
同樣,你可以通過JMF中RTP API實現傳輸捕獲的或存儲的媒體流到網上。RTP媒體流可以創建自一個本地文件或捕獲自媒體采集設備。這些RTP媒體流同樣可以在本地播放或存儲。
整體流程圖示:
1.RTP結構
1.1 SessionManager
在JMF架構中Session Manager對程序之間的會話進程進行控制和管理。Session Manager主要作用:
①明確每一個會話(session)中的所有參與者(participants)。
②管理每一個RTP會話。
③保存來自每一個發送或接收到的RTP和RTCP包中的統計信息。
JMF RTP Session結構圖:
SessionManagr包含2個部分:Session Statistics和Session Streams。
1.1.1Session Statistics
統計量(Statistics)是記錄基於每一條媒體流上的整個會話的統計信息。它包含:
①GobalReceptionStats:包含此會話的全局接收統計信息。
②GobalTransmissionStats:包含此會話的全局傳輸統計信息。
③RecetionStats:包含每一個參與者接收統計信息。
④TransmissionStats:包含每一個參與者的傳輸統計信息。
1.1.2Session Streams
①ReceiveStream:表示一個接收到的來自遠端參與者的媒體流。
②SendStream:表示一個來自本地的媒體流。
1.2 RTP事件
如下圖所示,通過繼承JMF中MediaEvent的類,可以創建響應的RTP事件。
⑴SessionListener:通過它得到一個會話狀態的改變。
①NewParticipantEvent:表示一個新的參與者加入會話。
②LocalCollisionEvent:表示參與者請求的同步資源正在使用。
⑵SendStreamListener:通過它得到一個正在傳送的RTP數據流狀態的改變。
①NewSendStreamEvent:表示本地參與者已經創建一個新的發送數據流。
②ActiveSendStreamEvent:表示從DataSource創建的數據流已經開始發送。
③InactiveSendStreamEvent:表示從本地DataSource創建的數據流已經停止。
④LocalPayloadChangeEvent:表示數據流格式已經開始改變。
⑤StreamClosedEvent:表示數據流已經停止。
⑶ReceiveStreamListener:通過它得到一個正在接收的RTP數據流狀態的改變。
①NewReceiveStreamEvent:表示SessionManager已經創建了一個從新的偵測到的地址傳來的接收數據流。
②ActiveReceiveStreamEvent: 表示數據的傳送已經開始。
③InactiveReceiveStreamEvent:表示數據的傳送已經停止。
④TimeoutEvent:表示數據傳送超時。
⑤RemotePayloadChangeEvent:表示接收到的數據流格式已經改變。
⑥ApplicationEvent:表示收到了一個RTCP App數據包。
⑷RemoteListener:通過它得到遠端會話參與者的時間或RTP控制信息。
①ReceiverReportEvent:表示接收到一個RTCP的RR包。
②SenderReportEvent:表示收到一個RTCP的SR包。
③RemoteCollisionEvent:表示兩個遠端的參與者使用了相同的SSRC 出錯。
1.3與RTP事件相對應的RTCP類型表
RTCP的控制類型和JMF事件類的一致性
RTCP類型 JMF中的事件類 SR SendStreamEvent RR ReceiveStreamEvent SDES SenderReportEvent BYE ByeEvent APP ApplicationEvent:
1.4數據傳輸格式
在RTP傳輸中,如果還是用傳統的AVI,MOV格式的話,將會增加服務器負荷,而且對網絡要求特別高,因此需要將傳統格式轉化至易於傳送,網絡適應性好,抗丟包性能和抗誤碼性能好的編碼格式。下表是JMF項目支持的視音頻在RTP傳送的壓縮格式,也就是說經過定制後的輸出視頻流,還得進行一次轉換,以便網絡發送。
表 JMF支持的視音頻在RTP傳送中的格式
多媒體類別 RTP傳輸格式 音頻 JAUDIO_G711_ULAW/rtp,dvi/rtp ,g723/rtp ,gsm/rtp 視頻 jpeg/rtp,h261/rtp,h263/rtp
轉化格式的關鍵代碼及其分析(視頻):
//從processor獲得軌道控制器
TrackControl [] tracks = processor.getTrackControls();
//為每個軌道的格式進行轉制
for (int i = 0; i < tracks.length; i++)
{
//此處省略獲得軌道信息格式和支持格式代碼
//下面一行為轉制函數,需要參數為:軌道格式和軌道支持的格式
chosen = checkForVideoSizes(tracks[i].getFormat(), supported[0]);
//此處省略如果不能對軌道格式轉變代碼
}
//轉制函數
/* 在傳輸視頻信息時,對於JPEG編碼格式,視頻圖像的寬和高是8像素的整數倍,對於
*H263編碼格式,只支持三種圖像的大小,即352X288,176X144,128X96像素,只要滿
*足了這些條件,才可以正常傳輸視頻信息,所以,需要對視頻格式進行轉制,
*不負荷條件的都需要轉化,以滿足正常傳輸。
*/
Format checkForVideoSizes(Format original, Format supported) {
int width, height;
Dimension size = ((VideoFormat)original).getSize();//獲取視頻圖像的尺寸
Format jpegFmt = new Format(VideoFormat.JPEG_RTP);
Format h263Fmt = new Format(VideoFormat.H263_RTP);
if (supported.matches(jpegFmt)) {//如果是JPEG格式
//調整寬
width = (size.width % 8 == 0 ? size.width :(int)(size.width / 8) * 8);
//調整高
height = (size.height % 8 == 0 ? size.height :(int)(size.height / 8) * 8);
} else if (supported.matches(h263Fmt)) {//如果是H263格式
if (size.width < 128) {
width = 128;
height = 96;
} else if (size.width < 176) {
width = 176;
height = 144;
} else {
width = 352;
height = 288;
}
} else {
//對於其他格式不受理
return supported;
}
return (new VideoFormat(null,
new Dimension(width, height),
Format.NOT_SPECIFIED,
null,
Format.NOT_SPECIFIED)).intersects(supported);
2.RTP媒體數據流的傳輸與接收2.1 RTP媒體數據流的傳輸過程
上圖為Transmit的整個設計架構,Processor處理來自Capture Device的數據後,輸入對方IP和Port,將數據傳送到網絡上等待接收端接收,其中音頻的端口為視頻的端口加2。
部分代碼及分析:
①將轉換格式後的數據放入一個DataSource
//獲得轉制後的DataSource
dataOutput = processor.getDataOutput();
//將DataSource轉化為Push數據流
PushBufferDataSource pbds = (PushBufferDataSource)dataOutput;
//獲取Push數據流
PushBufferStream pbss[] = pbds.getStreams();
數據源決定了軌道數的多少,如果數據源中包括視頻和音頻內容,則有兩個軌道,一個軌道分給視頻,一個軌道分給音頻,在數據源的格式轉制完成以後,每個軌道對應著一個RTP會話,這些RTP會話由會話管理器(RTPManager)統一管理。
②以下是建立RTP Session中發送的關鍵代碼和分析:
rtpMgrs[i] = RTPManager.newInstance();//RTP管理器實例化
2.2 RTP媒體數據流的接收過程
ipAddr = InetAddress.getByName("59.64.84.243");//獲得目的地址的IP地址
//獲取本機IP地址
localAddr = new SessionAddress( InetAddress.getLocalHost(),port);
//獲取目的機IP地址
destAddr = new SessionAddress( ipAddr, port);
//分別將本機和目的機IP地址加入至RTP會話管理器
rtpMgrs[i].initialize( localAddr);
rtpMgrs[i].addTarget( destAddr);
//產生第N條軌道的傳輸流
sendStream = rtpMgrs[i].createSendStream(dataOutput, i);
//傳輸流開始
sendStream.start();
接收方式為經由SessionManager到DataSource到Player,然後播放。上圖即為Receive的整個設計構架,傳送端送出數據後,接收端輸入對方IP然後等待接收數據,其中音頻的端口為視頻的端口加2。
部分代碼及分析:
/*
*目的機建立RTP會話管理器原理和步驟基本與發送端一至,我們主要分析接收多媒體流的事件類。
*/
public synchronized void update( ReceiveStreamEvent evt){
if(evt instanceof NewReceiveStreamEvent) {//接收到一個新的數據流
//根據獲取的數據流獲得一個數據源。這個數據源為播放使用
} else if (evt instanceof StreamMappedEvent) {
//數據流映射事件
//如果當前數據源為NULL,根據這個事件獲得一個Datasource,否則忽略
}else if (evt instanceof ByeEvent) {//數據接收完畢
//播放結束
}
}
2.3基於JMF 的RTP/RTCP 傳輸模型的整體設計
本文出自 “子 孑” 博客,請務必保留此出處http://zhangjunhd.blog.51cto.com/113473/25487