- Add DTMF for DTMF RFC4733 Listener.

- Now we can change the priority of send/receive threads (not activated yet).
cusax-fix
Damian Minkov 16 years ago
parent 86b04873e8
commit 3b1b592246

@ -6,6 +6,7 @@
*/
package net.java.sip.communicator.impl.neomedia;
import java.util.*;
import javax.media.*;
import javax.media.control.*;
import javax.media.format.*;
@ -43,6 +44,11 @@ public class AudioMediaStreamImpl
*/
private DtmfTransformEngine dtmfTransfrmEngine ;
/**
* List of DTMF listeners;
*/
private List<DTMFListener> dtmfListeners = new ArrayList<DTMFListener>();
/**
* List of RTP format strings which are supported by SIP Communicator in
* addition to the JMF standard formats.
@ -171,7 +177,10 @@ protected DtmfTransformEngine createDtmfTransformEngine()
*/
public void addDTMFListener(DTMFListener listener)
{
// TODO Auto-generated method stub
if(!dtmfListeners.contains(listener))
{
dtmfListeners.add(listener);
}
}
/**
@ -249,7 +258,7 @@ protected void registerCustomCodecFormats(RTPManager rtpManager)
*/
public void removeDTMFListener(DTMFListener listener)
{
// TODO Auto-generated method stub
dtmfListeners.remove(listener);
}
/**
@ -380,4 +389,54 @@ public void fireConferenceAudioLevelEvent(final long[][] audioLevels)
if (csrcAudioLevelListener != null)
csrcAudioLevelListener.audioLevelsReceived(audioLevels);
}
/**
* Delivers the <tt>DTMF</tt> tones. This
* method is meant for use primarily by the transform engine handling
* incoming RTP packets (currently <tt>DtmfTransformEngine</tt>).
*
* @param tone the new tone
* @param end is end or start of tone.
*/
public void fireDTMFEvent(DTMFTone tone, boolean end)
{
Iterator<DTMFListener> iter = dtmfListeners.iterator();
DTMFToneEvent ev = new DTMFToneEvent(this, tone);
while (iter.hasNext())
{
DTMFListener listener = iter.next();
if(end)
listener.dtmfToneReceptionEnded(ev);
else
listener.dtmfToneReceptionStarted(ev);
}
}
/**
* Releases the resources allocated by this instance in the course of its
* execution and prepares it to be garbage collected.
*
* @see MediaStream#close()
*/
public void close()
{
super.close();
if(dtmfTransfrmEngine != null)
{
dtmfTransfrmEngine.stop();
dtmfTransfrmEngine = null;
}
}
/**
* The priority of the audio is 3, which is meant to be higher than
* other threads and higher than the video one.
* @return audio priority.
*/
@Override
protected int getPriority()
{
return 3;
}
}

@ -20,7 +20,6 @@
import com.sun.media.rtp.*;
import net.java.sip.communicator.impl.neomedia.*;
import net.java.sip.communicator.impl.neomedia.device.*;
import net.java.sip.communicator.impl.neomedia.format.*;
import net.java.sip.communicator.impl.neomedia.transform.*;
@ -240,6 +239,18 @@ protected TransformOutputStream createDataOutputStream()
configureDataOutputStream(dataOutputStream);
return dataOutputStream;
}
@Override
protected TransformInputStream createDataInputStream()
throws IOException
{
TransformInputStream dataInputStream
= super.createDataInputStream();
if (dataInputStream != null)
configureDataInputStream(dataInputStream);
return dataInputStream;
}
};
this.zrtpControl
@ -262,6 +273,22 @@ protected TransformOutputStream createDataOutputStream()
protected void configureDataOutputStream(
RTPConnectorOutputStream dataOutputStream)
{
dataOutputStream.setPriority(getPriority());
}
/**
* Performs any optional configuration on a specific
* <tt>RTPConnectorInputStream</tt> of an <tt>RTPManager</tt> to be used by
* this <tt>MediaStreamImpl</tt>. Allows extenders to override.
*
* @param dataInputStream the <tt>RTPConnectorInputStream</tt> to be used
* by an <tt>RTPManager</tt> of this <tt>MediaStreamImpl</tt> and to be
* configured
*/
protected void configureDataInputStream(
RTPConnectorInputStream dataInputStream)
{
dataInputStream.setPriority(getPriority());
}
/**
@ -1808,4 +1835,16 @@ public long[] getRemoteContributingSourceIDs()
return remoteSsrcList;
}
/**
* Used to set the priority of the receive/send streams. Underling
* implementations can override this and return different than
* current default value.
*
* @return the priority for the current thread.
*/
protected int getPriority()
{
return Thread.currentThread().getPriority();
}
}

