/*
 * Copyright (C) 2012 AVM GmbH <info@avm.de>
 * Copyright (C) 2009 The Sipdroid Open Source Project
 * Copyright (C) 2005 Luca Veltri - University of Parma - Italy
 * 
 * This file is part of Sipdroid (http://www.sipdroid.org)
 * 
 * Sipdroid is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This source code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this source code; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.sipdroid.media;

import java.io.IOException;
import java.net.SocketException;

import org.sipdroid.net.RtpPacket;
import org.sipdroid.net.RtpSocket;
import org.sipdroid.net.SipdroidSocket;
import org.sipdroid.codecs.Codecs;

import android.content.Context;
import android.content.SharedPreferences.Editor;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.ToneGenerator;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.KeyEvent;
import de.avm.android.fritzapp.sipua.UserAgent;
import de.avm.android.fritzapp.sipua.ui.Receiver;
import de.avm.android.fritzapp.sipua.ui.Sipdroid;
import de.avm.android.fritzapp.util.BuildHelper;

/**
 * RtpStreamReceiver is a generic stream receiver. It receives packets from RTP
 * and writes them into an OutputStream.
 */
public class RtpStreamReceiver extends Thread
{
	private static final String TAG = "RtpStreamReceiver";

	/** Whether working in debug mode. */
	public static boolean DEBUG = true;

	/** Payload type */
	Codecs.Map p_type;

	static String codec = "";

	/** Size of the read buffer */
	public static final int BUFFER_SIZE_MULTI = 3; // 2*2*3
	public static final int BUFFER_SIZE = 1323;//1024;
	public static final int BUFFER_SIZE_OUT = BUFFER_SIZE *3 /* *2*/;
	public static final int UPSAMPLE_BUFFER_SIZE = BUFFER_SIZE*3 /* *2*/; // 1024 *6;
	public static final int DEFAULT_AUDIO_BUFFER_SIZE = 32768;

	static final int BUFFER_LIMIT_500 = 2646;//2756; // (500*44100Hz)/8000Hz
	static final int BUFFER_LIMIT_250 = 1323;//1378; // (250*44100Hz)/8000Hz

	
	/** Maximum blocking time, spent waiting for reading new bytes [milliseconds] */
	public static final int SO_TIMEOUT = 1000;

	static final long SILENCE_THRESHOLD = 3000 ;// 20000;  // 5000
	
	/** The RtpSocket */
	RtpSocket rtp_socket = null;

	public enum AudioEngineState {STATE_NOTHING, STATE_INITIALIZED, STATE_UNINITIALIZED};
	private AudioEngineState audioEngineState;
	
	/** Whether it is running */
	boolean restartAudioTrack = false;
	boolean running;
	AudioManager am;
	public static int speakermode = -1;
	int getMinBufferSizeValue = 0;
	static int MicDown = 100;
	
	
	/** Samsung Galaxy 3 (I5800) needs some preparation to ensure working audio recording and playing.
	 *  Let's open a new AudioTrack, play some audio data, and close the AudioTrack afterwards. */
	private void AudioTrackPreparation() {
		if(BuildHelper.needsAudioTrackPreparation()) {
			final int sampleRate = 8000;
			short val=0, a=1, buf[] = new short[sampleRate];
			int minSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
			for(int i=0; i<sampleRate; i++) {
				val+=a*10;
				if(val > 32700) a = -1;
				else if(val < -32700) a = 1;
			}
			AudioTrack t = new AudioTrack(stream(), sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minSize, AudioTrack.MODE_STREAM);
			t.play();
			t.write(buf,0,sampleRate);
			try { t.stop(); }
			catch (IllegalStateException e) { /* ignore */ }
			t.release();
			t = null;
		}
	}
	
	/**
	 * Constructs a RtpStreamReceiver.
	 * 
	 * @param output_stream
	 *            the stream sink
	 * @param socket
	 *            the local receiver SipdroidSocket
	 */
	public RtpStreamReceiver(SipdroidSocket socket, Codecs.Map payload_type) {
		init(socket);
		p_type = payload_type;
		audioEngineState = AudioEngineState.STATE_NOTHING;
		setCodecTypeDisplay((p_type == null) ? -1 : p_type.getNumber());
		AudioTrackPreparation();
	}

