mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-26 07:45:34 +00:00
Preserve the correct colour range in video exports
This commit is contained in:
@@ -93,6 +93,7 @@ import type {
|
||||
ExportRenderBackend,
|
||||
ExportResult,
|
||||
} from "./types";
|
||||
import { ENCODED_H264_COLOR_SPACE_FALLBACK, EXPORT_CANVAS_COLOR_SPACE } from "./videoColorSpace";
|
||||
|
||||
interface VideoExporterConfig extends ExportConfig {
|
||||
videoUrl: string;
|
||||
@@ -2726,9 +2727,11 @@ export class ModernVideoExporter {
|
||||
if (this.nativeEncoderError) throw this.nativeEncoderError;
|
||||
}
|
||||
const canvas = this.renderer!.getCanvas();
|
||||
// @ts-expect-error - colorSpace is supported at runtime but missing from this DOM typing.
|
||||
const frame = new VideoFrame(canvas, {
|
||||
timestamp,
|
||||
duration: frameDuration,
|
||||
colorSpace: EXPORT_CANVAS_COLOR_SPACE,
|
||||
});
|
||||
this.nativeH264Encoder.encode(frame, { keyFrame: frameIndex % 300 === 0 });
|
||||
frame.close();
|
||||
@@ -2957,12 +2960,7 @@ export class ModernVideoExporter {
|
||||
const exportFrame = new VideoFrame(canvas, {
|
||||
timestamp,
|
||||
duration: frameDuration,
|
||||
colorSpace: {
|
||||
primaries: "bt709",
|
||||
transfer: "iec61966-2-1",
|
||||
matrix: "rgb",
|
||||
fullRange: true,
|
||||
},
|
||||
colorSpace: EXPORT_CANVAS_COLOR_SPACE,
|
||||
});
|
||||
|
||||
while (
|
||||
@@ -3377,12 +3375,8 @@ export class ModernVideoExporter {
|
||||
try {
|
||||
if (isFirstChunk && this.videoDescription) {
|
||||
// Add decoder config for the first chunk
|
||||
const colorSpace = this.videoColorSpace || {
|
||||
primaries: "bt709",
|
||||
transfer: "iec61966-2-1",
|
||||
matrix: "rgb",
|
||||
fullRange: true,
|
||||
};
|
||||
const colorSpace =
|
||||
this.videoColorSpace || ENCODED_H264_COLOR_SPACE_FALLBACK;
|
||||
|
||||
const metadata: EncodedVideoChunkMetadata = {
|
||||
decoderConfig: {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { ENCODED_H264_COLOR_SPACE_FALLBACK, EXPORT_CANVAS_COLOR_SPACE } from "./videoColorSpace";
|
||||
|
||||
describe("export colour metadata", () => {
|
||||
it("does not confuse full-range RGB input with encoded YUV output", () => {
|
||||
expect(EXPORT_CANVAS_COLOR_SPACE).toMatchObject({ matrix: "rgb", fullRange: true });
|
||||
expect(ENCODED_H264_COLOR_SPACE_FALLBACK).toMatchObject({
|
||||
matrix: "bt709",
|
||||
transfer: "bt709",
|
||||
fullRange: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
/** The renderer composites into an sRGB canvas, whose pixels are full-range RGB. */
|
||||
export const EXPORT_CANVAS_COLOR_SPACE = {
|
||||
primaries: "bt709",
|
||||
transfer: "iec61966-2-1",
|
||||
matrix: "rgb",
|
||||
fullRange: true,
|
||||
} as const satisfies VideoColorSpaceInit;
|
||||
|
||||
/**
|
||||
* H.264 encoders normally convert the canvas to video-range YUV. Use this only
|
||||
* when the encoder does not report its own output colour metadata.
|
||||
*/
|
||||
export const ENCODED_H264_COLOR_SPACE_FALLBACK = {
|
||||
primaries: "bt709",
|
||||
transfer: "bt709",
|
||||
matrix: "bt709",
|
||||
fullRange: false,
|
||||
} as const satisfies VideoColorSpaceInit;
|
||||
@@ -8,8 +8,8 @@ import type {
|
||||
CursorStyle,
|
||||
CursorTelemetryPoint,
|
||||
Padding,
|
||||
SpeedRegion,
|
||||
SourceAudioTrackSettings,
|
||||
SpeedRegion,
|
||||
TrimRegion,
|
||||
WebcamOverlaySettings,
|
||||
ZoomMotionBlurTuning,
|
||||
@@ -38,6 +38,7 @@ import type {
|
||||
ExportProgress,
|
||||
ExportResult,
|
||||
} from "./types";
|
||||
import { ENCODED_H264_COLOR_SPACE_FALLBACK, EXPORT_CANVAS_COLOR_SPACE } from "./videoColorSpace";
|
||||
|
||||
const DEFAULT_MAX_ENCODE_QUEUE = 240;
|
||||
const PROGRESS_SAMPLE_WINDOW_MS = 1_000;
|
||||
@@ -825,12 +826,7 @@ export class VideoExporter {
|
||||
const frame = new VideoFrame(canvas, {
|
||||
timestamp,
|
||||
duration: frameDuration,
|
||||
colorSpace: {
|
||||
primaries: "bt709",
|
||||
transfer: "iec61966-2-1",
|
||||
matrix: "rgb",
|
||||
fullRange: true,
|
||||
},
|
||||
colorSpace: EXPORT_CANVAS_COLOR_SPACE,
|
||||
});
|
||||
this.nativeH264Encoder.encode(frame, { keyFrame: frameIndex % 300 === 0 });
|
||||
frame.close();
|
||||
@@ -1077,12 +1073,7 @@ export class VideoExporter {
|
||||
const exportFrame = new VideoFrame(canvas, {
|
||||
timestamp,
|
||||
duration: frameDuration,
|
||||
colorSpace: {
|
||||
primaries: "bt709",
|
||||
transfer: "iec61966-2-1",
|
||||
matrix: "rgb",
|
||||
fullRange: true,
|
||||
},
|
||||
colorSpace: EXPORT_CANVAS_COLOR_SPACE,
|
||||
});
|
||||
|
||||
while (
|
||||
@@ -1270,12 +1261,8 @@ export class VideoExporter {
|
||||
try {
|
||||
if (isFirstChunk && this.videoDescription) {
|
||||
// Add decoder config for the first chunk
|
||||
const colorSpace = this.videoColorSpace || {
|
||||
primaries: "bt709",
|
||||
transfer: "iec61966-2-1",
|
||||
matrix: "rgb",
|
||||
fullRange: true,
|
||||
};
|
||||
const colorSpace =
|
||||
this.videoColorSpace || ENCODED_H264_COLOR_SPACE_FALLBACK;
|
||||
|
||||
const metadata: EncodedVideoChunkMetadata = {
|
||||
decoderConfig: {
|
||||
|
||||
Reference in New Issue
Block a user