@ -64,6 +64,11 @@ public class RTPConnectorInputStream
*/
private SourceTransferHandler transferHandler;
/**
* The Thread receiving packets.
*/
private Thread receiverThread = null;
/**
* Initializes a new <tt>RTPConnectorInputStream</tt> which is to receive
* packet data from a specific UDP socket.
@ -75,7 +80,8 @@ public RTPConnectorInputStream(DatagramSocket socket)
this.socket = socket;
closed = false;
new Thread(this).start();
receiverThread = new Thread(this);
receiverThread.start();
}
/**
@ -220,7 +226,7 @@ public int read(byte[] inBuffer, int offset, int length)
new IOException("Input buffer not big enough for " + pktLength);
System.arraycopy(
pkt.getBuffer(), pkt.getOffset(), inBuffer, offset, pktLength);
pkt.getBuffer(), pkt.getOffset(), inBuffer, offset, pktLength);
return pktLength;
}
@ -270,4 +276,17 @@ public void setTransferHandler(SourceTransferHandler transferHandler)
if (!closed)
this.transferHandler = transferHandler;
}
/**
* Changes current thread priority.
* @param priority the new priority.
*/
public void setPriority(int priority)
{
// currently no priority is set
// if(receiverThread != null)
// {
// receiverThread.setPriority(priority);
// }
}
}

@ -249,6 +249,18 @@ public int write(byte[] buffer, int offset, int length)
return length;
}
/**
* Changes current thread priority.
* @param priority the new priority.
*/
public void setPriority(int priority)
{
// currently no priority is set
// if(maxPacketsPerMillisPolicy != null &&
// maxPacketsPerMillisPolicy.sendThread != null)
// maxPacketsPerMillisPolicy.sendThread.setPriority(priority);
}
/**
* Implements the functionality which allows this <tt>OutputDataStream</tt>
* to control how many RTP packets it sends through its

@ -721,4 +721,14 @@ protected void setRemoteSourceID(long ssrc)
((VideoMediaDeviceSession)deviceSession).setRemoteSSRC(ssrc);
}
/**
* The priority of the video is 5, which is meant to be higher than
* other threads and lower than the audio one.
* @return video priority.
*/
@Override
protected int getPriority()
{
return 5;
}
}

@ -20,7 +20,7 @@
* @author Damian Minkov
*/
public class DtmfRawPacket
extends RawPacket
extends RawPacket
{
/**
* Our class logger.
@ -33,6 +33,21 @@ public class DtmfRawPacket
*/
public static final int DTMF_PACKET_SIZE = 16;
/**
* The event code to send.
*/
private int code;
/**
* Is this an end packet.
*/
private boolean end;
/**
* The duration of the current packet.
*/
private int duration;
/**
* Creates a <tt>DtmfRawPacket</tt> using the specified buffer.
*
@ -49,6 +64,23 @@ public DtmfRawPacket(byte[] buffer, int offset, byte payload)
setPayload(payload);
}
/**
* Used for incoming DTMF packets, creating <tt>DtmfRawPacket</tt>
* from RTP one.
* @param pkt the RTP packet.
*/
public DtmfRawPacket(RawPacket pkt)
{
super(pkt.getBuffer(), pkt.getOffset(), pkt.getLength());
int at = getHeaderLength();
code = readByte(at++);
end = (readByte(at++) & 0x80) != 0;
duration = ((readByte(at++) & 0xFF) << 8) | (readByte(at++) & 0xFF);
}
/**
* Initializes DTMF specific values in this packet.
*
@ -105,6 +137,10 @@ public void init(int code,
*/
private void setDtmfPayload(int code, boolean end, int duration)
{
this.code = code;
this.end = end;
this.duration = duration;
int at = getHeaderLength();
writeByte(at++, (byte)code);
@ -112,4 +148,31 @@ private void setDtmfPayload(int code, boolean end, int duration)
writeByte(at++, (byte)(duration >> 8));
writeByte(at++, (byte)duration);
}
/**
* The event code of the current packet.
* @return the code
*/
public int getCode()
{
return code;
}
/**
* Is this an end packet.
* @return the end
*/
public boolean isEnd()
{
return end;
}
/**
* The duration of the current event.
* @return the duration
*/
public int getDuration()
{
return duration;
}
}

