Compile real publisher capture with controlled microphone and native boundaries
This commit is contained in:
1 parent
7a2fc0112c
commit
41a4a2e37e
3 files changed
+42
-3
No files matched your search
@@ -14,7 +14,8 @@ kotlin {
|
||||
"com/utun/chat/data/CallVideoCadence.kt", "com/utun/chat/data/CallVideoOrientation.kt")
|
||||
kotlin.include("com/utun/chat/data/StreamHost.kt", "com/utun/chat/data/StreamPlayerEngine.kt",
|
||||
"com/utun/chat/data/StreamAudioMixer.kt", "com/utun/chat/data/StreamAudioOutput.kt",
|
||||
"com/utun/chat/data/StreamAudioMix.kt", "com/utun/chat/data/StreamReadQueue.kt", "com/utun/chat/data/StreamPlaybackTelemetry.kt")
|
||||
"com/utun/chat/data/StreamAudioMix.kt", "com/utun/chat/data/StreamReadQueue.kt", "com/utun/chat/data/StreamPlaybackTelemetry.kt",
|
||||
"com/utun/chat/data/StreamCaptureEngine.kt", "com/utun/chat/data/StreamCaptureConfig.kt")
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
|
||||
@@ -5,6 +5,27 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
typealias RadioCompressorSettings = Any
|
||||
data class StreamCaptureMode(val camera: String = "0", val width: Int = 640, val height: Int = 480, val fps: Int = 30,
|
||||
val minimumFps: Int = 30, val highSpeed: Boolean = false, val codec: String = "host.encoder", val bitrate: Int = 1_000_000,
|
||||
val videoCodec: StreamVideoCodec = StreamVideoCodec.H264, val minimumBitrate: Int = 1, val maximumBitrate: Int = 12_000_000,
|
||||
val encoderProfile: Int? = 1) {
|
||||
val label get() = "${width}x$height@$fps"
|
||||
val manualBitrateRange get() = minimumBitrate..maximumBitrate
|
||||
}
|
||||
object MicrophoneCapture {
|
||||
data class Frame(val pcm: ShortArray, val timeNs: Long, val sequence: Long)
|
||||
class Subscription : AutoCloseable {
|
||||
val queue = LinkedBlockingQueue<Frame>()
|
||||
var failures = emptySet<String>()
|
||||
var closed = false
|
||||
fun poll(): Any? = queue.poll(20, TimeUnit.MILLISECONDS)
|
||||
override fun close() { check("close" !in failures); closed = true }
|
||||
}
|
||||
}
|
||||
|
||||
object CallController { var blocked = false; fun blocksMedia() = blocked }
|
||||
object StreamController { private val ids = AtomicLong(100); fun nextId() = ids.incrementAndGet() }
|
||||
@@ -13,6 +34,11 @@ object MediaAudioController {
|
||||
val state = MutableStateFlow(State())
|
||||
var failures = emptySet<String>()
|
||||
val outputs = ConcurrentHashMap.newKeySet<AudioTrack>()
|
||||
val subscriptions = ConcurrentLinkedQueue<MicrophoneCapture.Subscription>()
|
||||
fun subscribe(name: String): MicrophoneCapture.Subscription {
|
||||
check("subscribe" !in failures)
|
||||
return MicrophoneCapture.Subscription().also { subscriptions.add(it) }
|
||||
}
|
||||
fun attachOutput(track: AudioTrack): AutoCloseable {
|
||||
check("attach" !in failures); outputs.add(track)
|
||||
return AutoCloseable { check("detach" !in failures); outputs.remove(track) }
|
||||
@@ -24,6 +50,7 @@ object HostStreams {
|
||||
val ports = ConcurrentHashMap.newKeySet<Long>()
|
||||
val signals = ConcurrentHashMap.newKeySet<Long>()
|
||||
val playbacks = ConcurrentHashMap.newKeySet<Long>()
|
||||
val opuses = ConcurrentHashMap.newKeySet<Long>()
|
||||
val records = ConcurrentLinkedQueue<ByteArray>()
|
||||
private val sequence = AtomicLong()
|
||||
@Volatile var failures = emptySet<String>()
|
||||
@@ -40,8 +67,10 @@ object HostStreams {
|
||||
fun waitSignal(handle: Long): Int { onWait(); access("signal wait"); check(handle in signals); Thread.sleep(2); return 0 }
|
||||
fun createPlayback(): Long { access("playback create"); return sequence.incrementAndGet().also { playbacks.add(it) } }
|
||||
fun closePlayback(handle: Long) { access("playback close"); check(playbacks.remove(handle)) }
|
||||
fun createOpus(): Long { access("opus create"); return sequence.incrementAndGet().also { opuses.add(it) } }
|
||||
fun closeOpus(handle: Long) { access("opus close"); check(opuses.remove(handle)) }
|
||||
fun reset() {
|
||||
ports.clear(); signals.clear(); playbacks.clear(); records.clear(); failures = emptySet()
|
||||
ports.clear(); signals.clear(); playbacks.clear(); opuses.clear(); records.clear(); failures = emptySet()
|
||||
audioEnabled = false; status = false; onWait = {}; onPull = {}
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,8 @@ object NativeLib {
|
||||
fun callAudioClock(id: Long, now: Long, out: LongArray): Boolean { out[5] = 1; return true }
|
||||
fun setChatSetting(name: String, value: String) {}
|
||||
fun isReady() = true
|
||||
fun streamOpen(id: Long, group: Long, source: Long, audio: Boolean, epoch: Long) = HostStreams.open(id)
|
||||
fun streamOpen(id: Long, group: Long, source: Long = 0, width: Int = 0, height: Int = 0, rotation: Int = 0,
|
||||
audio: Boolean = true, videoCodec: Int = 1, epoch: Long = 0) = HostStreams.open(id)
|
||||
fun streamStop(id: Long) = HostStreams.stop(id)
|
||||
fun streamSignalCreate(id: Long) = HostStreams.createSignal()
|
||||
fun streamSignalClose(handle: Long) = HostStreams.closeSignal(handle)
|
||||
@@ -92,6 +93,14 @@ object NativeLib {
|
||||
fun streamPlaybackUpdate(handle: Long, now: Long, played: Long, ready: Long, guard: Long, starved: Boolean): LongArray {
|
||||
HostStreams.access("update"); return longArrayOf(1000 + played * 1_000_000 / 48_000, 0, 0, 1000, 0, 1, 0, 0, 3, 0, 0)
|
||||
}
|
||||
fun streamSetAudioEnabled(id: Long, enabled: Boolean): Boolean { HostStreams.access("audio enabled"); return true }
|
||||
fun streamFeed(id: Long, video: Boolean, pts: Long, key: Boolean, data: ByteArray, revision: Long = 0, width: Int = 0,
|
||||
height: Int = 0, rotation: Int = 0, audio: Boolean = true, codec: Int = 1): Int { HostStreams.access("feed"); return 1 }
|
||||
fun streamOpusCreate(encode: Boolean) = HostStreams.createOpus()
|
||||
fun streamOpusClose(handle: Long) = HostStreams.closeOpus(handle)
|
||||
fun streamOpusEncode(handle: Long, pcm: ShortArray, compressor: RadioCompressorSettings): ByteArray? {
|
||||
HostStreams.access("opus encode"); check(handle in HostStreams.opuses); return byteArrayOf(1)
|
||||
}
|
||||
}
|
||||
|
||||
/** Native playout/encoded FIFO: только управляемые границы, без загрузки JNI/декодирования/сетевого ядра. */
|
||||
|
||||
Reference in new issue
Block a user