2020-11-29 18:02:34 +00:00
|
|
|
package com.radio.codec2talkie;
|
|
|
|
|
|
|
|
import android.bluetooth.BluetoothSocket;
|
|
|
|
import android.media.AudioAttributes;
|
|
|
|
import android.media.AudioFormat;
|
|
|
|
import android.media.AudioRecord;
|
|
|
|
import android.media.AudioTrack;
|
|
|
|
import android.media.MediaRecorder;
|
2020-12-03 13:25:02 +00:00
|
|
|
import android.os.Handler;
|
|
|
|
import android.os.Message;
|
2020-11-29 18:02:34 +00:00
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.OutputStream;
|
2020-12-05 19:07:03 +00:00
|
|
|
import java.nio.BufferOverflowException;
|
2020-12-04 13:50:01 +00:00
|
|
|
import java.nio.BufferUnderflowException;
|
|
|
|
import java.nio.ByteBuffer;
|
2020-11-29 18:02:34 +00:00
|
|
|
|
2020-12-08 11:20:30 +00:00
|
|
|
import com.hoho.android.usbserial.driver.UsbSerialPort;
|
2020-12-04 20:11:37 +00:00
|
|
|
import com.radio.codec2talkie.kiss.KissCallback;
|
|
|
|
import com.radio.codec2talkie.kiss.KissProcessor;
|
2020-11-29 19:37:39 +00:00
|
|
|
import com.ustadmobile.codec2.Codec2;
|
|
|
|
|
2020-11-29 18:02:34 +00:00
|
|
|
public class Codec2Player extends Thread {
|
|
|
|
|
2020-12-03 14:01:01 +00:00
|
|
|
public static int PLAYER_DISCONNECT = 1;
|
2020-12-05 20:35:21 +00:00
|
|
|
public static int PLAYER_LISTENING = 2;
|
|
|
|
public static int PLAYER_RECORDING = 3;
|
|
|
|
public static int PLAYER_PLAYING = 4;
|
2020-12-09 17:10:01 +00:00
|
|
|
public static int PLAYER_RX_LEVEL = 5;
|
|
|
|
public static int PLAYER_TX_LEVEL = 6;
|
|
|
|
|
|
|
|
private static int AUDIO_MIN_LEVEL = -70;
|
|
|
|
private static int AUDIO_HIGH_LEVEL = -15;
|
2020-12-03 13:25:02 +00:00
|
|
|
|
2020-12-04 20:20:14 +00:00
|
|
|
private final int AUDIO_SAMPLE_SIZE = 8000;
|
2020-12-09 21:29:18 +00:00
|
|
|
private final int SLEEP_IDLE_DELAY_MS = 20;
|
2020-12-04 20:20:14 +00:00
|
|
|
|
2020-12-08 11:20:30 +00:00
|
|
|
private final int RX_TIMEOUT = 100;
|
|
|
|
private final int TX_TIMEOUT = 2000;
|
|
|
|
|
2020-12-08 16:01:16 +00:00
|
|
|
private final byte CSMA_PERSISTENCE = (byte)0xff;
|
2020-12-09 21:56:25 +00:00
|
|
|
private final byte CSMA_SLOT_TIME = (byte)0x00;
|
2020-12-10 08:34:54 +00:00
|
|
|
private final byte TX_TAIL_10MS_UNITS = (byte)20; // 200ms
|
2020-12-08 16:01:16 +00:00
|
|
|
|
2020-12-09 21:36:40 +00:00
|
|
|
private final int RX_BUFFER_SIZE = 8192;
|
|
|
|
|
2020-12-05 18:48:41 +00:00
|
|
|
private long _codec2Con;
|
2020-12-02 19:45:03 +00:00
|
|
|
|
2020-12-05 19:04:49 +00:00
|
|
|
private BluetoothSocket _btSocket;
|
2020-12-08 11:20:30 +00:00
|
|
|
private UsbSerialPort _usbPort;
|
2020-12-03 13:25:02 +00:00
|
|
|
|
2020-12-05 18:48:41 +00:00
|
|
|
private int _audioBufferSize;
|
2020-12-02 16:33:38 +00:00
|
|
|
|
2020-12-03 08:59:13 +00:00
|
|
|
private boolean _isRecording = false;
|
2020-12-05 20:35:21 +00:00
|
|
|
private int _currentStatus = PLAYER_DISCONNECT;
|
2020-12-03 08:59:13 +00:00
|
|
|
|
2020-12-02 16:33:38 +00:00
|
|
|
// input data, bt -> audio
|
2020-12-05 19:04:49 +00:00
|
|
|
private InputStream _btInputStream;
|
2020-11-29 18:02:34 +00:00
|
|
|
|
|
|
|
private final AudioTrack _audioPlayer;
|
|
|
|
|
2020-12-05 18:48:41 +00:00
|
|
|
private short[] _playbackAudioBuffer;
|
2020-12-02 19:45:03 +00:00
|
|
|
|
2020-12-02 16:33:38 +00:00
|
|
|
// output data., mic -> bt
|
2020-12-05 19:04:49 +00:00
|
|
|
private OutputStream _btOutputStream;
|
2020-12-02 16:33:38 +00:00
|
|
|
|
|
|
|
private final AudioRecord _audioRecorder;
|
2020-11-29 19:37:39 +00:00
|
|
|
|
2020-12-09 21:56:25 +00:00
|
|
|
private final byte[] _rxDataBuffer;
|
2020-12-05 18:48:41 +00:00
|
|
|
private short[] _recordAudioBuffer;
|
|
|
|
private char[] _recordAudioEncodedBuffer;
|
2020-12-02 19:45:03 +00:00
|
|
|
|
2020-12-04 20:20:14 +00:00
|
|
|
// loopback mode
|
2020-12-05 17:06:56 +00:00
|
|
|
private boolean _isLoopbackMode;
|
2020-12-05 18:48:41 +00:00
|
|
|
private ByteBuffer _loopbackBuffer;
|
2020-12-04 20:11:37 +00:00
|
|
|
|
2020-12-04 20:20:14 +00:00
|
|
|
// callbacks
|
2020-12-05 18:48:41 +00:00
|
|
|
private KissProcessor _kissProcessor;
|
2020-12-04 20:20:14 +00:00
|
|
|
private final Handler _onPlayerStateChanged;
|
2020-12-04 13:50:01 +00:00
|
|
|
|
2020-12-05 19:04:49 +00:00
|
|
|
public Codec2Player(Handler onPlayerStateChanged, int codec2Mode) {
|
2020-12-03 13:25:02 +00:00
|
|
|
_onPlayerStateChanged = onPlayerStateChanged;
|
2020-12-05 17:06:56 +00:00
|
|
|
_isLoopbackMode = false;
|
2020-12-09 21:36:40 +00:00
|
|
|
_rxDataBuffer = new byte[RX_BUFFER_SIZE];
|
2020-11-29 18:02:34 +00:00
|
|
|
|
2020-12-08 11:20:30 +00:00
|
|
|
setCodecModeInternal(codec2Mode);
|
|
|
|
|
2020-12-04 20:20:14 +00:00
|
|
|
int _audioRecorderMinBufferSize = AudioRecord.getMinBufferSize(
|
2020-12-03 17:17:11 +00:00
|
|
|
AUDIO_SAMPLE_SIZE,
|
2020-11-29 18:02:34 +00:00
|
|
|
AudioFormat.CHANNEL_IN_MONO,
|
|
|
|
AudioFormat.ENCODING_PCM_16BIT);
|
|
|
|
_audioRecorder = new AudioRecord(
|
|
|
|
MediaRecorder.AudioSource.MIC,
|
2020-12-03 17:17:11 +00:00
|
|
|
AUDIO_SAMPLE_SIZE,
|
2020-11-29 19:37:39 +00:00
|
|
|
AudioFormat.CHANNEL_IN_MONO,
|
2020-11-29 18:02:34 +00:00
|
|
|
AudioFormat.ENCODING_PCM_16BIT,
|
2020-12-04 13:50:01 +00:00
|
|
|
3 * _audioRecorderMinBufferSize);
|
2020-12-04 15:11:47 +00:00
|
|
|
_audioRecorder.startRecording();
|
2020-11-29 18:02:34 +00:00
|
|
|
|
2020-12-04 20:20:14 +00:00
|
|
|
int _audioPlayerMinBufferSize = AudioTrack.getMinBufferSize(
|
2020-12-03 17:17:11 +00:00
|
|
|
AUDIO_SAMPLE_SIZE,
|
2020-11-29 18:02:34 +00:00
|
|
|
AudioFormat.CHANNEL_OUT_MONO,
|
|
|
|
AudioFormat.ENCODING_PCM_16BIT);
|
|
|
|
_audioPlayer = new AudioTrack.Builder()
|
|
|
|
.setAudioAttributes(new AudioAttributes.Builder()
|
|
|
|
.setUsage(AudioAttributes.USAGE_MEDIA)
|
|
|
|
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
|
|
|
|
.build())
|
|
|
|
.setAudioFormat(new AudioFormat.Builder()
|
|
|
|
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
|
2020-12-03 17:17:11 +00:00
|
|
|
.setSampleRate(AUDIO_SAMPLE_SIZE)
|
2020-11-29 18:02:34 +00:00
|
|
|
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
|
|
|
|
.build())
|
2020-12-04 13:50:01 +00:00
|
|
|
.setTransferMode(AudioTrack.MODE_STREAM)
|
|
|
|
.setBufferSizeInBytes(3 * _audioPlayerMinBufferSize)
|
2020-11-29 18:02:34 +00:00
|
|
|
.build();
|
2020-12-04 13:50:01 +00:00
|
|
|
_audioPlayer.play();
|
2020-12-03 17:17:11 +00:00
|
|
|
}
|
|
|
|
|
2020-12-05 19:04:49 +00:00
|
|
|
public void setSocket(BluetoothSocket btSocket) throws IOException {
|
|
|
|
_btSocket = btSocket;
|
|
|
|
_btInputStream = _btSocket.getInputStream();
|
|
|
|
_btOutputStream = _btSocket.getOutputStream();
|
|
|
|
}
|
|
|
|
|
2020-12-08 11:20:30 +00:00
|
|
|
public void setUsbPort(UsbSerialPort port) {
|
|
|
|
_usbPort = port;
|
|
|
|
}
|
|
|
|
|
2020-12-05 17:06:56 +00:00
|
|
|
public void setLoopbackMode(boolean isLoopbackMode) {
|
|
|
|
_isLoopbackMode = isLoopbackMode;
|
|
|
|
}
|
|
|
|
|
2020-12-05 18:48:41 +00:00
|
|
|
public void setCodecMode(int codecMode) {
|
|
|
|
Codec2.destroy(_codec2Con);
|
|
|
|
setCodecModeInternal(codecMode);
|
|
|
|
}
|
|
|
|
|
2020-12-09 17:10:01 +00:00
|
|
|
public static int getAudioMinLevel() {
|
|
|
|
return AUDIO_MIN_LEVEL;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static int getAudioHighLevel() {
|
|
|
|
return AUDIO_HIGH_LEVEL;
|
|
|
|
}
|
|
|
|
|
2020-12-04 20:11:37 +00:00
|
|
|
public void startPlayback() {
|
|
|
|
_isRecording = false;
|
2020-12-04 13:50:01 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 20:11:37 +00:00
|
|
|
public void startRecording() {
|
|
|
|
_isRecording = true;
|
2020-12-02 19:45:03 +00:00
|
|
|
}
|
|
|
|
|
2020-12-05 18:48:41 +00:00
|
|
|
private void setCodecModeInternal(int codecMode) {
|
|
|
|
_codec2Con = Codec2.create(codecMode);
|
|
|
|
|
|
|
|
_audioBufferSize = Codec2.getSamplesPerFrame(_codec2Con);
|
|
|
|
int _audioEncodedBufferSize = Codec2.getBitsSize(_codec2Con); // returns number of bytes
|
|
|
|
|
|
|
|
_recordAudioBuffer = new short[_audioBufferSize];
|
|
|
|
_recordAudioEncodedBuffer = new char[_audioEncodedBufferSize];
|
|
|
|
|
|
|
|
_playbackAudioBuffer = new short[_audioBufferSize];
|
|
|
|
|
|
|
|
_loopbackBuffer = ByteBuffer.allocateDirect(1024 * _audioEncodedBufferSize);
|
|
|
|
|
2020-12-10 08:34:54 +00:00
|
|
|
_kissProcessor = new KissProcessor(
|
|
|
|
_audioEncodedBufferSize,
|
|
|
|
CSMA_PERSISTENCE,
|
|
|
|
CSMA_SLOT_TIME,
|
|
|
|
TX_TAIL_10MS_UNITS,
|
|
|
|
_kissCallback);
|
2020-12-05 18:48:41 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 20:11:37 +00:00
|
|
|
private final KissCallback _kissCallback = new KissCallback() {
|
|
|
|
@Override
|
2020-12-10 10:10:03 +00:00
|
|
|
protected void sendData(byte[] kissPacket) throws IOException {
|
2020-12-05 17:06:56 +00:00
|
|
|
if (_isLoopbackMode) {
|
2020-12-05 19:07:03 +00:00
|
|
|
try {
|
2020-12-10 10:10:03 +00:00
|
|
|
_loopbackBuffer.put(kissPacket);
|
2020-12-05 19:07:03 +00:00
|
|
|
} catch (BufferOverflowException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2020-12-04 20:11:37 +00:00
|
|
|
} else {
|
2020-12-08 11:20:30 +00:00
|
|
|
if (_btOutputStream != null)
|
2020-12-10 10:10:03 +00:00
|
|
|
_btOutputStream.write(kissPacket);
|
2020-12-08 11:20:30 +00:00
|
|
|
if (_usbPort != null) {
|
2020-12-10 10:10:03 +00:00
|
|
|
_usbPort.write(kissPacket, TX_TIMEOUT);
|
2020-12-08 11:20:30 +00:00
|
|
|
}
|
2020-12-04 20:11:37 +00:00
|
|
|
}
|
2020-12-04 13:50:01 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 20:11:37 +00:00
|
|
|
@Override
|
|
|
|
protected void receiveFrame(byte[] frame) {
|
2020-12-09 17:10:01 +00:00
|
|
|
notifyAudioLevel(_playbackAudioBuffer, false);
|
2020-12-10 10:10:03 +00:00
|
|
|
Codec2.decode(_codec2Con, _playbackAudioBuffer, frame);
|
2020-12-04 20:11:37 +00:00
|
|
|
_audioPlayer.write(_playbackAudioBuffer, 0, _audioBufferSize);
|
2020-12-04 13:50:01 +00:00
|
|
|
}
|
2020-12-04 20:11:37 +00:00
|
|
|
};
|
2020-12-04 13:50:01 +00:00
|
|
|
|
2020-12-09 17:10:01 +00:00
|
|
|
private void notifyAudioLevel(short [] audioSamples, boolean isTx) {
|
|
|
|
double db = getAudioMinLevel();
|
|
|
|
if (audioSamples != null) {
|
|
|
|
double acc = 0;
|
|
|
|
for (short v : audioSamples) {
|
|
|
|
acc += Math.abs(v);
|
|
|
|
}
|
|
|
|
double avg = acc / audioSamples.length;
|
|
|
|
db = (20.0 * Math.log10(avg / 32768.0));
|
|
|
|
}
|
|
|
|
Message msg = Message.obtain();
|
|
|
|
if (isTx)
|
|
|
|
msg.what = PLAYER_TX_LEVEL;
|
|
|
|
else
|
|
|
|
msg.what = PLAYER_RX_LEVEL;
|
|
|
|
msg.arg1 = (int)db;
|
|
|
|
_onPlayerStateChanged.sendMessage(msg);
|
|
|
|
}
|
|
|
|
|
2020-12-03 13:25:02 +00:00
|
|
|
private void processRecording() throws IOException {
|
2020-12-02 19:45:03 +00:00
|
|
|
_audioRecorder.read(_recordAudioBuffer, 0, _audioBufferSize);
|
|
|
|
Codec2.encode(_codec2Con, _recordAudioBuffer, _recordAudioEncodedBuffer);
|
2020-12-10 10:10:03 +00:00
|
|
|
notifyAudioLevel(_recordAudioBuffer, true);
|
2020-12-02 16:27:58 +00:00
|
|
|
|
2020-12-04 20:11:37 +00:00
|
|
|
byte [] frame = new byte[_recordAudioEncodedBuffer.length];
|
2020-12-04 13:50:01 +00:00
|
|
|
|
2020-12-04 20:11:37 +00:00
|
|
|
for (int i = 0; i < _recordAudioEncodedBuffer.length; i++) {
|
|
|
|
frame[i] = (byte)_recordAudioEncodedBuffer[i];
|
2020-12-04 13:50:01 +00:00
|
|
|
}
|
2020-12-04 20:11:37 +00:00
|
|
|
_kissProcessor.sendFrame(frame);
|
2020-12-04 13:50:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private boolean processLoopbackPlayback() {
|
|
|
|
try {
|
|
|
|
byte b = _loopbackBuffer.get();
|
2020-12-04 20:11:37 +00:00
|
|
|
_kissProcessor.receiveByte(b);
|
2020-12-04 13:50:01 +00:00
|
|
|
return true;
|
|
|
|
} catch (BufferUnderflowException e) {
|
|
|
|
return false;
|
2020-12-02 16:27:58 +00:00
|
|
|
}
|
2020-12-03 06:26:10 +00:00
|
|
|
}
|
|
|
|
|
2020-12-03 13:25:02 +00:00
|
|
|
private boolean processPlayback() throws IOException {
|
2020-12-05 17:06:56 +00:00
|
|
|
if (_isLoopbackMode) {
|
2020-12-04 16:16:54 +00:00
|
|
|
return processLoopbackPlayback();
|
|
|
|
}
|
2020-12-08 11:20:30 +00:00
|
|
|
int bytesRead = 0;
|
|
|
|
if (_btInputStream != null) {
|
|
|
|
bytesRead = _btInputStream.available();
|
|
|
|
if (bytesRead > 0) {
|
2020-12-09 21:36:40 +00:00
|
|
|
bytesRead = _btInputStream.read(_rxDataBuffer);
|
2020-12-08 11:20:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_usbPort != null) {
|
2020-12-09 21:36:40 +00:00
|
|
|
bytesRead = _usbPort.read(_rxDataBuffer, RX_TIMEOUT);
|
2020-12-08 11:20:30 +00:00
|
|
|
}
|
|
|
|
if (bytesRead > 0) {
|
2020-12-09 21:29:18 +00:00
|
|
|
for (int i = 0; i < bytesRead; i++) {
|
2020-12-09 21:36:40 +00:00
|
|
|
_kissProcessor.receiveByte(_rxDataBuffer[i]);
|
2020-12-09 21:29:18 +00:00
|
|
|
}
|
2020-12-03 13:27:11 +00:00
|
|
|
return true;
|
2020-12-02 16:27:58 +00:00
|
|
|
}
|
2020-12-09 09:38:19 +00:00
|
|
|
return false;
|
2020-12-02 19:45:03 +00:00
|
|
|
}
|
|
|
|
|
2020-12-03 17:17:11 +00:00
|
|
|
private void processRecordPlaybackToggle() throws IOException {
|
2020-12-04 15:11:47 +00:00
|
|
|
// playback -> recording
|
2020-12-03 12:50:53 +00:00
|
|
|
if (_isRecording && _audioRecorder.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
|
|
|
|
_audioRecorder.startRecording();
|
2020-12-04 13:50:01 +00:00
|
|
|
_audioPlayer.stop();
|
|
|
|
_loopbackBuffer.clear();
|
2020-12-09 17:10:01 +00:00
|
|
|
notifyAudioLevel(null, false);
|
2020-12-03 12:50:53 +00:00
|
|
|
}
|
2020-12-04 15:11:47 +00:00
|
|
|
// recording -> playback
|
2020-12-03 12:50:53 +00:00
|
|
|
if (!_isRecording && _audioRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
|
|
|
|
_audioRecorder.stop();
|
2020-12-04 13:50:01 +00:00
|
|
|
_audioPlayer.play();
|
2020-12-04 20:11:37 +00:00
|
|
|
_kissProcessor.flush();
|
2020-12-04 13:50:01 +00:00
|
|
|
_loopbackBuffer.flip();
|
2020-12-09 17:10:01 +00:00
|
|
|
notifyAudioLevel(null, true);
|
2020-12-03 12:50:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-04 20:11:37 +00:00
|
|
|
private void cleanup() {
|
|
|
|
try {
|
|
|
|
_kissProcessor.flush();
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
|
|
|
|
_audioRecorder.stop();
|
|
|
|
_audioRecorder.release();
|
|
|
|
|
|
|
|
_audioPlayer.stop();
|
|
|
|
_audioPlayer.release();
|
|
|
|
|
|
|
|
Codec2.destroy(_codec2Con);
|
|
|
|
}
|
|
|
|
|
2020-12-05 20:35:21 +00:00
|
|
|
private void setStatus(int status) {
|
|
|
|
if (status != _currentStatus) {
|
|
|
|
_currentStatus = status;
|
|
|
|
Message msg = Message.obtain();
|
|
|
|
msg.what = status;
|
|
|
|
_onPlayerStateChanged.sendMessage(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-02 16:27:58 +00:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2020-12-09 22:35:17 +00:00
|
|
|
setPriority(Thread.MAX_PRIORITY);
|
2020-12-03 13:25:02 +00:00
|
|
|
try {
|
2020-12-08 16:01:16 +00:00
|
|
|
if (!_isLoopbackMode) {
|
2020-12-10 08:34:54 +00:00
|
|
|
_kissProcessor.setupTnc();
|
2020-12-08 16:01:16 +00:00
|
|
|
}
|
2020-12-08 11:20:30 +00:00
|
|
|
while (true) {
|
2020-12-03 13:25:02 +00:00
|
|
|
processRecordPlaybackToggle();
|
2020-12-03 12:50:53 +00:00
|
|
|
|
2020-12-09 17:10:01 +00:00
|
|
|
// recording
|
2020-12-03 13:25:02 +00:00
|
|
|
if (_audioRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
|
|
|
|
processRecording();
|
2020-12-05 20:35:21 +00:00
|
|
|
setStatus(PLAYER_RECORDING);
|
2020-12-03 13:25:02 +00:00
|
|
|
} else {
|
2020-12-09 17:10:01 +00:00
|
|
|
// playback
|
2020-12-05 20:35:21 +00:00
|
|
|
if (processPlayback()) {
|
|
|
|
setStatus(PLAYER_PLAYING);
|
2020-12-09 17:10:01 +00:00
|
|
|
// idling
|
2020-12-05 20:35:21 +00:00
|
|
|
} else {
|
2020-12-03 13:25:02 +00:00
|
|
|
try {
|
2020-12-09 17:10:01 +00:00
|
|
|
Thread.sleep(SLEEP_IDLE_DELAY_MS);
|
|
|
|
if (_currentStatus != PLAYER_LISTENING) {
|
|
|
|
notifyAudioLevel(null, false);
|
|
|
|
notifyAudioLevel(null, true);
|
|
|
|
}
|
2020-12-05 20:35:21 +00:00
|
|
|
setStatus(PLAYER_LISTENING);
|
2020-12-03 13:25:02 +00:00
|
|
|
} catch (InterruptedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2020-12-02 19:45:03 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-01 20:25:15 +00:00
|
|
|
}
|
2020-12-04 15:11:47 +00:00
|
|
|
} catch (IOException e) {
|
2020-12-03 13:25:02 +00:00
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2020-12-04 15:11:47 +00:00
|
|
|
|
2020-12-04 20:11:37 +00:00
|
|
|
cleanup();
|
2020-12-05 20:35:21 +00:00
|
|
|
setStatus(PLAYER_DISCONNECT);
|
2020-11-29 18:02:34 +00:00
|
|
|
}
|
|
|
|
}
|