diff --git a/frontend/src/lib/whip.ts b/frontend/src/lib/whip.ts index 0203843..a32905f 100644 --- a/frontend/src/lib/whip.ts +++ b/frontend/src/lib/whip.ts @@ -12,7 +12,23 @@ export async function publishWhip( ): Promise { const pc = new RTCPeerConnection(); for (const track of stream.getTracks()) { - pc.addTransceiver(track, { direction: 'sendonly' }); + const transceiver = pc.addTransceiver(track, { direction: 'sendonly' }); + // Browsers default to VP8 for getUserMedia/getDisplayMedia video, which + // MediaMTX's HLS output can't mux at all (confirmed live: "the stream + // doesn't contain any supported codec, which are currently AV1, VP9, + // H265, H264, Opus, MPEG-4 Audio, KLV" — the muxer gets created then + // immediately destroyed, so the WHIP publish itself succeeds and the + // stream shows as live, but hls/live//index.m3u8 permanently 404s + // with "muxer is waiting to be created"). Reorder codec preference so + // H264 is offered first — matches what OBS already sends over RTMP, so + // this keeps a single well-tested codec through the whole pipeline + // (HLS, recording) instead of introducing a second one. + if (track.kind === 'video' && typeof transceiver.setCodecPreferences === 'function') { + const capabilities = RTCRtpSender.getCapabilities('video'); + const h264 = capabilities?.codecs.filter((c) => c.mimeType.toLowerCase() === 'video/h264') ?? []; + const rest = capabilities?.codecs.filter((c) => c.mimeType.toLowerCase() !== 'video/h264') ?? []; + if (h264.length > 0) transceiver.setCodecPreferences([...h264, ...rest]); + } } const offer = await pc.createOffer();