diff --git a/tools/chatgui-android/video-tests/build.gradle.kts b/tools/chatgui-android/video-tests/build.gradle.kts index 60b91be5..a1206423 100644 --- a/tools/chatgui-android/video-tests/build.gradle.kts +++ b/tools/chatgui-android/video-tests/build.gradle.kts @@ -12,10 +12,14 @@ kotlin { kotlin.include("com/utun/chat/VideoHostApp.kt", "com/utun/chat/data/VideoEngineHost.kt", "com/utun/chat/data/CallVideoEngine.kt", "com/utun/chat/data/CallVideoBitrateRange.kt", "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") } } dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") + implementation("org.json:json:20240303") testImplementation("junit:junit:4.13.2") } tasks.test { testLogging { showStandardStreams = true } } diff --git a/tools/chatgui-android/video-tests/src/main/kotlin/com/utun/chat/VideoHostApp.kt b/tools/chatgui-android/video-tests/src/main/kotlin/com/utun/chat/VideoHostApp.kt index 276aba5b..7af82190 100644 --- a/tools/chatgui-android/video-tests/src/main/kotlin/com/utun/chat/VideoHostApp.kt +++ b/tools/chatgui-android/video-tests/src/main/kotlin/com/utun/chat/VideoHostApp.kt @@ -7,4 +7,10 @@ object ChatApplication { val instance = VideoHostApplication() } class VideoHostApplication { val configProvider = VideoHostConfig() } -object R { object string { const val call_video_unavailable = 1; const val call_video_mode_restored = 2 } } +object R { object string { + const val call_video_unavailable = 1; const val call_video_mode_restored = 2 + const val stream_ended = 3; const val stream_connecting = 4; const val stream_live = 5; const val stream_waiting_key = 6 + const val stream_waiting_decoder = 7; const val stream_group_closed = 8; const val chat_service_stopped = 9 + const val stream_source_changed = 10; const val stream_member_removed = 11; const val stream_failed = 12 + const val stream_decoder_unsupported = 13 +} } diff --git a/tools/chatgui-android/video-tests/src/main/kotlin/com/utun/chat/data/VideoHost.kt b/tools/chatgui-android/video-tests/src/main/kotlin/com/utun/chat/data/VideoHost.kt index 8a67bb1e..418d3732 100644 --- a/tools/chatgui-android/video-tests/src/main/kotlin/com/utun/chat/data/VideoHost.kt +++ b/tools/chatgui-android/video-tests/src/main/kotlin/com/utun/chat/data/VideoHost.kt @@ -17,6 +17,7 @@ object LogManager { entries.add(Entry(level, category, message)); System.err.println("[$level] [$category] $message") } fun videoTrace(message: String) { addLog("DEBUG", "video", message) } + fun videoDebug(message: String) { addLog("DEBUG", "video", message) } } /** Граница аппаратной пробы; сам RX и разбор uAV2 KEY компилируются из приложения. */ @@ -64,6 +65,33 @@ internal class CallVideoCodec(val format: CallVideoFormat, var bitrate: Int, val 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 streamStop(id: Long) = HostStreams.stop(id) + fun streamSignalCreate(id: Long) = HostStreams.createSignal() + fun streamSignalClose(handle: Long) = HostStreams.closeSignal(handle) + fun streamSignalNotify(handle: Long) = HostStreams.notifySignal(handle) + fun streamSignalWait(handle: Long, timeout: Int) = HostStreams.waitSignal(handle) + fun streamStatus(id: Long): LongArray? = if (HostStreams.status) + longArrayOf(10_000_000, 10_000_000, 10_000_000, 1000, 1, if (HostStreams.audioEnabled) 3 else 1) else null + fun streamPull(id: Long, position: Long, video: Int, audioAhead: Long, count: Int): ByteArray? { + HostStreams.onPull(); HostStreams.access("pull"); return HostStreams.records.poll() + } + fun streamTelemetry(id: Long): ByteArray? = null + fun streamResync(id: Long): Boolean { HostStreams.access("resync"); return true } + fun streamPlaybackCreate(id: Long, pts: Long, audio: Boolean) = HostStreams.createPlayback() + fun streamPlaybackClose(handle: Long) = HostStreams.closePlayback(handle) + fun streamPlaybackEmpty(handle: Long) = true + fun streamPlaybackNeedsInput(handle: Long) = true + fun streamPlaybackBuffered(handle: Long) = 0 + fun streamPlaybackPrepare(handle: Long) = 0 + fun streamPlaybackFeed(handle: Long, pts: Long, packet: ByteArray): Int { HostStreams.access("feed"); return 0 } + fun streamPlaybackFinish(handle: Long) {} + fun streamPlaybackPull(handle: Long, pcm: ShortArray, frames: Int, limit: Long) = frames + fun streamPlaybackSilence(handle: Long, frames: Int) {} + 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) + } } /** Native playout/encoded FIFO: только управляемые границы, без загрузки JNI/декодирования/сетевого ядра. */