feat: handle webrtc data channel to send commands
This commit is contained in:
parent
22570e0e6d
commit
c8a0c4160c
@ -34,7 +34,7 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
private val bluetoothService by lazy { BluetoothService(applicationContext) }
|
||||
private val deviceService by lazy { DeviceService(applicationContext, webClient, websocketClient) }
|
||||
private val controllerService by lazy { ControllerService(bluetoothService) }
|
||||
private val controllerService by lazy { ControllerService(bluetoothService, webRtcService) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
package com.example.tvcontroller.data
|
||||
|
||||
class RemoteCommand {
|
||||
var functionName: String? = null
|
||||
var protocol: String? = null
|
||||
var device: String? = null
|
||||
var subdevice: String? = null
|
||||
var function: String? = null
|
||||
var command: String? = null
|
||||
}
|
||||
@ -1,32 +1,33 @@
|
||||
package com.example.tvcontroller.services
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.example.tvcontroller.data.RemoteCommand
|
||||
import com.example.tvcontroller.services.webrtc.WebRtcService
|
||||
import org.json.JSONObject
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
|
||||
class ControllerService(
|
||||
private val bluetoothService: BluetoothService
|
||||
private val bluetoothService: BluetoothService,
|
||||
private val webRtcService: WebRtcService
|
||||
) {
|
||||
private val samsungCommands = mutableMapOf<String, RemoteCommand>()
|
||||
|
||||
init {
|
||||
loadCommands()
|
||||
webRtcService.onDataChannelData(this::handleWebRtcData)
|
||||
}
|
||||
|
||||
fun sendCommand(command: String) {
|
||||
if (samsungCommands[command] == null) return
|
||||
Log.i("ControllerService", "Sending command: $command")
|
||||
val jsonString = remoteCommandToJsonString(samsungCommands[command]!!)
|
||||
fun sendCommand(command: RemoteCommand) {
|
||||
val jsonString = remoteCommandToJsonString(command)
|
||||
Log.i(TAG, "Sending command: $jsonString")
|
||||
sendData(jsonString)
|
||||
}
|
||||
|
||||
fun remoteCommandToJsonString(command: RemoteCommand): String {
|
||||
var commandObject = JSONObject()
|
||||
commandObject.put("protocol", command.protocol)
|
||||
commandObject.put("address", command.device)
|
||||
commandObject.put("command", command.function)
|
||||
commandObject.put("device", command.device)
|
||||
commandObject.put("command", command.command)
|
||||
return commandObject.toString()
|
||||
}
|
||||
|
||||
@ -37,7 +38,27 @@ class ControllerService(
|
||||
fun loadCommands() {
|
||||
}
|
||||
|
||||
fun handleWebRtcData(data: ByteBuffer) {
|
||||
val dataString = StandardCharsets.UTF_8.decode(data).toString()
|
||||
val json = JSONObject(dataString)
|
||||
if (!json.has("type") || json.getString("type") != MESSAGE_TYPE_COMMAND) return
|
||||
val commandJson = json.getJSONObject("data")
|
||||
val protocol = if (commandJson.has("protocol")) commandJson.getString("protocol") else null
|
||||
val device = if (commandJson.has("device")) commandJson.getString("device") else null
|
||||
val command = if (commandJson.has("commandNumber")) commandJson.getString("commandNumber") else null
|
||||
val remoteCommand = RemoteCommand().apply {
|
||||
this.protocol = protocol
|
||||
this.device = device
|
||||
this.command = command
|
||||
}
|
||||
sendCommand(remoteCommand)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ControllerService"
|
||||
|
||||
const val MESSAGE_TYPE_COMMAND = "command"
|
||||
|
||||
const val POWER = "POWER"
|
||||
const val CURSOR_UP = "CURSOR UP"
|
||||
const val CURSOR_DOWN = "CURSOR DOWN"
|
||||
|
||||
@ -1,16 +1,11 @@
|
||||
package com.example.tvcontroller.services.webrtc
|
||||
|
||||
import android.content.Context
|
||||
import android.hardware.camera2.CameraCharacteristics
|
||||
import android.hardware.camera2.CameraManager
|
||||
import android.util.Log
|
||||
import com.example.tvcontroller.services.CameraService
|
||||
import org.webrtc.AudioTrack
|
||||
import org.webrtc.Camera2Capturer
|
||||
import org.webrtc.DataChannel
|
||||
import org.webrtc.DefaultVideoDecoderFactory
|
||||
import org.webrtc.EglBase
|
||||
import org.webrtc.HardwareVideoEncoderFactory
|
||||
import org.webrtc.IceCandidate
|
||||
import org.webrtc.MediaConstraints
|
||||
import org.webrtc.MediaStream
|
||||
@ -20,11 +15,11 @@ import org.webrtc.PeerConnectionFactory
|
||||
import org.webrtc.PeerConnectionFactory.InitializationOptions
|
||||
import org.webrtc.SdpObserver
|
||||
import org.webrtc.SessionDescription
|
||||
import org.webrtc.SimulcastVideoEncoderFactory
|
||||
import org.webrtc.SoftwareVideoEncoderFactory
|
||||
import org.webrtc.SurfaceTextureHelper
|
||||
import org.webrtc.VideoSource
|
||||
import org.webrtc.VideoTrack
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
private const val TAG = "RtcPeerConnection"
|
||||
|
||||
@ -37,6 +32,7 @@ class RtcPeerConnection(private val context: Context, private val cameraService:
|
||||
private val videoSource by lazy { createVideoSource() }
|
||||
private var peerConnection: PeerConnection? = null
|
||||
private val iceCandidateHandlers = ArrayList<((IceCandidate) -> Unit)>()
|
||||
private val dataChannelHandlers = ArrayList<((ByteBuffer) -> Unit)>()
|
||||
|
||||
|
||||
fun initialize() {
|
||||
@ -52,7 +48,16 @@ class RtcPeerConnection(private val context: Context, private val cameraService:
|
||||
override fun onIceCandidatesRemoved(p0: Array<out IceCandidate?>?) {}
|
||||
override fun onAddStream(p0: MediaStream?) {}
|
||||
override fun onRemoveStream(p0: MediaStream?) {}
|
||||
override fun onDataChannel(p0: DataChannel?) {}
|
||||
override fun onDataChannel(channel: DataChannel?) {
|
||||
Log.i(TAG, "Data channel created: $channel")
|
||||
channel?.registerObserver(object : DataChannel.Observer {
|
||||
override fun onBufferedAmountChange(p0: Long) { }
|
||||
override fun onStateChange() { }
|
||||
override fun onMessage(p0: DataChannel.Buffer?) {
|
||||
dataChannelHandlers.forEach { it(p0?.data!!) }
|
||||
}
|
||||
})
|
||||
}
|
||||
override fun onRenegotiationNeeded() {}
|
||||
}
|
||||
var rtcConfig = PeerConnection.RTCConfiguration(iceServers)
|
||||
@ -132,6 +137,10 @@ class RtcPeerConnection(private val context: Context, private val cameraService:
|
||||
iceCandidateHandlers.add(handler)
|
||||
}
|
||||
|
||||
fun onDataChannelData(handler: (ByteBuffer) -> Unit) {
|
||||
dataChannelHandlers.add(handler)
|
||||
}
|
||||
|
||||
private fun initializeFactory(): PeerConnectionFactory {
|
||||
val initOptions = InitializationOptions.builder(context).createInitializationOptions()
|
||||
PeerConnectionFactory.initialize(initOptions)
|
||||
|
||||
@ -9,12 +9,14 @@ import org.json.JSONObject
|
||||
import org.webrtc.IceCandidate
|
||||
import org.webrtc.MediaConstraints
|
||||
import org.webrtc.SessionDescription
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class WebRtcService(
|
||||
private val context: Context,
|
||||
private val websocketClient: WebsocketClient,
|
||||
private val cameraService: CameraService
|
||||
) {
|
||||
private val dataChannelHandlers = ArrayList<((ByteBuffer) -> Unit)>()
|
||||
private var rtcPeerConnection: RtcPeerConnection = createRtcPeerConnection()
|
||||
val videoTrack by lazy { rtcPeerConnection.createVideoTrack() }
|
||||
val audioTrack by lazy { rtcPeerConnection.createAudioTrack() }
|
||||
@ -25,6 +27,10 @@ class WebRtcService(
|
||||
websocketClient.onData(this::handleData)
|
||||
}
|
||||
|
||||
fun onDataChannelData(handler: (ByteBuffer) -> Unit) {
|
||||
dataChannelHandlers.add(handler)
|
||||
}
|
||||
|
||||
private fun createRtcPeerConnection(): RtcPeerConnection {
|
||||
val iceServers = arrayOf("stun:stun.l.google.com:19302")
|
||||
val webRtcService = this
|
||||
@ -33,6 +39,7 @@ class WebRtcService(
|
||||
onIceCandidate(webRtcService::sendIceCandidate)
|
||||
initialize()
|
||||
}
|
||||
dataChannelHandlers.forEach { rtcPeerConnection.onDataChannelData(it) }
|
||||
return rtcPeerConnection
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ package com.example.tvcontroller.ui.views
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.tvcontroller.data.RemoteCommand
|
||||
import com.example.tvcontroller.services.BluetoothService
|
||||
import com.example.tvcontroller.services.ControllerService
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -12,8 +13,10 @@ import org.json.JSONObject
|
||||
class RemoteViewModel(
|
||||
private val controllerService: ControllerService
|
||||
) : ViewModel() {
|
||||
private val commands = mutableMapOf<String, RemoteCommand>()
|
||||
|
||||
fun sendCommand(command: String) {
|
||||
fun sendCommand(commandType: String) {
|
||||
val command = commands[commandType]?: return
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
controllerService.sendCommand(command)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user