src/org/sipdroid/media/RtpStreamSender.java
changeset 834 e8d6255306f8
parent 830 c8b4ace735ea
child 835 4e40f3481f23
--- a/src/org/sipdroid/media/RtpStreamSender.java	Sat Jan 23 21:48:58 2010 +0100
+++ b/src/org/sipdroid/media/RtpStreamSender.java	Sat Jan 23 22:19:43 2010 +0100
@@ -19,42 +19,50 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-package org.sipdroid.media;
+package src.org.sipdroid.media;
 
+import java.io.IOException;
 import java.io.InputStream;
-import java.net.DatagramPacket;
+import java.net.InetAddress;
 import java.util.Random;
 
-import jlibrtp.RTPSession;
-import jlibrtp.RtpPkt;
+import org.sipdroid.sipua.UserAgent;
+import org.sipdroid.sipua.ui.Receiver;
+import org.sipdroid.sipua.ui.Settings;
+import org.sipdroid.sipua.ui.Sipdroid;
+import org.sipdroid.pjlib.Codec;
 
-import org.sipdroid.media.codecs.Codec;
+import src.org.sipdroid.net.RtpPacket;
+import src.org.sipdroid.net.RtpSocket;
+import src.org.sipdroid.net.SipdroidSocket;
 
+import android.content.Context;
 import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioRecord;
 import android.media.MediaRecorder;
+import android.preference.PreferenceManager;
+import android.telephony.TelephonyManager;
 
 /**
  * RtpStreamSender is a generic stream sender. It takes an InputStream and sends
  * it through RTP.
  */
