Make two way communication over ip addresses

master
esensar 2017-01-10 00:11:01 +01:00
parent a0cea8b8da
commit aab2cbd3a4
10 changed files with 490 additions and 77 deletions

View File

@ -60,7 +60,7 @@
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -1,17 +1,27 @@
package com.smarthomies.realtimetalk;
import android.app.Application;
import android.util.Log;
import com.smarthomies.realtimetalk.utils.MediaRecorderThread;
import com.smarthomies.realtimetalk.utils.MediaStreamServer;
import com.smarthomies.realtimetalk.utils.MediaStreamerThread;
import com.smarthomies.realtimetalk.utils.RTTAppHelper;
import com.smarthomies.realtimetalk.utils.SocketAudioPlayer;
import com.smarthomies.realtimetalk.utils.SocketAudioRecorder;
import java.net.Socket;
import io.realm.Realm;
/**
* Created by ensar on 31/10/16.
*/
public class RTTApp extends Application {
public class RTTApp extends Application implements MediaStreamServer.CallCallbacks {
public static final String TAG = RTTApp.class.getSimpleName();
private SocketAudioRecorder socketAudioRecorder;
@Override
public void onCreate() {
super.onCreate();
@ -19,5 +29,46 @@ public class RTTApp extends Application {
RTTAppHelper.getInstance().initWithContext(getApplicationContext());
Realm.init(this);
MediaStreamServer.initWithContext(this);
MediaStreamServer.getInstance().setCallListener(this);
MediaStreamServer.getInstance().startListeningToPort();
MediaStreamerThread.initWithContext(this);
MediaStreamerThread.getInstance().start();
MediaRecorderThread.initWithContext(this);
MediaRecorderThread.getInstance().start();
}
@Override
public void onError(int errorCode) {
Log.d(TAG, "onError: " + errorCode);
}
@Override
public void onConnectionRequested(Socket sourceSocket) {
Log.d(TAG, "onConnectionRequested: ");
if(socketAudioRecorder != null) {
socketAudioRecorder.stop();
}
socketAudioRecorder = MediaRecorderThread.getInstance().startStreamingAudio(sourceSocket.getInetAddress().toString().substring(1), 8087);
}
@Override
public void onConnectionFinished() {
Log.d(TAG, "onConnectionFinished: ");
if(socketAudioRecorder != null) {
socketAudioRecorder.stop();
}
}
@Override
public void onTerminate() {
super.onTerminate();
MediaStreamServer.getInstance().stop();
MediaStreamerThread.getInstance().interrupt();
MediaRecorderThread.getInstance().interrupt();
}
}

View File

@ -0,0 +1,68 @@
package com.smarthomies.realtimetalk.utils;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import com.smarthomies.realtimetalk.R;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
public class MediaRecorderThread extends HandlerThread {
private static final String TAG = MediaRecorderThread.class.getSimpleName();
private int frequency;
private int channelConfiguration;
private int audioEncoding;
private static MediaRecorderThread instance;
public static void initWithContext(Context context) {
instance = new MediaRecorderThread(context);
}
private Handler mHandler;
public static MediaRecorderThread getInstance() {
if(instance == null) {
throw new RuntimeException("Initialize server first with initWithContext");
}
return instance;
}
private MediaRecorderThread(Context ctx) {
super(TAG);
Properties prop = new Properties();
try {
InputStream inputStream = ctx.getResources().openRawResource(R.raw.config);
prop.load(inputStream);
} catch (FileNotFoundException e) {
Log.d(TAG, "Can't find config");
} catch (IOException e) {
Log.d(TAG, "Can't load config");
}
frequency = Integer.parseInt(prop.getProperty("frequency"));
channelConfiguration = Integer.parseInt(prop.getProperty("client_channel"));
audioEncoding = Integer.parseInt(prop.getProperty("audio_encoding"));
}
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
mHandler = new Handler(getLooper());
}
public SocketAudioRecorder startStreamingAudio(String ip, int port) {
SocketAudioRecorder recorder = new SocketAudioRecorder(ip, port, frequency, channelConfiguration, audioEncoding);
mHandler.post(recorder);
return recorder;
}
}

View File

@ -4,6 +4,7 @@ import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;
import com.smarthomies.realtimetalk.R;
import com.smarthomies.realtimetalk.views.activities.CallActivity;
@ -31,17 +32,19 @@ public class MediaStreamClient {
InputStream inputStream = ctx.getResources().openRawResource(R.raw.config);
prop.load(inputStream);
} catch (FileNotFoundException e) {
System.out.println("Can't finde config");
System.out.println("Can't find config");
} catch (IOException e) {
System.out.println("Can't load config");
}
final int frequency = Integer.parseInt(prop.getProperty("frequency"));
final int channelConfiguration = Integer.parseInt(prop.getProperty("channal_server"));
final int channelConfiguration = Integer.parseInt(prop.getProperty("client_channel"));
final int audioEncoding = Integer.parseInt(prop.getProperty("audio_encoding"));
final int SERVERPORT = Integer.parseInt(prop.getProperty("serverport_server"));
final int SERVERPORT = Integer.parseInt(prop.getProperty("port_server"));
recBufSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
Log.d(TAG, "MediaStreamClient buffer size: " + recBufSize);
//Log.v(TAG,String.valueOf(AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_MONO , AudioFormat.ENCODING_PCM_16BIT)));
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding, recBufSize);
@ -70,7 +73,7 @@ public class MediaStreamClient {
}
}
audioRecord.stop();
//audioRecord.release();
audioRecord.release();
try { connfd.close(); }
catch (Exception e) {
e.printStackTrace();

View File

@ -1,108 +1,172 @@
package com.smarthomies.realtimetalk.utils;
import android.content.Context;
import android.content.Intent;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;
import com.smarthomies.realtimetalk.R;
import com.smarthomies.realtimetalk.views.activities.CallActivity;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
public class MediaStreamServer implements Runnable {
private static final String TAG = MediaStreamServer.class.getSimpleName();
boolean isPlaying;
int playBufSize;
Socket connfd;
ServerSocket sockfd;
AudioTrack audioTrack;
private static final String TAG = "MyActivity";
final int SERVERPORT;
Context ctx;
private boolean isListening;
private Socket socketConnection;
private ServerSocket serverSocket;
private final int SERVERPORT;
public MediaStreamServer(final Context ctx) {
private Thread serverThread;
private CallCallbacks callListener;
private static MediaStreamServer instance;
public static void initWithContext(Context context) {
instance = new MediaStreamServer(context);
}
public static MediaStreamServer getInstance() {
if(instance == null) {
throw new RuntimeException("Initialize server first with initWithContext");
}
return instance;
}
public void setCallListener(CallCallbacks callListener) {
this.callListener = callListener;
}
public void removeCallListener() {
this.callListener = null;
}
private MediaStreamServer(Context ctx) {
Properties prop = new Properties();
try {
InputStream inputStream = ctx.getResources().openRawResource(R.raw.config);
prop.load(inputStream);
} catch (FileNotFoundException e) {
System.out.println("Can't finde config");
Log.d(TAG, "Can't find config");
} catch (IOException e) {
System.out.println("Can't load config");
Log.d(TAG, "Can't load config");
}
final int frequency = Integer.parseInt(prop.getProperty("frequency"));
final int channelConfiguration = Integer.parseInt(prop.getProperty("channal_client"));
final int audioEncoding = Integer.parseInt(prop.getProperty("audio_encoding"));
SERVERPORT = Integer.parseInt(prop.getProperty("serverport_client"));
playBufSize=AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
audioTrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL, frequency, channelConfiguration, audioEncoding, playBufSize, AudioTrack.MODE_STREAM);
audioTrack.setStereoVolume(1f, 1f);
this.ctx=ctx;
SERVERPORT = Integer.parseInt(prop.getProperty("port_client"));
}
public void stop() {
isPlaying = false;
isListening = false;
serverThread.interrupt();
}
public void startListeningToPort() {
if(serverThread == null) {
serverThread = new Thread(this);
}
serverThread.start();
}
public void setVolume(float lvol, float rvol) {
audioTrack.setStereoVolume(lvol, rvol);
public void stopListeningToPort() {
isListening = false;
}
@Override
public void run() {
byte[] buffer = new byte[playBufSize];
try { sockfd = new ServerSocket(SERVERPORT); }
catch (Exception e) {
isListening = true;
try {
serverSocket = new ServerSocket(SERVERPORT);
Log.d(TAG, "Server socket open on port: " + SERVERPORT);
}
catch (IOException e) {
e.printStackTrace();
CallActivity.toast("Port unavailable",ctx);
notifyError(ERROR_PORT_UNAVAILABLE);
isListening = false;
return;
}
while(true) {
while(isListening) {
try {
connfd = sockfd.accept();
} catch (Exception e) {
socketConnection = serverSocket.accept();
Log.d(TAG, "Accepted socket connection!");
Log.d(TAG, "Socket connection info: " + socketConnection);
notifyConnectionRequested(socketConnection);
} catch (IOException e) {
e.printStackTrace();
CallActivity.toast("Connection not accepted",ctx);
notifyError(ERROR_WHILE_ACCEPTING);
continue;
}
CallActivity.toast("Connected",ctx);
audioTrack.play();
isPlaying = true;
while (isPlaying) {
int readSize = 0;
try {
readSize = connfd.getInputStream().read(buffer);
} catch (Exception e) {
Log.v(TAG, "palo");
e.printStackTrace();
CallActivity.toast("Closed stream by sender",ctx);
break;
}
audioTrack.write(buffer, 0, readSize);
}
audioTrack.stop();
audioTrack.flush();
final CountDownLatch callTerminationLock = new CountDownLatch(1);
Log.d(TAG, "Playing audio!");
MediaStreamerThread.getInstance().playAudioFromSocket(socketConnection, callTerminationLock);
try {
connfd.close();
callTerminationLock.await();
Log.d(TAG, "Done playing!");
} catch (InterruptedException ex) {
ex.printStackTrace();
notifyError(ERROR_CALL_INTERRUPTED);
continue;
}
try {
socketConnection.close();
notifyConnectionFinished();
} catch (Exception e) {
e.printStackTrace();
CallActivity.toast("Can't close connection!",ctx);
notifyError(ERROR_WHILE_CLOSING_CONNECTION);
}
}
try {
serverSocket.close();
}
catch (IOException e) {
e.printStackTrace();
notifyError(ERROR_WHILE_CLOSING);
isListening = false;
}
}
private void notifyError(int errorCode) {
if(callListener != null) {
callListener.onError(errorCode);
}
}
private void notifyConnectionRequested(Socket sourceSocket) {
if(callListener != null) {
callListener.onConnectionRequested(sourceSocket);
}
}
private void notifyConnectionFinished() {
if(callListener != null) {
callListener.onConnectionFinished();
}
}
public static final int ERROR_PORT_UNAVAILABLE = 0;
public static final int ERROR_WHILE_CLOSING = 1;
public static final int ERROR_WHILE_ACCEPTING = 2;
public static final int ERROR_WHILE_PLAYING = 3;
public static final int ERROR_WHILE_CLOSING_CONNECTION = 4;
public static final int ERROR_CALL_INTERRUPTED = 5;
public interface CallCallbacks {
void onError(int errorCode);
void onConnectionRequested(Socket sourceSocket);
void onConnectionFinished();
}
}

View File

@ -0,0 +1,72 @@
package com.smarthomies.realtimetalk.utils;
import android.content.Context;
import android.databinding.repacked.org.antlr.v4.codegen.model.Loop;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import com.smarthomies.realtimetalk.R;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
public class MediaStreamerThread extends HandlerThread {
private static final String TAG = MediaStreamerThread.class.getSimpleName();
private int frequency;
private int channelConfiguration;
private int audioEncoding;
private static MediaStreamerThread instance;
public static void initWithContext(Context context) {
instance = new MediaStreamerThread(context);
}
private Handler mHandler;
public static MediaStreamerThread getInstance() {
if(instance == null) {
throw new RuntimeException("Initialize server first with initWithContext");
}
return instance;
}
private MediaStreamerThread(Context ctx) {
super(TAG);
Properties prop = new Properties();
try {
InputStream inputStream = ctx.getResources().openRawResource(R.raw.config);
prop.load(inputStream);
} catch (FileNotFoundException e) {
Log.d(TAG, "Can't find config");
} catch (IOException e) {
Log.d(TAG, "Can't load config");
}
frequency = Integer.parseInt(prop.getProperty("frequency"));
channelConfiguration = Integer.parseInt(prop.getProperty("server_channel"));
audioEncoding = Integer.parseInt(prop.getProperty("audio_encoding"));
}
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
mHandler = new Handler(getLooper());
}
public void playAudioFromSocket(Socket socket, final CountDownLatch finishedLock) {
mHandler.post(new SocketAudioPlayer(socket, frequency, channelConfiguration, audioEncoding, finishedLock));
}
}

View File

@ -0,0 +1,73 @@
package com.smarthomies.realtimetalk.utils;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.CountDownLatch;
public class SocketAudioPlayer implements Runnable {
private static final String TAG = SocketAudioPlayer.class.getSimpleName();
private int playBufSize;
private AudioTrack audioTrack;
private boolean isPlaying;
private Socket socket;
private CountDownLatch completionLock;
private int noDataCounter = 0;
public SocketAudioPlayer(Socket socket, int frequency, int channelConfiguration, int audioEncoding, final CountDownLatch countDownLatch) {
this.socket = socket;
playBufSize=AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
audioTrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL, frequency, channelConfiguration, audioEncoding, playBufSize, AudioTrack.MODE_STREAM);
audioTrack.setStereoVolume(1f, 1f);
this.completionLock = countDownLatch;
}
@Override
public void run() {
try {
Log.d(TAG, "Start!");
byte[] buffer = new byte[playBufSize];
audioTrack.play();
isPlaying = true;
while (isPlaying) {
int readSize = 0;
try {
readSize = socket.getInputStream().read(buffer);
Log.d(TAG, "Received " + readSize + " bytes");
if(readSize <= 0) {
noDataCounter++;
} else {
noDataCounter = 0;
}
if(noDataCounter == 10) {
break;
}
} catch (IOException e) {
e.printStackTrace();
isPlaying = false;
break;
}
audioTrack.write(buffer, 0, readSize);
}
audioTrack.stop();
audioTrack.flush();
} finally {
Log.d(TAG, "Done!");
completionLock.countDown();
}
}
}

View File

@ -0,0 +1,80 @@
package com.smarthomies.realtimetalk.utils;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;
import com.smarthomies.realtimetalk.views.activities.CallActivity;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.CountDownLatch;
public class SocketAudioRecorder implements Runnable {
private static final String TAG = SocketAudioRecorder.class.getSimpleName();
private static int recBufSize;
private static AudioRecord audioRecord;
private boolean isRecording;
private Socket socket;
private String ip;
private int port;
public SocketAudioRecorder(String ip, int port, int frequency, int channelConfiguration, int audioEncoding) {
this.ip = ip;
this.port = port;
if(audioRecord == null || audioRecord.getState() == AudioRecord.STATE_UNINITIALIZED) {
recBufSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
Log.d(TAG, "MediaStreamClient buffer size: " + recBufSize);
//Log.v(TAG,String.valueOf(AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_MONO , AudioFormat.ENCODING_PCM_16BIT)));
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding, recBufSize);
}
}
@Override
public void run() {
byte[] buffer = new byte[recBufSize];
try {
socket = new Socket(ip, port);
Log.d(TAG, "Socket open!");
Log.d(TAG, "Socket info: " + socket);
} catch (Exception e) {
e.printStackTrace();
return;
}
audioRecord.startRecording();
Log.d(TAG, "Started recording");
isRecording = true;
while (isRecording) {
int readSize = audioRecord.read(buffer, 0, recBufSize);
Log.d(TAG, "Recorded " + readSize + " bytes");
try {
socket.getOutputStream().write(buffer, 0, readSize);
Log.d(TAG, "Sent " + readSize + " bytes");
} catch (Exception e) {
e.printStackTrace();
break;
}
}
audioRecord.stop();
Log.d(TAG, "Stopped recording!");
try {
socket.close();
Log.d(TAG, "Socket closed!");
} catch (Exception e) {
e.printStackTrace();
}
}
public void stop() {
isRecording = false;
}
}

View File

@ -12,20 +12,21 @@ import android.widget.TextView;
import android.widget.Toast;
import com.smarthomies.realtimetalk.R;
import com.smarthomies.realtimetalk.utils.MediaRecorderThread;
import com.smarthomies.realtimetalk.utils.MediaStreamClient;
import com.smarthomies.realtimetalk.utils.MediaStreamServer;
import com.smarthomies.realtimetalk.utils.SocketAudioRecorder;
public class CallActivity extends AppCompatActivity {
private TextView serverStatus;
private EditText serverIp;
private Button nazovi;
private MediaStreamClient mss;
private MediaStreamServer msc;
// DESIGNATE A PORT
boolean isRecording;
private static Handler handler = new Handler();
Thread t=null;
private SocketAudioRecorder socketAudioRecorder;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -37,9 +38,6 @@ public class CallActivity extends AppCompatActivity {
serverStatus=(TextView) findViewById(R.id.labela);
nazovi.setOnTouchListener(nazoviL);
isRecording=false;
msc=new MediaStreamServer(CallActivity.this);
t=new Thread(msc);
t.start();
}
@ -50,11 +48,15 @@ public class CallActivity extends AppCompatActivity {
String ip=serverIp.getText().toString();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
msc.stop();
mss=new MediaStreamClient(CallActivity.this,ip);
if(socketAudioRecorder != null) {
socketAudioRecorder.stop();
}
socketAudioRecorder=MediaRecorderThread.getInstance().startStreamingAudio(ip, 8087);
break;
case MotionEvent.ACTION_UP:
mss.stop(CallActivity.this);
if(socketAudioRecorder != null) {
socketAudioRecorder.stop();
}
break;
}
return false;

View File

@ -1,6 +1,6 @@
frequency = 44100
serverport_client = 8087
serverport_server = 8083
port_client = 8087
port_server = 8083
audio_encoding=2
channal_client=4
channal_server=2
client_channel=2
server_channel=2