基于JMF RTP的网络传输媒体流

本文主要介绍 基于 JMF RTP 的网络传输媒体流的实现,并给出相应源代码。
author: ZJ    06-11-18
Blog: [url]http://zhangjunhd.blog.51cto.com/[/url]
 
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会话。
③保存来自每一个发送或接收到的RTPRTCP包中的统计信息。
JMF RTP Session 结构图:
SessionManagr 包含2个部分:Session StatisticsSession Streams
 
  1.1.1 Session Statistics
统计量( Statistics )是记录基于每一条媒体流上的整个会话的统计信息。它包含:
GobalReceptionStats :包含此会话的全局接收统计信息。
GobalTransmissionStats :包含此会话的全局传输统计信息。
RecetionStats :包含每一个参与者接收统计信息。
TransmissionStats :包含每一个参与者的传输统计信息。
 
1.1.2 Session Streams
ReceiveStream :表示一个接收到的来自远端参与者的媒体流。
SendStream :表示一个来自本地的媒体流。
 
1.2 RTP 事件
如下图所示,通过继承JMFMediaEvent的类,可以创建响应的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:表示接收到一个RTCPRR包。
SenderReportEvent:表示收到一个RTCPSR包。
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/rtpdvi/rtp g723/rtp gsm/rtp
         
视频
jpeg/rtph261/rtph263/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 管理器实例化    
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();
 
2.2 RTP媒体数据流的接收过程
接收方式为经由SessionManagerDataSourcePlayer,然后播放。上图即为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 传输模型的整体设计
3. 参考资料
[1]JMF2.0 API Guide
[2] 台湾国立中央大学电机工程系通讯专题报告 VOIP
 
 

你可能感兴趣的:(JMF,传输,休闲,RTP,媒体流)