import java.io.
*
;
import java.awt. * ;
import java.net. * ;
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: " + sessions[i]);
return false ;
}
System.err.println( " - Open RTP session for: addr: " + session.addr + " port: " + session.port + " ttl: " + session.ttl);
mgrs[i] = (RTPManager) RTPManager.newInstance();
mgrs[i].addSessionListener( this );
mgrs[i].addReceiveStreamListener( this );
// Initialize the RTPManager with the RTPSocketAdapter
mgrs[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.BufferControl " );
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());
} else
System.err.println( " - Recevied new RTP stream " );
if (participant == null )
System.err.println( " The sender of this stream had yet to be identified. " );
else {
System.err.println( " The stream comes from: " + participant.getCNAME());
}
// create a player by passing datasource to the Media Manager
Player 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: " + participant.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 specified
off = 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 specified
off = 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();
}
} else
throw 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.net. * ;
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: " + sessions[i]);
return false ;
}
System.err.println( " - Open RTP session for: addr: " + session.addr + " port: " + session.port + " ttl: " + session.ttl);
mgrs[i] = (RTPManager) RTPManager.newInstance();
mgrs[i].addSessionListener( this );
mgrs[i].addReceiveStreamListener( this );
// Initialize the RTPManager with the RTPSocketAdapter
mgrs[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.BufferControl " );
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());
} else
System.err.println( " - Recevied new RTP stream " );
if (participant == null )
System.err.println( " The sender of this stream had yet to be identified. " );
else {
System.err.println( " The stream comes from: " + participant.getCNAME());
}
// create a player by passing datasource to the Media Manager
Player 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: " + participant.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 specified
off = 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 specified
off = 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();
}
} else
throw 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 java.net.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 source
private 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/RTP
result = 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 transmission
processor.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 configure
boolean result = waitForState(processor, Processor.Configured);
if (result == false )
return " Couldn't configure processor " ;
// Get the tracks from the processor
TrackControl [] 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 ]);
} else
chosen = supported[ 0 ];
tracks[i].setFormat(chosen);
System.err.println( " Track " + i + " is set to transmit as: " );
System.err.println( " " + chosen);
atLeastOneTrack = true ;
} else
tracks[i].setEnabled( false );
} else
tracks[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 processor
dataOutput = 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 RTPSocketAdapter
rtpMgrs[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 processor
if (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 class
while (p.getState() < state && ! failed) {
synchronized (getStateLock()) {
try {
getStateLock().wait();
} catch (InterruptedException ie) {
return false ;
}
}
}
if (failed)
return false ;
else
return 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 closed
if (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
if (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 transmission
String 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 transmission
at.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 for the transmission " );
System.err.println( " <destPortBase>: network port numbers for the transmission. " );
System.err.println( " The first track will use the destPortBase. " );
System.err.println( " The next track will use destPortBase + 2 and so on./n " );
System.exit( 0 );
}
}
import java.io. * ;
import java.net.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 source
private 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/RTP
result = 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 transmission
processor.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 configure
boolean result = waitForState(processor, Processor.Configured);
if (result == false )
return " Couldn't configure processor " ;
// Get the tracks from the processor
TrackControl [] 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 ]);
} else
chosen = supported[ 0 ];
tracks[i].setFormat(chosen);
System.err.println( " Track " + i + " is set to transmit as: " );
System.err.println( " " + chosen);
atLeastOneTrack = true ;
} else
tracks[i].setEnabled( false );
} else
tracks[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 processor
dataOutput = 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 RTPSocketAdapter
rtpMgrs[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 processor
if (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 class
while (p.getState() < state && ! failed) {
synchronized (getStateLock()) {
try {
getStateLock().wait();
} catch (InterruptedException ie) {
return false ;
}
}
}
if (failed)
return false ;
else
return 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 closed
if (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
if (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 transmission
String 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 transmission
at.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 for the transmission " );
System.err.println( " <destPortBase>: network port numbers for the transmission. " );
System.err.println( " The first track will use the destPortBase. " );
System.err.println( " The next track will use destPortBase + 2 and so on./n " );
System.exit( 0 );
}
}
底层传输部分代码:
import java.io.IOException;
import java.net.InetAddress;
import java.net.DatagramSocket;
import java.net.MulticastSocket;
import java.net.DatagramPacket;
import java.net.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.
*/
class SockInputStream extends Thread implements PushSourceStream {
DatagramSocket sock;
InetAddress addr;
int port;
boolean done = false ;
boolean dataRead = false ;
SourceTransferHandler sth = null ;
public SockInputStream(DatagramSocket sock, InetAddress addr, int port) {
this .sock = sock;
this .addr = addr;
this .port = port;
}
public int read( byte buffer[], int offset, int length) {
DatagramPacket p = new DatagramPacket(buffer, offset, length, addr, port);
try {
sock.receive(p);
} catch (IOException e) {
return - 1 ;
}
synchronized ( this ) {
dataRead = true ;
notify();
}
return p.getLength();
}
public synchronized void start() {
super.start();
if (sth != null ) {
dataRead = true ;
notify();
}
}
public synchronized void kill() {
done = true ;
notify();
}
public int getMinimumTransferSize() {
return 2 * 1024 ; // twice the MTU size, just to be safe.
}
public synchronized void setTransferHandler(SourceTransferHandler sth) {
this .sth = sth;
dataRead = true ;
notify();
}
// Not applicable.
public ContentDescriptor getContentDescriptor() {
return null ;
}
// Not applicable.
public long getContentLength() {
return LENGTH_UNKNOWN;
}
// Not applicable.
public boolean endOfStream() {
return false ;
}
// Not applicable.
public Object[] getControls() {
return new Object[ 0 ];
}
// Not applicable.
public Object getControl(String type) {
return null ;
}
/* *
* Loop and notify the transfer handler of new data.
*/
public void run() {
while ( ! done) {
synchronized ( this ) {
while ( ! dataRead && ! done) {
try {
wait();
} catch (InterruptedException e) { }
}
dataRead = false ;
}
if (sth != null && ! done) {
sth.transferData( this );
}
}
}
}
}
import java.net.InetAddress;
import java.net.DatagramSocket;
import java.net.MulticastSocket;
import java.net.DatagramPacket;
import java.net.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.
*/
class SockInputStream extends Thread implements PushSourceStream {
DatagramSocket sock;
InetAddress addr;
int port;
boolean done = false ;
boolean dataRead = false ;
SourceTransferHandler sth = null ;
public SockInputStream(DatagramSocket sock, InetAddress addr, int port) {
this .sock = sock;
this .addr = addr;
this .port = port;
}
public int read( byte buffer[], int offset, int length) {
DatagramPacket p = new DatagramPacket(buffer, offset, length, addr, port);
try {
sock.receive(p);
} catch (IOException e) {
return - 1 ;
}
synchronized ( this ) {
dataRead = true ;
notify();
}
return p.getLength();
}
public synchronized void start() {
super.start();
if (sth != null ) {
dataRead = true ;
notify();
}
}
public synchronized void kill() {
done = true ;
notify();
}
public int getMinimumTransferSize() {
return 2 * 1024 ; // twice the MTU size, just to be safe.
}
public synchronized void setTransferHandler(SourceTransferHandler sth) {
this .sth = sth;
dataRead = true ;
notify();
}
// Not applicable.
public ContentDescriptor getContentDescriptor() {
return null ;
}
// Not applicable.
public long getContentLength() {
return LENGTH_UNKNOWN;
}
// Not applicable.
public boolean endOfStream() {
return false ;
}
// Not applicable.
public Object[] getControls() {
return new Object[ 0 ];
}
// Not applicable.
public Object getControl(String type) {
return null ;
}
/* *
* Loop and notify the transfer handler of new data.
*/
public void run() {
while ( ! done) {
synchronized ( this ) {
while ( ! dataRead && ! done) {
try {
wait();
} catch (InterruptedException e) { }
}
dataRead = false ;
}
if (sth != null && ! done) {
sth.transferData( this );
}
}
}
}
}