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