Промежуточный коммит со звонками

This commit is contained in:
2026-03-26 00:31:35 +05:00
parent bc7efbfbd9
commit 3fffbd0392
2 changed files with 31 additions and 134 deletions

View File

@@ -72,13 +72,6 @@ struct GeneratedTsState {
uint32_t next_step = 960; // 20 ms @ 48 kHz (default Opus packetization)
};
struct AdditionalTsState {
bool initialized64 = false;
bool initialized32 = false;
uint64_t base64 = 0;
uint32_t base32 = 0;
};
static inline uint16_t load16_be(const uint8_t* p) {
return (uint16_t)(((uint16_t)p[0] << 8) | (uint16_t)p[1]);
}
@@ -90,27 +83,6 @@ static inline uint32_t load32_be(const uint8_t* p) {
((uint32_t)p[3]);
}
static inline uint64_t load64_be(const uint8_t* p) {
return ((uint64_t)p[0] << 56) |
((uint64_t)p[1] << 48) |
((uint64_t)p[2] << 40) |
((uint64_t)p[3] << 32) |
((uint64_t)p[4] << 24) |
((uint64_t)p[5] << 16) |
((uint64_t)p[6] << 8) |
((uint64_t)p[7]);
}
static inline void store64_be(uint8_t* p, uint64_t v) {
p[0] = (uint8_t)(v >> 56);
p[1] = (uint8_t)(v >> 48);
p[2] = (uint8_t)(v >> 40);
p[3] = (uint8_t)(v >> 32);
p[4] = (uint8_t)(v >> 24);
p[5] = (uint8_t)(v >> 16);
p[6] = (uint8_t)(v >> 8);
p[7] = (uint8_t)(v);
}
static bool parse_rtp_packet(const uint8_t* data, size_t len, ParsedRtpPacket* out) {
if (!data || !out || len < 12) return false;
@@ -216,27 +188,22 @@ static bool fill_nonce_from_rtp_frame(const uint8_t* data,
static bool fill_nonce_from_additional_data(const uint8_t* data,
size_t len,
uint8_t nonce[24],
AdditionalTsState* ts_state,
bool normalize_timestamps,
bool* used_normalized,
bool* used_rtp_header) {
if (used_normalized) *used_normalized = false;
if (used_rtp_header) *used_rtp_header = false;
if (!data || len < 8) return false;
// Common native WebRTC layout: additional_data is RTP header bytes.
// Desktop-compatible path: additional_data contains encoded frame timestamp
// as 8-byte BE value. Use it directly as nonce[0..7].
if (len == 8) {
memcpy(nonce, data, 8);
return true;
}
// Legacy native WebRTC layout: additional_data can be RTP header bytes.
if (len >= 12) {
const uint8_t version = (data[0] >> 6) & 0x03;
if (version == 2) {
uint32_t ts = load32_be(data + 4);
if (normalize_timestamps && ts_state) {
if (!ts_state->initialized32) {
ts_state->initialized32 = true;
ts_state->base32 = ts;
}
ts = (uint32_t)(ts - ts_state->base32);
if (used_normalized) *used_normalized = true;
}
nonce[4] = (uint8_t)(ts >> 24);
nonce[5] = (uint8_t)(ts >> 16);
nonce[6] = (uint8_t)(ts >> 8);
@@ -246,17 +213,8 @@ static bool fill_nonce_from_additional_data(const uint8_t* data,
}
}
// Generic 8-byte timestamp layout (desktop's nonce[0..7] layout).
uint64_t ts = load64_be(data);
if (normalize_timestamps && ts_state) {
if (!ts_state->initialized64) {
ts_state->initialized64 = true;
ts_state->base64 = ts;
}
ts = (uint64_t)(ts - ts_state->base64);
if (used_normalized) *used_normalized = true;
}
store64_be(nonce, ts);
// Generic fallback: first 8 bytes as BE timestamp-like payload.
memcpy(nonce, data, 8);
return true;
}
@@ -401,7 +359,6 @@ public:
bool nonce_from_rtp_header = false;
bool nonce_from_generated_ts = false;
bool nonce_from_additional_data = false;
bool nonce_from_additional_normalized = false;
bool additional_was_rtp_header = false;
uint32_t generated_ts_used = 0;
@@ -411,9 +368,6 @@ public:
additional_data.data(),
additional_data.size(),
nonce,
&additional_ts_,
true,
&nonce_from_additional_normalized,
&additional_was_rtp_header);
if (!nonce_from_additional_data) {
nonce_from_rtp_header =
@@ -464,9 +418,7 @@ public:
: (nonce_from_generated_ts
? "gen"
: (nonce_from_additional_data
? (additional_was_rtp_header
? (nonce_from_additional_normalized ? "ad-rtp-norm" : "ad-rtp")
: (nonce_from_additional_normalized ? "raw-norm" : "raw-abs"))
? (additional_was_rtp_header ? "ad-rtp" : "raw-abs")
: "raw-abs"));
LOGI("ENC frame#%d: sz=%zu ad=%zu hdr=%zu mode=%s nonce=%02x%02x%02x%02x",
n, frame.size(), additional_data.size(), header_size, mode,
@@ -490,7 +442,6 @@ private:
mutable std::atomic<int> diag_count_{0};
mutable RtpProbeState rtp_probe_;
mutable GeneratedTsState generated_ts_;
mutable AdditionalTsState additional_ts_;
uint8_t key_[32];
};
@@ -533,17 +484,12 @@ public:
bool nonce_from_rtp_header = false;
bool nonce_from_generated_ts = false;
bool nonce_from_additional_data = false;
bool nonce_from_additional_normalized = false;
bool additional_was_rtp_header = false;
bool used_absolute_additional_fallback = false;
uint32_t generated_ts_used = 0;
nonce_from_additional_data = fill_nonce_from_additional_data(
additional_data.data(),
additional_data.size(),
nonce,
&additional_ts_,
true,
&nonce_from_additional_normalized,
&additional_was_rtp_header);
if (!nonce_from_additional_data) {
nonce_from_rtp_header =
@@ -581,55 +527,6 @@ public:
rosetta_xchacha20_xor(frame.data(), encrypted_frame.data(), encrypted_frame.size(), nonce, key_);
}
// additional_data on Android can be absolute RTP-ish timestamp, while
// desktop nonce source is normalized stream timestamp. If normalized
// nonce gives implausible Opus, retry with absolute additional_data.
if (!nonce_from_generated_ts &&
nonce_from_additional_data &&
encrypted_frame.size() > 0 &&
additional_data.size() >= 8) {
const uint8_t* payload_ptr = frame.data() + header_size;
const size_t payload_size = encrypted_frame.size() - header_size;
if (!is_plausible_opus_packet(payload_ptr, payload_size)) {
uint8_t nonce_abs[24] = {0};
bool abs_norm = false;
bool abs_rtp = false;
if (fill_nonce_from_additional_data(
additional_data.data(),
additional_data.size(),
nonce_abs,
nullptr,
false,
&abs_norm,
&abs_rtp) &&
memcmp(nonce_abs, nonce, 24) != 0) {
if (nonce_from_rtp_header && header_size <= encrypted_frame.size()) {
if (header_size > 0) {
memcpy(frame.data(), encrypted_frame.data(), header_size);
}
rosetta_xchacha20_xor(
frame.data() + header_size,
encrypted_frame.data() + header_size,
payload_size,
nonce_abs,
key_);
} else {
rosetta_xchacha20_xor(
frame.data(),
encrypted_frame.data(),
encrypted_frame.size(),
nonce_abs,
key_);
}
payload_ptr = frame.data() + header_size;
if (is_plausible_opus_packet(payload_ptr, payload_size)) {
memcpy(nonce, nonce_abs, 24);
used_absolute_additional_fallback = true;
}
}
}
}
if (nonce_from_generated_ts) {
bool plausible = is_plausible_opus_packet(frame.data(), encrypted_frame.size());
@@ -669,13 +566,8 @@ public:
mode = "rtp";
} else if (nonce_from_generated_ts) {
mode = used_generated_resync ? "gen-resync" : "gen";
} else if (used_absolute_additional_fallback) {
mode = additional_was_rtp_header ? "ad-rtp-abs-fb" : "raw-abs-fb";
} else if (nonce_from_additional_data) {
mode =
additional_was_rtp_header
? (nonce_from_additional_normalized ? "ad-rtp-norm" : "ad-rtp")
: (nonce_from_additional_normalized ? "raw-norm" : "raw-abs");
mode = additional_was_rtp_header ? "ad-rtp" : "raw-abs";
} else {
mode = "raw-abs";
}
@@ -702,7 +594,6 @@ private:
mutable std::atomic<int> diag_count_{0};
mutable RtpProbeState rtp_probe_;
mutable GeneratedTsState generated_ts_;
mutable AdditionalTsState additional_ts_;
uint8_t key_[32];
};

View File

@@ -234,7 +234,12 @@ object CallManager {
}
fun endCall() {
breadcrumb("UI: endCall requested")
val callerTrace =
Throwable()
.stackTrace
.take(6)
.joinToString(" | ") { "${it.className}.${it.methodName}:${it.lineNumber}" }
breadcrumb("UI: endCall requested by $callerTrace")
resetSession(reason = null, notifyPeer = true)
}
@@ -605,29 +610,30 @@ object CallManager {
onCallConnected()
}
PeerConnection.PeerConnectionState.FAILED,
PeerConnection.PeerConnectionState.CLOSED -> {
disconnectResetJob?.cancel()
disconnectResetJob = null
// Dispatch to our scope — this callback fires on WebRTC thread
scope.launch {
resetSession(reason = "Connection lost", notifyPeer = false)
}
}
PeerConnection.PeerConnectionState.CLOSED,
PeerConnection.PeerConnectionState.DISCONNECTED -> {
// Desktop tolerates short network dips; do not kill call immediately.
// Desktop flow is signal-driven. Keep call alive across short
// transport glitches and only fallback-reset after timeout.
disconnectResetJob?.cancel()
disconnectResetJob =
scope.launch {
delay(5_000L)
val timeoutMs =
when (newState) {
PeerConnection.PeerConnectionState.DISCONNECTED -> 15_000L
PeerConnection.PeerConnectionState.FAILED -> 12_000L
PeerConnection.PeerConnectionState.CLOSED -> 12_000L
else -> 12_000L
}
delay(timeoutMs)
val pcState = peerConnection?.connectionState()
if (pcState == PeerConnection.PeerConnectionState.DISCONNECTED ||
pcState == PeerConnection.PeerConnectionState.FAILED ||
pcState == PeerConnection.PeerConnectionState.CLOSED
) {
breadcrumb("PC: DISCONNECTED timeout → reset")
breadcrumb("PC: $newState timeout ($timeoutMs ms) → reset")
resetSession(reason = "Connection lost", notifyPeer = false)
} else {
breadcrumb("PC: DISCONNECTED recovered (state=$pcState)")
breadcrumb("PC: $newState recovered (state=$pcState)")
}
}
}