mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-06 16:19:28 +00:00
- Changed casting connection timeout to 2 seconds.
- Added ping pong to FCast. - Removal of DataInputStream and just using raw InputStream. - Fix slider position crash.
This commit is contained in:
parent
f4cb1719e0
commit
f1860126a7
4 changed files with 62 additions and 25 deletions
|
@ -1,5 +1,6 @@
|
||||||
package com.futo.platformplayer
|
package com.futo.platformplayer
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import com.google.common.base.CharMatcher
|
import com.google.common.base.CharMatcher
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -215,17 +216,20 @@ private fun ByteArray.toInetAddress(): InetAddress {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getConnectedSocket(addresses: List<InetAddress>, port: Int): Socket? {
|
fun getConnectedSocket(addresses: List<InetAddress>, port: Int): Socket? {
|
||||||
val timeout = 5000
|
val timeout = 2000
|
||||||
|
|
||||||
if (addresses.isEmpty()) {
|
if (addresses.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addresses.size == 1) {
|
if (addresses.size == 1) {
|
||||||
|
val socket = Socket()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Socket().apply { this.connect(InetSocketAddress(addresses[0], port), timeout) };
|
return socket.apply { this.connect(InetSocketAddress(addresses[0], port), timeout) }
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
//Ignored.
|
Log.i("getConnectedSocket", "Failed to connect to: ${addresses[0]}", e)
|
||||||
|
socket.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -264,7 +268,7 @@ fun getConnectedSocket(addresses: List<InetAddress>, port: Int): Socket? {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
//Ignore
|
Log.i("getConnectedSocket", "Failed to connect to: $address", e)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -328,6 +328,8 @@ class ChromecastCastingDevice : CastingDevice {
|
||||||
|
|
||||||
val factory = sslContext.socketFactory;
|
val factory = sslContext.socketFactory;
|
||||||
|
|
||||||
|
val address = InetSocketAddress(usedRemoteAddress, port)
|
||||||
|
|
||||||
//Connection loop
|
//Connection loop
|
||||||
while (_scopeIO?.isActive == true) {
|
while (_scopeIO?.isActive == true) {
|
||||||
Logger.i(TAG, "Connecting to Chromecast.");
|
Logger.i(TAG, "Connecting to Chromecast.");
|
||||||
|
@ -341,7 +343,7 @@ class ChromecastCastingDevice : CastingDevice {
|
||||||
connectedSocket = null
|
connectedSocket = null
|
||||||
} else {
|
} else {
|
||||||
Logger.i(TAG, "Using new socket.")
|
Logger.i(TAG, "Using new socket.")
|
||||||
val s = Socket().apply { this.connect(InetSocketAddress(usedRemoteAddress, port), 5000) }
|
val s = Socket().apply { this.connect(address, 2000) }
|
||||||
_socket = factory.createSocket(s, s.inetAddress.hostAddress, s.port, true) as SSLSocket
|
_socket = factory.createSocket(s, s.inetAddress.hostAddress, s.port, true) as SSLSocket
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,10 +446,11 @@ class ChromecastCastingDevice : CastingDevice {
|
||||||
while (_scopeIO?.isActive == true) {
|
while (_scopeIO?.isActive == true) {
|
||||||
try {
|
try {
|
||||||
sendChannelMessage("sender-0", "receiver-0", "urn:x-cast:com.google.cast.tp.heartbeat", pingObject.toString());
|
sendChannelMessage("sender-0", "receiver-0", "urn:x-cast:com.google.cast.tp.heartbeat", pingObject.toString());
|
||||||
Thread.sleep(5000);
|
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Log.w(TAG, "Failed to send ping.");
|
Log.w(TAG, "Failed to send ping.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Thread.sleep(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.i(TAG, "Stopped ping loop.");
|
Logger.i(TAG, "Stopped ping loop.");
|
||||||
|
|
|
@ -27,9 +27,9 @@ import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.io.DataInputStream
|
|
||||||
import java.io.DataOutputStream
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
|
@ -82,12 +82,13 @@ class FCastCastingDevice : CastingDevice {
|
||||||
var port: Int = 0;
|
var port: Int = 0;
|
||||||
|
|
||||||
private var _socket: Socket? = null;
|
private var _socket: Socket? = null;
|
||||||
private var _outputStream: DataOutputStream? = null;
|
private var _outputStream: OutputStream? = null;
|
||||||
private var _inputStream: DataInputStream? = null;
|
private var _inputStream: InputStream? = null;
|
||||||
private var _scopeIO: CoroutineScope? = null;
|
private var _scopeIO: CoroutineScope? = null;
|
||||||
private var _started: Boolean = false;
|
private var _started: Boolean = false;
|
||||||
private var _version: Long = 1;
|
private var _version: Long = 1;
|
||||||
private var _thread: Thread? = null
|
private var _thread: Thread? = null
|
||||||
|
private var _pingThread: Thread? = null
|
||||||
|
|
||||||
constructor(name: String, addresses: Array<InetAddress>, port: Int) : super() {
|
constructor(name: String, addresses: Array<InetAddress>, port: Int) : super() {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
@ -241,7 +242,8 @@ class FCastCastingDevice : CastingDevice {
|
||||||
val adrs = addresses ?: return;
|
val adrs = addresses ?: return;
|
||||||
|
|
||||||
val thread = _thread
|
val thread = _thread
|
||||||
if (thread == null || !thread.isAlive) {
|
val pingThread = _pingThread
|
||||||
|
if (thread == null || !thread.isAlive || pingThread == null || !pingThread.isAlive) {
|
||||||
Log.i(TAG, "(Re)starting thread because the thread has died")
|
Log.i(TAG, "(Re)starting thread because the thread has died")
|
||||||
|
|
||||||
_scopeIO?.let {
|
_scopeIO?.let {
|
||||||
|
@ -258,7 +260,7 @@ class FCastCastingDevice : CastingDevice {
|
||||||
var connectedSocket: Socket? = null
|
var connectedSocket: Socket? = null
|
||||||
while (_scopeIO?.isActive == true) {
|
while (_scopeIO?.isActive == true) {
|
||||||
try {
|
try {
|
||||||
Log.i(TAG, "getConnectedSocket.")
|
Log.i(TAG, "getConnectedSocket (adrs = [ ${adrs.joinToString(", ")} ], port = ${port}).")
|
||||||
|
|
||||||
val resultSocket = getConnectedSocket(adrs.toList(), port);
|
val resultSocket = getConnectedSocket(adrs.toList(), port);
|
||||||
|
|
||||||
|
@ -279,6 +281,8 @@ class FCastCastingDevice : CastingDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val address = InetSocketAddress(usedRemoteAddress, port)
|
||||||
|
|
||||||
//Connection loop
|
//Connection loop
|
||||||
while (_scopeIO?.isActive == true) {
|
while (_scopeIO?.isActive == true) {
|
||||||
Logger.i(TAG, "Connecting to FastCast.");
|
Logger.i(TAG, "Connecting to FastCast.");
|
||||||
|
@ -292,12 +296,12 @@ class FCastCastingDevice : CastingDevice {
|
||||||
connectedSocket = null
|
connectedSocket = null
|
||||||
} else {
|
} else {
|
||||||
Logger.i(TAG, "Using new socket.");
|
Logger.i(TAG, "Using new socket.");
|
||||||
_socket = Socket().apply { this.connect(InetSocketAddress(usedRemoteAddress, port), 5000) };
|
_socket = Socket().apply { this.connect(address, 2000) };
|
||||||
}
|
}
|
||||||
Logger.i(TAG, "Successfully connected to FastCast at $usedRemoteAddress:$port");
|
Logger.i(TAG, "Successfully connected to FastCast at $usedRemoteAddress:$port");
|
||||||
|
|
||||||
_outputStream = DataOutputStream(_socket?.outputStream);
|
_outputStream = _socket?.outputStream;
|
||||||
_inputStream = DataInputStream(_socket?.inputStream);
|
_inputStream = _socket?.inputStream;
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
_socket?.close();
|
_socket?.close();
|
||||||
Logger.i(TAG, "Failed to connect to FastCast.", e);
|
Logger.i(TAG, "Failed to connect to FastCast.", e);
|
||||||
|
@ -318,11 +322,13 @@ class FCastCastingDevice : CastingDevice {
|
||||||
try {
|
try {
|
||||||
val inputStream = _inputStream ?: break;
|
val inputStream = _inputStream ?: break;
|
||||||
Log.d(TAG, "Receiving next packet...");
|
Log.d(TAG, "Receiving next packet...");
|
||||||
val b1 = inputStream.readUnsignedByte();
|
|
||||||
val b2 = inputStream.readUnsignedByte();
|
var headerBytesRead = 0
|
||||||
val b3 = inputStream.readUnsignedByte();
|
while (headerBytesRead < 4) {
|
||||||
val b4 = inputStream.readUnsignedByte();
|
headerBytesRead += inputStream.read(buffer, headerBytesRead, 4 - headerBytesRead)
|
||||||
val size = ((b4.toLong() shl 24) or (b3.toLong() shl 16) or (b2.toLong() shl 8) or b1.toLong()).toInt();
|
}
|
||||||
|
|
||||||
|
val size = ((buffer[3].toLong() shl 24) or (buffer[2].toLong() shl 16) or (buffer[1].toLong() shl 8) or buffer[0].toLong()).toInt();
|
||||||
if (size > buffer.size) {
|
if (size > buffer.size) {
|
||||||
Logger.w(TAG, "Skipping packet that is too large $size bytes.")
|
Logger.w(TAG, "Skipping packet that is too large $size bytes.")
|
||||||
inputStream.skip(size.toLong());
|
inputStream.skip(size.toLong());
|
||||||
|
@ -330,7 +336,10 @@ class FCastCastingDevice : CastingDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, "Received header indicating $size bytes. Waiting for message.");
|
Log.d(TAG, "Received header indicating $size bytes. Waiting for message.");
|
||||||
inputStream.read(buffer, 0, size);
|
var bytesRead = 0
|
||||||
|
while (bytesRead < size) {
|
||||||
|
bytesRead += inputStream.read(buffer, bytesRead, size - bytesRead)
|
||||||
|
}
|
||||||
|
|
||||||
val messageBytes = buffer.sliceArray(IntRange(0, size));
|
val messageBytes = buffer.sliceArray(IntRange(0, size));
|
||||||
Log.d(TAG, "Received $size bytes: ${messageBytes.toHexString()}.");
|
Log.d(TAG, "Received $size bytes: ${messageBytes.toHexString()}.");
|
||||||
|
@ -368,7 +377,23 @@ class FCastCastingDevice : CastingDevice {
|
||||||
|
|
||||||
Logger.i(TAG, "Stopped connection loop.");
|
Logger.i(TAG, "Stopped connection loop.");
|
||||||
connectionState = CastConnectionState.DISCONNECTED;
|
connectionState = CastConnectionState.DISCONNECTED;
|
||||||
}.apply { start() };
|
}.apply { start() }
|
||||||
|
|
||||||
|
_pingThread = Thread {
|
||||||
|
Logger.i(TAG, "Started ping loop.")
|
||||||
|
|
||||||
|
while (_scopeIO?.isActive == true) {
|
||||||
|
try {
|
||||||
|
send(Opcode.Ping)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Log.w(TAG, "Failed to send ping.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.sleep(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.i(TAG, "Stopped ping loop.");
|
||||||
|
}.apply { start() }
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "Thread was still alive, not restarted")
|
Log.i(TAG, "Thread was still alive, not restarted")
|
||||||
}
|
}
|
||||||
|
@ -473,6 +498,7 @@ class FCastCastingDevice : CastingDevice {
|
||||||
_started = false;
|
_started = false;
|
||||||
//TODO: Kill and/or join thread?
|
//TODO: Kill and/or join thread?
|
||||||
_thread = null;
|
_thread = null;
|
||||||
|
_pingThread = null;
|
||||||
|
|
||||||
val socket = _socket;
|
val socket = _socket;
|
||||||
val scopeIO = _scopeIO;
|
val scopeIO = _scopeIO;
|
||||||
|
|
|
@ -143,7 +143,9 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
|
||||||
|
|
||||||
StateCasting.instance.onActiveDeviceDurationChanged.remove(this);
|
StateCasting.instance.onActiveDeviceDurationChanged.remove(this);
|
||||||
StateCasting.instance.onActiveDeviceDurationChanged.subscribe {
|
StateCasting.instance.onActiveDeviceDurationChanged.subscribe {
|
||||||
_sliderPosition.valueTo = it.toFloat().coerceAtLeast(1.0f);
|
val dur = it.toFloat().coerceAtLeast(1.0f)
|
||||||
|
_sliderPosition.value = _sliderPosition.value.coerceAtLeast(0.0f).coerceAtMost(dur);
|
||||||
|
_sliderPosition.valueTo = dur
|
||||||
};
|
};
|
||||||
|
|
||||||
_device = StateCasting.instance.activeDevice;
|
_device = StateCasting.instance.activeDevice;
|
||||||
|
@ -185,8 +187,10 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
|
||||||
_sliderPosition.valueFrom = 0.0f;
|
_sliderPosition.valueFrom = 0.0f;
|
||||||
_sliderVolume.valueFrom = 0.0f;
|
_sliderVolume.valueFrom = 0.0f;
|
||||||
_sliderVolume.value = d.volume.toFloat().coerceAtLeast(0.0f).coerceAtMost(_sliderVolume.valueTo);
|
_sliderVolume.value = d.volume.toFloat().coerceAtLeast(0.0f).coerceAtMost(_sliderVolume.valueTo);
|
||||||
_sliderPosition.valueTo = d.duration.toFloat().coerceAtLeast(1.0f);
|
|
||||||
_sliderPosition.value = d.time.toFloat().coerceAtLeast(0.0f).coerceAtMost(_sliderVolume.valueTo);
|
val dur = d.duration.toFloat().coerceAtLeast(1.0f)
|
||||||
|
_sliderPosition.value = d.time.toFloat().coerceAtLeast(0.0f).coerceAtMost(dur)
|
||||||
|
_sliderPosition.valueTo = dur
|
||||||
|
|
||||||
if (d.canSetVolume) {
|
if (d.canSetVolume) {
|
||||||
_layoutVolumeAdjustable.visibility = View.VISIBLE;
|
_layoutVolumeAdjustable.visibility = View.VISIBLE;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue