Browse Source

Trace headset endpoint changes and bound AAudio probe queue

master
evgeny 4 days ago
parent
commit
7a6ecf700c
  1. 4
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallAudioRouter.kt
  2. 6
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/UtunConnection.kt
  3. 13
      tools/chatgui-android/audio-probe/src/main/cpp/probe.cpp

4
tools/chatgui-android/app/src/main/java/com/utun/chat/data/CallAudioRouter.kt

@ -330,7 +330,11 @@ class CallAudioRouter {
/* ── мониторинг гарнитуры ── */
private fun onDevicesChanged() {
if (!started) return
val previous = devices?.map { "${it.id}/${it.type}/${it.isSink}/${it.isSource}" }?.sorted()
devices = am.getDevices(AudioManager.GET_DEVICES_ALL)
val current = devices?.map { "${it.id}/${it.type}/${it.isSink}/${it.isSource}" }?.sorted()
if (previous != current) LogManager.addLog("INFO", "CallAudioRouter",
"devices changed id/type/output/input=$current telecomHeadset=${telecom?.headsetAvailable} route=$route")
/* если в режиме HEADSET появилось SCO-устройство — пере-применяем маршрут,
* иначе SCO не замаршрутизируется автоматически */
if (telecom == null && route == AudioRoute.HEADSET) applyRoute(route)

6
tools/chatgui-android/app/src/main/java/com/utun/chat/data/UtunConnection.kt

@ -108,10 +108,12 @@ class UtunConnection(
}
override fun onAvailableCallEndpointsChanged(availableEndpoints: MutableList<CallEndpoint>) {
val changed = this.availableEndpoints.map { it.identifier to it.endpointType }.toSet() !=
availableEndpoints.map { it.identifier to it.endpointType }.toSet()
this.availableEndpoints.clear()
this.availableEndpoints.addAll(availableEndpoints)
LogManager.addLog("DEBUG", "UtunConn",
"availableEndpoints -> %s".format(availableEndpoints.joinToString { "${it.endpointName}/${it.endpointType}" }))
if (changed) LogManager.addLog("INFO", "UtunConn",
"endpoints id=$callId types=${availableEndpoints.map { it.endpointType }} headset=$headsetAvailable confirmed=$confirmedRoute")
onEndpointsChanged?.invoke()
wantedRoute?.let { routeTo(it) }
}

13
tools/chatgui-android/audio-probe/src/main/cpp/probe.cpp

@ -13,6 +13,7 @@ struct Probe {
int channels = 0;
std::atomic<int> error{0}, callbacks{0}, callbackFrames{0};
int previousError = 0;
int previousBuffer = -1;
};
static Probe probe;
@ -60,6 +61,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_utun_audioprobe_ProbeActivity_ope
return JNI_FALSE;
}
probe.channels = channels; probe.error = 0; probe.callbacks = 0; probe.callbackFrames = 0; probe.previousError = 0;
probe.previousBuffer = -1;
AAudioStreamBuilder* builder = nullptr;
if (!check_result("builder", AAudio_createStreamBuilder(&builder))) return JNI_FALSE;
AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
@ -100,9 +102,12 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_utun_audioprobe_ProbeActivity_ope
}
extern "C" JNIEXPORT jint JNICALL Java_com_utun_audioprobe_ProbeActivity_writeNative(JNIEnv* env, jclass, jshortArray input) {
int64_t queued = AAudioStream_getFramesWritten(probe.stream) - AAudioStream_getFramesRead(probe.stream);
int available = int(std::max<int64_t>(0, AAudioStream_getBufferSizeInFrames(probe.stream) - queued));
if (!available) return AAudioStream_getState(probe.stream) == AAUDIO_STREAM_STATE_DISCONNECTED ? AAUDIO_ERROR_DISCONNECTED : 0;
int16_t samples[960]{};
env->GetShortArrayRegion(input, 0, 960, samples);
return AAudioStream_write(probe.stream, samples, 960 / probe.channels, 0);
return AAudioStream_write(probe.stream, samples, std::min(available, 960 / probe.channels), 0);
}
/* Timestamp опрашивается вне realtime callback. Java одинаково агрегирует AAudio и AudioTrack. */
@ -111,6 +116,12 @@ extern "C" JNIEXPORT void JNICALL Java_com_utun_audioprobe_ProbeActivity_sampleN
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "probe invalid sample"); return;
}
auto* s = probe.stream;
int buffer = AAudioStream_getBufferSizeInFrames(s);
if (buffer != probe.previousBuffer) {
DEBUG_INFO(DEBUG_CATEGORY_CALL, "probe buffer=%d capacity=%d written=%lld read=%lld", buffer,
AAudioStream_getBufferCapacityInFrames(s), (long long)AAudioStream_getFramesWritten(s), (long long)AAudioStream_getFramesRead(s));
probe.previousBuffer = buffer;
}
int64_t frame = -1, stamp = -1;
int64_t written = AAudioStream_getFramesWritten(s);
int result = AAudioStream_getTimestamp(s, CLOCK_MONOTONIC, &frame, &stamp);

Loading…
Cancel
Save