	/** Inits the RtpStreamReceiver */
	private void init(SipdroidSocket socket) {
		speakermode = -1;
		totalsampleswritten = 0;
		if (socket != null)
			rtp_socket = new RtpSocket(socket);
	}

	/** Whether is running */
	public boolean isRunning() {
		return running;
	}

	/** Stops running */
	public void halt() {
		running = false;
	}
	
	public int speaker(int mode) {
		int old = speakermode;
		
		if ((Receiver.headset > 0 || Receiver.docked > 0) &&
				mode != Receiver.speakermode())
			return old;

		setMode(speakermode = mode, false);

		if(BuildHelper.needsMuteHackForSpeakermodeChange())
			RtpStreamSender.mutecounter = 0;
		
		return old;
	}

	static int stream() {
		if (Receiver.headset > 0)
			return speakermode == AudioManager.MODE_IN_CALL ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_MUSIC;
		return AudioManager.STREAM_VOICE_CALL; // this results in same volume settings (speaker/in-ear-speaker) [1]
	}
	
	public static int nearend;
	long s=0;
	double smin=200;
	
	void calc(short[] lin,int len) {
		int i,j;
		double r;
		long sm=30000;
		int mu = upsampling_from_8kHz ? 1 : 2;
		for (i = 0; i < len; i += 5) { // nearend is important for noise generation (hands-free talking mode in RtpStreamSender)
			j = lin[i];
			s = (31*Math.abs(j) + 993*s) >> 10; // 0.03*Math.abs(j) + 0.97*s
			if (s < sm) sm = s;
			if (s > smin)
				nearend = 2400 >> (3-mu); // was 4800 before (instead of 2400)
			else if (nearend > 0)
				nearend--;
		}
		r = (double)(len)/(100000*mu);
		smin = (sm*r + smin*(1-r));
	}

	private static void manualVolume() {
		AudioManager am = (AudioManager) Receiver.mContext
				.getSystemService(Context.AUDIO_SERVICE);
		int max = am.getStreamMaxVolume(stream());
		int volume = am.getStreamVolume(stream());
		float volumeFloat = (float)0.2 / (float)java.lang.Math.pow(2, (double)max - (double)volume);
		// float volumeFloat = (float) java.lang.Math.pow((double) volume / (double) max, 3) / 5;
		synchronized(trackSync)
		{
			if (track != null) track.setStereoVolume(volumeFloat, volumeFloat);
		}
	}

	public static void adjust(int keyCode) {
        AudioManager mAudioManager = (AudioManager) Receiver.mContext.getSystemService(
                    Context.AUDIO_SERVICE);
        mAudioManager.adjustStreamVolume(stream(),
                    keyCode == KeyEvent.KEYCODE_VOLUME_UP
                            ? AudioManager.ADJUST_RAISE
                            : AudioManager.ADJUST_LOWER,
                    AudioManager.FLAG_SHOW_UI);
        if(BuildHelper.isSamsungGalaxyS())
            manualVolume();
	}
	
	public void setMode(int mode, boolean speakerOff)
	{
		if (am == null)
			am = (AudioManager)Receiver.mContext.getSystemService(Context.AUDIO_SERVICE);
		
		Editor edit = PreferenceManager.getDefaultSharedPreferences(
				Receiver.mContext).edit();
		edit.putBoolean(Sipdroid.PREF_SETMODE, true);
		edit.commit();

		if (speakerOff) {
			if(am.isSpeakerphoneOn()) {
				am.setSpeakerphoneOn(false);
			}
		} else {
			if(am.isSpeakerphoneOn() != (mode == AudioManager.MODE_NORMAL)) {
				am.setSpeakerphoneOn(mode == AudioManager.MODE_NORMAL);
			}
		}
		Receiver.onSpeakerChanged();
	}

	public static int getMode()
	{
		AudioManager am = (AudioManager)Receiver.mContext
				.getSystemService(Context.AUDIO_SERVICE);
		return am.isSpeakerphoneOn() ? AudioManager.MODE_NORMAL : AudioManager.MODE_IN_CALL;
	}

	public static float good, late, lost, loss;
	double avgheadroom;
	int smoothedHeadroom;
	public static int timeout;
	int seq;
	boolean upsampling_from_8kHz = false;
	
