diff --git a/packages/engine/src/services/videoFrameExtractor.ts b/packages/engine/src/services/videoFrameExtractor.ts index b0015b3ae3..296e9e47b4 100644 --- a/packages/engine/src/services/videoFrameExtractor.ts +++ b/packages/engine/src/services/videoFrameExtractor.ts @@ -768,7 +768,7 @@ export function parseVideoElements(html: string): VideoElement[] { mediaStart: readMediaStart(el), playbackRate: readElementRateSpec(el), loop: el.hasAttribute("loop"), - hasAudio: hasAudioAttr === "true", + hasAudio: hasAudioAttr === "true" && isAudibleVideoElement(el), ...(isSelfOrAncestorHidden(el) || (isAudibleVideoElement(el) && isMemberGroupHidden(groupsById, el)) ? { hidden: true } diff --git a/packages/producer/src/services/render/stages/mutedParity.test.ts b/packages/producer/src/services/render/stages/mutedParity.test.ts index 6fff334819..b4f67a697f 100644 --- a/packages/producer/src/services/render/stages/mutedParity.test.ts +++ b/packages/producer/src/services/render/stages/mutedParity.test.ts @@ -1,5 +1,7 @@ import { describe, expect, it } from "bun:test"; import { pruneMutedBrowserMedia } from "./probeStage.js"; +import { appendAutoDetectedVideoAudio } from "./extractVideosStage.js"; +import { collectRenderMedia } from "../../renderMediaCollector.js"; describe("pruneMutedBrowserMedia", () => { it("drops muted audio from the mix and clears hasAudio on muted video", () => { @@ -36,3 +38,38 @@ describe("pruneMutedBrowserMedia", () => { expect(composition.videos[0]?.hasAudio).toBe(true); }); }); + +describe("a muted video's sound in the mix", () => { + const html = `
+ + + +
`; + // Both files carry an audio stream, so the auto-detect fallback would add any video the parsers miss. + const extracted = ["quiet", "voice"].map((videoId) => ({ + videoId, + metadata: { hasAudio: true }, + })); + + for (const probed of [false, true]) { + it(`stays out of the mix ${probed ? "after" : "without"} the browser probe`, () => { + const composition = collectRenderMedia(html); + if (probed) { + pruneMutedBrowserMedia(composition, [ + { id: "quiet", tagName: "video", muted: true }, + { id: "voice", tagName: "video", muted: false }, + { id: "music", tagName: "audio", muted: false }, + ]); + } + appendAutoDetectedVideoAudio( + composition, + extracted as unknown as Parameters[1], + ); + + expect(composition.audios.map(({ id, volume }) => [id, volume])).toEqual([ + ["music", 0.19], + ["voice-audio", 0.5], + ]); + }); + } +});