Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test has a WPT meta file that expects 1 subtest issues.
- This WPT test may be referenced by the following Test IDs:
- /webcodecs/video-encoder-h26x-annexb.https.any.html?h264_annexb_hardware - WPT Dashboard Interop Dashboard
- /webcodecs/video-encoder-h26x-annexb.https.any.html?h264_annexb_software - WPT Dashboard Interop Dashboard
- /webcodecs/video-encoder-h26x-annexb.https.any.html?h265_annexb_hardware - WPT Dashboard Interop Dashboard
- /webcodecs/video-encoder-h26x-annexb.https.any.html?h265_annexb_software - WPT Dashboard Interop Dashboard
- /webcodecs/video-encoder-h26x-annexb.https.any.worker.html?h264_annexb_hardware - WPT Dashboard Interop Dashboard
- /webcodecs/video-encoder-h26x-annexb.https.any.worker.html?h264_annexb_software - WPT Dashboard Interop Dashboard
- /webcodecs/video-encoder-h26x-annexb.https.any.worker.html?h265_annexb_hardware - WPT Dashboard Interop Dashboard
- /webcodecs/video-encoder-h26x-annexb.https.any.worker.html?h265_annexb_software - WPT Dashboard Interop Dashboard
// 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
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');