Compare commits
3 Commits
199e7d1f1c
...
ad9281b609
| Author | SHA1 | Date | |
|---|---|---|---|
| ad9281b609 | |||
| 33a0298735 | |||
| ba5e7aa7f8 |
@@ -7,10 +7,11 @@ import org.freedesktop.dbus.interfaces.DBusInterface
|
|||||||
import org.freedesktop.dbus.interfaces.DBusSigHandler
|
import org.freedesktop.dbus.interfaces.DBusSigHandler
|
||||||
import org.freedesktop.dbus.messages.DBusSignal
|
import org.freedesktop.dbus.messages.DBusSignal
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.util.Queue
|
import java.util.concurrent.BlockingQueue
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
class DBus(private val q: Queue<Op>) {
|
class DBus(private val q: BlockingQueue<Op>) {
|
||||||
|
|
||||||
private val thread = thread(
|
private val thread = thread(
|
||||||
start = true,
|
start = true,
|
||||||
@@ -27,7 +28,11 @@ class DBus(private val q: Queue<Op>) {
|
|||||||
log.info("signal: ${signal.op}")
|
log.info("signal: ${signal.op}")
|
||||||
val op = Op.valueOf(signal.op)
|
val op = Op.valueOf(signal.op)
|
||||||
log.info("op: ${op}")
|
log.info("op: ${op}")
|
||||||
q.offer(op)
|
try {
|
||||||
|
q.offer(op, 1, TimeUnit.SECONDS)
|
||||||
|
} catch (e:InterruptedException) {
|
||||||
|
log.debug("queue offer interrupted")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
package net.eksb.obsdc
|
|
||||||
|
|
||||||
fun interface Handler {
|
|
||||||
fun handle(op:Op)
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,6 @@ object Main {
|
|||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
val q:BlockingQueue<Op> = LinkedBlockingQueue()
|
val q:BlockingQueue<Op> = LinkedBlockingQueue()
|
||||||
DBus(q) // forks daemon thread
|
DBus(q) // forks daemon thread
|
||||||
ObsCommunity(q).run() // blocks
|
Obs(q).run() // blocks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
129
src/main/kotlin/net/eksb/obsdc/Obs.kt
Normal file
129
src/main/kotlin/net/eksb/obsdc/Obs.kt
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package net.eksb.obsdc
|
||||||
|
|
||||||
|
import io.obswebsocket.community.client.OBSRemoteController
|
||||||
|
import io.obswebsocket.community.client.message.event.ui.StudioModeStateChangedEvent
|
||||||
|
import io.obswebsocket.community.client.model.Scene
|
||||||
|
import io.obswebsocket.community.client.model.SceneItem.Transform
|
||||||
|
import io.obswebsocket.community.client.model.SceneItem.Transform.TransformBuilder
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.util.concurrent.BlockingQueue
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
// protocol docs: https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md
|
||||||
|
class Obs(private val q:BlockingQueue<Op>): AutoCloseable {
|
||||||
|
|
||||||
|
private val panAmount = 50F
|
||||||
|
|
||||||
|
private val controller = OBSRemoteController.builder()
|
||||||
|
.host("localhost")
|
||||||
|
.port(4455)
|
||||||
|
.password("R3tRkVXhFofJ2wRF") // TODO put this in a file
|
||||||
|
.autoConnect(false)
|
||||||
|
.connectionTimeout(3)
|
||||||
|
.lifecycle()
|
||||||
|
.onReady(::ready)
|
||||||
|
.onClose { code -> log.error("closed:${code}")}
|
||||||
|
.onControllerError { e -> log.error("controller error", e ) }
|
||||||
|
.onCommunicatorError { e -> log.error("comm error", e ) }
|
||||||
|
.onDisconnect { log.info("disconnected") }
|
||||||
|
.and()
|
||||||
|
.registerEventListener(StudioModeStateChangedEvent::class.java) {
|
||||||
|
log.info("studio mode state change: ${it}")
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private var alive = true
|
||||||
|
|
||||||
|
init {
|
||||||
|
Runtime.getRuntime().addShutdownHook(thread(start=false) {
|
||||||
|
log.info("shutdown")
|
||||||
|
alive = false
|
||||||
|
controller.stop()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun run() {
|
||||||
|
controller.connect()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
controller.disconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ready() {
|
||||||
|
log.info("ready")
|
||||||
|
while(alive) {
|
||||||
|
val op: Op? = q.poll(1, TimeUnit.SECONDS) // blocks
|
||||||
|
if (op != null) {
|
||||||
|
log.info("op: ${op}")
|
||||||
|
op(op)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun op(op:Op) {
|
||||||
|
when(op) {
|
||||||
|
Op.SCENE_1 -> scene { scenes -> scenes.firstOrNull() }
|
||||||
|
Op.SCENE_2 -> scene { scenes -> scenes.asSequence().drop(1).firstOrNull() }
|
||||||
|
Op.STUDIO_TRANSITION -> {
|
||||||
|
controller.triggerStudioModeTransition { response ->
|
||||||
|
// This does not get called?
|
||||||
|
log.info("Response successful: ${response.isSuccessful}")
|
||||||
|
ready()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Op.PAN_UP -> pan { old -> positionY(old.positionY - panAmount ) }
|
||||||
|
Op.PAN_DOWN -> pan { old -> positionY(old.positionY + panAmount ) }
|
||||||
|
Op.PAN_LEFT -> pan { old -> positionX(old.positionX - panAmount ) }
|
||||||
|
Op.PAN_RIGHT -> pan { old -> positionX(old.positionX + panAmount ) }
|
||||||
|
Op.TODO -> {
|
||||||
|
log.info("OP=TODO")
|
||||||
|
ready()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scene(selector:(List<Scene>)->Scene?) {
|
||||||
|
controller.getSceneList { response ->
|
||||||
|
val scene = selector(response.scenes.sortedBy(Scene::getSceneIndex).reversed())
|
||||||
|
log.info("select scene ${scene?.sceneName} index:${scene?.sceneIndex}")
|
||||||
|
if (scene != null) {
|
||||||
|
controller.setCurrentProgramScene(scene.sceneName) { response ->
|
||||||
|
ready()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ready()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pan(block:TransformBuilder.(Transform)->TransformBuilder) {
|
||||||
|
controller.getCurrentProgramScene { response ->
|
||||||
|
val sceneName = response.currentProgramSceneName
|
||||||
|
log.info("scene name: ${sceneName}")
|
||||||
|
controller.getSceneItemList(sceneName) { response ->
|
||||||
|
val item = response.sceneItems.last()
|
||||||
|
val itemId = item.sceneItemId
|
||||||
|
log.info("last item id: ${itemId}")
|
||||||
|
controller.getSceneItemTransform(sceneName, itemId) { response ->
|
||||||
|
val transform = response.sceneItemTransform
|
||||||
|
log.info("position: ${transform.positionX} x ${transform.positionY}")
|
||||||
|
val newTransform = block(Transform.builder(), transform).build()
|
||||||
|
controller.setSceneItemTransform(sceneName, itemId, newTransform) { response ->
|
||||||
|
log.info("transform successful: ${response.isSuccessful}")
|
||||||
|
// Have to set the current scene to take effect if in studio mode.
|
||||||
|
controller.setCurrentProgramScene(sceneName) { response ->
|
||||||
|
ready()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val log = LoggerFactory.getLogger(Obs::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
package net.eksb.obsdc
|
|
||||||
|
|
||||||
import io.obswebsocket.community.client.OBSRemoteController
|
|
||||||
import io.obswebsocket.community.client.message.event.ui.StudioModeStateChangedEvent
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import java.util.concurrent.BlockingQueue
|
|
||||||
|
|
||||||
// protocol docs: https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md
|
|
||||||
class ObsCommunity(private val q:BlockingQueue<Op>): AutoCloseable {
|
|
||||||
|
|
||||||
private val controller = OBSRemoteController.builder()
|
|
||||||
.host("localhost")
|
|
||||||
.port(4455)
|
|
||||||
.password("R3tRkVXhFofJ2wRF") // TODO put this in a file
|
|
||||||
.autoConnect(false)
|
|
||||||
.connectionTimeout(3)
|
|
||||||
.lifecycle()
|
|
||||||
.onReady(::ready)
|
|
||||||
.onClose { code -> log.error("closed:${code}")}
|
|
||||||
.onControllerError { e -> log.error("controller error", e ) }
|
|
||||||
.onCommunicatorError { e -> log.error("comm error", e ) }
|
|
||||||
.onDisconnect { log.info("disconnected") }
|
|
||||||
.and()
|
|
||||||
.registerEventListener(StudioModeStateChangedEvent::class.java) {
|
|
||||||
log.info("studio mode state change: ${it}")
|
|
||||||
}
|
|
||||||
.build()
|
|
||||||
|
|
||||||
fun run() {
|
|
||||||
controller.connect()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
controller.disconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ready() {
|
|
||||||
log.info("ready")
|
|
||||||
val op: Op = q.take() // blocks
|
|
||||||
log.info("op: ${op}")
|
|
||||||
op(op)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun op(op:Op) {
|
|
||||||
when(op) {
|
|
||||||
Op.SCENE_TEST -> {
|
|
||||||
controller.setCurrentProgramScene("test") { response ->
|
|
||||||
log.info("set scene response: ${response.isSuccessful}")
|
|
||||||
ready()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Op.STUDIO_TRANSITION -> {
|
|
||||||
controller.triggerStudioModeTransition { response ->
|
|
||||||
// This does not get called?
|
|
||||||
log.info("Response successful: ${response.isSuccessful}")
|
|
||||||
ready()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Op.TODO -> {
|
|
||||||
log.info("OP=TODO")
|
|
||||||
ready()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val log = LoggerFactory.getLogger(ObsCommunity::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,12 @@ package net.eksb.obsdc
|
|||||||
|
|
||||||
enum class Op {
|
enum class Op {
|
||||||
STUDIO_TRANSITION,
|
STUDIO_TRANSITION,
|
||||||
SCENE_TEST,
|
SCENE_1,
|
||||||
|
SCENE_2,
|
||||||
|
PAN_UP,
|
||||||
|
PAN_DOWN,
|
||||||
|
PAN_LEFT,
|
||||||
|
PAN_RIGHT,
|
||||||
TODO,
|
TODO,
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user