WIP: стабилизация звонков и E2EE + инструменты сборки WebRTC

This commit is contained in:
2026-03-25 22:20:24 +05:00
parent 530047c5d0
commit eea650face
8 changed files with 1119 additions and 219 deletions

View File

@@ -0,0 +1,76 @@
# Custom WebRTC for Rosetta Android (Audio E2EE Timestamp)
This setup builds a custom `libwebrtc.aar` for Android and patches audio E2EE so
`FrameEncryptor/FrameDecryptor` receive non-empty `additional_data` with RTP timestamp bytes.
## Why
Stock `io.github.webrtc-sdk:android:125.6422.07` can call audio frame encryptor with empty
`additional_data` (`ad=0`), so nonce derivation based on timestamp is unavailable.
Desktop uses frame timestamp for nonce. This patch aligns Android with that approach by passing
an 8-byte big-endian timestamp payload in `additional_data`:
- bytes `0..3` = `0`
- bytes `4..7` = RTP timestamp (big-endian)
## Files
- `build_custom_webrtc.sh` — reproducible build script
- `patches/0001-audio-e2ee-pass-rtp-timestamp-as-additional-data.patch` — WebRTC patch
## Build
Recommended on Linux (macOS can work but is less predictable for long WebRTC builds).
Bootstrap `depot_tools` first:
```bash
cd /path/to/rosetta-android/tools/webrtc-custom
./bootstrap_depot_tools.sh
```
Then run:
```bash
cd /path/to/rosetta-android/tools/webrtc-custom
./build_custom_webrtc.sh
```
Optional env vars:
- `WEBRTC_ROOT` — checkout root (default: `$HOME/webrtc_android`)
- `WEBRTC_SRC` — direct path to `src/`
- `WEBRTC_BRANCH` — default `branch-heads/6422`
- `WEBRTC_TAG` — use a specific tag/commit instead of branch
- `OUT_AAR` — output AAR path (default: `app/libs/libwebrtc-custom.aar`)
- `SYNC_JOBS``gclient sync` jobs (default: `1`, safer for googlesource limits)
- `SYNC_RETRIES` — sync retry attempts (default: `8`)
- `SYNC_RETRY_BASE_SEC` — base retry delay in seconds (default: `20`)
## Troubleshooting (HTTP 429 / RESOURCE_EXHAUSTED)
If build fails with:
- `The requested URL returned error: 429`
- `RESOURCE_EXHAUSTED`
- `Short term server-time rate limit exceeded`
run with conservative sync settings:
```bash
SYNC_JOBS=1 SYNC_RETRIES=12 SYNC_RETRY_BASE_SEC=30 ./build_custom_webrtc.sh
```
The script now retries `fetch`, `git fetch`, and `gclient sync` with backoff.
## Integration in app
`app/build.gradle.kts` already prefers local `app/libs/libwebrtc-custom.aar` if present.
If file exists, Maven WebRTC dependency is not used.
## Maintenance policy
- Keep patch small and isolated to `audio/channel_send.cc` + `audio/channel_receive.cc`.
- Pin WebRTC branch/tag for releases.
- Rebuild AAR on version bumps and verify `e2ee_diag.txt` shows `ad=8` (or non-zero).

View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
DEPOT_TOOLS_DIR="${DEPOT_TOOLS_DIR:-$HOME/depot_tools}"
if [[ ! -d "${DEPOT_TOOLS_DIR}/.git" ]]; then
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git "${DEPOT_TOOLS_DIR}"
fi
echo
echo "depot_tools ready: ${DEPOT_TOOLS_DIR}"
echo "Add to PATH in your shell profile:"
echo " export PATH=\"${DEPOT_TOOLS_DIR}:\$PATH\""

View File

