src/org/sipdroid/media/RtpStreamSender.java
changeset 834 e8d6255306f8
parent 830 c8b4ace735ea
child 835 4e40f3481f23
equal deleted inserted replaced
833:f5a5d9237d69 834:e8d6255306f8
    17  * You should have received a copy of the GNU General Public License
    17  * You should have received a copy of the GNU General Public License
    18  * along with this source code; if not, write to the Free Software
    18  * along with this source code; if not, write to the Free Software
    19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    20  */
    20  */
    21 
    21 
    22 package org.sipdroid.media;
    22 package src.org.sipdroid.media;
    23 
    23 
       
    24 import java.io.IOException;
    24 import java.io.InputStream;
    25 import java.io.InputStream;
    25 import java.net.DatagramPacket;
    26 import java.net.InetAddress;
    26 import java.util.Random;
    27 import java.util.Random;
    27 
    28 
    28 import jlibrtp.RTPSession;
    29 import org.sipdroid.sipua.UserAgent;
    29 import jlibrtp.RtpPkt;
    30 import org.sipdroid.sipua.ui.Receiver;
    30 
    31 import org.sipdroid.sipua.ui.Settings;
    31 import org.sipdroid.media.codecs.Codec;
    32 import org.sipdroid.sipua.ui.Sipdroid;
    32 
    33 import org.sipdroid.pjlib.Codec;
       
    34 
       
    35 import src.org.sipdroid.net.RtpPacket;
       
    36 import src.org.sipdroid.net.RtpSocket;
       
    37 import src.org.sipdroid.net.SipdroidSocket;
       
    38 
       
    39 import android.content.Context;
    33 import android.media.AudioFormat;
    40 import android.media.AudioFormat;
    34 import android.media.AudioManager;
    41 import android.media.AudioManager;
    35 import android.media.AudioRecord;
    42 import android.media.AudioRecord;
    36 import android.media.MediaRecorder;
    43 import android.media.MediaRecorder;
       
    44 import android.preference.PreferenceManager;
       
    45 import android.telephony.TelephonyManager;
    37 
    46 
    38 /**
    47 /**
    39  * RtpStreamSender is a generic stream sender. It takes an InputStream and sends
    48  * RtpStreamSender is a generic stream sender. It takes an InputStream and sends
    40  * it through RTP.
    49  * it through RTP.
    41  */
    50  */
    42 public class RtpStreamSender extends Thread{
    51 public class RtpStreamSender extends Thread {
    43 
    52 	/** Whether working in debug mode. */
    44 	private static final boolean DEBUG = true;
    53 	public static boolean DEBUG = true;
    45 
    54 
    46 	/** The RtpSocket */
    55 	/** The RtpSocket */
    47 	private RTPSession rtpSession = null;
    56 	RtpSocket rtp_socket = null;
    48 
    57 
    49 	/** Codec */
    58 	/** Payload type */
    50 	private Codec codec;
    59 	int p_type;
    51 
    60 
    52 	private int sampling_rate;
    61 	/** Number of frame per second */
       
    62 	long frame_rate;
    53 
    63 
    54 	/** Number of bytes per frame */
    64 	/** Number of bytes per frame */
    55 	private int frame_size;
    65 	int frame_size;
    56 
       
    57 	private int codec_frame_size;
       
    58 
    66 
    59 	/**
    67 	/**
    60 	 * Whether it works synchronously with a local clock, or it it acts as slave
    68 	 * Whether it works synchronously with a local clock, or it it acts as slave
    61 	 * of the InputStream
    69 	 * of the InputStream
    62 	 */
    70 	 */
    63 	boolean do_sync = true;
    71 	boolean do_sync = true;
    64 
    72 
       
    73 	/**
       
    74 	 * Synchronization correction value, in milliseconds. It accellarates the
       
    75 	 * sending rate respect to the nominal value, in order to compensate program
       
    76 	 * latencies.
       
    77 	 */
    65 	int sync_adj = 0;
    78 	int sync_adj = 0;
    66 
    79 
    67 	/** Whether it is running */
    80 	/** Whether it is running */
    68 	boolean running = false;
    81 	boolean running = false;
    69 	boolean muted = false;
    82 	boolean muted = false;
    70 
    83 
    71 	private int codec_divider;
       
    72 
       
    73 	/**
    84 	/**
    74 	 * Constructs a RtpStreamSender.
    85 	 * Constructs a RtpStreamSender.
    75 	 * 
    86 	 * 
       
    87 	 * @param input_stream
       
    88 	 *            the stream to be sent
       
    89 	 * @param do_sync
       
    90 	 *            whether time synchronization must be performed by the
       
    91 	 *            RtpStreamSender, or it is performed by the InputStream (e.g.
       
    92 	 *            the system audio input)
       
    93 	 * @param payload_type
       
    94 	 *            the payload type
       
    95 	 * @param frame_rate
       
    96 	 *            the frame rate, i.e. the number of frames that should be sent
       
    97 	 *            per second; it is used to calculate the nominal packet time
       
    98 	 *            and,in case of do_sync==true, the next departure time
       
    99 	 * @param frame_size
       
   100 	 *            the size of the payload
       
   101 	 * @param src_socket
       
   102 	 *            the socket used to send the RTP packet
       
   103 	 * @param dest_addr
       
   104 	 *            the destination address
       
   105 	 * @param dest_port
       
   106 	 *            the destination port
    76 	 */
   107 	 */
    77 	public RtpStreamSender(Codec co, RTPSession rtpSession) {
   108 	public RtpStreamSender(boolean do_sync,
    78 		init(co, rtpSession);
   109 			int payload_type, long frame_rate, int frame_size,
       
   110 			SipdroidSocket src_socket, String dest_addr, int dest_port) {
       
   111 		init(do_sync, payload_type, frame_rate, frame_size,
       
   112 				src_socket, dest_addr, dest_port);
    79 	}
   113 	}
    80 
   114 
    81 	/** Inits the RtpStreamSender */
   115 	/** Inits the RtpStreamSender */
    82 	private void init(Codec co,	RTPSession rtpSession) {
   116 	private void init(boolean do_sync,
    83 		this.rtpSession = rtpSession;
   117 			int payload_type, long frame_rate, int frame_size,
    84 		codec = co;
   118 			SipdroidSocket src_socket, String dest_addr,
    85 		sampling_rate = codec.getInfo().samplingRate;
   119 			int dest_port) {
    86 		codec_frame_size = codec.getInfo().codecFrameSize;
   120 		this.p_type = payload_type;
    87 		codec_divider = codec.getInfo().rtpSampleDivider;
   121 		this.frame_rate = frame_rate;
    88 		frame_size = 160 * codec_divider;
   122 		this.frame_size = PreferenceManager.getDefaultSharedPreferences(Receiver.mContext).getString("server","").equals("pbxes.org")?
    89 		rtpSession.payloadType(codec.getInfo().rtpPayloadCode);
   123 				(payload_type == 3?960:1024):frame_size; //15
    90 
   124 		this.do_sync = do_sync;
    91 		this.do_sync = true;
   125 		try {
       
   126 			rtp_socket = new RtpSocket(src_socket, InetAddress
       
   127 					.getByName(dest_addr), dest_port);
       
   128 		} catch (Exception e) {
       
   129 			if (!Sipdroid.release) e.printStackTrace();
       
   130 		}
    92 	}
   131 	}
    93 
   132 
    94 	/** Sets the synchronization adjustment time (in milliseconds). */
   133 	/** Sets the synchronization adjustment time (in milliseconds). */
    95 	public void setSyncAdj(int millisecs) {
   134 	public void setSyncAdj(int millisecs) {
    96 		sync_adj = millisecs;
   135 		sync_adj = millisecs;
    98 
   137 
    99 	/** Whether is running */
   138 	/** Whether is running */
   100 	public boolean isRunning() {
   139 	public boolean isRunning() {
   101 		return running;
   140 		return running;
   102 	}
   141 	}
   103 
   142 	
   104 	public boolean mute() {
   143 	public boolean mute() {
   105 		return muted = !muted;
   144 		return muted = !muted;
   106 	}
   145 	}
   107 
   146 
   108 	public static int delay = 0;
   147 	public static int delay = 0;
   109 
   148 	
   110 	/** Stops running */
   149 	/** Stops running */
   111 	public void halt() {
   150 	public void halt() {
   112 		running = false;
   151 		running = false;
   113 	}
   152 	}
   114 
   153 
   115 	Random random;
   154 	Random random;
   116 	double smin = 200,s;
   155 	double smin = 200,s;
   117 	int nearend;
   156 	int nearend;
   118 
   157 	
   119 	void calc(short[] lin,int off,int len) {
   158 	void calc(short[] lin,int off,int len) {
   120 		int i,j;
   159 		int i,j;
   121 		double sm = 30000,r;
   160 		double sm = 30000,r;
   122 
   161 		
   123 		for (i = 0; i < len; i += 5) {
   162 		for (i = 0; i < len; i += 5) {
   124 			j = lin[i+off];
   163 			j = lin[i+off];
   125 			s = 0.03*Math.abs(j) + 0.97*s;
   164 			s = 0.03*Math.abs(j) + 0.97*s;
   126 			if (s < sm) sm = s;
   165 			if (s < sm) sm = s;
   127 			if (s > smin) nearend = 3000/5;
   166 			if (s > smin) nearend = 3000/5;
   136 			else
   175 			else
   137 				lin[i+off] = (short)(j*5);
   176 				lin[i+off] = (short)(j*5);
   138 		}
   177 		}
   139 		r = (double)len/100000;
   178 		r = (double)len/100000;
   140 		smin = sm*r + smin*(1-r);
   179 		smin = sm*r + smin*(1-r);
       
   180 	}
       
   181 
       
   182 	void calc1(short[] lin,int off,int len) {
       
   183 		int i,j;
       
   184 		
       
   185 		for (i = 0; i < len; i++) {
       
   186 			j = lin[i+off];
       
   187 			lin[i+off] = (short)(j>>1);
       
   188 		}
       
   189 	}
       
   190 
       
   191 	void calc5(short[] lin,int off,int len) {
       
   192 		int i,j;
       
   193 		
       
   194 		for (i = 0; i < len; i++) {
       
   195 			j = lin[i+off];
       
   196 			if (j > 16350)
       
   197 				lin[i+off] = 16350<<1;
       
   198 			else if (j < -16350)
       
   199 				lin[i+off] = -16350<<1;
       
   200 			else
       
   201 				lin[i+off] = (short)(j<<1);
       
   202 		}
       
   203 	}
       
   204 
       
   205 	void calc10(short[] lin,int off,int len) {
       
   206 		int i,j;
       
   207 		
       
   208 		for (i = 0; i < len; i++) {
       
   209 			j = lin[i+off];
       
   210 			if (j > 8150)
       
   211 				lin[i+off] = 8150<<2;
       
   212 			else if (j < -8150)
       
   213 				lin[i+off] = -8150<<2;
       
   214 			else
       
   215 				lin[i+off] = (short)(j<<2);
       
   216 		}
   141 	}
   217 	}
   142 
   218 
   143 	void noise(short[] lin,int off,int len,double power) {
   219 	void noise(short[] lin,int off,int len,double power) {
   144 		int i,r = (int)(power*2);
   220 		int i,r = (int)(power*2);
   145 		short ran;
   221 		short ran;
   151 			lin[i+off+1] = ran;
   227 			lin[i+off+1] = ran;
   152 			lin[i+off+2] = ran;
   228 			lin[i+off+2] = ran;
   153 			lin[i+off+3] = ran;
   229 			lin[i+off+3] = ran;
   154 		}
   230 		}
   155 	}
   231 	}
   156 
   232 	
   157 	public static int m;
   233 	public static int m;
   158 
   234 	
   159 	/** Runs it in a new Thread. */
   235 	/** Runs it in a new Thread. */
   160 	public void run() {
   236 	public void run() {
   161 		if (rtpSession == null)
   237 		if (rtp_socket == null)
   162 			return;
   238 			return;
   163 		byte[] buffer = new byte[codec_frame_size + 12];
   239 		byte[] buffer = new byte[frame_size + 12];
   164 		DatagramPacket packet = new DatagramPacket(buffer, codec_frame_size + 12);
   240 		RtpPacket rtp_packet = new RtpPacket(buffer, 0);
   165 		RtpPkt pkt = new RtpPkt();
   241 		rtp_packet.setPayloadType(p_type);
   166 		pkt.setRawPkt(buffer);
       
   167 		pkt.setPayloadType(codec.getInfo().rtpPayloadCode);
       
   168 		int seqn = 0;
   242 		int seqn = 0;
   169 		long time = 0;
   243 		long time = 0;
   170 		double p = 0;
   244 		double p = 0;
       
   245 		TelephonyManager tm = (TelephonyManager) Receiver.mContext.getSystemService(Context.TELEPHONY_SERVICE);
       
   246 		boolean improve = PreferenceManager.getDefaultSharedPreferences(Receiver.mContext).getBoolean("improve",false);
       
   247 		boolean useGSM = !PreferenceManager.getDefaultSharedPreferences(Receiver.mContext).getString("compression","edge").equals("never");
       
   248 		int micgain = (int)(Settings.getMicGain()*10);
   171 		running = true;
   249 		running = true;
   172 		m = 1;
   250 		m = 1;
   173 
   251 
   174 		if (DEBUG)
   252 		if (DEBUG)
   175 			println("Reading blocks of " + buffer.length + " bytes");
   253 			println("Reading blocks of " + buffer.length + " bytes");
   176 
   254 
   177 		android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
   255 		android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
   178 
   256 		AudioRecord record = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, 
   179 		Codec.Context codecCtx = codec.initEncoder();
   257 				AudioRecord.getMinBufferSize(8000, 
   180 
       
   181 		AudioRecord record = new AudioRecord(MediaRecorder.AudioSource.MIC, sampling_rate, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, 
       
   182 				AudioRecord.getMinBufferSize(sampling_rate, 
       
   183 						AudioFormat.CHANNEL_CONFIGURATION_MONO, 
   258 						AudioFormat.CHANNEL_CONFIGURATION_MONO, 
   184 						AudioFormat.ENCODING_PCM_16BIT)*2);
   259 						AudioFormat.ENCODING_PCM_16BIT)*3/2);
   185 		record.startRecording();
       
   186 		short[] lin = new short[frame_size*11];
   260 		short[] lin = new short[frame_size*11];
   187 		int num,ring = 0;
   261 		int num,ring = 0;
   188 		random = new Random();
   262 		random = new Random();
       
   263 		InputStream alerting = null;
       
   264 		try {
       
   265 			alerting = Receiver.mContext.getAssets().open("alerting");
       
   266 		} catch (IOException e2) {
       
   267 			if (!Sipdroid.release) e2.printStackTrace();
       
   268 		}
       
   269 		switch (p_type) {
       
   270 		case 3:
       
   271 			Codec.init();
       
   272 			break;
       
   273 		case 0:
       
   274 		case 8:
       
   275 			G711.init();
       
   276 			break;
       
   277 		}
       
   278 		record.startRecording();
   189 		while (running) {
   279 		while (running) {
   190 			num = record.read(lin,(ring+delay)%(frame_size*11),frame_size);
   280 			 if (muted || Receiver.call_state == UserAgent.UA_STATE_HOLD) {
   191 			if (RtpStreamReceiver.speakermode == AudioManager.MODE_NORMAL) {
   281 				record.stop();
   192 				calc(lin,(ring+delay)%(frame_size*11),num);
   282 				while (running && (muted || Receiver.call_state == UserAgent.UA_STATE_HOLD)) {
   193 				if (RtpStreamReceiver.nearend != 0)
   283 					try {
   194 					noise(lin,(ring+delay)%(frame_size*11),num,p);
   284 						sleep(1000);
   195 				else if (nearend == 0)
   285 					} catch (InterruptedException e1) {
   196 					p = 0.9*p + 0.1*s;
   286 					}
   197 			}
   287 				}
   198 			codec.encode(codecCtx, lin, ring%(frame_size*11), frame_size, buffer, 12);
   288 				record.startRecording();
   199 			ring += frame_size;
   289 			 }
   200 			rtpSession.sendData(packet, pkt);
   290 			 num = record.read(lin,(ring+delay)%(frame_size*11),frame_size);
   201 			if (m == 2) {
   291 
   202 				rtpSession.sendData(packet, pkt);
   292 			 if (RtpStreamReceiver.speakermode == AudioManager.MODE_NORMAL) {
   203 				println("retransmit");
   293  				 calc(lin,(ring+delay)%(frame_size*11),num);
   204 			}
   294  	 			 if (RtpStreamReceiver.nearend != 0)
   205 			seqn++;
   295 	 				 noise(lin,(ring+delay)%(frame_size*11),num,p);
   206 			time += num;
   296 	 			 else if (nearend == 0)
       
   297 	 				 p = 0.9*p + 0.1*s;
       
   298  			 } else switch (micgain) {
       
   299  			 case 1:
       
   300  				 calc1(lin,(ring+delay)%(frame_size*11),num);
       
   301  				 break;
       
   302  			 case 5:
       
   303  				 calc5(lin,(ring+delay)%(frame_size*11),num);
       
   304  				 break;
       
   305  			 case 10:
       
   306  				 calc10(lin,(ring+delay)%(frame_size*11),num);
       
   307  				 break;
       
   308  			 }
       
   309 			 if (Receiver.call_state != UserAgent.UA_STATE_INCALL && alerting != null) {
       
   310 				 try {
       
   311 					if (alerting.available() < num)
       
   312 						alerting.reset();
       
   313 					alerting.read(buffer,12,num);
       
   314 				 } catch (IOException e) {
       
   315 					if (!Sipdroid.release) e.printStackTrace();
       
   316 				 }
       
   317 				 switch (p_type) {// have to add ulaw case?
       
   318 				 case 3:
       
   319 					 G711.alaw2linear(buffer, lin, num);
       
   320 					 num = Codec.encode(lin, 0, buffer, num);
       
   321 					 break;
       
   322 				 case 0:
       
   323 					 G711.alaw2linear(buffer, lin, num);
       
   324 					 G711.linear2ulaw(lin, 0, buffer, num);
       
   325 					 break;
       
   326 				 }
       
   327 			 } else {
       
   328 				 switch (p_type) {
       
   329 				 case 3:
       
   330 					 num = Codec.encode(lin, ring%(frame_size*11), buffer, num);
       
   331 					 break;
       
   332 				 case 0:
       
   333 					 G711.linear2ulaw(lin, ring%(frame_size*11), buffer, num);
       
   334 					 break;
       
   335 				 case 8:
       
   336 					 G711.linear2alaw(lin, ring%(frame_size*11), buffer, num);
       
   337 					 break;
       
   338 				 }
       
   339 			 }
       
   340  			 ring += frame_size;
       
   341  			 rtp_packet.setSequenceNumber(seqn++);
       
   342  			 rtp_packet.setTimestamp(time);
       
   343  			 rtp_packet.setPayloadLength(num);
       
   344  			 try {
       
   345  				 rtp_socket.send(rtp_packet);
       
   346  				 if (m == 2)
       
   347  					 rtp_socket.send(rtp_packet);
       
   348  			 } catch (IOException e) {
       
   349  			 }
       
   350 			 time += frame_size;
       
   351  			 if (improve && RtpStreamReceiver.good != 0 &&
       
   352  					 RtpStreamReceiver.loss/RtpStreamReceiver.good > 0.01 &&
       
   353  					 (Receiver.on_wlan || tm.getNetworkType() != TelephonyManager.NETWORK_TYPE_EDGE))        	
       
   354  				 m = 2;
       
   355  			 else
       
   356  				 m = 1;
       
   357  			 if (useGSM && p_type == 8 && !Receiver.on_wlan && tm.getNetworkType() == TelephonyManager.NETWORK_TYPE_EDGE) {
       
   358  				 rtp_packet.setPayloadType(p_type = 3);
       
   359  				 if (frame_size == 1024) {
       
   360  					 frame_size = 960;
       
   361  					 ring = 0;
       
   362  				 }
       
   363  			 }
   207 		}
   364 		}
   208 		record.stop();
   365 		record.stop();
   209 		rtpSession = null;
   366 		
   210 		codec.cleanEncoder(codecCtx);
   367 		rtp_socket.close();
       
   368 		rtp_socket = null;
       
   369 
   211 		if (DEBUG)
   370 		if (DEBUG)
   212 			println("rtp sender terminated");
   371 			println("rtp sender terminated");
   213 	}
   372 	}
   214 
   373 
   215 	/** Debug output */
   374 	/** Debug output */
   216 	private static void println(String str) {
   375 	private static void println(String str) {
   217 		android.util.Log.d("DEBUG","RtpStreamSender: " + str);
   376 		if (!Sipdroid.release) System.out.println("RtpStreamSender: " + str);
   218 	}
   377 	}
       
   378 
   219 }
   379 }