diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/dtmf/DtmfRawPacket.java b/src/net/java/sip/communicator/impl/neomedia/transform/dtmf/DtmfRawPacket.java new file mode 100644 index 000000000..726170842 --- /dev/null +++ b/src/net/java/sip/communicator/impl/neomedia/transform/dtmf/DtmfRawPacket.java @@ -0,0 +1,140 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.neomedia.transform.dtmf; + +import net.java.sip.communicator.impl.media.transform.*; +import net.java.sip.communicator.impl.neomedia.*; +import net.java.sip.communicator.util.*; + +/** + * DtmfRawPacket represent an RTP Packet. + * You create your DtmfRawPacket by calling the constructor. + * You specify the DTMF attributes : code=9, end=false, marker=truen ... + * Then you fill the packet using fillRawPacket( ... dtmf attributes ... ); + * + * @author Romain Philibert + * @author Emil Ivov + */ +public class DtmfRawPacket + extends RawPacket +{ + /** + * Our class logger. + */ + private static final Logger logger + = Logger.getLogger(DtmfRawPacket.class); + + /** + * Creates a DtmfRawPacket using the specified buffer. + * + * @param buffer Byte array holding the content of this Packet + */ + public DtmfRawPacket(byte[] buffer) + { + //DTMF buffer length = 16. + super (buffer, 0, 16); + } + + /** + * Fill the RTP packet with DTMF fields. + * + * @param code the DTMF code representing the digit. + * @param end the DTMF End flag + * @param marker the RTP Marker flag + * @param duration the DTMF duration + * @param timestamp the RTP timestamp + */ + public void fillRawPacket(int code, boolean end, boolean marker, int duration, long timestamp) + { + logger.trace("DTMF send on RTP, code : " + code + " " + + "dur = "+duration +" "+ + "ts = "+timestamp +" "+ + (marker?"Marker":"") + + (end?"End":"")); + + // Set the payload type and the marker + setMarker(marker); + + // set the Timestamp + setTimestamp(timestamp); + + // Create the RTP data + setData(code, end, duration); + } + + /** + * Create a DTMF raw data using event, E and duration field. + * Event : the digits to transmit (0-15). + * E : End field, used to mark the two last packets. + * R always = 0. + * Volume always = 0. + * Duration : duration increments for each dtmf sending updates, + * stay unchanged at the end for the 3 last packets. + *
+     *  0                   1                   2                   3
+     *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+     *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     *  |     event     |E R| volume    |          duration             |
+     *  |       ?       |? 0|    0      |              ?                |
+     *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     * 
+ * + * @param code the digit to transmit 0-15 + * @param end : boolean used to mark the two last packets + * @param duration : int increments for each dtmf sending + * updates, stay unchanged at the end for the 2 last packets. + * @return the DTMF raw data + */ + private void setData(int code, boolean end, int duration) + { + byte[] data = new byte[4]; + data[0]=(byte)code; + data[1]= end ? (byte)0x80 : (byte)0; + data[2]=(byte)(duration >> 8); + data[3]=(byte) duration; + System.arraycopy(data, 0, this.buffer, 12, 4); + } + + /** + * Read the timestamp value of the RTP Packet; + * @return the timestamp value + */ + public long getTimestamp() + { + return readInt(4); + } + + /** + * Set the timestamp value of the RTP Packet + * @param timestamp : the RTP Timestamp + */ + public void setTimestamp(long timestamp) + { + this.buffer[4] = (byte) ((timestamp >> 24) & 0xFF); + this.buffer[5] = (byte) ((timestamp >> 16) & 0xFF); + this.buffer[6] = (byte) ((timestamp >> 8) & 0xFF); + this.buffer[7] = (byte) (timestamp & 0xFF); + } + + /** + * Set the marker of the RTP Packet and the PayloadType to + * DtmfConstants.DtmfSDP; + * @param marker : the RTP Marker + */ + private void setMarker(boolean marker) + { + if(marker) + { + this.buffer[1] = (byte) (DtmfConstants.DtmfSDP | 0x80); + } + else + { + this.buffer[1] = (byte) DtmfConstants.DtmfSDP; + } + } + +} diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/dtmf/DtmfTransformEngine.java b/src/net/java/sip/communicator/impl/neomedia/transform/dtmf/DtmfTransformEngine.java index 57159d82d..9bc3607e1 100644 --- a/src/net/java/sip/communicator/impl/neomedia/transform/dtmf/DtmfTransformEngine.java +++ b/src/net/java/sip/communicator/impl/neomedia/transform/dtmf/DtmfTransformEngine.java @@ -89,6 +89,96 @@ public RawPacket reverseTransform(RawPacket pkt) */ public RawPacket transform(RawPacket pkt) { + if (engine.isTransformationEnable()) + + { + + byte[] buffer = new byte[16]; + + System.arraycopy(pkt.getBuffer(), 0, buffer, 0, 12); + + DtmfRawPacket dtmfPkt = new DtmfRawPacket(buffer); + + long audioPacketTimestamp = dtmfPkt.getTimestamp(); + + int pktCode = engine.getDtmfCode(); + + boolean pktEnd = false; + + boolean pktMarker = false; + + int pktDuration = 0; + + long pktTimestamp = engine.getCurrentTimestamp(); + + if (engine.isSendingStateEquals(DtmfTransformEngine.START_SENDING)) + + { + + logger.trace("START_SENDING"); + + /* + + * The first packet is send with the RTP Marker set to 1. + + */ + + pktMarker=true; + + pktTimestamp = audioPacketTimestamp; + + + + /* + + * Save the audioPacketTimestamp value. T + + * This value will be used in the next dtmf packets. + + */ + + logger.trace("Timestamp read = "+audioPacketTimestamp); + + engine.setCurrentTimestamp(audioPacketTimestamp); + + engine.setSendingState(DtmfTransformEngine.SENDING_UPDATE); + + } + + + + else if (engine.isSendingStateEquals(DtmfTransformEngine.SENDING_UPDATE)) + + { + + logger.trace("SENDING_UPDATE"); + + int duration = (int)(audioPacketTimestamp-pktTimestamp); + + + + // Check for long state event + + if (duration>0xFFFF) + + { + + logger.trace("LONG_DURATION_EVENT"); + + /* + + * When duration > 0xFFFF we first send a packet with + + * duration = 0xFFFF. For the next packet, the duration + + * start from begining but the audioPacketTimestamp is set to the + + * time when the long duration event occurs. + + */ + + pktDuration = 0xFFFF; + + pktTimestamp = audioPacketTimestamp; + + engine.setCurrentTimestamp(audioPacketTimestamp); + + } + + else + + { + + pktDuration = duration; + + } + + } + + else if (engine.isSendingStateEquals(DtmfTransformEngine.STOP_SENDING)) + + { + + logger.trace("STOP_SENDING"); + + /** + + * The first ending packet do have the End flag set. + + * But the 2 next will have the End flag set. + + * + + * The audioPacketTimestamp and the duration field stay unchanged for + + * the 3 last packets + + */ + + pktDuration = (int)(audioPacketTimestamp-pktTimestamp); + + + + engine.setEndTimestamp(audioPacketTimestamp); + + engine.remainingsEndPackets = 2; + + engine.setSendingState(DtmfTransformEngine.STOP_SENDING_REPEATING); + + } + + else if (engine.isSendingStateEquals(DtmfTransformEngine.STOP_SENDING_REPEATING)) + + { + + logger.trace("STOP_SENDING_REPEATING"); + + /** + + * We set the End flag for the 2 last packets. + + * The audioPacketTimestamp and the duration field stay unchanged for + + * the 3 last packets. + + */ + + pktEnd=true; + + pktDuration=(int)(engine.getEndTimestamp()-pktTimestamp); + + + + engine.remainingsEndPackets --; + + if (engine.remainingsEndPackets<=0) + + { + + engine.disableTransformation(); + + } + + } + + dtmfPkt.fillRawPacket(pktCode, pktEnd, pktMarker, pktDuration, pktTimestamp); + + pkt = dtmfPkt; + + } + + return pkt; return pkt; }