-
Notifications
You must be signed in to change notification settings - Fork 6.6k
/
encode-color-space.html
172 lines (151 loc) · 6.44 KB
/
encode-color-space.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
<!DOCTYPE html>
<!--
Encodes two RGB frames and checks that output color space is changed to Rec601.
Then encodes two I420 frame w/ Rec709 colors and checks that the output color
space is changed to Rec709, and that a key frame is generated alongside the
change.
The spec doesn't mandate any particular output color space, so this just checks
that our internal behvaior is surfaced correctly. A key frame is expected when
color space changes to mirror the our decoder requirement to provide a key frame
whenever VideoDecoderConfig changes.
-->
<html>
<head>
<title>Encode color space test</title>
<script src="webcodecs_common.js"></script>
<script type="text/javascript">
'use strict';
// Use 16x16 aligned resolution since some platforms require that.
// See https://crbug.com/1084702.
// Also, some platforms require a resolution that isn't tiny (e.g. 160) to
// use hardware acceleration.
const FRAME_WIDTH = 640;
const FRAME_HEIGHT = 480;
function isRec709(colorSpace) {
return colorSpace.primaries === 'bt709'
&& colorSpace.transfer === 'bt709'
&& colorSpace.matrix === 'bt709'
&& colorSpace.fullRange === false;
}
function isSRGB(colorSpace) {
return colorSpace.primaries === 'bt709'
&& colorSpace.transfer === 'iec61966-2-1'
&& colorSpace.matrix === 'rgb'
&& colorSpace.fullRange === true;
}
function isRec601(colorSpace) {
return colorSpace.primaries === 'smpte170m'
&& colorSpace.transfer === 'smpte170m'
&& colorSpace.matrix === 'smpte170m'
&& colorSpace.fullRange === false;
}
function makePixelArray(byteLength) {
let data = new Uint8Array(byteLength);
for (let i = 0; i < byteLength; i++) {
data[i] = i;
}
return data;
}
function makeFrame(type, timestamp) {
let init = {
format: 'RGBA',
timestamp: timestamp,
codedWidth: FRAME_WIDTH,
codedHeight: FRAME_HEIGHT
};
switch (type) {
case 'I420': {
const yuvByteLength = 1.5 * FRAME_WIDTH * FRAME_HEIGHT;
let data = makePixelArray(yuvByteLength);
return new VideoFrame(data, {...init, format: 'I420'});
}
case 'RGBA': {
const rgbaByteLength = 4 * FRAME_WIDTH * FRAME_HEIGHT;
let data = makePixelArray(rgbaByteLength);
return new VideoFrame(data, {...init, format: 'RGBA'});
}
}
}
async function main(arg) {
const encoderConfig = {
codec: arg.codec,
hardwareAcceleration: arg.acceleration,
width: FRAME_WIDTH,
height: FRAME_HEIGHT,
};
let support = await VideoEncoder.isConfigSupported(encoderConfig);
if (!support.supported) {
TEST.skip('Unsupported codec: ' + arg.codec);
return;
}
const frameDuration = 16666;
let inputFrames = [makeFrame('RGBA', 0 * frameDuration),
makeFrame('RGBA', 1 * frameDuration),
makeFrame('I420', 2 * frameDuration),
makeFrame('I420', 3 * frameDuration)];
let outputChunks = [];
let outputMetadatas = [];
let errors = 0;
const init = {
output(chunk, metadata) {
outputChunks.push(chunk);
outputMetadatas.push(metadata);
},
error(e) {
errors++;
TEST.log(e);
}
};
let encoder = new VideoEncoder(init);
encoder.configure(encoderConfig);
for (let frame of inputFrames) {
encoder.encode(frame);
await waitForNextFrame();
}
await encoder.flush();
encoder.close();
TEST.assert(errors == 0, 'Encoding errors occurred during the test');
TEST.assert(outputChunks.length == 4, 'Unexpected number of outputs');
TEST.assert(outputMetadatas.length == 4, 'Unexpected number of output metadatas');
// First output should be a key frame and have acompanying metadata.
// Corresponding input RGBA sRGB, so we expect encoder will have used
// libYUV to convert to I420 or NV12 rec601.
TEST.assert(inputFrames[0].format == 'RGBA', 'inputs[0] is RGBA');
TEST.assert(isSRGB(inputFrames[0].colorSpace), 'inputs[0] is sRGB');
TEST.assert(outputChunks[0].type == 'key', 'outputs[0] is key');
TEST.assert('decoderConfig' in outputMetadatas[0], 'metdatas[0] has decoderConfig');
TEST.assert(isRec601(outputMetadatas[0].decoderConfig.colorSpace), 'metdatas[0] is rec601');
// Second outupt may or may not be a key frame w/ metadata (up to
// encoder). Corresponding input is still RGBA sRGB, so if metadata is
// given, we expect same colorSpace as for the previous frame.
TEST.assert(inputFrames[1].format == 'RGBA', 'inputs[1] is RGBA');
TEST.assert(isSRGB(inputFrames[1].colorSpace), 'inputs[1] is sRGB');
if('decoderConfig' in outputMetadatas[1]) {
TEST.assert(isRec601(outputMetadatas[1].decoderConfig.colorSpace), 'metdatas[1] is rec601');
}
// Third output should be a key frame and have acompanying metadata
// because the correpsonding input format changed to I420, which means
// we passthrough without libYUV conversion and a new decoderConfig
// should reflect that.
TEST.assert(inputFrames[2].format == 'I420', 'inputs[2] is I420');
TEST.assert(isRec709(inputFrames[2].colorSpace), 'inputs[2] is rec709');
// TODO(https://crbug.com/1241448): Uncomment the line below once android
// reliably produces a key frame at the appropriate time. For now this
// is covered by a DCHECK (excluding android) in video_encoder.cc.
// TEST.assert(outputChunks[2].type == 'key', 'outputs[2] is key');
TEST.assert('decoderConfig' in outputMetadatas[2], 'metadatas[2] has decoderConfig');
TEST.assert(isRec709(outputMetadatas[2].decoderConfig.colorSpace), 'metadatas[2] is rec709');
// Fourth outupt may or may not be a key frame w/ metadata (up to
// encoder). Corresponding input is still I420 rec709, so if metadata is
// given, we expect same colorSpace as for the previous frame.
TEST.assert(inputFrames[3].format == 'I420', 'inputs[3] is I420');
TEST.assert(isRec709(inputFrames[3].colorSpace, 'inputs[3] is rec709'));
if('decoderConfig' in outputMetadatas[3]) {
TEST.assert(isRec709(outputMetadatas[3].decoderConfig.colorSpace), 'metadatas[3] is rec709');
}
}
</script>
</head>
<body>
</body>
</html>