diff -r 537ddd8aa407 -r 2036ebfaccda src/org/sipdroid/media/RtpStreamSender.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/sipdroid/media/RtpStreamSender.java Fri Nov 20 19:29:42 2009 +0100 @@ -0,0 +1,220 @@ +/* + * 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.InputStream; +import java.net.DatagramPacket; +import java.util.Random; + +import jlibrtp.RTPSession; +import jlibrtp.RtpPkt; + +import org.sipdroid.media.codecs.Codec; + +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioRecord; +import android.media.MediaRecorder; + +/** + * 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; + + /** The RtpSocket */ + //private RtpSocket rtp_socket = null; + private RTPSession rtpSession = null; + + /** Codec */ + private Codec codec; + + private int sampling_rate; + + /** Number of bytes per frame */ + private int frame_size; + + private int codec_frame_size; + + /** + * Whether it works synchronously with a local clock, or it it acts as slave + * of the InputStream + */ + boolean do_sync = true; + + int sync_adj = 0; + + /** Whether it is running */ + boolean running = false; + boolean muted = false; + + private int codec_divider; + + /** + * Constructs a RtpStreamSender. + * + */ + public RtpStreamSender(Codec co, RTPSession rtpSession) { + init(co, rtpSession); + } + + /** 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; + } + + /** Sets the synchronization adjustment time (in milliseconds). */ + public void setSyncAdj(int millisecs) { + sync_adj = millisecs; + } + + /** Whether is running */ + public boolean isRunning() { + return running; + } + + public boolean mute() { + return muted = !muted; + } + + public static int delay = 0; + + /** Stops running */ + public void halt() { + running = false; + } + + 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; + if (s < sm) sm = s; + if (s > smin) nearend = 3000/5; + else if (nearend > 0) nearend--; + } + for (i = 0; i < len; i++) { + j = lin[i+off]; + if (j > 6550) + lin[i+off] = 6550*5; + else if (j < -6550) + lin[i+off] = -6550*5; + else + lin[i+off] = (short)(j*5); + } + r = (double)len/100000; + smin = sm*r + smin*(1-r); + } + + void noise(short[] lin,int off,int len,double power) { + int i,r = (int)(power*2); + short ran; + + if (r == 0) r = 1; + for (i = 0; i < len; i += 4) { + ran = (short)(random.nextInt(r*2)-r); + lin[i+off] = ran; + lin[i+off+1] = ran; + lin[i+off+2] = ran; + lin[i+off+3] = ran; + } + } + + public static int m; + + /** Runs it in a new Thread. */ + public void run() { + if (rtpSession == 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); + int seqn = 0; + long time = 0; + double p = 0; + running = true; + m = 1; + + if (DEBUG) + 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, + AudioFormat.CHANNEL_CONFIGURATION_MONO, + AudioFormat.ENCODING_PCM_16BIT)*2); + record.startRecording(); + short[] lin = new short[frame_size*11]; + int num,ring = 0; + random = new Random(); + 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; + } + record.stop(); + rtpSession = null; + codec.cleanEncoder(codecCtx); + if (DEBUG) + println("rtp sender terminated"); + } + + /** Debug output */ + private static void println(String str) { + android.util.Log.d("DEBUG","RtpStreamSender: " + str); + } +}