	void empty() {
		try {
			rtp_socket.getDatagramSocket().setSoTimeout(1);
			for (;;)
				rtp_socket.receive(rtp_packet);
		} catch (SocketException e2) {
			if (!Sipdroid.release) e2.printStackTrace();
		} catch (IOException e) {
		}
		try {
			rtp_socket.getDatagramSocket().setSoTimeout(SO_TIMEOUT);
		} catch (SocketException e2) {
			if (!Sipdroid.release) e2.printStackTrace();
		}
		seq = 0;
	}
	
	public synchronized AudioEngineState audioEngineInitialized() {
		return audioEngineState;
	}
	
	private synchronized void changeAudioEngineState(AudioEngineState state) {
		audioEngineState = state;
	}
	
	RtpPacket rtp_packet;
	static AudioTrack track = null;
	static Object trackSync = new Object();
	int maxjitter,minjitter,minjitteradjust,minheadroom;
	int loopsWithSamePlaybackPos,totalsampleswritten,luser,luser2,lastplaybackpos;
	final int internal_samplerate = 44100;
	public enum CodecType {CODECTYPE_UNKNOWN, CODECTYPE_SD, CODECTYPE_HD};
	// ADO: reflect codec type for display purposes
	private static CodecType mCodecTypeDisplay = CodecType.CODECTYPE_UNKNOWN;

	public static CodecType getCodecTypeDisplay()
	{
		return mCodecTypeDisplay;
	}
	
	private void setCodecTypeDisplay(int codecnum)
	{
		switch (codecnum) 
		{
			case 9: 
				mCodecTypeDisplay = CodecType.CODECTYPE_HD;
				break;
			case 0: 
			case 8:
				mCodecTypeDisplay = CodecType.CODECTYPE_SD;
				break;
			default:
				mCodecTypeDisplay = CodecType.CODECTYPE_UNKNOWN;
				break;
		}
	}
	
	void setCodec() {
		p_type.init();
		codec = p_type.codec.getTitle();
		maxjitter = AudioTrack.getMinBufferSize(internal_samplerate,
				AudioFormat.CHANNEL_CONFIGURATION_MONO,
				AudioFormat.ENCODING_PCM_16BIT);

		getMinBufferSizeValue = maxjitter;
		if(maxjitter < DEFAULT_AUDIO_BUFFER_SIZE) {
			maxjitter = DEFAULT_AUDIO_BUFFER_SIZE;
		}

//		android.util.Log.e(TAG,"getMinBufferSize  :"+(getMinBufferSizeValue)+ " maxjitter:" + maxjitter + "(used)");

		synchronized(trackSync)
		{
			if (track != null)
			{
				try { track.stop(); }
				catch (IllegalStateException e) { /* ignore */ }
				track.release();
				track = null;
			}
			track  = new AudioTrack(stream(), internal_samplerate,
					AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
					maxjitter, AudioTrack.MODE_STREAM);
		}
		maxjitter /= 2;
		minjitter = minjitteradjust = BUFFER_LIMIT_500;
		minheadroom = maxjitter*2;
		timeout = 1;
		luser = luser2 = -internal_samplerate;
		loopsWithSamePlaybackPos = totalsampleswritten = lastplaybackpos = 0;
		
		int codecnum = p_type.getNumber();
		upsampling_from_8kHz = (codecnum == 8 || codecnum == 0);
		resampler = new Resampler(2, upsampling_from_8kHz ? 8000: 16000, internal_samplerate, 1024);

		setCodecTypeDisplay(codecnum);
	}
	
	private Resampler resampler = null;
	
	void write(short buf[], int start, int len) {
		totalsampleswritten += track.write(buf, start, len);
	}

	synchronized void setRestartAudioTrack() {
		restartAudioTrack = true;
	}

