Skip to content

Commit 6055435

Browse files
committed
Fix Android WebRTC stream startup
1 parent 17c36b5 commit 6055435

5 files changed

Lines changed: 68 additions & 11 deletions

File tree

client/src/features/stream/streamTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Size } from "../viewport/types";
22

33
export interface StreamConnectTarget {
44
clientId?: string;
5+
platform?: string;
56
remote?: boolean;
67
streamConfig?: StreamConfig;
78
transport?: StreamTransport;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { describe, expect, it } from "vitest";
2+
3+
import {
4+
buildStreamTarget,
5+
initialStreamBackend,
6+
preferredStreamBackend,
7+
} from "./streamWorkerClient";
8+
9+
describe("streamWorkerClient", () => {
10+
it("forces Android emulator streams onto WebRTC even when H264 is requested", () => {
11+
const target = buildStreamTarget("android:emulator-5554", {
12+
platform: "android-emulator",
13+
transport: "h264",
14+
});
15+
16+
expect(preferredStreamBackend(target)).toBe("webrtc");
17+
expect(initialStreamBackend(target)).toBe("webrtc");
18+
});
19+
20+
it("treats Android UDID prefixes as WebRTC-only stream targets", () => {
21+
const target = buildStreamTarget("android:Pixel_8", {
22+
transport: "h264",
23+
});
24+
25+
expect(preferredStreamBackend(target)).toBe("webrtc");
26+
expect(initialStreamBackend(target)).toBe("webrtc");
27+
});
28+
});

client/src/features/stream/streamWorkerClient.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,13 +224,15 @@ export function buildStreamTarget(
224224
udid: string,
225225
options: {
226226
clientId?: string;
227+
platform?: string;
227228
remote?: boolean;
228229
streamConfig?: StreamConfig;
229230
transport?: StreamTransport;
230231
} = {},
231232
): StreamConnectTarget {
232233
return {
233234
clientId: options.clientId,
235+
platform: options.platform,
234236
remote: options.remote,
235237
streamConfig: options.streamConfig,
236238
transport: options.transport,
@@ -2725,9 +2727,12 @@ export class StreamWorkerClient {
27252727
};
27262728
}
27272729

2728-
function preferredStreamBackend(
2730+
export function preferredStreamBackend(
27292731
target?: StreamConnectTarget | null,
27302732
): "auto" | StreamBackend {
2733+
if (isAndroidStreamTarget(target)) {
2734+
return "webrtc";
2735+
}
27312736
const value =
27322737
target?.transport ??
27332738
new URLSearchParams(window.location.search).get("stream");
@@ -2737,7 +2742,12 @@ function preferredStreamBackend(
27372742
return value === "webrtc" ? "webrtc" : "auto";
27382743
}
27392744

2740-
function initialStreamBackend(target: StreamConnectTarget): StreamBackend {
2745+
export function initialStreamBackend(
2746+
target: StreamConnectTarget,
2747+
): StreamBackend {
2748+
if (isAndroidStreamTarget(target)) {
2749+
return "webrtc";
2750+
}
27412751
const preferredBackend = preferredStreamBackend(target);
27422752
if (preferredBackend === "h264-ws") {
27432753
return canUseH264WebSocket() ? "h264-ws" : "webrtc";
@@ -2756,3 +2766,10 @@ function nextAutoFallbackBackend(
27562766
}
27572767
return null;
27582768
}
2769+
2770+
function isAndroidStreamTarget(target?: StreamConnectTarget | null): boolean {
2771+
return (
2772+
target?.platform === "android-emulator" ||
2773+
Boolean(target?.udid.startsWith("android:"))
2774+
);
2775+
}

client/src/features/stream/useLiveStream.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ export function useLiveStream({
301301

302302
const targetKey = [
303303
simulator.udid,
304+
simulator.platform ?? "",
304305
remote ? "remote" : "local",
305306
streamTransport,
306307
].join("|");
@@ -316,6 +317,7 @@ export function useLiveStream({
316317
workerClient.connect(
317318
buildStreamTarget(simulator.udid, {
318319
clientId: clientTelemetryIdRef.current,
320+
platform: simulator.platform,
319321
remote,
320322
streamConfig,
321323
transport: streamTransport,
@@ -324,6 +326,7 @@ export function useLiveStream({
324326
}, [
325327
canvasElement,
326328
simulator?.isBooted,
329+
simulator?.platform,
327330
simulator?.udid,
328331
paused,
329332
remote,

server/src/transport/webrtc.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,6 +1181,9 @@ impl AndroidWebRtcSource {
11811181
let Some(frame) = frame else {
11821182
continue;
11831183
};
1184+
if inner.latest_keyframe.read().unwrap().is_none() {
1185+
inner.request_keyframe();
1186+
}
11841187
let handle = inner.encoder_handle.load(Ordering::Acquire);
11851188
let udid = inner.udid.clone();
11861189
let encode_result = task::spawn_blocking(move || {
@@ -1238,15 +1241,7 @@ impl AndroidWebRtcSource {
12381241
fn request_refresh(&self) {}
12391242

12401243
fn request_keyframe(&self) {
1241-
self.inner
1242-
.metrics
1243-
.keyframe_requests
1244-
.fetch_add(1, Ordering::Relaxed);
1245-
unsafe {
1246-
ffi::xcw_native_h264_encoder_request_keyframe(
1247-
self.inner.encoder_handle.load(Ordering::Acquire) as *mut c_void,
1248-
);
1249-
}
1244+
self.inner.request_keyframe();
12501245
}
12511246
}
12521247

@@ -1284,6 +1279,19 @@ unsafe extern "C" fn android_h264_encoder_frame_callback(
12841279
}
12851280

12861281
impl AndroidWebRtcSourceInner {
1282+
fn request_keyframe(&self) {
1283+
self.metrics
1284+
.keyframe_requests
1285+
.fetch_add(1, Ordering::Relaxed);
1286+
let encoder_handle = self.encoder_handle.load(Ordering::Acquire);
1287+
if encoder_handle == 0 {
1288+
return;
1289+
}
1290+
unsafe {
1291+
ffi::xcw_native_h264_encoder_request_keyframe(encoder_handle as *mut c_void);
1292+
}
1293+
}
1294+
12871295
fn handle_encoded_frame(&self, frame: &ffi::xcw_native_frame) {
12881296
let description = unsafe { copy_native_shared_bytes(frame.description) };
12891297
let Some(data) = (unsafe { copy_native_shared_bytes(frame.data) }) else {

0 commit comments

Comments
 (0)