Промежуточный коммит со звонками
This commit is contained in:
@@ -72,13 +72,6 @@ struct GeneratedTsState {
|
|||||||
uint32_t next_step = 960; // 20 ms @ 48 kHz (default Opus packetization)
|
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) {
|
static inline uint16_t load16_be(const uint8_t* p) {
|
||||||
return (uint16_t)(((uint16_t)p[0] << 8) | (uint16_t)p[1]);
|
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]);
|
((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) {
|
static bool parse_rtp_packet(const uint8_t* data, size_t len, ParsedRtpPacket* out) {
|
||||||
if (!data || !out || len < 12) return false;
|
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,
|
static bool fill_nonce_from_additional_data(const uint8_t* data,
|
||||||
size_t len,
|
size_t len,
|
||||||
uint8_t nonce[24],
|
uint8_t nonce[24],
|
||||||
AdditionalTsState* ts_state,
|
|
||||||
bool normalize_timestamps,
|
|
||||||
bool* used_normalized,
|
|
||||||
bool* used_rtp_header) {
|
bool* used_rtp_header) {
|
||||||
if (used_normalized) *used_normalized = false;
|
|
||||||
if (used_rtp_header) *used_rtp_header = false;
|
if (used_rtp_header) *used_rtp_header = false;
|
||||||
if (!data || len < 8) return 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) {
|
if (len >= 12) {
|
||||||
const uint8_t version = (data[0] >> 6) & 0x03;
|
const uint8_t version = (data[0] >> 6) & 0x03;
|
||||||
if (version == 2) {
|
if (version == 2) {
|
||||||
uint32_t ts = load32_be(data + 4);
|
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[4] = (uint8_t)(ts >> 24);
|
||||||
nonce[5] = (uint8_t)(ts >> 16);
|
nonce[5] = (uint8_t)(ts >> 16);
|
||||||
nonce[6] = (uint8_t)(ts >> 8);
|
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).
|
// Generic fallback: first 8 bytes as BE timestamp-like payload.
|
||||||
uint64_t ts = load64_be(data);
|
memcpy(nonce, data, 8);
|
||||||
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);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,7 +359,6 @@ public:
|
|||||||
bool nonce_from_rtp_header = false;
|
bool nonce_from_rtp_header = false;
|
||||||
bool nonce_from_generated_ts = false;
|
bool nonce_from_generated_ts = false;
|
||||||
bool nonce_from_additional_data = false;
|
bool nonce_from_additional_data = false;
|
||||||
bool nonce_from_additional_normalized = false;
|
|
||||||
bool additional_was_rtp_header = false;
|
bool additional_was_rtp_header = false;
|
||||||
uint32_t generated_ts_used = 0;
|
uint32_t generated_ts_used = 0;
|
||||||
|
|
||||||
@@ -411,9 +368,6 @@ public:
|
|||||||
additional_data.data(),
|
additional_data.data(),
|
||||||
additional_data.size(),
|
additional_data.size(),
|
||||||
nonce,
|
nonce,
|
||||||
&additional_ts_,
|
|
||||||
true,
|
|
||||||
&nonce_from_additional_normalized,
|
|
||||||
&additional_was_rtp_header);
|
&additional_was_rtp_header);
|
||||||
if (!nonce_from_additional_data) {
|
if (!nonce_from_additional_data) {
|
||||||
nonce_from_rtp_header =
|
nonce_from_rtp_header =
|
||||||
@@ -464,9 +418,7 @@ public:
|
|||||||
: (nonce_from_generated_ts
|
: (nonce_from_generated_ts
|
||||||
? "gen"
|
? "gen"
|
||||||
: (nonce_from_additional_data
|
: (nonce_from_additional_data
|
||||||
? (additional_was_rtp_header
|
? (additional_was_rtp_header ? "ad-rtp" : "raw-abs")
|
||||||
? (nonce_from_additional_normalized ? "ad-rtp-norm" : "ad-rtp")
|
|
||||||
: (nonce_from_additional_normalized ? "raw-norm" : "raw-abs"))
|
|
||||||
: "raw-abs"));
|
: "raw-abs"));
|
||||||
LOGI("ENC frame#%d: sz=%zu ad=%zu hdr=%zu mode=%s nonce=%02x%02x%02x%02x",
|
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,
|
n, frame.size(), additional_data.size(), header_size, mode,
|
||||||
@@ -490,7 +442,6 @@ private:
|
|||||||
mutable std::atomic<int> diag_count_{0};
|
mutable std::atomic<int> diag_count_{0};
|
||||||
mutable RtpProbeState rtp_probe_;
|
mutable RtpProbeState rtp_probe_;
|
||||||
mutable GeneratedTsState generated_ts_;
|
mutable GeneratedTsState generated_ts_;
|
||||||
mutable AdditionalTsState additional_ts_;
|
|
||||||
uint8_t key_[32];
|
uint8_t key_[32];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -533,17 +484,12 @@ public:
|
|||||||
bool nonce_from_rtp_header = false;
|
bool nonce_from_rtp_header = false;
|
||||||
bool nonce_from_generated_ts = false;
|
bool nonce_from_generated_ts = false;
|
||||||
bool nonce_from_additional_data = false;
|
bool nonce_from_additional_data = false;
|
||||||
bool nonce_from_additional_normalized = false;
|
|
||||||
bool additional_was_rtp_header = false;
|
bool additional_was_rtp_header = false;
|
||||||
bool used_absolute_additional_fallback = false;
|
|
||||||
uint32_t generated_ts_used = 0;
|
uint32_t generated_ts_used = 0;
|
||||||
nonce_from_additional_data = fill_nonce_from_additional_data(
|
nonce_from_additional_data = fill_nonce_from_additional_data(
|
||||||
additional_data.data(),
|
additional_data.data(),
|
||||||
additional_data.size(),
|
additional_data.size(),
|
||||||
nonce,
|
nonce,
|
||||||
&additional_ts_,
|
|
||||||
true,
|
|
||||||
&nonce_from_additional_normalized,
|
|
||||||
&additional_was_rtp_header);
|
&additional_was_rtp_header);
|
||||||
if (!nonce_from_additional_data) {
|
if (!nonce_from_additional_data) {
|
||||||
nonce_from_rtp_header =
|
nonce_from_rtp_header =
|
||||||
@@ -581,55 +527,6 @@ public:
|
|||||||
rosetta_xchacha20_xor(frame.data(), encrypted_frame.data(), encrypted_frame.size(), nonce, key_);
|
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) {
|
if (nonce_from_generated_ts) {
|
||||||
bool plausible = is_plausible_opus_packet(frame.data(), encrypted_frame.size());
|
bool plausible = is_plausible_opus_packet(frame.data(), encrypted_frame.size());
|
||||||
|
|
||||||
@@ -669,13 +566,8 @@ public:
|
|||||||
mode = "rtp";
|
mode = "rtp";
|
||||||
} else if (nonce_from_generated_ts) {
|
} else if (nonce_from_generated_ts) {
|
||||||
mode = used_generated_resync ? "gen-resync" : "gen";
|
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) {
|
} else if (nonce_from_additional_data) {
|
||||||
mode =
|
mode = additional_was_rtp_header ? "ad-rtp" : "raw-abs";
|
||||||
additional_was_rtp_header
|
|
||||||
? (nonce_from_additional_normalized ? "ad-rtp-norm" : "ad-rtp")
|
|
||||||
: (nonce_from_additional_normalized ? "raw-norm" : "raw-abs");
|
|
||||||
} else {
|
} else {
|
||||||
mode = "raw-abs";
|
mode = "raw-abs";
|
||||||
}
|
}
|
||||||
@@ -702,7 +594,6 @@ private:
|
|||||||
mutable std::atomic<int> diag_count_{0};
|
mutable std::atomic<int> diag_count_{0};
|
||||||
mutable RtpProbeState rtp_probe_;
|
mutable RtpProbeState rtp_probe_;
|
||||||
mutable GeneratedTsState generated_ts_;
|
mutable GeneratedTsState generated_ts_;
|
||||||
mutable AdditionalTsState additional_ts_;
|
|
||||||
uint8_t key_[32];
|
uint8_t key_[32];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -234,7 +234,12 @@ object CallManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun endCall() {
|
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)
|
resetSession(reason = null, notifyPeer = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,29 +610,30 @@ object CallManager {
|
|||||||
onCallConnected()
|
onCallConnected()
|
||||||
}
|
}
|
||||||
PeerConnection.PeerConnectionState.FAILED,
|
PeerConnection.PeerConnectionState.FAILED,
|
||||||
PeerConnection.PeerConnectionState.CLOSED -> {
|
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.DISCONNECTED -> {
|
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?.cancel()
|
||||||
disconnectResetJob =
|
disconnectResetJob =
|
||||||
scope.launch {
|
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()
|
val pcState = peerConnection?.connectionState()
|
||||||
if (pcState == PeerConnection.PeerConnectionState.DISCONNECTED ||
|
if (pcState == PeerConnection.PeerConnectionState.DISCONNECTED ||
|
||||||
pcState == PeerConnection.PeerConnectionState.FAILED ||
|
pcState == PeerConnection.PeerConnectionState.FAILED ||
|
||||||
pcState == PeerConnection.PeerConnectionState.CLOSED
|
pcState == PeerConnection.PeerConnectionState.CLOSED
|
||||||
) {
|
) {
|
||||||
breadcrumb("PC: DISCONNECTED timeout → reset")
|
breadcrumb("PC: $newState timeout ($timeoutMs ms) → reset")
|
||||||
resetSession(reason = "Connection lost", notifyPeer = false)
|
resetSession(reason = "Connection lost", notifyPeer = false)
|
||||||
} else {
|
} else {
|
||||||
breadcrumb("PC: DISCONNECTED recovered (state=$pcState)")
|
breadcrumb("PC: $newState recovered (state=$pcState)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user