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]
# 1
Got a new idea from one of the JMF-INTEREST threads. Gotta clone DataSource(video) first before feeding it to Processor. Hope it works :(
andreyvka at 2007-7-15 6:28:18 > top of Java-index,Security,Cryptography...
# 2
Ha! It works!!!
andreyvka at 2007-7-15 6:28:18 > top of Java-index,Security,Cryptography...
# 3

Dear andreyvk ,

I've read your post

http://forum.java.sun.com/thread.jspa?threadID=785134&tstart=165

about how to use a single RTP session for both media reception and trasmission (I'm referring to you RTPSocketAdapter modified version), but, at the moment, I'receive a BIND error.

I think that your post is an EXCELLENT solution. I'modified AVReceive3 and AVTransmit3 in order to accept all parameters (Local IP & Port, Remote IP & Port).

Can you please give me a simple scenario so I can understand what the mistake?

I'use AVTransmit3 and AVReceive3 from different prompts and if I run these 2 classes contemporarely both in 2 different PC (172.17.32.27 and 172.17.32.30) I can transmit the media (vfw://0 for example) using AVTransmit3 but I'can't receive nothing if I run also AVReceive3 in the same PC?

What's the problem? Furthermore, If I run first AVReceive3 from a MSDOS Prompt and subsequently I run AVTransmit3 from another prompt I see a BIND error (port already in use).

How can I use your modified RTPSocketAdapter in order to send and receive a single media from the same port (e.g. 7500).

I've used this scenario PC1: IP 172.17.32.30 Local Port 5000 and PC2:IP 172.17.32.27 LocalPort 10000

So in the PC1 I run:

AVTransmit3 vfw://0 <Local IP 172.17.32.30> <5000> <Remote IP 172.17.32.27> <10000>

AVReceive3 <Local IP 172.17.32.30/5000> <Remote IP 172.17.32.27/10000>

and in PC2:

AVTransmit3 vfw://0 <Local IP 172.17.32.27> <10000> <Remote IP 172.17.32.30> <5000>

AVReceive3 <Local IP 172.17.32.27/10000> <Remote IP 172.17.32.30/5000>

I'd like to use the same port 5000 (in PC1) and 10000 (in PC2) in order to transmit and receive rtp packets. How can i do that without receive a Bind Error? How can I receive packets (and playing these media if audio &/or video) from the same port used to send stream over the network?

How can I obtain a RTP Symmetric Transmission/Reception solution?

Please give me an hint. If you can't post this is my email: Siracg99@libero.it

TheSiraca at 2007-7-15 6:28:18 > top of Java-index,Security,Cryptography...
# 4
sorry for my late response... been busy for a while.. i'll post the code to your mail...
andreyvka at 2007-7-15 6:28:18 > top of Java-index,Security,Cryptography...
# 5

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

andreyvka at 2007-7-15 6:28:18 > top of Java-index,Security,Cryptography...
# 6

Local/Remote IPs and ports I extract from local and remote SDP content. But you can just generate them manually

RTPManager rtpManager = RTPManager.newInstance();

try {

rtpManager.initialize(

new RTPSocketAdapter(localIP, localPort,

remoteIP, remotePort,

16)

);

}

catch(IOException ex) {

System.err.println("Error initializing RTP manager!");

ex.printStackTrace();

}

And then use codes from AVTransmit and AVReceive to initialize audio/video streams sending/receving.

andreyvka at 2007-7-15 6:28:18 > top of Java-index,Security,Cryptography...
# 7
Hope it works out for you.. Post me back for any other questions.
andreyvka at 2007-7-15 6:28:18 > top of Java-index,Security,Cryptography...