Промежуточный коммит со звонками
This commit is contained in:
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
@@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user