fix(voice): judge H.264 hardware support by negotiated format (#2680)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-11 14:59:04 +02:00
1 parent 81fd8c9aad
commit 67e01be34a
6 files changed
+429 -28

No files matched your search

@@ -1,11 +1,38 @@
// @vitest-environment happy-dom
// SPDX-License-Identifier: AGPL-3.0-or-later
import {findStalledVideoEncoder} from '@app/features/voice/engine/voice_screen_share_manager/shared';
import {describe, expect, test, vi} from 'vitest';
import {
findStalledVideoEncoder,
scheduleScreenShareEncoderVerification,
shouldTriggerSoftwareEncoderWarning,
} from '@app/features/voice/engine/voice_screen_share_manager/shared';
import SoftwareEncoderWarning from '@app/features/voice/state/SoftwareEncoderWarning';
import VoiceSettings from '@app/features/voice/state/VoiceSettings';
import {afterEach, beforeEach, describe, expect, test, vi} from 'vitest';
vi.mock('@app/features/voice/utils/VideoQualityEntitlement', () => ({hasHigherVideoQuality: () => false}));
let h264Acceleration = 'hardware';
const markSoftwareEncodeObserved = vi.fn();
vi.mock('@app/features/voice/utils/CodecCapabilityDetector', () => ({
adjustScreenShareEncodingForCodec: (encoding: unknown) => encoding,
getCodecCapabilityReport: () => ({
vp8: {hardwareAccelerated: 'software'},
vp9: {hardwareAccelerated: 'software'},
h264: {hardwareAccelerated: h264Acceleration},
h265: {hardwareAccelerated: 'software'},
av1: {hardwareAccelerated: 'software'},
}),
markScreenShareCodecSoftwareEncodeObserved: (codec: string) => markSoftwareEncodeObserved(codec),
resolveVideoPublishCodecPolicy: (requested: string) => ({
allowed: [requested],
requested,
primary: requested,
backupCodec: false,
}),
}));
function makeStats(entries: ReadonlyArray<Record<string, unknown>>): RTCStatsReport {
return new Map(entries.map((entry) => [entry.id as string, entry])) as unknown as RTCStatsReport;
}
@@ -59,3 +86,72 @@ describe('findStalledVideoEncoder', () => {
expect(findStalledVideoEncoder(stats, 'h264')?.sourceFrames).toBe(240);
});
});
describe('the screen share encoder verification', () => {
function openH264Stats(): RTCStatsReport {
return makeStats([
CODEC_REPORT,
SOURCE_REPORT,
outboundReport({
active: true,
framesEncoded: 120,
framesSent: 120,
encoderImplementation: 'OpenH264',
powerEfficientEncoder: false,
}),
]);
}
async function verifyOpenH264Share(): Promise<void> {
scheduleScreenShareEncoderVerification(async () => openH264Stats(), 'h264');
await vi.advanceTimersByTimeAsync(2500);
}
beforeEach(() => {
vi.useFakeTimers();
h264Acceleration = 'hardware';
markSoftwareEncodeObserved.mockClear();
SoftwareEncoderWarning.reset();
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
SoftwareEncoderWarning.reset();
});
test('records a software session verdict when the encoder turns out to be OpenH264', async () => {
await verifyOpenH264Share();
expect(markSoftwareEncodeObserved).toHaveBeenCalledWith('h264');
});
test('warns about the software encoder while the capability layer still claims hardware', async () => {
await verifyOpenH264Share();
expect(SoftwareEncoderWarning.showWarning).toBe(true);
});
test('does not warn once the session verdict says the codec encodes in software', async () => {
h264Acceleration = 'software';
await verifyOpenH264Share();
expect(SoftwareEncoderWarning.showWarning).toBe(false);
expect(markSoftwareEncodeObserved).toHaveBeenCalledWith('h264');
});
test('records no verdict when the user asked for the software encoder', async () => {
vi.spyOn(VoiceSettings, 'getScreenShareEncoderMode').mockReturnValue('software');
await verifyOpenH264Share();
expect(markSoftwareEncodeObserved).not.toHaveBeenCalled();
});
});
describe('the software encoder warning gate', () => {
test('stays quiet once the session verdict says the codec encodes in software', () => {
h264Acceleration = 'software';
expect(shouldTriggerSoftwareEncoderWarning('h264')).toBe(false);
});
test('still speaks up while the capability layer claims hardware', () => {
h264Acceleration = 'hardware';
expect(shouldTriggerSoftwareEncoderWarning('h264')).toBe(true);
});
});
@@ -9,6 +9,7 @@ import VoiceSettings from '@app/features/voice/state/VoiceSettings';
import {
adjustScreenShareEncodingForCodec,
getCodecCapabilityReport,
markScreenShareCodecSoftwareEncodeObserved,
resolveVideoPublishCodecPolicy,
type VideoPublishCodecPolicy,
} from '@app/features/voice/utils/CodecCapabilityDetector';
@@ -681,7 +682,11 @@ export function scheduleScreenShareEncoderVerification(
powerEfficientEncoder: encoder.powerEfficientEncoder,
expectedHardware,
});
if (shouldTriggerSoftwareEncoderWarning(codec)) {
const warnAboutSoftwareEncoder = shouldTriggerSoftwareEncoderWarning(codec);
if (VoiceSettings.getScreenShareEncoderMode() !== 'software') {
markScreenShareCodecSoftwareEncodeObserved(codec);
}
if (warnAboutSoftwareEncoder) {
SoftwareEncoderWarning.triggerWarning(codec, encoder.implementation);
}
} else {