@@ -0,0 +1,154 @@
#!/usr/bin/env bash
set -euo pipefail
# Reproducible custom WebRTC AAR build for Rosetta Android.
# Requirements:
# - Linux machine
# - depot_tools in PATH
# - python3, git
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROSETTA_ANDROID_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)"
PATCH_FILE="${SCRIPT_DIR}/patches/0001-audio-e2ee-pass-rtp-timestamp-as-additional-data.patch"
# Default target: WebRTC M125 family used by app dependency 125.6422.07.
WEBRTC_BRANCH="${WEBRTC_BRANCH:-branch-heads/6422}"
WEBRTC_TAG="${WEBRTC_TAG:-}"
# Source checkout root (contains src/)
WEBRTC_ROOT="${WEBRTC_ROOT:-$HOME/webrtc_android}"
WEBRTC_SRC="${WEBRTC_SRC:-${WEBRTC_ROOT}/src}"
# Output AAR consumed by app/build.gradle.kts.
OUT_AAR="${OUT_AAR:-${ROSETTA_ANDROID_DIR}/app/libs/libwebrtc-custom.aar}"
# Sync tuning to survive chromium.googlesource short-term 429 limits.
SYNC_JOBS="${SYNC_JOBS:-1}"
SYNC_RETRIES="${SYNC_RETRIES:-8}"
SYNC_RETRY_BASE_SEC="${SYNC_RETRY_BASE_SEC:-20}"
# Architectures used by the app.
ARCHS=("armeabi-v7a" "arm64-v8a" "x86_64")
echo "[webrtc-custom] root: ${WEBRTC_ROOT}"
echo "[webrtc-custom] src: ${WEBRTC_SRC}"
echo "[webrtc-custom] out: ${OUT_AAR}"
echo "[webrtc-custom] sync jobs: ${SYNC_JOBS}, retries: ${SYNC_RETRIES}"
# Keep depot_tools from auto-updating during long runs.
export DEPOT_TOOLS_UPDATE=0
retry_cmd() {
local max_attempts="$1"
shift
local attempt=1
local backoff="${SYNC_RETRY_BASE_SEC}"
while true; do
if "$@"; then
return 0
fi
if (( attempt >= max_attempts )); then
return 1
fi
echo "[webrtc-custom] attempt ${attempt}/${max_attempts} failed, retrying in ${backoff}s: $*"
sleep "${backoff}"
backoff=$(( backoff * 2 ))
if (( backoff > 300 )); then
backoff=300
fi
attempt=$(( attempt + 1 ))
done
}
sync_with_retry() {
local attempt=1
while true; do
# Heal known broken checkout state after interrupted/failed gclient runs.
if [[ -d "${WEBRTC_SRC}/third_party/libjpeg_turbo/.git" ]]; then
git -C "${WEBRTC_SRC}/third_party/libjpeg_turbo" reset --hard >/dev/null 2>&1 || true
git -C "${WEBRTC_SRC}/third_party/libjpeg_turbo" clean -fd >/dev/null 2>&1 || true
fi
if [[ -d "${WEBRTC_ROOT}/_bad_scm/src/third_party" ]]; then
find "${WEBRTC_ROOT}/_bad_scm/src/third_party" -maxdepth 1 -type d -name 'libjpeg_turbo*' -exec rm -rf {} + >/dev/null 2>&1 || true
fi
if gclient sync -D --jobs "${SYNC_JOBS}"; then
return 0
fi
if (( attempt >= SYNC_RETRIES )); then
echo "[webrtc-custom] ERROR: gclient sync failed after ${SYNC_RETRIES} attempts"
echo "[webrtc-custom] Tip: wait 10-15 min and rerun with lower burst:"
echo "[webrtc-custom] SYNC_JOBS=1 SYNC_RETRIES=12 ./build_custom_webrtc.sh"
return 1
fi
local wait_sec=$(( SYNC_RETRY_BASE_SEC * attempt ))
if (( wait_sec > 300 )); then
wait_sec=300
fi
echo "[webrtc-custom] gclient sync failed (attempt ${attempt}/${SYNC_RETRIES}), sleeping ${wait_sec}s..."
sleep "${wait_sec}"
attempt=$(( attempt + 1 ))
done
}
if ! command -v fetch >/dev/null 2>&1; then
echo "[webrtc-custom] ERROR: depot_tools 'fetch' not found in PATH"
exit 1
fi
if [[ ! -d "${WEBRTC_SRC}/.git" ]]; then
echo "[webrtc-custom] checkout not found, fetching webrtc_android..."
mkdir -p "${WEBRTC_ROOT}"
pushd "${WEBRTC_ROOT}" >/dev/null
retry_cmd "${SYNC_RETRIES}" fetch --nohooks --no-history webrtc_android
sync_with_retry
popd >/dev/null
fi
pushd "${WEBRTC_SRC}" >/dev/null
echo "[webrtc-custom] syncing source..."
retry_cmd "${SYNC_RETRIES}" git fetch --all --tags
if [[ -n "${WEBRTC_TAG}" ]]; then
retry_cmd "${SYNC_RETRIES}" git checkout "${WEBRTC_TAG}"
else
if git show-ref --verify --quiet "refs/remotes/origin/${WEBRTC_BRANCH}"; then
retry_cmd "${SYNC_RETRIES}" git checkout -B "${WEBRTC_BRANCH}" "origin/${WEBRTC_BRANCH}"
else
retry_cmd "${SYNC_RETRIES}" git checkout "${WEBRTC_BRANCH}"
fi
if git rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1; then
retry_cmd "${SYNC_RETRIES}" git pull --ff-only
else
echo "[webrtc-custom] no upstream for current branch, skipping git pull"
fi
fi
sync_with_retry
echo "[webrtc-custom] applying Rosetta patch..."
git reset --hard
git apply --check "${PATCH_FILE}"
git apply "${PATCH_FILE}"
mkdir -p "$(dirname "${OUT_AAR}")"
echo "[webrtc-custom] building AAR (this can take a while)..."
python3 tools_webrtc/android/build_aar.py \
--build-dir out_rosetta_aar \
--output "${OUT_AAR}" \
--arch "${ARCHS[@]}" \
--extra-gn-args \
is_debug=false \
is_component_build=false \
rtc_include_tests=false \
rtc_build_examples=false
echo "[webrtc-custom] done"
echo "[webrtc-custom] AAR: ${OUT_AAR}"
popd >/dev/null

