001/** 002 * 003 * Copyright 2003-2006 Jive Software. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.jingleold.mediaimpl.jmf; 018 019import java.io.IOException; 020import java.net.ServerSocket; 021import java.util.logging.Level; 022import java.util.logging.Logger; 023 024import javax.media.MediaLocator; 025 026import org.jivesoftware.smackx.jingleold.JingleSession; 027import org.jivesoftware.smackx.jingleold.media.JingleMediaSession; 028import org.jivesoftware.smackx.jingleold.media.PayloadType; 029import org.jivesoftware.smackx.jingleold.nat.TransportCandidate; 030 031/** 032 * This Class implements a complete JingleMediaSession. 033 * It should be used to transmit and receive audio captured from the Mic. 034 * This Class should be automatically controlled by JingleSession. 035 * But you could also use in any VOIP application. 036 * For better NAT Traversal support this implementation don't support only receive or only transmit. 037 * To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit() 038 * 039 * @author Thiago Camargo 040 */ 041public class AudioMediaSession extends JingleMediaSession { 042 043 private static final Logger LOGGER = Logger.getLogger(AudioMediaSession.class.getName()); 044 045 private AudioChannel audioChannel; 046 047 /** 048 * Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates. 049 * 050 * @param payloadType Payload of the jmf 051 * @param remote the remote information. The candidate that the jmf will be sent to. 052 * @param local the local information. The candidate that will receive the jmf 053 * @param locator media locator 054 * @param jingleSession the jingle session. 055 */ 056 public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote, 057 final TransportCandidate local, String locator, JingleSession jingleSession) { 058 super(payloadType, remote, local, locator == null ? "dsound://" : locator, jingleSession); 059 initialize(); 060 } 061 062 /** 063 * Initialize the Audio Channel to make it able to send and receive audio. 064 */ 065 @Override 066 public void initialize() { 067 068 String ip; 069 String localIp; 070 int localPort; 071 int remotePort; 072 073 if (this.getLocal().getSymmetric() != null) { 074 ip = this.getLocal().getIp(); 075 localIp = this.getLocal().getLocalIp(); 076 localPort = getFreePort(); 077 remotePort = this.getLocal().getSymmetric().getPort(); 078 079 LOGGER.fine(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort); 080 081 } 082 else { 083 ip = this.getRemote().getIp(); 084 localIp = this.getLocal().getLocalIp(); 085 localPort = this.getLocal().getPort(); 086 remotePort = this.getRemote().getPort(); 087 } 088 089 audioChannel = new AudioChannel(new MediaLocator(this.getMediaLocator()), localIp, ip, localPort, remotePort, AudioFormatUtils.getAudioFormat(this.getPayloadType()), this); 090 } 091 092 /** 093 * Starts transmission and for NAT Traversal reasons start receiving also. 094 * 095 * @deprecated use {@link #startTransmit()} instead. 096 */ 097 @Deprecated 098 public void startTrasmit() { 099 startTransmit(); 100 } 101 102 /** 103 * Starts transmission and for NAT Traversal reasons start receiving also. 104 */ 105 @Override 106 public void startTransmit() { 107 audioChannel.start(); 108 } 109 110 /** 111 * Set transmit activity. If the active is true, the instance should transmit. 112 * If it is set to false, the instance should pause transmit. 113 * 114 * @param active active state 115 * @deprecated use {@link #setTransmit(boolean)} instead. 116 */ 117 @Deprecated 118 public void setTrasmit(boolean active) { 119 setTransmit(active); 120 } 121 122 /** 123 * Set transmit activity. If the active is true, the instance should transmit. 124 * If it is set to false, the instance should pause transmit. 125 * 126 * @param active active state 127 */ 128 @Override 129 public void setTransmit(boolean active) { 130 audioChannel.setTrasmit(active); 131 } 132 133 /** 134 * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 135 */ 136 @Override 137 public void startReceive() { 138 // Do nothing 139 } 140 141 /** 142 * Stops transmission and for NAT Traversal reasons stop receiving also. 143 * 144 * @deprecated use {@link #stopTransmit()} instead. 145 */ 146 @Deprecated 147 public void stopTrasmit() { 148 stopTransmit(); 149 } 150 151 /** 152 * Stops transmission and for NAT Traversal reasons stop receiving also. 153 */ 154 @Override 155 public void stopTransmit() { 156 if (audioChannel != null) 157 audioChannel.stop(); 158 } 159 160 /** 161 * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 162 */ 163 @Override 164 public void stopReceive() { 165 // Do nothing 166 } 167 168 /** 169 * Obtain a free port we can use. 170 * 171 * @return A free port number. 172 */ 173 protected int getFreePort() { 174 ServerSocket ss; 175 int freePort = 0; 176 177 for (int i = 0; i < 10; i++) { 178 freePort = (int) (10000 + Math.round(Math.random() * 10000)); 179 freePort = freePort % 2 == 0 ? freePort : freePort + 1; 180 try { 181 ss = new ServerSocket(freePort); 182 freePort = ss.getLocalPort(); 183 ss.close(); 184 return freePort; 185 } 186 catch (IOException e) { 187 LOGGER.log(Level.WARNING, "exception", e); 188 } 189 } 190 try { 191 ss = new ServerSocket(0); 192 freePort = ss.getLocalPort(); 193 ss.close(); 194 } 195 catch (IOException e) { 196 LOGGER.log(Level.WARNING, "exception", e); 197 } 198 return freePort; 199 } 200 201}