基于RTP实时传输协议的JMF多媒体传输 源代码
基于JMF RTP的网络传输媒体流
基于JMF RTP的网络传输媒体流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:表示一个来自本地的媒体流。
基于JMF的内外网多媒体数据传输技术
F Qi , Z axa U a QU oi n h (bt c wi t dvl mnoIe e t tnpre nl y uieiibi uem rad rwdlBt e ioe bm A sa ] t h eep et t thr s t ogom l da eg d e m e e . trislnp l rt h e o f r , a o th o f t nn e c m s s o n o i y u h s n e tl r e o eii im sa laos aast da s t g t ot nt h ient er e. h aiea td ti n i xs g ot itn nwdy: a tn t fm u r t t n r irtcdIti rc, eo f h ad t n p ci o n p h t r min r h e e o n e s i e a i o e e s t n tl m h o s t s r s
网,因 此,跨 越防火墙MA 限 T 制解决多媒体数据在内外部
网主机之间以及处于 不同内部网的主机 之间的双 向通信问题 显得尤为重要,这是多媒 体数据在网络中传输 的关键技术之 一 ,也是本文研究的重点 。
2 MF J 简介 JF u csts Mio sm公司开发的一个可选软件包, M 是Sn rye 它扩展了2E J 平台的多媒体功能, S 能够捕获、播放、 转换包 括音频和视频在内的多 种媒体格式,可以 频、 将音 视频和其 它基于时间的媒体整合到J a a 应用程序和小程序中, v 为多媒 体开 发者提供了一个强大的工具箱,使开发者 关心 不用 底层
复 实 细 就 够 写 功 强 的媒 程 开 杂的 现 节, 能 编 出 能 大 多 体 序,
基于RTP的音频流多播系统的JMF实现
第一作者简介 : 卫 , , 7 年生 ,93 张 男 1 1 9 19 年毕业 于华北 电力学院 ,
工 师,西 唐自 云冈 电 限 任 司,西 大同 0 0 . 程 山 大 际 热 有 责 公 山 省 市,7 9 3 3
[] 冯 济缨. 2 可编程序控制器应用基础 [ ] 重庆:重庆大学出版社 , M.
1 98: —3 . 9 21 3
( 责任编辑 : 王雅利 )
降低反渗透 装置的投资费用 ; 第五 , 操作简单 、 运行费用低 。 经 过以上工艺处理 , 中大部分的悬浮物 、 水 细菌 、 微生 物 、 胶体 等均
第, 卷 第 5 l 7 期
收稿 日期 :0 6 1-1 20- 0 2
基于 R P的音频流 多播 系统 的 J T MF实现
闰 改珍 , 师 卫
( 太原理工大学信息工程学院 , 山西太原 ,3 0 4 0 02 )
摘 要 :1 了音 频 流 多播 系统 结 构设 计 及 协 议 选 择 , 讨 了服 务 器端 的 J 4绍 - 探 MF实现 。 并
论 述 了客 户端 实现 方 法 。
关键词: 音频流多播 系统 ; 实时传输协议 ; 实时传输控 制协议 中图分类号 :N 3 T 9 文献标识码 : A
目前 , 越来越 多的嵌入式设备具有 互联 网接人功能 , 除了浏览 网页 之外 , 人们更多地 希望能够实时地欣赏音视频等 多媒体文件 。对于资源 有 限的嵌入式设备来说 , 直接 将多媒体文件下载下来播放 , 磁盘空问显 得 不 足 一因 此提 出使 用 流 媒体播 放 的形式 。而 市场 上 比较 流行 的 R a l e , n o s ei Pae 流播放 器代码 很大 , 合于在 嵌入式 e p yr d w da l r l a Wi M y 不适
RTP协议详解实时传输协议的音视频数据传输机制
RTP协议详解实时传输协议的音视频数据传输机制实时传输协议(RTP)是一种专门用于音视频数据传输的协议。
它通过提供时间戳、序列号和同步源等机制,以确保音视频数据能够实时、有序、可靠地传输。
本文将详细讲解RTP协议的音视频数据传输机制。
一、RTP协议概述RTP协议是由IETF(Internet Engineering Task Force)制定的,在音视频通信领域得到了广泛应用。
它通过在音视频数据上附加头信息的方式,实现对数据的分组、传输和重组。
二、RTP报文结构RTP报文采用二进制的格式进行传输,一般由固定长度的头部和可变长度的有效载荷组成。
头部包含了报文的一些关键信息,如版本号、序列号、时间戳等,而有效载荷部分则存放着音视频数据。
三、RTP序列号与时间戳1. 序列号:RTP序列号是一个16位的无符号整数,用于标识RTP报文的顺序。
发送者在每发送一个RTP报文时,将序列号递增1并附加在报文头部,接收者通过对序列号进行排序,可以还原出音视频数据的正确顺序。
2. 时间戳:RTP时间戳用于标识音视频数据的播放时间,以毫秒为单位。
发送者在每发送一个RTP报文时,会将当前时间戳附加在报文头部,接收者可以根据时间戳信息对音视频数据进行同步。
四、RTP同步源(SSRC)RTP同步源标识了一路音视频数据的来源,它是一个32位的无符号整数。
通过SSRC,接收者可以确定音视频数据所属的流,并将不同流的数据进行分离与重组。
五、RTP报文传输流程RTP协议的音视频数据传输可以简要分为以下几个步骤:1. 数据封装:发送端将音视频数据打包成RTP报文,包括头部和有效载荷两部分。
2. 报文传输:发送端通过UDP(User Datagram Protocol)将RTP报文传输给接收端。
3. 报文接收:接收端通过UDP接收RTP报文,并对数据进行解析,提取出音视频数据和报文头部的各项信息。
4. 数据解封:接收端根据解析得到的信息,将收到的RTP报文解封得到音视频数据。
基于RTP实时传输协议的JMF多媒体传输源代码
基于RTP实时传输协议的JMF多媒体传输源代码import java.io.*;import java.awt.*;import .*;import java.awt.event.*;import java.util.Vector;import javax.media.*;import javax.media.rtp.*;import javax.media.rtp.event.*;import javax.media.rtp.rtcp.*;import javax.media.protocol.*;import javax.media.protocol.DataSource;import javax.media.format.AudioFormat;import javax.media.format.VideoFormat;import javax.media.Format;import javax.media.format.FormatChangeEvent;import javax.media.control.BufferControl;/*** AVReceive3 to receive RTP transmission using the RTPConnector.*/public class AVReceive3 implements ReceiveStreamListener, SessionListener, ControllerListener{String sessions[] = null;RTPManager mgrs[] = null;Vector playerWindows = null;boolean dataReceived = false;Object dataSync = new Object();public AVReceive3(String sessions[]) {this.sessions = sessions;}protected boolean initialize() {try {mgrs = new RTPManager[sessions.length];playerWindows = new Vector();SessionLabel session;// Open the RTP sessions.for (int i = 0; i < sessions.length; i++) {// Parse the session addresses.try {session = new SessionLabel(sessions[i]);} catch (IllegalArgumentException e) {System.err.println("Failed to parse the session address given: " + sess ions[i]);return false;}System.err.println(" - Open RTP session for: addr: " + session.addr + " po rt: " + session.port + " ttl: " + session.ttl);mgrs[i] = (RTPManager) RTPManager.newInstance();mgrs[i].addSessionListener(this);mgrs[i].addReceiveStreamListener(this);// Initialize the RTPManager with the RTPSocketAdaptermgrs[i].initialize(new RTPSocketAdapter(InetAddress.getByName(session.addr),session.port, session.ttl));// You can try out some other buffer size to see// if you can get better smoothness.BufferControl bc =(BufferControl)mgrs[i].getControl("javax.media.control.B ufferControl");if (bc != null)bc.setBufferLength(350);}} catch (Exception e){System.err.println("Cannot create the RTP Session: " + e.getMessage());return false;}// Wait for data to arrive before moving on.long then = System.currentTimeMillis();long waitingPeriod = 30000; // wait for a maximum of 30 secs.try{synchronized (dataSync) {while (!dataReceived &&System.currentTimeMillis() - then < waitingPeriod) {if (!dataReceived)System.err.println(" - Waiting for RTP data to arrive");dataSync.wait(1000);}}} catch (Exception e) {}if (!dataReceived) {System.err.println("No RTP data was received.");close();return false;}return true;}public boolean isDone() {return playerWindows.size() == 0;}/*** Close the players and the session managers.*/protected void close() {for (int i = 0; i < playerWindows.size(); i++) {try {((PlayerWindow)playerWindows.elementAt(i)).close();} catch (Exception e) {}}playerWindows.removeAllElements();// close the RTP session.for (int i = 0; i < mgrs.length; i++) {if (mgrs[i] != null) {mgrs[i].removeTargets( "Closing session from AVReceive3"); mgrs[i].dispose();mgrs[i] = null;}}}PlayerWindow find(Player p) {for (int i = 0; i < playerWindows.size(); i++) {PlayerWindow pw = (PlayerWindow)playerWindows.elementAt(i);if (pw.player == p)return pw;}return null;}PlayerWindow find(ReceiveStream strm) {for (int i = 0; i < playerWindows.size(); i++) {PlayerWindow pw = (PlayerWindow)playerWindows.elementAt(i);if (pw.stream == strm)return pw;}return null;}/*** SessionListener.*/public synchronized void update(SessionEvent evt) {if (evt instanceof NewParticipantEvent) {Participant p = ((NewParticipantEvent)evt).getParticipant();System.err.println(" - A new participant had just joined: " + p.getCNAME() );}}/*** ReceiveStreamListener*/public synchronized void update( ReceiveStreamEvent evt) { RTPManager mgr = (RTPManager)evt.getSource();Participant participant = evt.getParticipant(); // could be null.ReceiveStream stream = evt.getReceiveStream(); // could be null.if (evt instanceof RemotePayloadChangeEvent) {System.err.println(" - Received an RTPPayloadChangeEvent.");System.err.println("Sorry, cannot handle payload change.");System.exit(0);}else if (evt instanceof NewReceiveStreamEvent) {try {stream = ((NewReceiveStreamEvent)evt).getReceiveStream();DataSource ds = stream.getDataSource();// Find out the formats.RTPControl ctl = (RTPControl)ds.getControl("javax.media.rtp.RTPControl");if (ctl != null){System.err.println(" - Recevied new RTP stream: " + ctl.getFormat()); } elseSystem.err.println(" - Recevied new RTP stream");if (participant == null)System.err.println(" The sender of this stream had yet to be ident ified.");else {System.err.println(" The stream comes from: " + participant.getCNA ME());}// create a player by passing datasource to the Media ManagerPlayer p = javax.media.Manager.createPlayer(ds);if (p == null)return;p.addControllerListener(this);p.realize();PlayerWindow pw = new PlayerWindow(p, stream);playerWindows.addElement(pw);// Notify intialize() that a new stream had arrived.synchronized (dataSync) {dataReceived = true;dataSync.notifyAll();}} catch (Exception e) {System.err.println("NewReceiveStreamEvent exception " + e.getMessage());return;}}else if (evt instanceof StreamMappedEvent) {if (stream != null && stream.getDataSource() != null) {DataSource ds = stream.getDataSource();// Find out the formats.RTPControl ctl = (RTPControl)ds.getControl("javax.media.rtp.RTPControl"); System.err.println(" - The previously unidentified stream ");if (ctl != null)System.err.println(" " + ctl.getFormat());System.err.println(" had now been identified as sent by: " + participa nt.getCNAME());}}else if (evt instanceof ByeEvent) {System.err.println(" - Got \"bye\" from: " + participant.getCNAME());PlayerWindow pw = find(stream);if (pw != null) {pw.close();playerWindows.removeElement(pw);}}}/*** ControllerListener for the Players.*/public synchronized void controllerUpdate(ControllerEvent ce) {Player p = (Player)ce.getSourceController();if (p == null)return;// Get this when the internal players are realized.if (ce instanceof RealizeCompleteEvent) {PlayerWindow pw = find(p);if (pw == null) {// Some strange happened.System.err.println("Internal error!");System.exit(-1);}pw.initialize();pw.setVisible(true);p.start();}if (ce instanceof ControllerErrorEvent) {p.removeControllerListener(this);PlayerWindow pw = find(p);if (pw != null) {pw.close();playerWindows.removeElement(pw);}System.err.println("AVReceive3 internal error: " + ce);}}/*** A utility class to parse the session addresses.*/class SessionLabel {public String addr = null;public int port;public int ttl = 1;SessionLabel(String session) throws IllegalArgumentException {int off;String portStr = null, ttlStr = null;if (session != null && session.length() > 0) {while (session.length() > 1 && session.charAt(0) == '/') session = session.substring(1);// Now see if there's a addr specified.off = session.indexOf('/');if (off == -1) {if (!session.equals(""))addr = session;} else {addr = session.substring(0, off);session = session.substring(off + 1);// Now see if there's a port specifiedoff = session.indexOf('/');if (off == -1) {if (!session.equals(""))portStr = session;} else {portStr = session.substring(0, off); session = session.substring(off + 1);// Now see if there's a ttl specifiedoff = session.indexOf('/');if (off == -1) {if (!session.equals(""))ttlStr = session;} else {ttlStr = session.substring(0, off);}}}}if (addr == null)throw new IllegalArgumentException();if (portStr != null) {try {Integer integer = Integer.valueOf(portStr); if (integer != null)port = integer.intValue();} catch (Throwable t) {throw new IllegalArgumentException(); }} elsethrow new IllegalArgumentException();if (ttlStr != null) {try {Integer integer = Integer.valueOf(ttlStr);if (integer != null)ttl = integer.intValue();} catch (Throwable t) {throw new IllegalArgumentException();}}}}/*** GUI classes for the Player.*/class PlayerWindow extends Frame {Player player;ReceiveStream stream;PlayerWindow(Player p, ReceiveStream strm) { player = p;stream = strm;}public void initialize() {add(new PlayerPanel(player));}public void close() {player.close();setVisible(false);dispose();}public void addNotify() {super.addNotify();pack();}/*** GUI classes for the Player.*/class PlayerPanel extends Panel { Component vc, cc;PlayerPanel(Player p) {setLayout(new BorderLayout());if ((vc = p.getVisualComponent()) != null)add("Center", vc);if ((cc = p.getControlPanelComponent()) != null) add("South", cc);}public Dimension getPreferredSize() {int w = 0, h = 0;if (vc != null) {Dimension size = vc.getPreferredSize();w = size.width;h = size.height;}if (cc != null) {Dimension size = cc.getPreferredSize();if (w == 0)w = size.width;h += size.height;}if (w < 160)w = 160;return new Dimension(w, h);}public static void main(String argv[]) {if (argv.length == 0)prUsage();AVReceive3 avReceive = new AVReceive3(argv);if (!avReceive.initialize()) {System.err.println("Failed to initialize the sessions."); System.exit(-1);}// Check to see if AVReceive3 is done.try {while (!avReceive.isDone())Thread.sleep(1000);} catch (Exception e) {}System.err.println("Exiting AVReceive3");}static void prUsage() {System.err.println("Usage: AVReceive3 "); System.err.println(" : //");System.exit(0);}}// end of AVReceive3发送端代码:import java.awt.*;import java.io.*;import .InetAddress;import javax.media.*;import javax.media.protocol.*;import javax.media.protocol.DataSource;import javax.media.format.*;import javax.media.control.TrackControl;import javax.media.control.QualityControl;import javax.media.rtp.*;import javax.media.rtp.rtcp.*;import com.sun.media.rtp.*;public class AVTransmit3 {// Input MediaLocator// Can be a file or http or capture sourceprivate MediaLocator locator;private String ipAddress;private int portBase;private Processor processor = null;private RTPManager rtpMgrs[];private DataSource dataOutput = null;public AVTransmit3(MediaLocator locator,String ipAddress,String pb,Format format) {this.locator = locator;this.ipAddress = ipAddress;Integer integer = Integer.valueOf(pb);if (integer != null)this.portBase = integer.intValue();}/*** Starts the transmission. Returns null if transmission started ok. * Otherwise it returns a string with the reason why the setup failed. */public synchronized String start() {String result;// Create a processor for the specified media locator // and program it to output JPEG/RTPresult = createProcessor();if (result != null)return result;// Create an RTP session to transmit the output of the // processor to the specified IP address and port no. result = createTransmitter();if (result != null) {processor.close();processor = null;return result;}// Start the transmissionprocessor.start();return null;}/*** Stops the transmission if already started*/public void stop() {synchronized (this) {if (processor != null) {processor.stop();processor.close();processor = null;for (int i = 0; i < rtpMgrs.length; i++) {rtpMgrs[i].removeTargets( "Session ended."); rtpMgrs[i].dispose();}}}}private String createProcessor() {if (locator == null)return "Locator is null";DataSource ds;DataSource clone;try {ds = javax.media.Manager.createDataSource(locator);} catch (Exception e) {return "Couldn't create DataSource";}// Try to create a processor to handle the input media locator try {processor = javax.media.Manager.createProcessor(ds);} catch (NoProcessorException npe) {return "Couldn't create processor";} catch (IOException ioe) {return "IOException creating processor";}// Wait for it to configureboolean result = waitForState(processor, Processor.Configured);if (result == false)return "Couldn't configure processor";// Get the tracks from the processorTrackControl [] tracks = processor.getTrackControls();// Do we have atleast one track?if (tracks == null || tracks.length < 1)return "Couldn't find tracks in processor";// Set the output content descriptor to RAW_RTP// This will limit the supported formats reported from// Track.getSupportedFormats to only valid RTP formats.ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP); processor.setContentDescriptor(cd);Format supported[];Format chosen;boolean atLeastOneTrack = false;// Program the tracks.for (int i = 0; i < tracks.length; i++) {Format format = tracks[i].getFormat();if (tracks[i].isEnabled()) {supported = tracks[i].getSupportedFormats();// We've set the output content to the RAW_RTP.// So all the supported formats should work with RTP.// We'll just pick the first one.if (supported.length > 0) {if (supported[0] instanceof VideoFormat) {// For video formats, we should double check the// sizes since not all formats work in all sizes.chosen = checkForVideoSizes(tracks[i].getFormat(),supported[0]);} elsechosen = supported[0];tracks[i].setFormat(chosen);System.err.println("Track " + i + " is set to transmit as:");System.err.println(" " + chosen);atLeastOneTrack = true;} elsetracks[i].setEnabled(false);} elsetracks[i].setEnabled(false);}if (!atLeastOneTrack)return "Couldn't set any of the tracks to a valid RTP format";// Realize the processor. This will internally create a flow// graph and attempt to create an output datasource for JPEG/RTP // audio frames.result = waitForState(processor, Controller.Realized);if (result == false)return "Couldn't realize processor";// Set the JPEG quality to .5.setJPEGQuality(processor, 0.5f);// Get the output data source of the processordataOutput = processor.getDataOutput();return null;}/*** Use the RTPManager API to create sessions for each media * track of the processor.*/private String createTransmitter() {// Cheated. Should have checked the type.PushBufferDataSource pbds = (PushBufferDataSource)dataOutput;PushBufferStream pbss[] = pbds.getStreams();rtpMgrs = new RTPManager[pbss.length];SendStream sendStream;int port;SourceDescription srcDesList[];for (int i = 0; i < pbss.length; i++) {try {rtpMgrs[i] = RTPManager.newInstance();port = portBase + 2*i;// Initialize the RTPManager with the RTPSocketAdapterrtpMgrs[i].initialize(new RTPSocketAdapter(InetAddress.getByName(ipAddress),port));System.err.println( "Created RTP session: " + ipAddress + " " + port);sendStream = rtpMgrs[i].createSendStream(dataOutput, i);sendStream.start();} catch (Exception e) {return e.getMessage();}}return null;}/*** For JPEG and H263, we know that they only work for particular* sizes. So we'll perform extra checking here to make sure they* are of the right sizes.*/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)) {// For JPEG, make sure width and height are divisible by 8. 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)) {// For H.263, we only support some specific sizes.if (size.width < 128) {width = 128;height = 96;} else if (size.width < 176) {width = 176;height = 144;} else {width = 352;height = 288;}} else {// We don't know this particular format. We'll just// leave it alone then.return supported;}return (new VideoFormat(null,new Dimension(width, height),Format.NOT_SPECIFIED,null,Format.NOT_SPECIFIED)).intersects(supported);}/*** Setting the encoding quality to the specified value on the JPEG encoder. * 0.5 is a good default.*/void setJPEGQuality(Player p, float val) {Control cs[] = p.getControls();QualityControl qc = null;VideoFormat jpegFmt = new VideoFormat(VideoFormat.JPEG);// Loop through the controls to find the Quality control for // the JPEG encoder.for (int i = 0; i < cs.length; i++) {if (cs[i] instanceof QualityControl &&cs[i] instanceof Owned) {Object owner = ((Owned)cs[i]).getOwner();// Check to see if the owner is a Codec.// Then check for the output format.if (owner instanceof Codec) {Format fmts[] = ((Codec)owner).getSupportedOutputFormats(null);for (int j = 0; j < fmts.length; j++) {if (fmts[j].matches(jpegFmt)) {qc = (QualityControl)cs[i];qc.setQuality(val);System.err.println("- Setting quality to " +val + " on " + qc);break;}}}if (qc != null)break;}}}/************************************************************** ** * Convenience methods to handle processor's state changes.*************************************************************** */private Integer stateLock = new Integer(0);private boolean failed = false;Integer getStateLock() {return stateLock;}void setFailed() {failed = true;}private synchronized boolean waitForState(Processor p, int state) { p.addControllerListener(new StateListener());failed = false;// Call the required method on the processorif (state == Processor.Configured) {p.configure();} else if (state == Processor.Realized) {p.realize();}// Wait until we get an event that confirms the// success of the method, or a failure event.// See StateListener inner classwhile (p.getState() < state && !failed) {synchronized (getStateLock()) {try {getStateLock().wait();} catch (InterruptedException ie) {return false;}}}if (failed)return false;elsereturn true;}/************************************************************** *** Inner Classes*************************************************************** */ class StateListener implements ControllerListener { public void controllerUpdate(ControllerEvent ce) {// If there was an error during configure or// realize, the processor will be closedif (ce instanceof ControllerClosedEvent)setFailed();// All controller events, send a notification// to the waiting thread in waitForState method.if (ce instanceof ControllerEvent) {synchronized (getStateLock()) {getStateLock().notifyAll();}}}}/************************************************************** *** Sample Usage for AVTransmit3 class*************************************************************** */public static void main(String [] args) {// We need three parameters to do the transmission// For example,// java AVTransmit3 file:/C:/media/test.mov 129.130.131.132 42050。
rtp 组播编程实例
rtp 组播编程实例RTP组播编程实例标题:使用RTP实现音频组播引言:音频组播是一种使用RTP(Real-time Transport Protocol)实现的技术,能够在网络中同时传输音频流到多个接收器。
本文将介绍如何使用RTP来实现音频组播,并展示其在实际场景中的应用。
1. RTP简介RTP是一种实时传输协议,常用于音频和视频的传输。
它提供了数据包分组、时间戳、序列号等功能,以确保音频和视频能够按顺序、实时地传输到接收端。
2. 音频组播原理音频组播通过将音频流分成若干个数据包,并使用RTP协议进行传输。
发送方将音频数据打包成RTP数据包,并添加相应的时间戳和序列号。
接收方通过解析RTP数据包,按照时间戳和序列号来还原音频数据,并播放出来。
3. 使用RTP实现音频组播的步骤步骤一:创建RTP会话我们需要创建一个RTP会话,用于发送和接收音频数据。
在创建会话时,需要指定发送端口和接收端口。
步骤二:音频编码与解码在发送端,我们需要对音频进行编码,将其转换为RTP数据包。
常用的音频编码算法包括G.711、G.729等。
在接收端,需要对接收到的RTP数据包进行解码,还原音频数据。
步骤三:数据分组和传输在发送端,我们将音频数据按照一定的大小进行分组,并打包成RTP数据包。
然后,通过UDP协议将RTP数据包发送到指定的组播地址和端口。
在接收端,通过监听指定的组播地址和端口,收到RTP数据包后进行解析和还原。
步骤四:播放音频在接收端,我们将解析和还原后的音频数据进行播放。
4. 音频组播的应用音频组播广泛应用于多媒体会议、音频广播、实时语音通信等领域。
它可以实现多个用户同时收听同一音频源,提高资源利用率,并减少网络带宽的占用。
结论:通过使用RTP实现音频组播,我们可以在网络中实现高质量、实时的音频传输。
它是一种非常有效的技术,广泛应用于各种实时音频通信场景。
希望本文对读者理解RTP组播编程有所帮助,并能够在实际项目中应用起来。
如何实现音视频通讯的JMF技术
如何实现音视频通讯的JMF技术随着网络技术的发展,在网络上进行语音、视频进行通话交流、数据传输等即时通讯等功能成为了网络通信的热点。
佰锐科技的AnyChat音视频开发互动平台可以实现双方音视频通话、文字通信,数字传输等功能,那么如何通过应用编程接口为音视频的编解码、传输和播放提供整套的架构呢!这就需要使用JMF。
java media framework (JMF )为java 在多媒体领域的开发提供了便利的平台。
本文基于JMF 遵从RTP 协议对音/视频聊天进行了实现,解决了会话的管理和流媒体的发送、接收、播放等关键问题。
如今VoIP 已经成为一种经济高效的通讯方式。
不过只有语音方面的交互已经不能满足人们的需求。
只闻其声不见其人显得过于单调。
音/视频聊天在这种需求下应运而生。
音/视频聊天的关键是实时性和同步。
因此RTP(实时传输协议)就作为其底层传输协议的不二之选。
Sun公司所推出的java 凭借其面向对象、跨平台、安全、多线程等性能成为现今最流行的网络编程语言。
Sun在java 的基础上针对多媒体的播放与传输开发了JMF。
RTP与JMF 2. 1 RTP与RTCP RTP 是由IETF 的AVT 工作组针对流媒体开发的协议,它位于传输层之上但并不要求传输层协议使用UDP(用户数据报协议),不过迄今为止UDP 是最常用的RTP 的底层协议。
由于UDP 是面向无连接的不可靠协议,这使其更适于流媒体传输。
UDP 作为轻量级的传输协议并不保证传输质量但实现了高效的盲目传输。
QOS 由UDP 的上层协议RTP 实现。
针对UDP 的无连接传输所导致的误码、丢失、乱序问题,RTP 规定了其组成。
RTP 包由两部分组成:包头和负载。
包头中含有负载类型、序号、时间戳、同步源标识等。
负载存储了包头指定的流媒体数据。
RTCP (实时传输控制协议)为RTP 连接提供了QOS 信息并维持各个参与者的状态信息。
RTCP 共有五个组成部分:RR (接收端报文)、SR (发送端报文)、SDES (信源描述)、 BYE(结束报文)和AS(应用程序特定)。
基于RTSP_RTP的媒体点播服务器的设计与实现_方群
1引言计算机技术和Internet 的发展给教育方式带来了革命,一种新型的教育模式——远程教育(或称网络教育)正在被越来越多的人所接受。
现代远程教育通过音频、视频(直播或录像)及包括实时和非实时在内的计算机技术把课程传送到校园之外。
从技术上讲,现代远程教育系统是建立在现代传媒技术基础上的多媒体应用系统。
在远程教育中,非实时教学起着主体作用,它充分体现了学生学习的自主性,可以不受时间、空间的限制,学习到与教师课堂教学相同的课程,收到相同的教学效果;体现了个性化学习,学生不一定按照教师的教学过程按部就班地学,可以根据自身的学习情况自己掌握学习的内容和学习的进度。
媒体点播(Media on Demand )是非实时教学系统中的一个非常重要的技术,媒体点播是将一些教学资源(教师上课时实时录制的课件、利用多媒体制作工具的多媒体教学素材等)放在服务器上,使学生可以进行异步模式的播放学习。
本文从流媒体技术入手,介绍了一个自行设计和实现的媒体点播服务系统,该系统基于RTSP (Real Time Streaming Protocol )协议和RTP (Real-time Transport Protocol )协议,实现流媒体数据的传输和控制,并具有一定的带宽自适应能力。
2流媒体简介多媒体技术的不断发展及其在Internet 上的应用,迫切需要相关的技术来实现视频、音频和动画等数据的网络传输,早期采用类似于将网页、文本或者图片下载到本地的方式来播放一些多媒体内容,但是大部分的多媒体文件都很庞大,特别是一些视频文件,如果采用传统的方式,用户必须等待整个文件下载完毕才能进行播放。
在这种背景之下,出现了流媒体技术(或者称为流式媒体技术),所谓流媒体技术就是把连续的媒体信息经过压缩处理后放到网络服务器上,让用户可以一边下载一边观看或者收听,而不需要等待整个媒体文件下载完毕。
实际上流媒体技术是网络视音频技术发展到一定阶段的产物,是一种解决多媒体播放时网络带宽问题的“软技术”。
RTP协议实时传输协议详解
RTP协议实时传输协议详解RTP(Real-time Transport Protocol)是一种用于在互联网上传输实时数据的协议,被广泛应用于音频、视频以及其他多媒体数据的传输。
本文将详细解析RTP协议的特点、组成以及工作原理。
一、RTP协议特点RTP协议的主要特点如下:1. 实时性:RTP协议旨在传输实时数据,如音频、视频等。
它采用时间戳来确保数据的顺序和同步性,从而提供更好的实时性。
2. 独立性:RTP协议可以在不同的传输层协议(如UDP、TCP等)上运行,因此具有较好的独立性和兼容性。
3. 扩展性:RTP协议的头部可以添加自定义的扩展字段,以满足不同应用场景的需求。
4. 传输效率:RTP协议采用数据分片和压缩等技术,提高了传输效率和带宽利用率。
5. 错误恢复:RTP协议对丢失、重复和损坏的数据包进行处理和恢复,提高了传输的可靠性。
二、RTP协议组成RTP协议由头部和有效载荷两部分组成。
1. 头部(Header):RTP头部用于存储传输相关的信息,包括版本号、负载类型、序列号、时间戳等。
头部的长度为12个字节。
2. 有效载荷(Payload):有效载荷部分用于存储实际的数据,如音频、视频等。
三、RTP协议工作原理RTP协议的工作原理可以分为以下几个步骤:1. 建立会话:通信双方通过协商建立RTP会话。
会话的参数包括传输协议类型、有效载荷类型、时钟频率等。
2. 数据分帧:发送方将连续的音频或视频数据进行切割,生成RTP数据包。
每个数据包都包含RTP头部和有效载荷。
3. 添加序列号和时间戳:发送方为每个RTP数据包添加序列号和时间戳。
序列号用于标识数据包的顺序,时间戳用于实现同步播放。
4. 传输数据:发送方通过底层传输协议(如UDP)将RTP数据包发送给接收方。
5. 数据恢复:接收方根据序列号对接收到的数据包进行排序和恢复。
如果数据包有丢失或损坏,接收方可以根据序列号和时间戳进行错误恢复。
6. 解包和播放:接收方将RTP数据包解析成原始的音频或视频数据,并进行解码和播放。
使用Java进行音视频处理与流媒体传输
使用Java进行音视频处理与流媒体传输Java是一种广泛用于开发各种应用程序的编程语言。
在音视频处理和流媒体传输方面,Java也提供了一些强大的工具和库,使开发人员能够轻松处理音视频数据,并实现高效的流媒体传输。
一、音视频处理在音视频处理方面,Java提供了一些常用的库和工具,例如Java Media Framework(JMF)和Java Sound API等,它们可以帮助我们对音频和视频数据进行处理和编辑。
1. 音频处理Java Sound API是Java平台上用于处理音频的一套API,它提供了一些类和方法,用于读取、写入、混合和处理音频数据。
使用Java Sound API,我们可以实现音频播放、录制、编辑等功能。
以下是一个简单的示例代码,演示了如何使用Java Sound API播放音频文件:```javaimport javax.sound.sampled.*;public class AudioPlayer {public static void main(String[] args) {try {AudioInputStream audioInputStream =AudioSystem.getAudioInputStream(new File("audio.wav"));Clip clip = AudioSystem.getClip();clip.open(audioInputStream);clip.start();Thread.sleep(clip.getMicrosecondLength() / 1000);} catch (Exception e) {e.printStackTrace();}}}```2. 视频处理在视频处理方面,Java Media Framework(JMF)是一个功能强大的库,它提供了用于处理视频流、捕获视频和播放视频的API和工具。
基于RTP的实时多媒体数据网络传输拥塞控制策略
实时流协议 RTSP 、IPV6 [9]协议等, 。
2 传输方案
多媒体数据流实时传输协议, 主要包括: 流协议 ST Ⅱ、实时传输协议 RTP、资源预留协议 RSVP[1],[2],[3],[4]、
图 1 拥塞控制对网络性能的影响
在上述流媒体协议中, 有的因为网络环境和设备条 件等的限制, 还没有普及应用。目前在流式传输的实现 方案中 , 一般采用 H T T P / T C P 来传输控制信息 , 采用 RTP/UDP 来传输实时音视频数据。控制策略常是通过
3.1 反馈拥塞控制分析
反馈控制 , 即由接收方统计流的包丢失率 , 再反馈 给发送方, 发送方根据此信息调整发送的速率来避免网 络的拥塞。
3 控制策略
实时流媒体应用如视音频等都要求数据流的平滑 性 , 即发送方的发送速率不能有太剧烈的抖动 , 剧烈的 速率变化成为流媒体应用的一大障碍, 为了改变这种发 送方速率的抖动状况 , 出现了基于速率的拥塞控制算 法, 即按每秒发送多少比特来控制数据发送。因为流媒 体本质都是基于速率的。速率控制方法根据网络可用 带宽的变化 , 动态调整媒体流的速率 , 把网络拥塞发生 的可能性降到最低 为了便于分析实时多媒体数据传输中的速率控制问 题, 先描述一个基于源端速率控制的传输框架。如图 2 。 算法描述为 : P(n) 表示返回的 RTCP 包所包含丢包率 , 含义是反 映了接收端从前一个 RTCP 包后的 n 个包时间间隔内数 据包丢失情况 , SV 表示发送端初始速率, 并设两个阈值
通信与信息处理
Communication and Inform制增加的幅度 , 既能提高带宽的利用率 , 还能减 少发生拥塞的几率。但是其缺点是其鲁棒性不够好。为 改善传输系统稳定性, 实行自适应动态调整算法。数据 经平滑处理后为(4) 式中的 p(n) 。
毕业设计(论文)-jmf语音视频聊天软件的实现[管理资料]
摘要随着网络的不断发展,在网络上的语音视频通信成为研究和应用的热点之一。
要在网络上进行语音视频通信,便要解决音、视频信号的采集、回放、编解码以及数据的传输的问题。
本文将用Java的JMF解决这些问题。
JMF是Java的一种可选用的应用编程接口(API)软件包,它为音频和视频等媒体内容的采集、回放、传输和编码转换等提供了一个统一的架构。
JMF用RTP协议传输实时媒体信号。
RTP是针对Internet上多媒体数据流的一个传输协议。
RTP能在一对一或一对多的传输情况下工作,其目的是提供时间信息和实现流同步。
RTP建立在UDP上。
RTP只保证实时数据的传输,并不提供可靠的传送机制,也不提供流量控制或拥塞控制,它依靠RTCP提供这些服务。
RTP的一对多的传输,由IP组播实现。
IP组播是主机之间“一对一组”的通讯模式。
加入了同一个组的主机可以接收到此组内的所有数据,网络中的交换机和路由器只向有需求者复制并转发其所需数据。
IP组播能有效地节省网络和主机的资源,并且它允许在广域网上传输。
由于NAT的存在,令许多P2P应用无法应用于NAT背后的主机,语音视频通信也不例外。
根据NAT的原理,能用UDP打孔技术进行NAT 的穿透。
本文使用JMF完成语音视频聊天软件的实现。
该软件既能进行点对点的语音视频通信,也能进行多人语音视频通信,并用UDP打孔技术完成了对NAT的穿透,使该软件能在广域网上使用。
关键字:语音视频通信,JMF,RTP,组播,穿透NAT,UDP打孔技术ABSTRACTAlong with the constant development of the network, the voice and video communications on Internet is one of hot topic of research and application. In order to communication with voice and video on the network, it needs to solve sound and video signals’ collection, playback, codecs and data transmission problems. This paper uses the Java’s JMF to solve these problems. JMF is an available Application Programming Interface (API) package of Java. It provides a unified framework for audio and video media content collection, playback, data conversion and transmission.JMF uses RTP to transmit real-time media signal. RTP is a special transmission protocol for Internet multimedia data streams. RTP can in point-to-point or point-to-multi-point transmission mode, the aim is to provide time information and to achieve flow synchronization. RTP bases on UDP. RTP only guaranteed real-time data’s transmission. It does not provide reliable delivery mechanisms, flow control or congestion control. It relies on RTCP to provide these services.Point-to-multi-point transmission mode of RTP uses IP Multicast technology. IP Multicast is communication mode of hosts "one-to-one group". Computers join in a group can receive all the data from this group. Switches and routers only replicate and transmit the data to the computers who need the data. It can effectively to save network’s and computer’s resources, and it is allowed to transmit on the wide-area network.Due to the existence of NAT, many P2P applications can not be used to the computers behind NAT. Audio and video communication is no exception. According to the theory of NAT, we can use UDP hole punching technology to complete NAT penetration.This paper uses JMF to complete voice and video chatting software. The software not only can be used to do point-to-point voice and video communications, but also can be used to do many-to-many communications. The software uses UDP hole punching technology to complete NAT penetration so that it can be successfully applied on WAN.Keywords:voice and video communication, JMF, RTP, multicast, NAT penetration, UDP hole punching目录第一章概述 (1) (1) (2)第二章JMF基础 (4)关于JMF技术 (4)JMF模型 (4)JMF常用类 (5)事件模型 (11)第三章RTP、RTCP (16)流媒体 (16)实时传输协议RTP (17)实时传输控制协议RTCP (20)第四章组播技术 (22)概述 (22)单播/组播/广播通讯协议的特点及应用对比 (22)第五章穿透NAT (27)NAT的作用 (27)NAT的分类及工作原理 (28)NAT产生的问题 (31)穿透NAT——UDP打孔技术 (32)第六章基于JMF的语音聊天软件的实现 (34)编程和运行环境 (34)主要功能模块的设计与实现 (34)运行结果 (44)总结 (47)参考文献 (49)致谢 (51)第一章概述语音通信系统可分为以下几个模块,如图1-1所示。
基于JMF架构的流媒体RTP/RTCP传输模型设计
摘
要 : 提 供 了对 R P R S ( J MF T / T P 实时传 送协 议 和 实时 流转 协 议 ) 支持 , 的 通过 在 分 析 R P R C T/T P
协 议 时得 出的反 馈 机 制 的 方向 , 实现 一 种基 于 J MF的 R P R C T / T P传 输 模 型 的整 体 设 计 。 实现 了一 个 动 态 的 网络反馈 机制 , 利 用其提 供 的动 态的反馈 信 息 实现 了对 发 送 端 和接 收 端 B f r 并 uf 的控 制 。 证 流媒 e 保
第2 3卷 第 1 期
21 0 0年 2月
四川理 工学院学报( 自然科学版 )
Junl f i unU ie i f c ne& E g er g N trl c neE io ) o ra o Sc a n r t o Si c h v sy e ni ei ( a a Si c dt n n n u e i
在 J F中对应播放器的接 口是 Pae。Pae 对象 M l r l r y y
( )向互联网上传音频和视频数据流。 5
( )在互 联 网上广 播 音频 和 视 频数 据 。 6
1 J MF架 构
11 J . MF简 介
J F架构 中还包括了一个 开放和统一的媒体架构 , M 可使开发人员灵 活采用各 种媒体 回放 、 获组件 , 捕 或采
用他 们 自己 的定 制 的 内插组 件 。
中, 出现 了一 个 不 可 避 免 的 问 题 , 是 在 客 户 端 数 量 过 就 多 的时 候 , 出现 了 服务 器 端 由 于 负载 过 重 而导 致 系统 性
基于RTP的MPEG24视频传输和多媒体同步
基于RTP的MPEG24视频传输和多媒体同步摘要在RTP协议原理和MPEG24编码特性的基础上,提出了适用于MPEG24视频传输和多媒体同步的算法。
利用RTP /RTCP端到端的反馈机制实现流量控制、拥塞控制以及媒体同步控制,使整个传输过程充分利用带宽,而不引起网络拥塞;同时保证在线播放时有良好的视觉质量又满足实时播放的要求。
关键词RTP MPEG24 同步控制拥塞控制0 引言随着网络技术的发展,产生了对诸如电子商务,远程教育,视频监视等服务的巨大需求。
而原有的基于MPEG21,MPEG22系统因为需占用太大带宽仅仅能在局域网中工作,限制了它们在Internet上的应用。
MPEG24标准以其出色的媒体性能,高压缩比成为Internet上视频音频压缩的主要标准。
RTP /RTCP协议是用于Internet 上针对多媒体数据流的一种传输协议。
RTP /RTCP协议提供流量控制和拥塞控制服务。
在RTP会话期间,各参与者周期性地传送RTCP包。
RTCP包中含有已发送的数据包的数量、丢失的数据包的数量等统计资料,服务器可以利用这些信息动态地改变传输速率,甚至改变有效载荷类型。
RTP和RTCP配合使用,用户可根据其相关信息实现实时,流畅的播放。
因而RTP /RTCP特别适合传送网上的实时数据。
随着适于MPEG24流的RTP载荷格式的提出,基于RTP的MPEG24流媒体传输渐渐地得到应用。
关于RTP协议[ 1 ]和MPEG24流的RTP载荷格式[ 2 ]的文献仅规范了RTP协议的传输格式和MPEG24流的RTP载荷格式,对于具体如何实现MPEG24比特流的打包;如何计算生成RTP包头和RTCP包;如何利用RTP协议实施流量控制,拥塞控制;如何媒体播放的同步的实现则没有过多的提及。
本文中将详细讨论这些问题。
1 用RTP传输M PEG24比特流1. 1 MPEG24比特流的打包MPEG24比特流提供了分级描述的语法层次:视觉对象序列(VS) ,视频对象(VO) ,视频对象层(VOL ) ,视频对象平面(VOP) 。
[实用参考]基于RTP协议的流媒体的实时传输的实现
毕业设计(论文)题目:基于RTP协议的流媒体的实时传输的实现系别:电子信息科学系专业:电子信息科学与技术班级:学生姓名:学号:指导教师:学位论文原创性声明本人郑重声明:所呈交的论文是本人在导师的指导下独立进行研究所取得的研究成果。
除了文中特别加以标注引用的内容外,本论文不包含任何其他个人或集体已经发表或撰写的成果作品。
本人完全意识到本声明的法律后果由本人承担。
作者签名:20GG年6月14日学位论文版权使用授权书本学位论文作者完全了解学校有关保障、使用学位论文的规定,同意学校保留并向有关学位论文管理部门或机构送交论文的复印件和电子版,允许论文被查阅和借阅。
本人授权省级优秀学士学位论文评选机构将本学位论文的全部或部分内容编入有关数据库进行检索,可以采用影印、缩印或扫描等复制手段保存和汇编本学位论文。
本学位论文属于1、保密□,在_________年解密后适用本授权书。
2、不保密√。
(请在以上相应方框内打“√”)作者签名:20GG年6月14日导师签名:20GG年6月15日摘要基于IP的网络中提供的尽力而为的服务并不适合流媒体的传输[4]。
本文的研究项目由网络流媒体传输需求提出,旨在研究基于RTP协议的流媒体的实时传输,使之能够适应网络状态的变化。
论文的论述从以下三个方面展开:(1)本文首先分析了网络多媒体应用中常用的流媒体技术,视频压缩编码技术。
(2)本文深入分析了RTP/RTCP的特点、内容,认为该协议非常适合多媒体信息的网上传输。
(3)为了实现实时传输,本文采用sun公司所提供的平台。
利用JAVA 提供的宽松的格式支持和基于JMF组件对象模型的特征,研究了JMF的体系结构、基本原理和基本构件,利用JMF的体系结构和已有的采集、编码组件,实现了一套完整的流媒体传输实验模型。
关键词:RTP;流媒体传输;JMF;InternetAbstractThebest-effortservicebasedonIPprovidedbP Internetisn’tsuitablefor ingfromaStreaming Mediatransmissionneedinnetworkedmultimediaapplication,theresearchp rojectthethesisdiscussesistoresearchanRTP-basedStreamingMediatrans missionwhichcanadapttothechangesofnetworkstates.Thethesisincludest hefollowingthreeparts:FirstlP,thisthesisanalPzesstreammediatechnologPa ndvideocompressioncodingtechnologPinnetworkedmultimediaapplicati on.SecondlP,thisthesislucubratesthecontentsandcharactersofRTP/RTCPan dthinksthatRTPiswellsuitablefortheStreamingMediainformationtransmiss ion.Fourt-hlP,inordertorealizeRTPandtransmissioncontrolpolicP,thisthesis ingthewidevarietPofformatssupported bPJAVAandthecharactersbasedonJMFcomponentobjectmodel,thisthesisr esearchesthearchitectureofJMF,itsbasictheorPandconstructionofitsbasicc omponent.WiththehelpofeGistingcaptureandencodecomponents,makin guseofJMFarchitecture,thisthesisrealizesanintegratedStreamingMediatra nsmissioneGperimentmodel.KePwords:RTP;StreamingMediaTransmission;JMF;Internet目录第一章绪论 (1)1.1课题的背景 (1)1.2本课题所做的工作 (1)1.3流媒体技术 (2)1.3.1视频技术发展的现状 (2)1.3.2多媒体数据压缩技术 (3)1.4实时传输协议RTP/RTCP (7)1.4.1 RTP的特点 (7)1.4.2 RTP的数据包格式 (8)1.4.3 RTP在协议层中的位置 (9)1.4.4 RTCP的控制功能 (10)1.4.5 RTCP发送方报告数据包格式 (11)第二章总体方案设计 (14)2.1 方案论证 (14)2.1.1方案一.采用DirectShow框架实现流媒体实时传输 (14)2.1.2 方案二. 在嵌入式平台下实现流媒体实时传输 (15)2.1.3 方案三. 采用JAVA媒体框架(JMF)实现流媒体实时传输 (16)2.2.系统总体设计 (17)2.3 系统处理流程图 (17)2.4 系统模块的划分及功能描述 (18)2.5 JMF体系结构 (18)2.6 建立Java多媒体开发环境所需的硬件和软件 (19)2.6.1 硬件环境 (19)2.6.2 软件环境 (19)2.7一种流媒体传输控制方法的提出 (20)2.7.1流媒体传输控制的特点 (20)2.7.2流媒体传输控制的研究 (21)2.7.3 本文提出的控制方法 (23)第三章用Java实现流媒体实时传输 (24)3.1服务器端媒体处理程序 (24)3.1.1 发送端程序流程图 (24)3.1.2 流媒体的捕获 (25)3.1.3 流媒体的压缩 (26)3.1.4 流媒体的实时传输 (27)3.1.5 停止传输流媒体 (29)3.2 采用JMF RTP API 接收流媒体数据 (30)3.2.1 JMF的回放机制 (30)3.2.2接收端程序流程图 (31)3.2.3 流媒体的接收 (32)3.2.4 流媒体解压缩、实时播放 (33)3.2.5 监听接收媒体流事件 (34)3.2.6 监听数据是否接收完毕 (35)3.3 本章小节 (36)第四章系统测试 (37)4.1 系统测试结果与分析 (37)4.2本章小节 (37)第五章结束语 (38)致谢 (39)参考文献 (40)附件 (42)第一章绪论1.1课题的背景目前,多媒体技术和计算机网络通信技术都有了很大的发展。
多媒体传输中的实时流媒体编码与传输研究
多媒体传输中的实时流媒体编码与传输研究摘要:随着互联网的快速发展,流媒体技术的应用也越来越广泛。
实时流媒体的编码与传输是流媒体系统中的关键环节。
本文将详细探讨多媒体传输中的实时流媒体编码与传输技术,并分析其在不同应用场景下的优势和挑战。
1. 引言实时流媒体是指将多媒体数据实时传输到用户端,使用户能够实时观看或聆听音视频内容。
实时流媒体技术的研究意义在于提供高质量的音视频传输服务,满足用户对高清、流畅的观看体验需求。
2. 实时流媒体编码实时流媒体编码是为了提高传输效率和节省带宽而进行的数据压缩处理。
常用的实时流媒体编码算法有H.264、H.265等。
这些编码算法能够将音视频数据转换为压缩格式,减小数据大小,提高传输效率。
3. 实时流媒体传输实时流媒体传输是指将经过编码的音视频数据通过网络传输到用户端的过程。
在传输过程中,需要克服网络延迟和丢包等问题,以保证音视频数据的实时性和质量。
常用的实时流媒体传输协议有RTP/RTSP、RTMP等。
4. 实时流媒体编码与传输技术应用场景4.1 视频会议实时流媒体编码与传输技术在视频会议中起到至关重要的作用。
通过对视频数据进行编码和压缩,能够减小数据传输的大小,提高网络传输效率,保证视频会议的清晰度和稳定性。
4.2 直播系统随着在线直播的流行,实时流媒体编码与传输技术在直播系统中发挥着重要的作用。
通过实时流媒体编码和传输技术,能够实现高质量、低延迟的直播服务,满足用户对实时互动的需求。
4.3 远程教育实时流媒体编码与传输技术在远程教育中也扮演着重要角色。
通过实时传输技术,学生可以在线观看课程视频,并与教师进行实时互动。
同时,高效的编码算法能够提升视频质量,提供良好的用户体验。
5. 实时流媒体编码与传输技术面临的挑战5.1 带宽限制实时流媒体编码与传输需要大量的带宽资源,而且对网络稳定性要求较高。
在网络条件较差的情况下,可能会出现数据丢包、延迟增大等问题,影响用户的观看体验。
基于JMF的实时多媒体传输系统的研究与实现(全文)
基于JMF的实时多媒体传输系统的研究与实现XX:1009-3044(20XX)14-3407-03Reserch nd Implementtion of Rel-time Multimedi Communiction System Bsed on JMFQING Xiu-hu(School of Electronic & Electricl Engineering, Wuhn Textile University, Wuhn 430073, Chin)bstrct: The multimedi Frmework nd the running mode of JMF were nlysis. JMF design of Rel-time Multimedi Communi ction System hd n in depth reserch. nd use the JMF frmework for the reliztion of Rel-time Multimedi Communiction Sys tem.Key words: JMF; multimedi communiction system; rel-time communiction1概述目前,微软公司的MSN、腾讯公司的QQ等XX络上流行的很多支持多媒体功能的即时通信软件,虽然都能提供两个用户之间的点对点音视频通信以及多用户之间的文字通信,但是对于多用户之间的音视频等多媒体通信的支持却不够好。
JMF是Jv多媒体框架(Jv Medi FrmeWork)的简称,JMF 不用关怀底层复杂的实现细节就可以编写出功能强大的多媒体程序。
JMF中包含了许多用于处理多媒体的PI。
在JMF中,用来接收和传输多媒体数据的协议是RTP协议(Rel-time Trnsport Pro tocol,实时传输协议)。
RTP是一种针对多媒体数据传输的实时传输协议,RTP会话将音视频等多媒体数据分为一系列的数据报,组成RTP数据流来传输,这种多媒体数据流是一种实时数据流,实时流的最大优点是:当客户端以实时流的形式接收音视频数据时,可以不用等待所有的数据接收完毕,就可以开始播放。
虚拟教室系统中音频视频实时传输模块的JMF实现
虚拟教室系统中音频视频实时传输模块的JMF实现
刘勇军
【期刊名称】《内江师范学院学报》
【年(卷),期】2004(019)002
【摘要】介绍实时传输协议(RTP)的基本内容和Java媒体框架应用编程接口JMF 的结构特点,并描述利用JMF技术开发多媒体虚拟教室系统的音频/视频处理模块的方法.
【总页数】5页(P90-94)
【作者】刘勇军
【作者单位】厦门大学,计算机科学系,福建,厦门,361005
【正文语种】中文
【中图分类】TP39
【相关文献】
1.基于DirectX的音频视频无线传输系统设计与实现 [J], 曹阳;梁华;蔡宣平
2.基于JMF的实时多媒体传输系统的研究与实现 [J], 卿秀华
3.基于JMF的远程教育系统中实时音频的实现 [J], 漆莲芝;冉蜀阳
4.基于JMF实时传输的远程授课系统的设计与实现 [J], 赵红宇;刘志勤
5.基于多种传输信道的实时动态图象语音传输系统的实现 [J], 方勇;熊淑华
因版权原因,仅展示原文概要,查看原文内容请购买。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
import java.io.*;import java.awt.*;import .*;import java.awt.event.*;import java.util.Vector;import javax.media.*;import javax.media.rtp.*;import javax.media.rtp.event.*;import javax.media.rtp.rtcp.*;import javax.media.protocol.*;import javax.media.protocol.DataSource;import javax.media.format.AudioFormat;import javax.media.format.VideoFormat;import javax.media.Format;import javax.media.format.FormatChangeEvent;import javax.media.control.BufferControl;/*** AVReceive3 to receive RTP transmission using the RTPConnector.*/public class AVReceive3 implements ReceiveStreamListener, SessionListener, ControllerListener{String sessions[] = null;RTPManager mgrs[] = null;Vector playerWindows = null;boolean dataReceived = false;Object dataSync = new Object();public AVReceive3(String sessions[]) {this.sessions = sessions;}protected boolean initialize() {try {mgrs = new RTPManager[sessions.length];playerWindows = new Vector();SessionLabel session;// Open the RTP sessions.for (int i = 0; i < sessions.length; i++) {// Parse the session addresses.try {session = new SessionLabel(sessions[i]);} catch (IllegalArgumentException e) {System.err.println("Failed to parse the session address given: " + sess ions[i]);return false;}System.err.println(" - Open RTP session for: addr: " + session.addr + " po rt: " + session.port + " ttl: " + session.ttl);mgrs[i] = (RTPManager) RTPManager.newInstance();mgrs[i].addSessionListener(this);mgrs[i].addReceiveStreamListener(this);// Initialize the RTPManager with the RTPSocketAdaptermgrs[i].initialize(new RTPSocketAdapter(InetAddress.getByName(session.addr),session.port, session.ttl));// You can try out some other buffer size to see// if you can get better smoothness.BufferControl bc = (BufferControl)mgrs[i].getControl("javax.media.control.B ufferControl");if (bc != null)bc.setBufferLength(350);}} catch (Exception e){System.err.println("Cannot create the RTP Session: " + e.getMessage());return false;}// Wait for data to arrive before moving on.long then = System.currentTimeMillis();long waitingPeriod = 30000; // wait for a maximum of 30 secs.try{synchronized (dataSync) {while (!dataReceived &&System.currentTimeMillis() - then < waitingPeriod) {if (!dataReceived)System.err.println(" - Waiting for RTP data to arrive");dataSync.wait(1000);}}} catch (Exception e) {}if (!dataReceived) {System.err.println("No RTP data was received.");close();return false;}return true;}public boolean isDone() {return playerWindows.size() == 0;}/*** Close the players and the session managers.*/protected void close() {for (int i = 0; i < playerWindows.size(); i++) {try {((PlayerWindow)playerWindows.elementAt(i)).close();} catch (Exception e) {}}playerWindows.removeAllElements();// close the RTP session.for (int i = 0; i < mgrs.length; i++) {if (mgrs[i] != null) {mgrs[i].removeTargets( "Closing session from AVReceive3"); mgrs[i].dispose();mgrs[i] = null;}}}PlayerWindow find(Player p) {for (int i = 0; i < playerWindows.size(); i++) {PlayerWindow pw = (PlayerWindow)playerWindows.elementAt(i);if (pw.player == p)return pw;}return null;}PlayerWindow find(ReceiveStream strm) {for (int i = 0; i < playerWindows.size(); i++) {PlayerWindow pw = (PlayerWindow)playerWindows.elementAt(i);if (pw.stream == strm)return pw;}return null;}/*** SessionListener.*/public synchronized void update(SessionEvent evt) {if (evt instanceof NewParticipantEvent) {Participant p = ((NewParticipantEvent)evt).getParticipant();System.err.println(" - A new participant had just joined: " + p.getCNAME() );}}/*** ReceiveStreamListener*/public synchronized void update( ReceiveStreamEvent evt) {RTPManager mgr = (RTPManager)evt.getSource();Participant participant = evt.getParticipant(); // could be null.ReceiveStream stream = evt.getReceiveStream(); // could be null.if (evt instanceof RemotePayloadChangeEvent) {System.err.println(" - Received an RTP PayloadChangeEvent.");System.err.println("Sorry, cannot handle payload change.");System.exit(0);}else if (evt instanceof NewReceiveStreamEvent) {try {stream = ((NewReceiveStreamEvent)evt).getReceiveStream();DataSource ds = stream.getDataSource();// Find out the formats.RTPControl ctl = (RTPControl)ds.getControl("javax.media.rtp.RTPControl");if (ctl != null){System.err.println(" - Recevied new RTP stream: " + ctl.getFormat()); } elseSystem.err.println(" - Recevied new RTP stream");if (participant == null)System.err.println(" The sender of this stream had yet to be ident ified.");else {System.err.println(" The stream comes from: " + participant.getCNA ME());}// create a player by passing datasource to the Media ManagerPlayer p = javax.media.Manager.createPlayer(ds);if (p == null)return;p.addControllerListener(this);p.realize();PlayerWindow pw = new PlayerWindow(p, stream);playerWindows.addElement(pw);// Notify intialize() that a new stream had arrived.synchronized (dataSync) {dataReceived = true;dataSync.notifyAll();}} catch (Exception e) {System.err.println("NewReceiveStreamEvent exception " + e.getMessage());return;}}else if (evt instanceof StreamMappedEvent) {if (stream != null && stream.getDataSource() != null) {DataSource ds = stream.getDataSource();// Find out the formats.RTPControl ctl = (RTPControl)ds.getControl("javax.media.rtp.RTPControl"); System.err.println(" - The previously unidentified stream ");if (ctl != null)System.err.println(" " + ctl.getFormat());System.err.println(" had now been identified as sent by: " + participa nt.getCNAME());}}else if (evt instanceof ByeEvent) {System.err.println(" - Got \"bye\" from: " + participant.getCNAME());PlayerWindow pw = find(stream);if (pw != null) {pw.close();playerWindows.removeElement(pw);}}}/*** ControllerListener for the Players.*/public synchronized void controllerUpdate(ControllerEvent ce) {Player p = (Player)ce.getSourceController();if (p == null)return;// Get this when the internal players are realized.if (ce instanceof RealizeCompleteEvent) {PlayerWindow pw = find(p);if (pw == null) {// Some strange happened.System.err.println("Internal error!");System.exit(-1);}pw.initialize();pw.setVisible(true);p.start();}if (ce instanceof ControllerErrorEvent) {p.removeControllerListener(this);PlayerWindow pw = find(p);if (pw != null) {pw.close();playerWindows.removeElement(pw);}System.err.println("AVReceive3 internal error: " + ce);}}/*** A utility class to parse the session addresses.*/class SessionLabel {public String addr = null;public int port;public int ttl = 1;SessionLabel(String session) throws IllegalArgumentException {int off;String portStr = null, ttlStr = null;if (session != null && session.length() > 0) {while (session.length() > 1 && session.charAt(0) == '/') session = session.substring(1);// Now see if there's a addr specified.off = session.indexOf('/');if (off == -1) {if (!session.equals(""))addr = session;} else {addr = session.substring(0, off);session = session.substring(off + 1);// Now see if there's a port specifiedoff = session.indexOf('/');if (off == -1) {if (!session.equals(""))portStr = session;} else {portStr = session.substring(0, off);session = session.substring(off + 1);// Now see if there's a ttl specifiedoff = session.indexOf('/');if (off == -1) {if (!session.equals(""))ttlStr = session;} else {ttlStr = session.substring(0, off);}}}}if (addr == null)throw new IllegalArgumentException();if (portStr != null) {try {Integer integer = Integer.valueOf(portStr);if (integer != null)port = integer.intValue();} catch (Throwable t) {throw new IllegalArgumentException();}} elsethrow new IllegalArgumentException();if (ttlStr != null) {try {Integer integer = Integer.valueOf(ttlStr);if (integer != null)ttl = integer.intValue();} catch (Throwable t) {throw new IllegalArgumentException();}}}}/*** GUI classes for the Player.*/class PlayerWindow extends Frame {Player player;ReceiveStream stream;PlayerWindow(Player p, ReceiveStream strm) {player = p;stream = strm;}public void initialize() {add(new PlayerPanel(player));}public void close() {player.close();setVisible(false);dispose();}public void addNotify() {super.addNotify();pack();}}/*** GUI classes for the Player.*/class PlayerPanel extends Panel {Component vc, cc;PlayerPanel(Player p) {setLayout(new BorderLayout());if ((vc = p.getVisualComponent()) != null)add("Center", vc);if ((cc = p.getControlPanelComponent()) != null)add("South", cc);}public Dimension getPreferredSize() {int w = 0, h = 0;if (vc != null) {Dimension size = vc.getPreferredSize();w = size.width;h = size.height;}if (cc != null) {Dimension size = cc.getPreferredSize();if (w == 0)w = size.width;h += size.height;}if (w < 160)w = 160;return new Dimension(w, h);}}public static void main(String argv[]) {if (argv.length == 0)prUsage();AVReceive3 avReceive = new AVReceive3(argv);if (!avReceive.initialize()) {System.err.println("Failed to initialize the sessions."); System.exit(-1);}// Check to see if AVReceive3 is done.try {while (!avReceive.isDone())Thread.sleep(1000);} catch (Exception e) {}System.err.println("Exiting AVReceive3");}static void prUsage() {System.err.println("Usage: AVReceive3 <session> <session> "); System.err.println(" <session>: <address>/<port>/<ttl>");System.exit(0);}}// end of AVReceive3发送端代码:import java.awt.*;import java.io.*;import .InetAddress;import javax.media.*;import javax.media.protocol.*;import javax.media.protocol.DataSource;import javax.media.format.*;import javax.media.control.TrackControl;import javax.media.control.QualityControl;import javax.media.rtp.*;import javax.media.rtp.rtcp.*;import com.sun.media.rtp.*;public class AVTransmit3 {// Input MediaLocator// Can be a file or http or capture sourceprivate MediaLocator locator;private String ipAddress;private int portBase;private Processor processor = null;private RTPManager rtpMgrs[];private DataSource dataOutput = null;public AVTransmit3(MediaLocator locator,String ipAddress,String pb,Format format) {this.locator = locator;this.ipAddress = ipAddress;Integer integer = Integer.valueOf(pb);if (integer != null)this.portBase = integer.intValue();}/*** Starts the transmission. Returns null if transmission started ok. * Otherwise it returns a string with the reason why the setup failed. */public synchronized String start() {String result;// Create a processor for the specified media locator// and program it to output JPEG/RTPresult = createProcessor();if (result != null)return result;// Create an RTP session to transmit the output of the// processor to the specified IP address and port no.result = createTransmitter();if (result != null) {processor.close();processor = null;return result;}// Start the transmissionprocessor.start();return null;}/*** Stops the transmission if already started*/public void stop() {synchronized (this) {if (processor != null) {processor.stop();processor.close();processor = null;for (int i = 0; i < rtpMgrs.length; i++) {rtpMgrs[i].removeTargets( "Session ended.");rtpMgrs[i].dispose();}}}}private String createProcessor() {if (locator == null)return "Locator is null";DataSource ds;DataSource clone;try {ds = javax.media.Manager.createDataSource(locator);} catch (Exception e) {return "Couldn't create DataSource";}// Try to create a processor to handle the input media locator try {processor = javax.media.Manager.createProcessor(ds);} catch (NoProcessorException npe) {return "Couldn't create processor";} catch (IOException ioe) {return "IOException creating processor";}// Wait for it to configureboolean result = waitForState(processor, Processor.Configured);if (result == false)return "Couldn't configure processor";// Get the tracks from the processorTrackControl [] tracks = processor.getTrackControls();// Do we have atleast one track?if (tracks == null || tracks.length < 1)return "Couldn't find tracks in processor";// Set the output content descriptor to RAW_RTP// This will limit the supported formats reported from// Track.getSupportedFormats to only valid RTP formats.ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP); processor.setContentDescriptor(cd);Format supported[];Format chosen;boolean atLeastOneTrack = false;// Program the tracks.for (int i = 0; i < tracks.length; i++) {Format format = tracks[i].getFormat();if (tracks[i].isEnabled()) {supported = tracks[i].getSupportedFormats();// We've set the output content to the RAW_RTP.// So all the supported formats should work with RTP.// We'll just pick the first one.if (supported.length > 0) {if (supported[0] instanceof VideoFormat) {// For video formats, we should double check the// sizes since not all formats work in all sizes.chosen = checkForVideoSizes(tracks[i].getFormat(),supported[0]);} elsechosen = supported[0];tracks[i].setFormat(chosen);System.err.println("Track " + i + " is set to transmit as:");System.err.println(" " + chosen);atLeastOneTrack = true;} elsetracks[i].setEnabled(false);} elsetracks[i].setEnabled(false);}if (!atLeastOneTrack)return "Couldn't set any of the tracks to a valid RTP format";// Realize the processor. This will internally create a flow// graph and attempt to create an output datasource for JPEG/RTP // audio frames.result = waitForState(processor, Controller.Realized);if (result == false)return "Couldn't realize processor";// Set the JPEG quality to .5.setJPEGQuality(processor, 0.5f);// Get the output data source of the processordataOutput = processor.getDataOutput();return null;}/*** Use the RTPManager API to create sessions for each media* track of the processor.*/private String createTransmitter() {// Cheated. Should have checked the type.PushBufferDataSource pbds = (PushBufferDataSource)dataOutput;PushBufferStream pbss[] = pbds.getStreams();rtpMgrs = new RTPManager[pbss.length];SendStream sendStream;int port;SourceDescription srcDesList[];for (int i = 0; i < pbss.length; i++) {try {rtpMgrs[i] = RTPManager.newInstance();port = portBase + 2*i;// Initialize the RTPManager with the RTPSocketAdapterrtpMgrs[i].initialize(new RTPSocketAdapter(InetAddress.getByName(ipAddress),port));System.err.println( "Created RTP session: " + ipAddress + " " + port);sendStream = rtpMgrs[i].createSendStream(dataOutput, i);sendStream.start();} catch (Exception e) {return e.getMessage();}}return null;}/*** For JPEG and H263, we know that they only work for particular* sizes. So we'll perform extra checking here to make sure they* are of the right sizes.*/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)) {// For JPEG, make sure width and height are divisible by 8.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)) {// For H.263, we only support some specific sizes.if (size.width < 128) {width = 128;height = 96;} else if (size.width < 176) {width = 176;height = 144;} else {width = 352;height = 288;}} else {// We don't know this particular format. We'll just// leave it alone then.return supported;}return (new VideoFormat(null,new Dimension(width, height),Format.NOT_SPECIFIED,null,Format.NOT_SPECIFIED)).intersects(supported);}/*** Setting the encoding quality to the specified value on the JPEG encoder. * 0.5 is a good default.*/void setJPEGQuality(Player p, float val) {Control cs[] = p.getControls();QualityControl qc = null;VideoFormat jpegFmt = new VideoFormat(VideoFormat.JPEG);// Loop through the controls to find the Quality control for// the JPEG encoder.for (int i = 0; i < cs.length; i++) {if (cs[i] instanceof QualityControl &&cs[i] instanceof Owned) {Object owner = ((Owned)cs[i]).getOwner();// Check to see if the owner is a Codec.// Then check for the output format.if (owner instanceof Codec) {Format fmts[] = ((Codec)owner).getSupportedOutputFormats(null);for (int j = 0; j < fmts.length; j++) {if (fmts[j].matches(jpegFmt)) {qc = (QualityControl)cs[i];qc.setQuality(val);System.err.println("- Setting quality to " +val + " on " + qc);break;}}}if (qc != null)break;}}}/**************************************************************** * Convenience methods to handle processor's state changes.****************************************************************/private Integer stateLock = new Integer(0);private boolean failed = false;Integer getStateLock() {return stateLock;}void setFailed() {failed = true;}private synchronized boolean waitForState(Processor p, int state) { p.addControllerListener(new StateListener());failed = false;// Call the required method on the processorif (state == Processor.Configured) {p.configure();} else if (state == Processor.Realized) {p.realize();}// Wait until we get an event that confirms the// success of the method, or a failure event.// See StateListener inner classwhile (p.getState() < state && !failed) {synchronized (getStateLock()) {try {getStateLock().wait();} catch (InterruptedException ie) {return false;}}}if (failed)return false;elsereturn true;}/***************************************************************** Inner Classes****************************************************************/ class StateListener implements ControllerListener {public void controllerUpdate(ControllerEvent ce) {// If there was an error during configure or// realize, the processor will be closedif (ce instanceof ControllerClosedEvent)setFailed();// All controller events, send a notification// to the waiting thread in waitForState method.if (ce instanceof ControllerEvent) {synchronized (getStateLock()) {getStateLock().notifyAll();}}}}/***************************************************************** Sample Usage for AVTransmit3 class****************************************************************/public static void main(String [] args) {// We need three parameters to do the transmission// For example,// java AVTransmit3 file:/C:/media/test.mov 129.130.131.132 42050if (args.length < 3) {prUsage();}Format fmt = null;int i = 0;// Create a audio transmit object with the specified params. AVTransmit3 at = new AVTransmit3(new MediaLocator(args[i]), args[i+1], args[i+2], fmt);// Start the transmissionString result = at.start();// result will be non-null if there was an error. The return // value is a String describing the possible error. Print it.if (result != null) {System.err.println("Error : " + result);System.exit(0);}System.err.println("Start transmission for 60 seconds");// Transmit for 60 seconds and then close the processor// This is a safeguard when using a capture data source// so that the capture device will be properly released// before quitting.// The right thing to do would be to have a GUI with a// "Stop" button that would call stop on AVTransmit3 try {Thread.currentThread().sleep(60000);} catch (InterruptedException ie) {}// Stop the transmissionat.stop();System.err.println("transmission ended.");System.exit(0);}static void prUsage() {System.err.println("Usage: AVTransmit3 <sourceURL> <destIP> <destPortBase>"); System.err.println(" <sourceURL>: input URL or file name");System.err.println(" <destIP>: multicast, broadcast or unicast IP address f or the transmission");System.err.println(" <destPortBase>: network port numbers for the transmiss ion.");System.err.println(" The first track will use the destPortB ase.");System.err.println(" The next track will use destPortBase + 2 and so on.\n");System.exit(0);}}底层传输部分代码:import java.io.IOException;import .InetAddress;import .DatagramSocket;import .MulticastSocket;import .DatagramPacket;import .SocketException;import javax.media.protocol.DataSource;import javax.media.protocol.PushSourceStream;import javax.media.protocol.ContentDescriptor;import javax.media.protocol.SourceTransferHandler;import javax.media.rtp.RTPConnector;import javax.media.rtp.OutputDataStream;/*** An implementation of RTPConnector based on UDP sockets.*/public class RTPSocketAdapter implements RTPConnector {DatagramSocket dataSock;DatagramSocket ctrlSock;InetAddress addr;int port;SockInputStream dataInStrm = null, ctrlInStrm = null;SockOutputStream dataOutStrm = null, ctrlOutStrm = null;public RTPSocketAdapter(InetAddress addr, int port) throws IOException {this(addr, port, 1);}public RTPSocketAdapter(InetAddress addr, int port, int ttl) throws IOException {try {if (addr.isMulticastAddress()) {dataSock = new MulticastSocket(port);ctrlSock = new MulticastSocket(port+1);((MulticastSocket)dataSock).joinGroup(addr);((MulticastSocket)dataSock).setTimeToLive(ttl);((MulticastSocket)ctrlSock).joinGroup(addr);((MulticastSocket)ctrlSock).setTimeToLive(ttl);} else {dataSock = new DatagramSocket(port, InetAddress.getLocalHost());ctrlSock = new DatagramSocket(port+1, InetAddress.getLocalHost());}} catch (SocketException e) {throw new IOException(e.getMessage());}this.addr = addr;this.port = port;}/*** Returns an input stream to receive the RTP data.*/public PushSourceStream getDataInputStream() throws IOException {if (dataInStrm == null) {dataInStrm = new SockInputStream(dataSock, addr, port);dataInStrm.start();}return dataInStrm;}/*** Returns an output stream to send the RTP data.*/public OutputDataStream getDataOutputStream() throws IOException {if (dataOutStrm == null)dataOutStrm = new SockOutputStream(dataSock, addr, port);return dataOutStrm;}/*** Returns an input stream to receive the RTCP data.*/public PushSourceStream getControlInputStream() throws IOException {if (ctrlInStrm == null) {ctrlInStrm = new SockInputStream(ctrlSock, addr, port+1);ctrlInStrm.start();}return ctrlInStrm;}/*** Returns an output stream to send the RTCP data.*/public OutputDataStream getControlOutputStream() throws IOException { if (ctrlOutStrm == null)ctrlOutStrm = new SockOutputStream(ctrlSock, addr, port+1);return ctrlOutStrm;}/*** Close all the RTP, RTCP streams.*/public void close() {if (dataInStrm != null)dataInStrm.kill();if (ctrlInStrm != null)ctrlInStrm.kill();dataSock.close();ctrlSock.close();}/*** Set the receive buffer size of the RTP data channel.* This is only a hint to the implementation. The actual implementation* may not be able to do anything to this.*/public void setReceiveBufferSize( int size) throws IOException {dataSock.setReceiveBufferSize(size);}/*** Get the receive buffer size set on the RTP data channel.* Return -1 if the receive buffer size is not applicable for* the implementation.*/public int getReceiveBufferSize() {try {return dataSock.getReceiveBufferSize();} catch (Exception e) {return -1;}}/*** Set the send buffer size of the RTP data channel.* This is only a hint to the implementation. The actual implementation * may not be able to do anything to this.*/public void setSendBufferSize( int size) throws IOException {dataSock.setSendBufferSize(size);}/*** Get the send buffer size set on the RTP data channel.* Return -1 if the send buffer size is not applicable for* the implementation.*/public int getSendBufferSize() {try {return dataSock.getSendBufferSize();} catch (Exception e) {return -1;}}/*** Return the RTCP bandwidth fraction. This value is used to* initialize the RTPManager. Check RTPManager for more detauls.* Return -1 to use the default values.*/public double getRTCPBandwidthFraction() {return -1;}/*** Return the RTCP sender bandwidth fraction. This value is used to* initialize the RTPManager. Check RTPManager for more detauls.* Return -1 to use the default values.*/public double getRTCPSenderBandwidthFraction() {return -1;}/*** An inner class to implement an OutputDataStream based on UDP sockets. */class SockOutputStream implements OutputDataStream {DatagramSocket sock;InetAddress addr;int port;public SockOutputStream(DatagramSocket sock, InetAddress addr, int port) { this.sock = sock;this.addr = addr;this.port = port;}public int write(byte data[], int offset, int len) {try {sock.send(new DatagramPacket(data, offset, len, addr, port));} catch (Exception e) {return -1;}return len;}}/*** An inner class to implement an PushSourceStream based on UDP sockets.。