View File

@@ -0,0 +1,54 @@
diff --git a/audio/channel_receive.cc b/audio/channel_receive.cc
index 17cf859ed8..b9d9ab14c8 100644
--- a/audio/channel_receive.cc
+++ b/audio/channel_receive.cc
@@ -693,10 +693,20 @@ void ChannelReceive::ReceivePacket(const uint8_t* packet,
const std::vector<uint32_t> csrcs(header.arrOfCSRCs,
header.arrOfCSRCs + header.numCSRCs);
+ const uint8_t additional_data_bytes[8] = {
+ 0,
+ 0,
+ 0,
+ 0,
+ static_cast<uint8_t>((header.timestamp >> 24) & 0xff),
+ static_cast<uint8_t>((header.timestamp >> 16) & 0xff),
+ static_cast<uint8_t>((header.timestamp >> 8) & 0xff),
+ static_cast<uint8_t>(header.timestamp & 0xff),
+ };
const FrameDecryptorInterface::Result decrypt_result =
frame_decryptor_->Decrypt(
cricket::MEDIA_TYPE_AUDIO, csrcs,
- /*additional_data=*/nullptr,
+ /*additional_data=*/additional_data_bytes,
rtc::ArrayView<const uint8_t>(payload, payload_data_length),
decrypted_audio_payload);
diff --git a/audio/channel_send.cc b/audio/channel_send.cc
index 4a2700177b..93283c2e78 100644
--- a/audio/channel_send.cc
+++ b/audio/channel_send.cc
@@ -320,10 +320,21 @@ int32_t ChannelSend::SendRtpAudio(AudioFrameType frameType,
// Encrypt the audio payload into the buffer.
size_t bytes_written = 0;
+ const uint8_t additional_data_bytes[8] = {
+ 0,
+ 0,
+ 0,
+ 0,
+ static_cast<uint8_t>((rtp_timestamp_without_offset >> 24) & 0xff),
+ static_cast<uint8_t>((rtp_timestamp_without_offset >> 16) & 0xff),
+ static_cast<uint8_t>((rtp_timestamp_without_offset >> 8) & 0xff),
+ static_cast<uint8_t>(rtp_timestamp_without_offset & 0xff),
+ };
+
int encrypt_status = frame_encryptor_->Encrypt(
cricket::MEDIA_TYPE_AUDIO, rtp_rtcp_->SSRC(),
- /*additional_data=*/nullptr, payload, encrypted_audio_payload,
- &bytes_written);
+ /*additional_data=*/additional_data_bytes, payload,
+ encrypted_audio_payload, &bytes_written);
if (encrypt_status != 0) {
RTC_DLOG(LS_ERROR)
<< "Channel::SendData() failed encrypt audio payload: "