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 @SuppressWarnings("this-escape") 057 public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote, 058 final TransportCandidate local, String locator, JingleSession jingleSession) { 059 super(payloadType, remote, local, locator == null ? "dsound://" : locator, jingleSession); 060 initialize(); 061 } 062 063 /** 064 * Initialize the Audio Channel to make it able to send and receive audio. 065 */ 066 @Override 067 public void initialize() { 068 069 String ip; 070 String localIp; 071 int localPort; 072 int remotePort; 073 074 if (this.getLocal().getSymmetric() != null) { 075 ip = this.getLocal().getIp(); 076 localIp = this.getLocal().getLocalIp(); 077 localPort = getFreePort(); 078 remotePort = this.getLocal().getSymmetric().getPort(); 079 080 LOGGER.fine(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort); 081 082 } 083 else { 084 ip = this.getRemote().getIp(); 085 localIp = this.getLocal().getLocalIp(); 086 localPort = this.getLocal().getPort(); 087 remotePort = this.getRemote().getPort(); 088 } 089 090 audioChannel = new AudioChannel(new MediaLocator(this.getMediaLocator()), localIp, ip, localPort, remotePort, AudioFormatUtils.getAudioFormat(this.getPayloadType()), this); 091 } 092 093 /** 094 * Starts transmission and for NAT Traversal reasons start receiving also. 095 * 096 * @deprecated use {@link #startTransmit()} instead. 097 */ 098 @Deprecated 099 public void startTrasmit() { 100 startTransmit(); 101 } 102 103 /** 104 * Starts transmission and for NAT Traversal reasons start receiving also. 105 */ 106 @Override 107 public void startTransmit() { 108 audioChannel.start(); 109 } 110 111 /** 112 * Set transmit activity. If the active is true, the instance should transmit. 113 * If it is set to false, the instance should pause transmit. 114 * 115 * @param active active state 116 * @deprecated use {@link #setTransmit(boolean)} instead. 117 */ 118 @Deprecated 119 public void setTrasmit(boolean active) { 120 setTransmit(active); 121 } 122 123 /** 124 * Set transmit activity. If the active is true, the instance should transmit. 125 * If it is set to false, the instance should pause transmit. 126 * 127 * @param active active state 128 */ 129 @Override 130 public void setTransmit(boolean active) { 131 audioChannel.setTrasmit(active); 132 } 133 134 /** 135 * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 136 */ 137 @Override 138 public void startReceive() { 139 // Do nothing 140 } 141 142 /** 143 * Stops transmission and for NAT Traversal reasons stop receiving also. 144 * 145 * @deprecated use {@link #stopTransmit()} instead. 146 */ 147 @Deprecated 148 public void stopTrasmit() { 149 stopTransmit(); 150 } 151 152 /** 153 * Stops transmission and for NAT Traversal reasons stop receiving also. 154 */ 155 @Override 156 public void stopTransmit() { 157 if (audioChannel != null) 158 audioChannel.stop(); 159 } 160 161 /** 162 * For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf 163 */ 164 @Override 165 public void stopReceive() { 166 // Do nothing 167 } 168 169 /** 170 * Obtain a free port we can use. 171 * 172 * @return A free port number. 173 */ 174 protected int getFreePort() { 175 ServerSocket ss; 176 int freePort = 0; 177 178 for (int i = 0; i < 10; i++) { 179 freePort = (int) (10000 + Math.round(Math.random() * 10000)); 180 freePort = freePort % 2 == 0 ? freePort : freePort + 1; 181 try { 182 ss = new ServerSocket(freePort); 183 freePort = ss.getLocalPort(); 184 ss.close(); 185 return freePort; 186 } 187 catch (IOException e) { 188 LOGGER.log(Level.WARNING, "exception", e); 189 } 190 } 191 try { 192 ss = new ServerSocket(0); 193 freePort = ss.getLocalPort(); 194 ss.close(); 195 } 196 catch (IOException e) { 197 LOGGER.log(Level.WARNING, "exception", e); 198 } 199 return freePort; 200 } 201 202}