-public class RtpStreamSender extends Thread{
-
-	private static final boolean DEBUG = true;
+public class RtpStreamSender extends Thread {
+	/** Whether working in debug mode. */
+	public static boolean DEBUG = true;
 
 	/** The RtpSocket */
-	private RTPSession rtpSession = null;
+	RtpSocket rtp_socket = null;
 
-	/** Codec */
-	private Codec codec;
+	/** Payload type */
+	int p_type;
 
-	private int sampling_rate;
+	/** Number of frame per second */
+	long frame_rate;
 
 	/** Number of bytes per frame */
-	private int frame_size;
-
-	private int codec_frame_size;
+	int frame_size;
 
 	/**
 	 * Whether it works synchronously with a local clock, or it it acts as slave
@@ -62,33 +70,64 @@
 	 */
 	boolean do_sync = true;
 
+	/**
+	 * Synchronization correction value, in milliseconds. It accellarates the
+	 * sending rate respect to the nominal value, in order to compensate program
+	 * latencies.
+	 */
 	int sync_adj = 0;
 
 	/** Whether it is running */
 	boolean running = false;
 	boolean muted = false;
 
-	private int codec_divider;
-
 	/**
 	 * Constructs a RtpStreamSender.
 	 * 
+	 * @param input_stream
+	 *            the stream to be sent
+	 * @param do_sync
+	 *            whether time synchronization must be performed by the
+	 *            RtpStreamSender, or it is performed by the InputStream (e.g.
+	 *            the system audio input)
+	 * @param payload_type
+	 *            the payload type
+	 * @param frame_rate
+	 *            the frame rate, i.e. the number of frames that should be sent
+	 *            per second; it is used to calculate the nominal packet time
+	 *            and,in case of do_sync==true, the next departure time
+	 * @param frame_size
+	 *            the size of the payload
+	 * @param src_socket
+	 *            the socket used to send the RTP packet
+	 * @param dest_addr
+	 *            the destination address
+	 * @param dest_port
+	 *            the destination port
 	 */
-	public RtpStreamSender(Codec co, RTPSession rtpSession) {
-		init(co, rtpSession);
+	public RtpStreamSender(boolean do_sync,
+			int payload_type, long frame_rate, int frame_size,
+			SipdroidSocket src_socket, String dest_addr, int dest_port) {
+		init(do_sync, payload_type, frame_rate, frame_size,
+				src_socket, dest_addr, dest_port);
 	}
 
 	/** Inits the RtpStreamSender */
-	private void init(Codec co,	RTPSession rtpSession) {
-		this.rtpSession = rtpSession;
-		codec = co;
-		sampling_rate = codec.getInfo().samplingRate;
-		codec_frame_size = codec.getInfo().codecFrameSize;
-		codec_divider = codec.getInfo().rtpSampleDivider;
-		frame_size = 160 * codec_divider;
-		rtpSession.payloadType(codec.getInfo().rtpPayloadCode);
-
-		this.do_sync = true;
+	private void init(boolean do_sync,
+			int payload_type, long frame_rate, int frame_size,
+			SipdroidSocket src_socket, String dest_addr,
+			int dest_port) {
+		this.p_type = payload_type;
+		this.frame_rate = frame_rate;
+		this.frame_size = PreferenceManager.getDefaultSharedPreferences(Receiver.mContext).getString("server","").equals("pbxes.org")?
+				(payload_type == 3?960:1024):frame_size; //15
+		this.do_sync = do_sync;
+		try {
+			rtp_socket = new RtpSocket(src_socket, InetAddress
+					.getByName(dest_addr), dest_port);
+		} catch (Exception e) {
+			if (!Sipdroid.release) e.printStackTrace();
+		}
 	}
 
 	/** Sets the synchronization adjustment time (in milliseconds). */
@@ -100,13 +139,13 @@
 	public boolean isRunning() {
 		return running;
 	}
-
+	
 	public boolean mute() {
 		return muted = !muted;
 	}
 
 	public static int delay = 0;
-
+	
 	/** Stops running */
 	public void halt() {
 		running = false;
@@ -115,11 +154,11 @@
 	Random random;
 	double smin = 200,s;
 	int nearend;
-
+	
 	void calc(short[] lin,int off,int len) {
 		int i,j;
 		double sm = 30000,r;
-
+		
 		for (i = 0; i < len; i += 5) {
 			j = lin[i+off];
 			s = 0.03*Math.abs(j) + 0.97*s;
@@ -140,6 +179,43 @@
 		smin = sm*r + smin*(1-r);
 	}
 
+	void calc1(short[] lin,int off,int len) {
+		int i,j;
+		
+		for (i = 0; i < len; i++) {
+			j = lin[i+off];
+			lin[i+off] = (short)(j>>1);
+		}
+	}
+
+	void calc5(short[] lin,int off,int len) {
+		int i,j;
+		
+		for (i = 0; i < len; i++) {
+			j = lin[i+off];
+			if (j > 16350)
+				lin[i+off] = 16350<<1;
+			else if (j < -16350)
+				lin[i+off] = -16350<<1;
+			else
+				lin[i+off] = (short)(j<<1);
+		}
+	}
+
+	void calc10(short[] lin,int off,int len) {
+		int i,j;
+		
+		for (i = 0; i < len; i++) {
+			j = lin[i+off];
+			if (j > 8150)
+				lin[i+off] = 8150<<2;
+			else if (j < -8150)
+				lin[i+off] = -8150<<2;
+			else
+				lin[i+off] = (short)(j<<2);
+		}
+	}
+
 	void noise(short[] lin,int off,int len,double power) {
 		int i,r = (int)(power*2);
 		short ran;
@@ -153,21 +229,23 @@
 			lin[i+off+3] = ran;
 		}
 	}
-
+	
 	public static int m;
-
+	
 	/** Runs it in a new Thread. */
 	public void run() {
-		if (rtpSession == null)
+		if (rtp_socket == null)
 			return;
-		byte[] buffer = new byte[codec_frame_size + 12];
-		DatagramPacket packet = new DatagramPacket(buffer, codec_frame_size + 12);
-		RtpPkt pkt = new RtpPkt();
-		pkt.setRawPkt(buffer);
-		pkt.setPayloadType(codec.getInfo().rtpPayloadCode);
+		byte[] buffer = new byte[frame_size + 12];
+		RtpPacket rtp_packet = new RtpPacket(buffer, 0);
+		rtp_packet.setPayloadType(p_type);
 		int seqn = 0;
 		long time = 0;
 		double p = 0;
+		TelephonyManager tm = (TelephonyManager) Receiver.mContext.getSystemService(Context.TELEPHONY_SERVICE);
+		boolean improve = PreferenceManager.getDefaultSharedPreferences(Receiver.mContext).getBoolean("improve",false);
+		boolean useGSM = !PreferenceManager.getDefaultSharedPreferences(Receiver.mContext).getString("compression","edge").equals("never");
+		int micgain = (int)(Settings.getMicGain()*10);
 		running = true;
 		m = 1;
 
@@ -175,45 +253,127 @@
 			println("Reading blocks of " + buffer.length + " bytes");
 
 		android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
-
-		Codec.Context codecCtx = codec.initEncoder();
-
-		AudioRecord record = new AudioRecord(MediaRecorder.AudioSource.MIC, sampling_rate, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, 
-				AudioRecord.getMinBufferSize(sampling_rate, 
+		AudioRecord record = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, 
+				AudioRecord.getMinBufferSize(8000, 
 						AudioFormat.CHANNEL_CONFIGURATION_MONO, 
-						AudioFormat.ENCODING_PCM_16BIT)*2);
-		record.startRecording();
+						AudioFormat.ENCODING_PCM_16BIT)*3/2);
 		short[] lin = new short[frame_size*11];
 		int num,ring = 0;
 		random = new Random();
+		InputStream alerting = null;
+		try {
+			alerting = Receiver.mContext.getAssets().open("alerting");
+		} catch (IOException e2) {
+			if (!Sipdroid.release) e2.printStackTrace();
+		}
+		switch (p_type) {
+		case 3:
+			Codec.init();
+			break;
+		case 0:
+		case 8:
+			G711.init();
+			break;
+		}
+		record.startRecording();
 		while (running) {
-			num = record.read(lin,(ring+delay)%(frame_size*11),frame_size);
-			if (RtpStreamReceiver.speakermode == AudioManager.MODE_NORMAL) {
-				calc(lin,(ring+delay)%(frame_size*11),num);
-				if (RtpStreamReceiver.nearend != 0)
-					noise(lin,(ring+delay)%(frame_size*11),num,p);
-				else if (nearend == 0)
-					p = 0.9*p + 0.1*s;
-			}
-			codec.encode(codecCtx, lin, ring%(frame_size*11), frame_size, buffer, 12);
-			ring += frame_size;
-			rtpSession.sendData(packet, pkt);
-			if (m == 2) {
-				rtpSession.sendData(packet, pkt);
-				println("retransmit");
-			}
-			seqn++;
-			time += num;
+			 if (muted || Receiver.call_state == UserAgent.UA_STATE_HOLD) {
+				record.stop();
+				while (running && (muted || Receiver.call_state == UserAgent.UA_STATE_HOLD)) {
+					try {
+						sleep(1000);
+					} catch (InterruptedException e1) {
+					}
+				}
+				record.startRecording();
+			 }
+			 num = record.read(lin,(ring+delay)%(frame_size*11),frame_size);
+
+			 if (RtpStreamReceiver.speakermode == AudioManager.MODE_NORMAL) {
+ 				 calc(lin,(ring+delay)%(frame_size*11),num);
+ 	 			 if (RtpStreamReceiver.nearend != 0)
+	 				 noise(lin,(ring+delay)%(frame_size*11),num,p);
+	 			 else if (nearend == 0)
+	 				 p = 0.9*p + 0.1*s;
+ 			 } else switch (micgain) {
+ 			 case 1:
+ 				 calc1(lin,(ring+delay)%(frame_size*11),num);
+ 				 break;
+ 			 case 5:
+ 				 calc5(lin,(ring+delay)%(frame_size*11),num);
+ 				 break;
+ 			 case 10:
+ 				 calc10(lin,(ring+delay)%(frame_size*11),num);
+ 				 break;
+ 			 }
+			 if (Receiver.call_state != UserAgent.UA_STATE_INCALL && alerting != null) {
+				 try {
+					if (alerting.available() < num)
+						alerting.reset();
+					alerting.read(buffer,12,num);
+				 } catch (IOException e) {
+					if (!Sipdroid.release) e.printStackTrace();
+				 }
+				 switch (p_type) {// have to add ulaw case?
+				 case 3:
+					 G711.alaw2linear(buffer, lin, num);
+					 num = Codec.encode(lin, 0, buffer, num);
+					 break;
+				 case 0:
+					 G711.alaw2linear(buffer, lin, num);
+					 G711.linear2ulaw(lin, 0, buffer, num);
+					 break;
+				 }
+			 } else {
+				 switch (p_type) {
+				 case 3:
+					 num = Codec.encode(lin, ring%(frame_size*11), buffer, num);
+					 break;
+				 case 0:
+					 G711.linear2ulaw(lin, ring%(frame_size*11), buffer, num);
+					 break;
+				 case 8:
+					 G711.linear2alaw(lin, ring%(frame_size*11), buffer, num);
+					 break;
+				 }
+			 }
+ 			 ring += frame_size;
+ 			 rtp_packet.setSequenceNumber(seqn++);
+ 			 rtp_packet.setTimestamp(time);
+ 			 rtp_packet.setPayloadLength(num);
+ 			 try {
+ 				 rtp_socket.send(rtp_packet);
+ 				 if (m == 2)
+ 					 rtp_socket.send(rtp_packet);
+ 			 } catch (IOException e) {
+ 			 }
+			 time += frame_size;
+ 			 if (improve && RtpStreamReceiver.good != 0 &&
+ 					 RtpStreamReceiver.loss/RtpStreamReceiver.good > 0.01 &&
+ 					 (Receiver.on_wlan || tm.getNetworkType() != TelephonyManager.NETWORK_TYPE_EDGE))        	
+ 				 m = 2;
+ 			 else
+ 				 m = 1;
+ 			 if (useGSM && p_type == 8 && !Receiver.on_wlan && tm.getNetworkType() == TelephonyManager.NETWORK_TYPE_EDGE) {
+ 				 rtp_packet.setPayloadType(p_type = 3);
+ 				 if (frame_size == 1024) {
+ 					 frame_size = 960;
+ 					 ring = 0;
+ 				 }
+ 			 }
 		}
 		record.stop();
-		rtpSession = null;
-		codec.cleanEncoder(codecCtx);
+		
+		rtp_socket.close();
+		rtp_socket = null;
+
 		if (DEBUG)
 			println("rtp sender terminated");
 	}
 
 	/** Debug output */
 	private static void println(String str) {
-		android.util.Log.d("DEBUG","RtpStreamSender: " + str);
+		if (!Sipdroid.release) System.out.println("RtpStreamSender: " + str);
 	}
-}
+
+}
\ No newline at end of file