	synchronized boolean restartAudioTrack() {
		boolean ret = restartAudioTrack;
		restartAudioTrack = false;
		return ret;
	}
	
	
	/** Runs it in a new Thread. */
	public void run() {
		boolean nodata = PreferenceManager.getDefaultSharedPreferences(Receiver.mContext).getBoolean(Sipdroid.PREF_NODATA, Sipdroid.DEFAULT_NODATA);
		
		if (rtp_socket == null) {
			if (DEBUG)
				println("ERROR: RTP socket is null");
			return;
		}

		byte[] buffer = new byte[BUFFER_SIZE+12];
		rtp_packet = new RtpPacket(buffer, 0);

		if (DEBUG)
			println("Reading blocks of max " + buffer.length + " bytes");

		running = true;
		speakermode = Receiver.speakermode();

		android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
		am = (AudioManager) Receiver.mContext.getSystemService(Context.AUDIO_SERVICE);

		if (am.isSpeakerphoneOn())
		{
			Log.i(TAG, "setting speakerphone off (because am.isSpeakerphoneOn() says it's switched on)");
			am.setSpeakerphoneOn(false);
			Receiver.onSpeakerChanged();
		}

		setCodec();
		synchronized(trackSync)
		{
			if ((track == null) || track.getState() == AudioTrack.STATE_UNINITIALIZED)
			{
				if (track != null)
					Log.e(TAG, "Error initializing audio playback engine.");
				changeAudioEngineState(AudioEngineState.STATE_UNINITIALIZED);
				running = false;
				track = null;
				return;
			}
			else
				changeAudioEngineState(AudioEngineState.STATE_INITIALIZED);
			track.setStereoVolume(AudioTrack.getMaxVolume()*
					Sipdroid.getEarGain()
					,AudioTrack.getMaxVolume()*
					Sipdroid.getEarGain());
		}
		short lin[] = new short[BUFFER_SIZE];
		short silence_buf[] = new short[BUFFER_SIZE/*BUFFER_SIZE_OUT*/]; // silence buffer
		short[] upsample_buf = new short[UPSAMPLE_BUFFER_SIZE];
		int playbackpos, headroom, len = 0, m = 1, expseq, getseq, vm = 1, gap, gseq;
		
		if(BuildHelper.needsSetMode()) {
			am.setMode(AudioManager.MODE_IN_CALL);
		}
		
		if(BuildHelper.needsManualVolume())
			manualVolume();
		
		timeout = 1;
		luser = luser2 = -internal_samplerate;
		loopsWithSamePlaybackPos = totalsampleswritten = lastplaybackpos = 0;
		minheadroom = maxjitter*2;
		ToneGenerator tg = null;
		try
		{
			tg = new ToneGenerator(AudioManager.STREAM_VOICE_CALL,(int)(ToneGenerator.MAX_VOLUME*2*Sipdroid.getEarGain()));
		}
		catch(Exception e)
		{
			Log.e(TAG, "Error initializing ToneGenerator.", e);
		}
		synchronized(trackSync) { track.play(); }
		System.gc();  // Garbage Collection aufrufen, damit wir nach Moeglichkeit waehrend des Gespraechs Ruhe haben 
		smoothedHeadroom = maxjitter * 2;
		int ts_step1=0, ts_step2=0;
		long Time1 = 0;
		long Time2 = 0;
		long TimeDiff = 0;
		int samplesToDrop;
		int lenwrite = 0;
		int minheadsize = BUFFER_SIZE *2 ;  /*+BUFFER_SIZE/2*/
		Time2 = Time1 = System.currentTimeMillis();
		int SilenceDropTime = 1 * 44100; // 1 Sekunde
		int HardDropTime    = 2 * 44100; // 2 Sekunden
		Boolean isSilence = true;  // default ein, falls ggf. VAD ausgeschalet wurde
		int VADThreshold = (int)SILENCE_THRESHOLD;
		// Werte ggf. vom Benutzer veraendert (Debug-Menue) 
		Boolean UseSilenceDetection = Sipdroid.isVAD();
		if (Sipdroid.getVADThreshold() != 0)
			VADThreshold = Sipdroid.getVADThreshold();
		if (Sipdroid.getHeadroom() != 0)
			minheadsize = Sipdroid.getHeadroom();
		
		
		int silence_ret = 0;
		int len_dropsample = 0;
		double energy = 0; 
		int stat_blocks = 0;
		int stat_block_underruns = 0;
		int stat_underruns = 0;
		long stat_max_blocktime = 0;
		long TimeStart = System.currentTimeMillis();
		int stat_silencedrop = 0;
		int stat_sampledrop = 0;
		int stat_harddrop = 0;
		int stat_receive_packets = 0;

		//Log.e("RtpStreamReveiver", "vor der while-Schleife: VADThreshold " + VADThreshold + " minheadsize " + minheadsize + " UseSilenceDetection " + UseSilenceDetection);

		while (running) {
			if(restartAudioTrack()) {
				setCodec();
			}

			// Verbindung halten
			if (Receiver.call_state == UserAgent.UA_STATE_HOLD) {
				if (tg != null) tg.stopTone();
				synchronized(trackSync) {track.pause(); }
				// bei Verbindung halten, solange hier in der Schleife verbleiben
				while (running && Receiver.call_state == UserAgent.UA_STATE_HOLD) {
					try {
						sleep(1000);
					} catch (InterruptedException e1) {
					}
				}
				synchronized(trackSync) { track.play(); }
				System.gc();
				timeout = 1;
				luser = luser2 = -internal_samplerate;
			}
			try {
				rtp_socket.receive(rtp_packet);
				if (timeout != 0) {
					//Log.e("RtpStreamReveiver", "**** TIMEOUT ****" );
				
					if (tg != null) tg.stopTone();
					synchronized(trackSync) { track.pause(); }
					// gesamten Ausgabebuffer mit Stille fuellen, z.B. am Anfang
					for (int i = maxjitter*2; i > 0; i -= BUFFER_SIZE) {
						write(silence_buf,0,i>BUFFER_SIZE?BUFFER_SIZE:i);
						//write(silence_buf,0,i>BUFFER_SIZE_OUT?BUFFER_SIZE_OUT:i);
						//Log.e("RtpStreamReveiver", "write silcene (a)" + (i>BUFFER_SIZE?BUFFER_SIZE:i));
					}
					synchronized(trackSync) { track.play(); }
					empty();   // Angestaute Paket verwerfen
				}
				timeout = 0;
			} catch (IOException e) {
				if (timeout == 0 && nodata) {
					if (tg != null) tg.startTone(ToneGenerator.TONE_SUP_RINGTONE);
				}
				rtp_socket.getDatagramSocket().disconnect();
				if (++timeout > 22) {
					// zu oft Fehler, Abbruch
					Receiver.engine(Receiver.mContext).rejectcall();
					break;
				}
			}
			if (running && timeout == 0) {
				 gseq = rtp_packet.getSequenceNumber();
				 if (seq == gseq) {
					 m++;
					 //Log.e("RtpStreamReveiver", "doppeltes Packet => continue" + gseq);
					 continue;
				 }
				 stat_receive_packets ++;
				 playbackpos = track.getPlaybackHeadPosition();
				 headroom = totalsampleswritten-playbackpos;
				 
				if (lastplaybackpos == playbackpos)  // nix abgespielt seid dem letzten vorbeikommen hier ?
					loopsWithSamePlaybackPos++;
				else
					loopsWithSamePlaybackPos = 0;

				int codecnum = p_type.getNumber();
				int new_codecnum = rtp_packet.getPayloadType();
				 
				if (new_codecnum >= 0 && new_codecnum != codecnum) {
					 if(p_type.change(new_codecnum)) {
						setCodecTypeDisplay(new_codecnum);
						upsampling_from_8kHz = (new_codecnum == 8 || new_codecnum == 0);
						resampler = new Resampler(2, upsampling_from_8kHz ? 8000 : 16000, internal_samplerate, 1024);
					} else {
						 Log.e("RtpStreamReveiver", "p_type.change() failed!!!");
					}
				}

				// Manchmal liefert die Box zu grosse RTP Payloads
				if (rtp_packet.getPayloadLength() > 240) {
					Log.e("RtpStreamReveiver", "payload=" + rtp_packet.getPayloadLength());
					continue;
				}
				len = p_type.codec.decode(buffer, lin, rtp_packet.getPayloadLength());
				if (speakermode == AudioManager.MODE_NORMAL)
					 calc(lin,len);

				len = resampler.doit(lin, len, 0, upsample_buf);

				Time1 = System.currentTimeMillis();
				TimeDiff = Time1 - Time2; 
				if (TimeDiff > 70) {
					//Log.e(TAG, "********  block for "+ TimeDiff +"ms ********");
					// ohh, da könnte ein Paketstau sein, drum gleich anfangen es abzubauen
					if (ts_step1 < SilenceDropTime)
						ts_step1 = SilenceDropTime;
					stat_blocks ++;  // Statistik erhöhen
					if (stat_max_blocktime < TimeDiff)
						stat_max_blocktime = TimeDiff;  // neuer Maxwert
					if (headroom == 0) {  // Block + underrun Statistik erhöhen
						stat_block_underruns ++;
					}
					// Anpassen des Headrooms   
					if (TimeDiff < 500) {
						minheadsize = (int)(minheadsize*15 + (TimeDiff * internal_samplerate/1000) )/16;
						//Log.e(TAG, "********  minheadsize automatisch auf " + minheadsize + " gesetzt") ;
					}
				} else {
					// < 70 ms also keine Blockierung
					if (headroom == 0) {
						// Apspielbuffer leer gelaufen, also etwas Stille einfuegen
						write(silence_buf, 0, BUFFER_SIZE/2);
						//Log.e(TAG, "underrun, headroom = 0   => write silence (BUFFER_SIZE/2)");
						stat_underruns ++;
					}
				}
				// nach oben langsam anpassen, nach unten sofort springen
				if(headroom < smoothedHeadroom) 
					smoothedHeadroom = headroom;
				else
					smoothedHeadroom = (smoothedHeadroom * 15 + headroom * 1) / 16;
				
				// minimum soll im Buffer drin sein
				if(smoothedHeadroom > minheadsize)
					samplesToDrop = smoothedHeadroom - minheadsize;
				else
					samplesToDrop = 0;

				if(samplesToDrop > 0) {
					// After 1 seconds start to drop.
					if(ts_step1 > SilenceDropTime) {

						// Try to remove silence from the buffer.
						isSilence = false;
						if (UseSilenceDetection) {
							if (samplesToDrop >= (internal_samplerate*5/1000)) { // mindestens 5ms zu verwerfen 
								silence_ret = resampler.silence(upsample_buf, len, VADThreshold, internal_samplerate /*upsampling_from_8kHz ? 8000 : 16000*/, samplesToDrop);
								isSilence = (silence_ret != len);
							} else {
								ts_step2 = 0;   // hartes Verwerfen zurueck setzen
							}
						}
						if (isSilence) {
	    					ts_step2 = 0;   // hartes Verwerfen zurueck setzen
	    					//ts_step1 = 0;  // test ob es dann ruhiger wird
	    					lenwrite = silence_ret; //(len-samplesToDrop >0) ? (len-samplesToDrop) : 0;
	    					smoothedHeadroom -= (len-silence_ret);//samplesToDrop;//len;
	    					if (lenwrite > 0) {
	    						//write unten
	    						//write(upsample_buf, 0, (len-samplesToDrop >0) ? (len-samplesToDrop) : 0 );
	    						//Log.e("RtpStreamReveiver", "********* Stille verwerfen, write " + (lenwrite) + "   samplesToDrop " + samplesToDrop + " len " +len);
	    						len = lenwrite;
	    					} else {
	    						len = 0;
	    					}
	    					stat_silencedrop++;
	    				}
				        // After 1 second without dropping a packet force dropping.
						else if(ts_step2 >= HardDropTime) {
					         if (/*false &&*/ samplesToDrop > BUFFER_SIZE) {
					        	 len = 0;
					        	 ts_step2 = 0; //zurücksetzen, also ggf. wieder 2 Sekunden warten bis zum nächsten h
					        	 //Log.e("RtpStreamReveiver", "**** verwerfen nach  3  sekunden  samplesToDrop " + samplesToDrop);
					        	 stat_harddrop++;
					         } else { 
					        	 len_dropsample = resampler.dropsample(upsample_buf, len, internal_samplerate);
					        	 //upsample_buf = upsample_buf_tmp;
					        	 len = len_dropsample;
					        	 //Log.e("RtpStreamReveiver", "**** dropsample nach  3  sekunden len "+len+"   samplesToDrop " + samplesToDrop);
					        	 stat_sampledrop++;
					         }
						}
					} else {
						// wir haben noch nicht lange genug zuviel 
						ts_step2 = 0;
					}
				} else {
					// alles gut, wir haben nicht zu viel
					ts_step1 = 0;
					ts_step2 = 0;
				}
				if (headroom + len > 2*maxjitter ) {
					// Oberkannte sofort drop
					smoothedHeadroom -= len;
					len = 0;
					//Log.e("RtpStreamReveiver", "**** Buffer Full, sofort verwerfen");
				}		

				if(len > 0) {
					write(upsample_buf, 0, len);
					ts_step1 += len;
					ts_step2 += len;

					// "Sprachwaage" beginn
					energy = resampler.energy(upsample_buf, len, 0);

					if (energy > 1000000 ) {
						MicDown = 50;
					} else {
						MicDown = 100;
					}
					//Log.i("RtpStreamReveiver", "**** durchschnitt energy per sample" + energy );
					// "Sprachwaage"ende
				}
				//Log.i("RtpStreamReveiver", "headroom " + headroom + " smoothedHeadroom " + smoothedHeadroom + " samplesToDrop " + samplesToDrop + " ts_step1 " + ts_step1 + " ts_step2 " + ts_step2 + " len " + len );//+ " " + + " " + + " " + ));				
				

				// statistic
				if (seq != 0) {
					getseq = gseq&0xff;
					expseq = ++seq&0xff;
					if (m == RtpStreamSender.m) vm = m;
					gap = (getseq - expseq) & 0xff;
					if (gap > 0) {
						if (gap > 100) gap = 1;
						loss += gap;
						lost += gap;
						good += gap - 1;
					} else {
						if (m < vm)
							loss++;
					}
					good++;
					if (good > 100) {  // Statistik ueber 100 Pakete - 99% von den alten Werten
						good *= 0.99;
						lost *= 0.99;
						loss *= 0.99;
						late *= 0.99;
					}
				}
				 
				m = 1;
				seq = gseq;
				 
				if (totalsampleswritten >= luser + internal_samplerate && (
						Receiver.call_state == UserAgent.UA_STATE_INCALL ||
						Receiver.call_state == UserAgent.UA_STATE_OUTGOING_CALL)) {
					
					if ((speakermode >= 0) && getMode() != speakermode) { // TODO: Docking-Mode testen
						setMode(speakermode, false);
					}					
					 
					luser = totalsampleswritten;
				}
				lastplaybackpos = playbackpos;
				
				Time2 = Time1; // aktuelle Zeit merken, damit wir eine Blockierung erkennen koennen
			
			} //if (running && timeout == 0) {
		} //while (running) 

//Log.i("RtpStreamReveiver", "Stat1: Zeit der Verbindung: " + (Time2 /*ende*/ -TimeStart)+ "ms  VolumeSpeaker:"+ GLOBAL.VolumeSpeaker + "  VolumeMicro:"+GLOBAL.VolumeMicro);
Log.i("RtpStreamReveiver", "Stat1: Zeit der Verbindung: " + (Time2 /*ende*/ -TimeStart)+ "ms " );
Log.i("RtpStreamReveiver", "Stat2: getMinBufferSizeValue:" + getMinBufferSizeValue+ " maxjitter:" + (maxjitter*2) + " minheadsize:" + minheadsize);
Log.i("RtpStreamReveiver", "Stat3: blocks(>70ms):" + stat_blocks + " max_blocktime:" + stat_max_blocktime + " block_underruns:" + stat_block_underruns +" underruns:" + stat_underruns );
Log.i("RtpStreamReveiver", "Stat4: silencedrop:" + stat_silencedrop + " sampledrop:" + stat_sampledrop + " harddrop:" + stat_harddrop );
Log.i("RtpStreamReveiver", "Stat5: receive_packets:"+ stat_receive_packets +" good:" + good + " lost:" + lost + " loss:" + loss + " late:" + late );

		Sipdroid.setHeadroom(minheadsize);

		synchronized(trackSync)
		{
			if (track != null)
			{
				try { track.stop(); }
				catch (IllegalStateException e) { /* ignore */ }
				track.release();
				track = null;
			}
		}
		if (tg != null)
		{
			tg.stopTone();
			tg.release();
		}
		p_type.codec.close();
		p_type.deinit();
		setCodecTypeDisplay(-1);

		rtp_socket.close();
		rtp_socket = null;
		codec = "";
		speakermode = -1;

		if(BuildHelper.needsSetMode()) {
			am.setMode(AudioManager.MODE_NORMAL);
		}
		
		setMode(0, true); // switch off ext. speaker

		if (DEBUG)
			println("rtp receiver terminated");
	}

	/** Debug output */
	private static void println(String str) {
		if (!Sipdroid.release) System.out.println("RtpStreamReceiver: " + str);
	}

	public static int byte2int(byte b) {
		return (b + 0x100) % 0x100;
	}

	public static int byte2int(byte b1, byte b2) {
		return (((b1 + 0x100) % 0x100) << 8) + (b2 + 0x100) % 0x100;
	}

	public static String getCodec() {
		return codec;
	}
}