@ -24,7 +24,6 @@ public class DtmfTransformEngine
implements TransformEngine,
PacketTransformer
{
/**
* Our class logger.
*/
@ -73,6 +72,22 @@ private enum ToneTransmissionState
END_SEQUENCE_INITIATED
};
/**
* Array of all supported tones.
*/
private static final DTMFTone[] supportedTones =
new DTMFTone[]
{DTMFTone.DTMF_0, DTMFTone.DTMF_1, DTMFTone.DTMF_2, DTMFTone.DTMF_3,
DTMFTone.DTMF_4, DTMFTone.DTMF_5, DTMFTone.DTMF_6, DTMFTone.DTMF_7,
DTMFTone.DTMF_8, DTMFTone.DTMF_9, DTMFTone.DTMF_A, DTMFTone.DTMF_B,
DTMFTone.DTMF_C, DTMFTone.DTMF_D,
DTMFTone.DTMF_SHARP, DTMFTone.DTMF_STAR};
/**
* The dispatcher that is delivering tones to the media steam.
*/
private DTMFDispatcher dtmfDispatcher = null;
/**
* The status that this engine is currently in.
*/
@ -161,6 +176,15 @@ public RawPacket reverseTransform(RawPacket pkt)
if(currentDtmfPayload == pkt.getPayloadType())
{
DtmfRawPacket p = new DtmfRawPacket(pkt);
if (dtmfDispatcher == null)
{
dtmfDispatcher = new DTMFDispatcher();
new Thread(dtmfDispatcher).start();
}
dtmfDispatcher.addTonePacket(p);
// ignore received dtmf packets
// if jmf receive change in rtp payload stops reception
return null;
@ -293,4 +317,134 @@ public void stopSendingDTMF()
toneTransmissionState = ToneTransmissionState.END_REQUESTED;
}
/**
* Stops threads that this transform engine is using for even delivery.
*/
public void stop()
{
if(dtmfDispatcher != null)
dtmfDispatcher.stop();
}
/**
* A simple thread that waits for new tones to be reported from incoming
* RTP packets and then delivers them to the <tt>AudioMediaStream</tt>
* associated with this engine. The reason we need to do this in a separate
* thread is of course the time sensitive nature of incoming RTP packets.
*/
private class DTMFDispatcher
implements Runnable
{
/** Indicates whether this thread is supposed to be running */
private boolean isRunning = false;
/** The tone that we last received from the reverseTransform thread*/
private DTMFTone lastReceivedTone = null;
/** The tone that we last received from the reverseTransform thread*/
private DTMFTone lastReportedTone = null;
/**
* Have we received end of the currently started tone.
*/
private boolean toEnd = false;
/**
* Waits for new tone to be reported via the <tt>addTonePacket()</tt>
* method and then delivers them to the <tt>AudioMediaStream</tt> that
* we are associated with.
*/
public void run()
{
isRunning = true;
DTMFTone temp = null;
while(isRunning)
{
synchronized(this)
{
if(lastReceivedTone == null)
{
try
{
wait();
}
catch (InterruptedException ie) {}
}
temp = lastReceivedTone;
// make lastReportedLevels to null
// so we will wait for the next tone on next iteration
lastReceivedTone = null;
}
if(temp != null
&& ((lastReportedTone == null && !toEnd)
|| (lastReportedTone != null && toEnd)))
{
//now notify our listener
if (mediaStream != null)
{
mediaStream.fireDTMFEvent(temp, toEnd);
if(toEnd)
lastReportedTone = null;
else
lastReportedTone = temp;
toEnd = false;
}
}
}
}
/**
* A packet that we should convert to tone and deliver
* to our media stream and its listeners in a separate thread.
*
* @param p the packet we will convert and deliver.
*/
public void addTonePacket(DtmfRawPacket p)
{
synchronized(this)
{
this.lastReceivedTone = getToneFromPacket(p);
this.toEnd = p.isEnd();
notifyAll();
}
}
/**
* Causes our run method to exit so that this thread would stop
* handling levels.
*/
public void stop()
{
synchronized(this)
{
this.lastReceivedTone = null;
isRunning = false;
notifyAll();
}
}
/**
* Maps DTMF packet codes to our DTMFTone objects.
* @param p the packet
* @return the corresponding tone.
*/
private DTMFTone getToneFromPacket(DtmfRawPacket p)
{
for (int i = 0; i < supportedTones.length; i++)
{
DTMFTone t = supportedTones[i];
if(t.getCode() == p.getCode())
return t;
}
return null;
}
}
}

Loading…
Cancel
Save