multiple RTP streams + local video Player - S.O.S.
Hi guys,
I ran out of ideas, so I need your help now. It's gonna be a long one...
Given:
MediaLocator(vfw://0) --> DataSource(video) --> Processor(video)
Then I send processor's output (videoProcessor.getDataOutput()) over RTP to multiple destinations. Of course, I am using cloneable DataSources for this, cause there's no other way. So, now I also need to have a local video feed just for self-reference, but it seems to be a tough one! Let me go over what I've tried til now:
1. Using DataSource clone from video processor, I tried to create a Player (i.e. Manager.createPlayer(clonedDS) ). BOOM!
javax.media.NoPlayerException: Cannot find a Playerfor: com.ibm.media.protocol.SuperCloneableDataSource$PushBufferDataSourceSlave@126c6ea
at javax.media.Manager.createPlayerForSource(Manager.java:1512)
at javax.media.Manager.createPlayer(Manager.java:500)
at org.interlab.mc.media.MediaManager.setVideoProcessorEnabled(MediaManager.java:2046)
at org.interlab.mc.media.MediaManager.openVideoStreams(MediaManager.java:1807)
at org.interlab.mc.UserAgent.callStateChanged(UserAgent.java:2516)
at org.interlab.mc.call.Call.fireCallStateChangedEvent(Call.java:366)
at org.interlab.mc.call.Call.setState(Call.java:244)
at org.interlab.mc.call.CallManager.processInviteOK(CallManager.java:330)
at org.interlab.mc.UserAgent.processResponse(UserAgent.java:1632)
at gov.nist.javax.sip.EventScanner.deliverEvent(EventScanner.java:288)
at gov.nist.javax.sip.EventScanner.run(EventScanner.java:489)
at java.lang.Thread.run(Unknown Source)
So i checked. If I create a clone from my DataSource(video) then Manager would return this instance:
class com.sun.media.multiplexer.RawBufferMux$RawBufferDataSource
But if, like now, I want to create a clone from DataSource from video processor (i.e. Processor.getDataOutput()) then I get this instance:
com.ibm.media.protocol.SuperCloneableDataSource$PushBufferDataSourceSlave
Hope you noticed the difference. :) Now, from the latter one neither I can create a Player nor can I create a Processor. Too bad. So, I tried another one.
2. I took my DataSource(video), created a cloneable (just like in Clone.java) and from there I finally got my precious Player! Yes!! But not so fast.... Player was working, but the rest now wasn't. Once I have my DataSource(video) cloned, my Processor(video)'s data output got starved - i could not send video anymore. What a life?! Fine... Let's try another one.
3. Just like I send video remote I decided to send the video locally in a loop, for instance, from 203.159.2.3:25000 to 203.159.2.3:25002. And what do you think? Still doesnt work!! This "loop" stream is not detected (i.e. controller's update doest sense anything). But if i open JMStudio, and start listening (i.e. open RTP session) on 203.159.2.3:25002, i get my stream showing nice and clear!
Someone, anyone, please, help! Point my nose to some document, piece of code - anything to make this work.
Kind regards.
[3189 byte] By [
andreyvka] at [2007-10-3 11:01:30]

I'd better post here, so everyone else can get it too. so here it goes:
That's RTPSocketAdapter
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.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=null;
DatagramSocket ctrlSock=null;
InetAddress remoteAddress=null;
int remotePort=0;
InetAddress localAddress=null;
int localPort=0;
SockInputStream dataInStrm = null, ctrlInStrm = null;
SockOutputStream dataOutStrm = null, ctrlOutStrm = null;
public RTPSocketAdapter(InetAddress localAddress, int localPort,
InetAddress remoteAddress, int remotePort) throws IOException {
this(localAddress, localPort, remoteAddress, remotePort, 1);
}
public RTPSocketAdapter(InetAddress localAddress, int localPort,
InetAddress remoteAddress, int remotePort, int ttl) throws IOException {
try {
if (remoteAddress.isMulticastAddress()) {
dataSock = new MulticastSocket(localPort);
ctrlSock = new MulticastSocket(localPort+1);
((MulticastSocket)dataSock).joinGroup(remoteAddress);
((MulticastSocket)dataSock).setTimeToLive(ttl);
((MulticastSocket)ctrlSock).joinGroup(remoteAddress);
((MulticastSocket)ctrlSock).setTimeToLive(ttl);
}
else {
dataSock = new DatagramSocket(localPort, localAddress);
ctrlSock = new DatagramSocket(localPort+1, localAddress);
}
}
catch (SocketException e) {
throw new IOException(e.getMessage());
}
this.localAddress = localAddress;
this.localPort = localPort;
this.remoteAddress = remoteAddress;
this.remotePort = remotePort;
}
/**
* Returns an input stream to receive the RTP data.
*/
public PushSourceStream getDataInputStream() throws IOException {
if (dataInStrm == null) {
dataInStrm = new SockInputStream(dataSock, remoteAddress, remotePort);
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, remoteAddress, remotePort);
return dataOutStrm;
}
/**
* Returns an input stream to receive the RTCP data.
*/
public PushSourceStream getControlInputStream() throws IOException {
if (ctrlInStrm == null) {
ctrlInStrm = new SockInputStream(ctrlSock, remoteAddress, remotePort+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, remoteAddress, remotePort+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);
}
}
}
}
}
See, how to create RTPManager in next post