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 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
|
// META: global=window,dedicatedworker
// META: script=/webcodecs/video-encoder-utils.js
// META: variant=?h264_annexb_software
// META: variant=?h264_annexb_hardware
// META: variant=?h265_annexb_software
// META: variant=?h265_annexb_hardware
var ENCODER_CONFIG = null;
var ANNEXB_CODEC = ''
promise_setup(async () => {
const config = {
'?h264_annexb_software': {
codec: 'avc1.42001E',
avc: {format: 'annexb'},
hardwareAcceleration: 'prefer-software',
},
'?h264_annexb_hardware': {
codec: 'avc1.42001E',
avc: {format: 'annexb'},
hardwareAcceleration: 'prefer-hardware',
},
'?h265_annexb_software': {
codec: 'hvc1.1.6.L123.00',
hevc: {format: 'annexb'},
hardwareAcceleration: 'prefer-software',
},
'?h265_annexb_hardware': {
codec: 'hvc1.1.6.L123.00',
hevc: {format: 'annexb'},
hardwareAcceleration: 'prefer-hardware',
}
}[location.search];
if (config.avc) {
ANNEXB_CODEC = 'h264'
}
if (config.hevc) {
ANNEXB_CODEC = 'h265'
}
config.width = 320;
config.height = 200;
config.bitrate = 1000000;
config.framerate = 30;
ENCODER_CONFIG = config;
});
// The code is inspired from https://source.chromium.org/chromium/chromium/src/+/main:media/formats/mp4/avc.cc;l=190;drc=a6567f4fac823a8a319652bdb5070b5b72a60f30
// and https://source.chromium.org/chromium/chromium/src/+/main:media/formats/mp4/hevc.cc;l=425;drc=a6567f4fac823a8a319652bdb5070b5b72a60f30?
function checkNaluSyntax(test, chunk) {
test.step(() => {
const buffer = new Uint8Array(chunk.byteLength);
const keyFrame = chunk.type === "key";
chunk.copyTo(buffer);
const kAUDAllowed = 1;
const kBeforeFirstVCL = 2; // VCL == nal_unit_types 1-5
const kAfterFirstVCL = 3;
const kEOStreamAllowed = 4;
const kNoMoreDataAllowed = 5;
// Define constants for h264 NALU types
const kAUD = 9;
const kSEIMessage = 6;
const kPrefix = 14;
const kSubsetSPS = 15;
const kDPS = 16;
const kReserved17 = 17;
const kReserved18 = 18;
const kPPS = 8;
const kSPS = 7;
const kSPSExt = 13;
const kNonIDRSlice = 1;
const kSliceDataA = 2;
const kSliceDataB = 3;
const kSliceDataC = 4;
const kIDRSlice = 5;
const kCodedSliceAux = 19;
const kEOSeq = 10;
const kEOStream = 11;
const kFiller = 12;
const kUnspecified = 0;
// Define constants for h265 NALU types
const AUD_NUT = 35;
const VPS_NUT = 32;
const SPS_NUT = 33;
const PPS_NUT = 34;
const PREFIX_SEI_NUT = 39;
const RSV_NVCL41 = 41;
const RSV_NVCL42 = 42;
const RSV_NVCL43 = 43;
const RSV_NVCL44 = 44;
const UNSPEC48 = 48;
const UNSPEC49 = 49;
const UNSPEC50 = 50;
const UNSPEC51 = 51;
const UNSPEC52 = 52;
const UNSPEC53 = 53;
const UNSPEC54 = 54;
const UNSPEC55 = 55;
const FD_NUT = 38;
const SUFFIX_SEI_NUT = 40;
const RSV_NVCL45 = 45;
const RSV_NVCL46 = 46;
const RSV_NVCL47 = 47;
const UNSPEC56 = 56;
const UNSPEC57 = 57;
const UNSPEC58 = 58;
const UNSPEC59 = 59;
const UNSPEC60 = 60;
const UNSPEC61 = 61;
const UNSPEC62 = 62;
const UNSPEC63 = 63;
const EOS_NUT = 36;
const EOB_NUT = 37;
const TRAIL_N = 0;
const TRAIL_R = 1;
const TSA_N = 2;
const TSA_R = 3;
const STSA_N = 4;
const STSA_R = 5;
const RADL_N = 6;
const RADL_R = 7;
const RASL_N = 8;
const RASL_R = 9;
const RSV_VCL_N10 = 10;
const RSV_VCL_R11 = 11;
const RSV_VCL_N12 = 12;
const RSV_VCL_R13 = 13;
const RSV_VCL_N14 = 14;
const RSV_VCL_R15 = 15;
const RSV_VCL24 = 24;
const RSV_VCL25 = 25;
const RSV_VCL26 = 26;
const RSV_VCL27 = 27;
const RSV_VCL28 = 28;
const RSV_VCL29 = 29;
const RSV_VCL30 = 30;
const RSV_VCL31 = 31;
const BLA_W_LP = 16;
const BLA_W_RADL = 17;
const BLA_N_LP = 18;
const IDR_W_RADL = 19;
const IDR_N_LP = 20;
const CRA_NUT = 21;
const RSV_IRAP_VCL22 = 22;
const RSV_IRAP_VCL23 = 23;
let order_state = kAUDAllowed;
let lastBytes = [0xFF, 0xFF, 0xFF];
for (let pos = 0; pos < buffer.length; pos++) {
if (lastBytes[0] == 0x00 && lastBytes[1] == 0x00
&& lastBytes[2] == 0x01) {
let naluType = buffer[pos] & 0x1f;
if (ANNEXB_CODEC === "h264") {
switch (naluType) {
case kAUD:
assert_less_than_equal(order_state, kAUDAllowed, "Unexpected AUD in order_state " + order_state);
order_state = kBeforeFirstVCL;
break;
case kSEIMessage:
case kPrefix:
case kSubsetSPS:
case kDPS:
case kReserved17:
case kReserved18:
case kPPS:
case kSPS:
assert_less_than_equal(order_state, kBeforeFirstVCL, "Unexpected NALU type " + naluType + " in order_state " + order_state);
order_state = kBeforeFirstVCL;
break;
case kSPSExt:
assert_equals(last_nalu_type, kSPS, "SPS extension does not follow an SPS.");
break;
case kNonIDRSlice:
case kSliceDataA:
case kSliceDataB:
case kSliceDataC:
case kIDRSlice:
assert_less_than_equal(order_state, kAfterFirstVCL, "Unexpected VCL in order_state " + order_state);
assert_equals(naluType == kIDRSlice, keyFrame, "Keyframe indicator does not match: " + (naluType == kIDRSlice) + " versus " + keyFrame);
order_state = kAfterFirstVCL;
break;
case kCodedSliceAux:
assert_equals(order_state, kAfterFirstVCL, "Unexpected extension in order_state " + order_state);
break;
case kEOSeq:
assert_equals(order_state, kAfterFirstVCL, "Unexpected EOSeq in order_state " + order_state);
order_state = kEOStreamAllowed;
break;
case kEOStream:
assert_greater_than(kAfterFirstVCL, order_state, "Unexpected EOStream in order_state " + order_state);
order_state = kNoMoreDataAllowed;
break;
// These syntax elements are to simply be ignored according to H264
// Annex B 7.4.2.7
case kFiller:
case kUnspecified:
// These syntax elements are to simply be ignored according to H264 Annex B 7.4.2.7
break;
default:
assert_greater_than(naluType, 19, "NALU TYPE smaller than 20 for unknown type");
break;
}
} else if (ANNEXB_CODEC === 'h265') {
// When any VPS NAL units, SPS NAL units, PPS NAL units, prefix SEI NAL
// units, NAL units with nal_unit_type in the range of
// RSV_NVCL41..RSV_NVCL44, or NAL units with nal_unit_type in the range of
// UNSPEC48..UNSPEC55 are present, they shall not follow the last VCL NAL
// unit of the access unit.
switch (naluType) {
case AUD_NUT:
assert_less_than_equal(order_state, kAUDAllowed, "Unexpected AUD in order_state " + order_state);
order_state = kBeforeFirstVCL;
break;
case VPS_NUT:
case SPS_NUT:
case PPS_NUT:
case PREFIX_SEI_NUT:
case RSV_NVCL41:
case RSV_NVCL42:
case RSV_NVCL43:
case RSV_NVCL44:
case UNSPEC48:
case UNSPEC49:
case UNSPEC50:
case UNSPEC51:
case UNSPEC52:
case UNSPEC53:
case UNSPEC54:
case UNSPEC55:
assert_less_than_equal(order_state, kBeforeFirstVCL, "Unexpected NALU type " + nalu.nal_unit_type + " in order_state " + order_state);
order_state = kBeforeFirstVCL;
break;
// NAL units having nal_unit_type equal to FD_NUT or SUFFIX_SEI_NUT or in
// the range of RSV_NVCL45..RSV_NVCL47 or UNSPEC56..UNSPEC63 shall not
// precede the first VCL NAL unit of the access unit.
case FD_NUT:
case SUFFIX_SEI_NUT:
case RSV_NVCL45:
case RSV_NVCL46:
case RSV_NVCL47:
case UNSPEC56:
case UNSPEC57:
case UNSPEC58:
case UNSPEC59:
case UNSPEC60:
case UNSPEC61:
case UNSPEC62:
case UNSPEC63:
assert_less_than_equal(order_state, kAfterFirstVC, "Unexpected NALU type " + nalu.nal_unit_type + " in order_state " + order_state);
break;
// When an end of sequence NAL unit is present, it shall be the last NAL
// unit among all NAL units in the access unit other than an end of
// bitstream NAL unit (when present).
case EOS_NUT:
assert_equals(order_state, kAfterFirstVCL, "Unexpected EOS in order_state " + order_state);
order_state = kEOBitstreamAllowed;
break;
// When an end of bitstream NAL unit is present, it shall be the last NAL
// unit in the access unit.
case EOB_NUT:
assert_less_than_equal(order_state, kAfterFirstVCL, "Unexpected EOB in order_state " + order_state);
order_state = kNoMoreDataAllowed;
break;
// VCL, non-IRAP
case TRAIL_N:
case TRAIL_R:
case TSA_N:
case TSA_R:
case STSA_N:
case STSA_R:
case RADL_N:
case RADL_R:
case RASL_N:
case RASL_R:
case RSV_VCL_N10:
case RSV_VCL_R11:
case RSV_VCL_N12:
case RSV_VCL_R13:
case RSV_VCL_N14:
case RSV_VCL_R15:
case RSV_VCL24:
case RSV_VCL25:
case RSV_VCL26:
case RSV_VCL27:
case RSV_VCL28:
case RSV_VCL29:
case RSV_VCL30:
case RSV_VCL31:
assert_less_than_equal(order_state, kAfterFirstVCL, "Unexpected VCL in order_state " + order_state);
order_state = kAfterFirstVCL;
break;
// VCL, IRAP
case BLA_W_LP:
case BLA_W_RADL:
case BLA_N_LP:
case IDR_W_RADL:
case IDR_N_LP:
case CRA_NUT:
case RSV_IRAP_VCL22:
case RSV_IRAP_VCL23:
assert_less_than_equal(order_state, kAfterFirstVCL, "Unexpected VCL in order_state " + order_state);
assert_equals(keyFrame, true, "The frame is coded as Keyframe, but indicator does not match");
order_state = kAfterFirstVCL;
break;
default:
assert_true(false, "Unsupported NALU type " + naluType);
break;
};
}
last_nalu_type = naluType;
}
lastBytes.push(buffer[pos]);
lastBytes.shift(); // advance reading
}
})
}
async function runAnnexBTest(t) {
let encoder_config = { ...ENCODER_CONFIG };
const w = encoder_config.width;
const h = encoder_config.height;
let frames_to_encode = 16;
await checkEncoderSupport(t, encoder_config);
const encodedResults = [];
const encoder_init = {
output(chunk, metadata) {
encodedResults.push(chunk);
},
error(e) {
assert_unreached(e.message);
}
};
let encoder = new VideoEncoder(encoder_init);
encoder.configure(encoder_config);
for (let i = 0; i < frames_to_encode; i++) {
let frame = createDottedFrame(w, h, i);
let keyframe = (i % 5 == 0);
encoder.encode(frame, { keyFrame: keyframe });
frame.close();
}
await encoder.flush();
encoder.close();
encodedResults.forEach((chunk) => checkNaluSyntax(t, chunk));
assert_greater_than(encodedResults.length, 0, "frames_encoded");
}
promise_test(async t => {
return runAnnexBTest(t);
}, 'Verify stream compliance h26x annexb');
|