Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/ads/video/encode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,13 @@ export function posterArgs(inPath: string, outPath: string): string[] {
* drift into describing different offers. A silent creative has no companion:
* five seconds of silence would be recorded as an audio ad having played, and
* the spec is explicit that silence never satisfies an audio slot.
*
* It carries the same mastering as the picture's own track. This is the file a
* music player asks for, and it is built from the raw narration rather than
* from the finished video, so without the filter here it keeps whatever level
* the voice came back at. That was the whole of the bug on this path: the
* video was brought up to streaming level while the audible spot, which is the
* only thing a radio or music listener ever gets, stayed near -29 dB.
*/
export function audioCompanionArgs(inPath: string, outPath: string): string[] {
return [
Expand All @@ -344,6 +351,8 @@ export function audioCompanionArgs(inPath: string, outPath: string): string[] {
"-i",
inPath,
"-vn",
"-af",
LOUDNESS,
"-c:a",
"aac",
"-profile:a",
Expand Down
19 changes: 18 additions & 1 deletion tests/ads-video-compose-validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
timeline,
} from "@/lib/ads/video/compose";
import type { VideoDesignSnapshot } from "@/lib/ads/video/snapshot";
import { AAC_LC_CODEC, avcCodecString, GOP_FRAMES, hlsArgs, mp4Args, multivariantPlaylist, posterArgs } from "@/lib/ads/video/encode";
import { AAC_LC_CODEC, audioCompanionArgs, avcCodecString, GOP_FRAMES, hlsArgs, mp4Args, multivariantPlaylist, posterArgs } from "@/lib/ads/video/encode";
import {
evaluateProbe,
parseRational,
Expand Down Expand Up @@ -538,4 +538,21 @@ describe("loudness", () => {
expect(a).toContain("-an");
expect(a.join(" ")).not.toContain("loudnorm");
});

it("masters the audible companion too, not only the picture", () => {
// The companion is built from the raw narration, not from the finished
// video, so it does not inherit the video track's mastering. A music
// player asks for this file and nothing else, which makes it the only
// thing most listeners ever hear.
const a = audioCompanionArgs("/tmp/narration.mp3", "/tmp/audio.m4a");
expect(a[a.indexOf("-af") + 1]).toBe("loudnorm=I=-16:TP=-1.5:LRA=11");
});

it("gives the companion the same target and ceiling as the video", () => {
// Two levels for one spot would mean the advert changed loudness when a
// listener moved between a video slot and an audio one.
const companion = audioCompanionArgs("/tmp/narration.mp3", "/tmp/audio.m4a");
const filter = companion[companion.indexOf("-af") + 1];
expect(narrated().join(" ")).toContain(filter);
});
});