|
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.net.SocketException; |
|
26 |
|
27 import com.beem.project.beem.jingle.JingleService; |
|
28 import com.beem.project.beem.ui.Call; |
|
29 import org.sipdroid.net.RtpPacket; |
|
30 import org.sipdroid.net.RtpSocket; |
|
31 import org.sipdroid.net.SipdroidSocket; |
|
32 import org.sipdroid.pjlib.Codec; |
|
33 |
|
34 import android.content.ContentResolver; |
|
35 import android.content.Context; |
|
36 import android.content.SharedPreferences.Editor; |
|
37 import android.media.AudioFormat; |
|
38 import android.media.AudioManager; |
|
39 import android.media.AudioTrack; |
|
40 import android.media.ToneGenerator; |
|
41 import android.os.PowerManager; |
|
42 import android.os.RemoteException; |
|
43 import android.preference.PreferenceManager; |
|
44 import android.provider.Settings; |
|
45 |
|
46 /** |
|
47 * RtpStreamReceiver is a generic stream receiver. It receives packets from RTP |
|
48 * and writes them into an OutputStream. |
|
49 */ |
|
50 public class RtpStreamReceiver extends Thread { |
|
51 |
|
52 /** Whether working in debug mode. */ |
|
53 public static boolean DEBUG = true; |
|
54 |
|
55 /** Payload type */ |
|
56 int p_type; |
|
57 |
|
58 /** Size of the read buffer */ |
|
59 public static final int BUFFER_SIZE = 1024; |
|
60 |
|
61 /** Maximum blocking time, spent waiting for reading new bytes [milliseconds] */ |
|
62 public static final int SO_TIMEOUT = 200; |
|
63 |
|
64 /** The RtpSocket */ |
|
65 private RtpSocket rtp_socket = null; |
|
66 |
|
67 /** Whether it is running */ |
|
68 private boolean running; |
|
69 private AudioManager am; |
|
70 private ContentResolver cr; |
|
71 public static int speakermode; |
|
72 private JingleService mJingle; |
|
73 |
|
74 /** |
|
75 * Constructs a RtpStreamReceiver. |
|
76 * |
|
77 * @param output_stream |
|
78 * the stream sink |
|
79 * @param socket |
|
80 * the local receiver SipdroidSocket |
|
81 */ |
|
82 public RtpStreamReceiver(SipdroidSocket socket, int payload_type) { |
|
83 init(socket); |
|
84 p_type = payload_type; |
|
85 } |
|
86 |
|
87 /** Inits the RtpStreamReceiver */ |
|
88 private void init(SipdroidSocket socket) { |
|
89 if (socket != null) |
|
90 rtp_socket = new RtpSocket(socket); |
|
91 } |
|
92 |
|
93 /** Whether is running */ |
|
94 public boolean isRunning() { |
|
95 return running; |
|
96 } |
|
97 |
|
98 /** Stops running */ |
|
99 public void halt() { |
|
100 running = false; |
|
101 } |
|
102 |
|
103 public int speaker(int mode) { |
|
104 int old = speakermode; |
|
105 |
|
106 if (Call.headset > 0 && mode == AudioManager.MODE_NORMAL) |
|
107 return old; |
|
108 saveVolume(); |
|
109 setMode(speakermode = mode); |
|
110 restoreVolume(); |
|
111 return old; |
|
112 } |
|
113 |
|
114 double smin = 200,s; |
|
115 public static int nearend; |
|
116 |
|
117 void calc(short[] lin,int off,int len) { |
|
118 int i,j; |
|
119 double sm = 30000,r; |
|
120 |
|
121 for (i = 0; i < len; i += 5) { |
|
122 j = lin[i+off]; |
|
123 s = 0.03*Math.abs(j) + 0.97*s; |
|
124 if (s < sm) sm = s; |
|
125 if (s > smin) nearend = 3000/5; |
|
126 else if (nearend > 0) nearend--; |
|
127 } |
|
128 for (i = 0; i < len; i++) { |
|
129 j = lin[i+off]; |
|
130 if (j > 6550) |
|
131 lin[i+off] = 6550*5; |
|
132 else if (j < -6550) |
|
133 lin[i+off] = -6550*5; |
|
134 else |
|
135 lin[i+off] = (short)(j*5); |
|
136 } |
|
137 r = (double)len/100000; |
|
138 smin = sm*r + smin*(1-r); |
|
139 } |
|
140 |
|
141 static void setStreamVolume(final int stream,final int vol,final int flags) { |
|
142 (new Thread() { |
|
143 public void run() { |
|
144 AudioManager am = (AudioManager) Call.mContext.getSystemService(Context.AUDIO_SERVICE); |
|
145 am.setStreamVolume(stream, vol, flags); |
|
146 if (stream == AudioManager.STREAM_MUSIC) restored = true; |
|
147 } |
|
148 }).start(); |
|
149 } |
|
150 |
|
151 static boolean restored; |
|
152 |
|
153 public static float getEarGain() { |
|
154 try { |
|
155 return Float.valueOf(PreferenceManager.getDefaultSharedPreferences(Call.mContext).getString(Call.headset > 0?"heargain":"eargain", "0.25")); |
|
156 } catch (NumberFormatException i) { |
|
157 return (float)0.25; |
|
158 } |
|
159 } |
|
160 |
|
161 void restoreVolume() { |
|
162 switch (am.getMode()) { |
|
163 case AudioManager.MODE_IN_CALL: |
|
164 setStreamVolume(AudioManager.STREAM_RING,(int)( |
|
165 am.getStreamMaxVolume(AudioManager.STREAM_RING)* |
|
166 getEarGain()), 0); |
|
167 track.setStereoVolume(AudioTrack.getMaxVolume()* |
|
168 getEarGain() |
|
169 ,AudioTrack.getMaxVolume()* |
|
170 getEarGain()); |
|
171 break; |
|
172 case AudioManager.MODE_NORMAL: |
|
173 track.setStereoVolume(AudioTrack.getMaxVolume(),AudioTrack.getMaxVolume()); |
|
174 break; |
|
175 } |
|
176 setStreamVolume(AudioManager.STREAM_MUSIC, |
|
177 PreferenceManager.getDefaultSharedPreferences(Call.mContext).getInt("volume"+speakermode, |
|
178 am.getStreamMaxVolume(AudioManager.STREAM_MUSIC)* |
|
179 (speakermode == AudioManager.MODE_NORMAL?4:3)/4 |
|
180 ),0); |
|
181 } |
|
182 |
|
183 void saveVolume() { |
|
184 if (restored) { |
|
185 Editor edit = PreferenceManager.getDefaultSharedPreferences(Call.mContext).edit(); |
|
186 edit.putInt("volume"+speakermode,am.getStreamVolume(AudioManager.STREAM_MUSIC)); |
|
187 edit.commit(); |
|
188 } |
|
189 } |
|
190 |
|
191 void saveSettings() { |
|
192 if (!PreferenceManager.getDefaultSharedPreferences(Call.mContext).getBoolean("oldvalid",false)) { |
|
193 int oldvibrate = am.getVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER); |
|
194 int oldvibrate2 = am.getVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION); |
|
195 if (!PreferenceManager.getDefaultSharedPreferences(Call.mContext).contains("oldvibrate2")) |
|
196 oldvibrate2 = AudioManager.VIBRATE_SETTING_ON; |
|
197 int oldpolicy = android.provider.Settings.System.getInt(cr, android.provider.Settings.System.WIFI_SLEEP_POLICY, |
|
198 Settings.System.WIFI_SLEEP_POLICY_DEFAULT); |
|
199 Editor edit = PreferenceManager.getDefaultSharedPreferences(Call.mContext).edit(); |
|
200 edit.putInt("oldvibrate", oldvibrate); |
|
201 edit.putInt("oldvibrate2", oldvibrate2); |
|
202 edit.putInt("oldpolicy", oldpolicy); |
|
203 edit.putInt("oldring",am.getStreamVolume(AudioManager.STREAM_RING)); |
|
204 edit.putBoolean("oldvalid", true); |
|
205 edit.commit(); |
|
206 } |
|
207 } |
|
208 |
|
209 public static void setMode(int mode) { |
|
210 Editor edit = PreferenceManager.getDefaultSharedPreferences(Call.mContext).edit(); |
|
211 edit.putBoolean("setmode", mode != AudioManager.MODE_NORMAL); |
|
212 edit.commit(); |
|
213 AudioManager am = (AudioManager) Call.mContext.getSystemService(Context.AUDIO_SERVICE); |
|
214 am.setMode(mode); |
|
215 } |
|
216 |
|
217 public static void restoreMode() { |
|
218 if (PreferenceManager.getDefaultSharedPreferences(Call.mContext).getBoolean("setmode",true)) { |
|
219 setMode(AudioManager.MODE_NORMAL); |
|
220 } |
|
221 } |
|
222 |
|
223 public static void restoreSettings() { |
|
224 if (PreferenceManager.getDefaultSharedPreferences(Call.mContext).getBoolean("oldvalid",true)) { |
|
225 AudioManager am = (AudioManager) Call.mContext.getSystemService(Context.AUDIO_SERVICE); |
|
226 ContentResolver cr = Call.mContext.getContentResolver(); |
|
227 int oldvibrate = PreferenceManager.getDefaultSharedPreferences(Call.mContext).getInt("oldvibrate",0); |
|
228 int oldvibrate2 = PreferenceManager.getDefaultSharedPreferences(Call.mContext).getInt("oldvibrate2",0); |
|
229 int oldpolicy = PreferenceManager.getDefaultSharedPreferences(Call.mContext).getInt("oldpolicy",0); |
|
230 am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,oldvibrate); |
|
231 am.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION,oldvibrate2); |
|
232 Settings.System.putInt(cr, Settings.System.WIFI_SLEEP_POLICY, oldpolicy); |
|
233 setStreamVolume(AudioManager.STREAM_RING, PreferenceManager.getDefaultSharedPreferences(Call.mContext).getInt("oldring",0), 0); |
|
234 Editor edit = PreferenceManager.getDefaultSharedPreferences(Call.mContext).edit(); |
|
235 edit.putBoolean("oldvalid", false); |
|
236 edit.commit(); |
|
237 PowerManager pm = (PowerManager) Call.mContext.getSystemService(Context.POWER_SERVICE); |
|
238 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | |
|
239 PowerManager.ACQUIRE_CAUSES_WAKEUP, "Sipdroid.RtpStreamReceiver"); |
|
240 wl.acquire(1000); |
|
241 } |
|
242 restoreMode(); |
|
243 } |
|
244 |
|
245 public static float good, late, lost, loss; |
|
246 public static int timeout; |
|
247 |
|
248 void empty() { |
|
249 try { |
|
250 rtp_socket.getDatagramSocket().setSoTimeout(1); |
|
251 for (;;) |
|
252 rtp_socket.receive(rtp_packet); |
|
253 } catch (SocketException e2) { |
|
254 e2.printStackTrace(); |
|
255 } catch (IOException e) { |
|
256 } |
|
257 try { |
|
258 rtp_socket.getDatagramSocket().setSoTimeout(1000); |
|
259 } catch (SocketException e2) { |
|
260 e2.printStackTrace(); |
|
261 } |
|
262 } |
|
263 |
|
264 RtpPacket rtp_packet; |
|
265 AudioTrack track; |
|
266 |
|
267 /** Runs it in a new Thread. */ |
|
268 public void run() { |
|
269 boolean nodata = PreferenceManager.getDefaultSharedPreferences(Call.mContext).getBoolean("nodata",false); |
|
270 |
|
271 if (rtp_socket == null) { |
|
272 if (DEBUG) |
|
273 println("ERROR: RTP socket is null"); |
|
274 return; |
|
275 } |
|
276 |
|
277 byte[] buffer = new byte[BUFFER_SIZE+12]; |
|
278 byte[] buffer_gsm = new byte[33+12]; |
|
279 int i; |
|
280 rtp_packet = new RtpPacket(buffer, 0); |
|
281 |
|
282 if (DEBUG) |
|
283 println("Reading blocks of max " + buffer.length + " bytes"); |
|
284 |
|
285 running = true; |
|
286 speakermode = Call.docked > 0?AudioManager.MODE_NORMAL:AudioManager.MODE_IN_CALL; |
|
287 restored = false; |
|
288 |
|
289 android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO); |
|
290 am = (AudioManager) Call.mContext.getSystemService(Context.AUDIO_SERVICE); |
|
291 cr = Call.mContext.getContentResolver(); |
|
292 saveSettings(); |
|
293 Settings.System.putInt(cr, Settings.System.WIFI_SLEEP_POLICY,Settings.System.WIFI_SLEEP_POLICY_NEVER); |
|
294 am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,AudioManager.VIBRATE_SETTING_OFF); |
|
295 am.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION,AudioManager.VIBRATE_SETTING_OFF); |
|
296 int oldvol = am.getStreamVolume(AudioManager.STREAM_MUSIC); |
|
297 track = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, |
|
298 BUFFER_SIZE*2*2, AudioTrack.MODE_STREAM); |
|
299 short lin[] = new short[BUFFER_SIZE]; |
|
300 short lin2[] = new short[BUFFER_SIZE]; |
|
301 int user, server, lserver, luser, cnt, todo, headroom, len = 0, seq = 0, cnt2 = 0, m = 1, |
|
302 expseq, getseq, vm = 1, gap, gseq; |
|
303 timeout = 1; |
|
304 boolean islate; |
|
305 user = 0; |
|
306 lserver = 0; |
|
307 luser = -8000; |
|
308 cnt = 0; |
|
309 switch (p_type) { |
|
310 case 3: |
|
311 Codec.init(); |
|
312 break; |
|
313 case 0: |
|
314 case 8: |
|
315 G711.init(); |
|
316 break; |
|
317 } |
|
318 ToneGenerator tg = new ToneGenerator(AudioManager.STREAM_MUSIC,(int)(ToneGenerator.MAX_VOLUME*2*0.95)); |
|
319 track.play(); |
|
320 if (Call.headset > 0 && Call.oRingtone != null) { |
|
321 ToneGenerator tg2 = new ToneGenerator(AudioManager.STREAM_RING,(int)(ToneGenerator.MAX_VOLUME*2*0.95)); |
|
322 tg2.startTone(ToneGenerator.TONE_SUP_RINGTONE); |
|
323 System.gc(); |
|
324 tg2.stopTone(); |
|
325 } else |
|
326 System.gc(); |
|
327 while (running) { |
|
328 if (Call.call_state == Call.UA_STATE_HOLD) { |
|
329 tg.stopTone(); |
|
330 track.pause(); |
|
331 while (running && Call.call_state == Call.UA_STATE_HOLD) { |
|
332 try { |
|
333 sleep(1000); |
|
334 } catch (InterruptedException e1) { |
|
335 e1.printStackTrace(); |
|
336 } |
|
337 } |
|
338 track.play(); |
|
339 System.gc(); |
|
340 timeout = 1; |
|
341 seq = 0; |
|
342 } |
|
343 try { |
|
344 rtp_socket.receive(rtp_packet); |
|
345 if (timeout != 0) { |
|
346 tg.stopTone(); |
|
347 track.pause(); |
|
348 user += track.write(lin2,0,BUFFER_SIZE); |
|
349 user += track.write(lin2,0,BUFFER_SIZE); |
|
350 track.play(); |
|
351 cnt += 2*BUFFER_SIZE; |
|
352 empty(); |
|
353 } |
|
354 timeout = 0; |
|
355 } catch (IOException e) { |
|
356 if (timeout == 0 && nodata) { |
|
357 tg.startTone(ToneGenerator.TONE_SUP_RINGTONE); |
|
358 } |
|
359 rtp_socket.getDatagramSocket().disconnect(); |
|
360 if (++timeout > 22) { |
|
361 try { |
|
362 mJingle.closeCall(); |
|
363 } catch (RemoteException e1) { |
|
364 e1.printStackTrace(); |
|
365 } |
|
366 break; |
|
367 } |
|
368 } |
|
369 if (running && timeout == 0) { |
|
370 gseq = rtp_packet.getSequenceNumber(); |
|
371 if (seq == gseq) { |
|
372 m++; |
|
373 continue; |
|
374 } |
|
375 |
|
376 server = track.getPlaybackHeadPosition(); |
|
377 headroom = user-server; |
|
378 |
|
379 if (headroom > 1500) |
|
380 cnt += len; |
|
381 else |
|
382 cnt = 0; |
|
383 |
|
384 if (lserver == server) |
|
385 cnt2++; |
|
386 else |
|
387 cnt2 = 0; |
|
388 |
|
389 if (cnt <= 500 || cnt2 >= 2 || headroom - 875 < len) { |
|
390 switch (rtp_packet.getPayloadType()) { |
|
391 case 0: |
|
392 len = rtp_packet.getPayloadLength(); |
|
393 G711.ulaw2linear(buffer, lin, len); |
|
394 break; |
|
395 case 8: |
|
396 len = rtp_packet.getPayloadLength(); |
|
397 G711.alaw2linear(buffer, lin, len); |
|
398 break; |
|
399 case 3: |
|
400 for (i = 12; i < 45; i++) |
|
401 buffer_gsm[i] = buffer[i]; |
|
402 len = Codec.decode(buffer_gsm, lin, 0); |
|
403 break; |
|
404 } |
|
405 |
|
406 if (speakermode == AudioManager.MODE_NORMAL) |
|
407 calc(lin,0,len); |
|
408 } |
|
409 |
|
410 if (headroom < 250) { |
|
411 todo = 875 - headroom; |
|
412 println("insert "+todo); |
|
413 islate = true; |
|
414 user += track.write(lin2,0,todo); |
|
415 } else |
|
416 islate = false; |
|
417 |
|
418 if (cnt > 500 && cnt2 < 2) { |
|
419 todo = headroom - 875; |
|
420 println("cut "+todo); |
|
421 if (todo < len) |
|
422 user += track.write(lin,todo,len-todo); |
|
423 } else |
|
424 user += track.write(lin,0,len); |
|
425 |
|
426 seq = gseq; |
|
427 |
|
428 if (user >= luser + 8000 && Call.call_state == Call.UA_STATE_INCALL) { |
|
429 if (luser == -8000 || am.getMode() != speakermode) { |
|
430 saveVolume(); |
|
431 setMode(speakermode); |
|
432 restoreVolume(); |
|
433 } |
|
434 luser = user; |
|
435 } |
|
436 lserver = server; |
|
437 } |
|
438 } |
|
439 track.stop(); |
|
440 saveVolume(); |
|
441 setStreamVolume(AudioManager.STREAM_MUSIC,oldvol,0); |
|
442 restoreSettings(); |
|
443 setStreamVolume(AudioManager.STREAM_MUSIC,oldvol,0); |
|
444 tg.stopTone(); |
|
445 tg = new ToneGenerator(AudioManager.STREAM_RING,ToneGenerator.MAX_VOLUME/4*3); |
|
446 tg.startTone(ToneGenerator.TONE_PROP_PROMPT); |
|
447 try { |
|
448 sleep(500); |
|
449 } catch (InterruptedException e) { |
|
450 e.printStackTrace(); |
|
451 } |
|
452 tg.stopTone(); |
|
453 |
|
454 rtp_socket.close(); |
|
455 rtp_socket = null; |
|
456 |
|
457 if (DEBUG) |
|
458 println("rtp receiver terminated"); |
|
459 } |
|
460 |
|
461 /** Debug output */ |
|
462 private static void println(String str) { |
|
463 System.out.println("RtpStreamReceiver: " + str); |
|
464 } |
|
465 |
|
466 public static int byte2int(byte b) { // return (b>=0)? b : -((b^0xFF)+1); |
|
467 // return (b>=0)? b : b+0x100; |
|
468 return (b + 0x100) % 0x100; |
|
469 } |
|
470 |
|
471 public static int byte2int(byte b1, byte b2) { |
|
472 return (((b1 + 0x100) % 0x100) << 8) + (b2 + 0x100) % 0x100; |
|
473 } |
|
474 } |