Skip to content

Commit

Permalink
feat(*): add a coroutine SRT socket
Browse files Browse the repository at this point in the history
  • Loading branch information
ThibaultBee committed Jul 8, 2024
1 parent 05bb4d4 commit 5586a5b
Show file tree
Hide file tree
Showing 16 changed files with 1,333 additions and 219 deletions.
2 changes: 1 addition & 1 deletion example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ android {
}

dependencies {
implementation project(':srtdroid')
implementation project(':srtdroid-ktx')
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.6.1'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@ package io.github.thibaultbee.srtdroid.example

import android.app.Application
import android.content.Context
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import io.github.thibaultbee.srtdroid.example.tests.RecvFile
import io.github.thibaultbee.srtdroid.example.tests.SendFile
import io.github.thibaultbee.srtdroid.example.tests.TestClient
import io.github.thibaultbee.srtdroid.example.tests.TestServer
import java.util.concurrent.Executors
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.launch

class MainViewModel(application: Application) : AndroidViewModel(application) {
private val configuration = Configuration(getApplication())
private val sendFileName = "MyFileToSend"
private val recvFileName = "RecvFile"

private val executor = Executors.newFixedThreadPool(4)

val error = MutableLiveData<String>()
val success = MutableLiveData<String>()

Expand All @@ -26,68 +28,52 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
val sendFileCompletion = MutableLiveData<Boolean>()

private val testServer =
TestServer(executor,
{ msg ->
testServerCompletion.postValue(true)
success.postValue(msg)
},
{ msg ->
testClientCompletion.postValue(true)
error.postValue(msg)
}
)
TestServer()
private val testClient =
TestClient(
executor,
{ msg ->
testClientCompletion.postValue(true)
success.postValue(msg)
},
{ msg ->
testClientCompletion.postValue(true)
error.postValue(msg)
}
)
TestClient()

private val recvFile =
RecvFile(sendFileName,
RecvFile(
sendFileName,
recvFileName,
(getApplication() as Context).filesDir,
executor,
{ msg ->
recvFileCompletion.postValue(true)
success.postValue(msg)
},
{ msg ->
recvFileCompletion.postValue(true)
error.postValue(msg)
}
(getApplication() as Context).filesDir
)

private val sendFile =
SendFile((getApplication() as Context).filesDir,
executor,
{ msg ->
sendFileCompletion.postValue(true)
success.postValue(msg)
},
{ msg ->
sendFileCompletion.postValue(true)
error.postValue(msg)
}
)
SendFile((getApplication() as Context).filesDir)


private var testClientJob: Job? = null
private var testServerJob: Job? = null
private var recvFileJob: Job? = null
private var sendFileJob: Job? = null

/**
* Sends multiple messages to a SRT server.
*
* Same as SRT examples/test-c-client.c
*/
fun launchTestClient() {
testClient.launch(configuration.clientIP, configuration.clientPort)
if (testClientJob?.isActive == true) {
Log.w(TAG, "Test client job is already running")
return
}
testClientJob = viewModelScope.launch {
try {
testClient.run(configuration.clientIP, configuration.clientPort)
success.postValue("Client success!")
} catch (e: Exception) {
Log.e(TAG, "Client error: ${e.message}", e)
error.postValue(e.message)
} finally {
testClientCompletion.postValue(true)
}
}
}

fun cancelTestClient() {
testClient.cancel()
Log.i(TAG, "Canceling test client job")
testClientJob?.cancelChildren()
testClientJob = null
}

/**
Expand All @@ -96,11 +82,27 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
* Same as SRT examples/test-c-server.c
*/
fun launchTestServer() {
testServer.launch(configuration.serverIP, configuration.serverPort)
if (testServerJob?.isActive == true) {
Log.w(TAG, "Test server job is already running")
return
}
testServerJob = viewModelScope.launch {
try {
testServer.run(configuration.serverIP, configuration.serverPort)
success.postValue("Server success!")
} catch (e: Exception) {
Log.e(TAG, "Server error: ${e.message}", e)
error.postValue(e.message)
} finally {
testServerCompletion.postValue(true)
}
}
}

fun cancelTestServer() {
testServer.cancel()
Log.i(TAG, "Canceling test server job")
testServerJob?.cancel()
testServerJob = null
}

/**
Expand All @@ -109,11 +111,27 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
* Same as SRT examples/recvfile.cpp
*/
fun launchRecvFile() {
recvFile.launch(configuration.clientIP, configuration.clientPort)
if (recvFileJob?.isActive == true) {
Log.w(TAG, "Recv file job is already running")
return
}
recvFileJob = viewModelScope.launch {
try {
recvFile.run(configuration.clientIP, configuration.clientPort)
success.postValue("RecvFile success!")
} catch (e: Exception) {
Log.e(TAG, "RecvFile error: ${e.message}", e)
error.postValue(e.message)
} finally {
recvFileCompletion.postValue(true)
}
}
}

fun cancelRecvFile() {
recvFile.cancel()
Log.i(TAG, "Canceling recv file job")
recvFileJob?.cancel()
recvFileJob = null
}

/**
Expand All @@ -123,10 +141,30 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
* Same as SRT examples/sendfile.cpp
*/
fun launchSendFile() {
sendFile.launch(configuration.serverIP, configuration.serverPort)
if (sendFileJob?.isActive == true) {
Log.w(TAG, "Send file job is already running")
return
}
sendFileJob = viewModelScope.launch {
try {
sendFile.run(configuration.serverIP, configuration.serverPort)
success.postValue("SendFile success!")
} catch (e: Exception) {
Log.e(TAG, "SendFile error: ${e.message}", e)
error.postValue(e.message)
} finally {
sendFileCompletion.postValue(true)
}
}
}

fun cancelSendFile() {
sendFile.cancel()
Log.i(TAG, "Canceling send file job")
sendFileJob?.cancel()
sendFileJob = null
}

companion object {
private val TAG = MainViewModel::class.simpleName
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,4 @@ object Utils {
Error.clearLastError()
return message
}

fun writeFile(file: File, text: String) {
FileOutputStream(file).use {
it.write(text.toByteArray())
}
}
}
Original file line number Diff line number Diff line change
@@ -1,56 +1,64 @@
package io.github.thibaultbee.srtdroid.example.tests

import android.util.Log
import com.google.common.primitives.Ints
import com.google.common.primitives.Longs
import io.github.thibaultbee.srtdroid.enums.SockOpt
import io.github.thibaultbee.srtdroid.enums.Transtype
import io.github.thibaultbee.srtdroid.example.Utils
import io.github.thibaultbee.srtdroid.models.Socket
import com.google.common.primitives.Ints
import com.google.common.primitives.Longs
import io.github.thibaultbee.srtdroid.ktx.CoroutineSocket
import io.github.thibaultbee.srtdroid.ktx.extensions.connect
import io.github.thibaultbee.srtdroid.ktx.extensions.isConnected
import io.github.thibaultbee.srtdroid.ktx.extensions.recvFile
import io.github.thibaultbee.srtdroid.ktx.extensions.send
import kotlinx.coroutines.delay
import java.io.File
import java.util.concurrent.ExecutorService

class RecvFile(
private val sendFileName: String,
private val recvFileName: String,
private val recvFileDir: File,
executorService: ExecutorService,
onSuccess: (String) -> Unit,
onError: (String) -> Unit
) : Test(executorService, onSuccess, onError) {
) : Test {
companion object {
private val TAG = RecvFile::class.simpleName
}

override val testName: String = this::class.simpleName!!
override val name: String = this::class.simpleName!!

override fun launchImpl(ip: String, port: Int, socket: Socket) {
override suspend fun run(ip: String, port: Int) {
Log.i(TAG, "Will get file $sendFileName from server")
val socket = CoroutineSocket()

if (!socket.isValid) {
throw Exception("Invalid socket")
}
try {
socket.setSockFlag(SockOpt.TRANSTYPE, Transtype.FILE)
socket.connect(ip, port)
Log.i(
TAG,
"Is connected: ${socket.isConnected}"
)

socket.setSockFlag(SockOpt.TRANSTYPE, Transtype.FILE)
socket.connect(ip, port)
// Request server file
socket.send(Ints.toByteArray(sendFileName.length).reversedArray())

// Request server file
socket.send(Ints.toByteArray(sendFileName.length).reversedArray())
socket.send(sendFileName)

socket.send(sendFileName)
val array = socket.recv(Longs.BYTES)
val fileSize = Longs.fromByteArray(array.reversedArray())

val fileSize = Longs.fromByteArray(socket.recv(Longs.BYTES).reversedArray())
// Where file will be written
val recvFile = File(recvFileDir, recvFileName)
Log.i(TAG, "Receiving file ${recvFile.path}")
if (fileSize != socket.recvFile(recvFile, 0, fileSize)) {
throw Exception("Failed to recv file: ${Utils.getErrorMessage()}")
}

// Where file will be written
val recvFile = File(recvFileDir, recvFileName)
if (fileSize != socket.recvFile(recvFile, 0, fileSize)) {
throw Exception("Failed to recv file: ${Utils.getErrorMessage()}")
// If session is close too early, last msg will not be receive by server
delay(1000)
Log.i(TAG, "Received file $recvFile")
} catch (e: Exception) {
throw e
} finally {
socket.close()
}

// If session is close too early, last msg will not be receive by server
Thread.sleep(1000)

successMsg = "Recv file is in $recvFile"
}

}
Loading

0 comments on commit 5586a5b

Please sign in to comment.