Bug 1225722 - Test RTCCodecStats in stats.js. r=jib

Differential Revision: https://phabricator.services.mozilla.com/D135858
This commit is contained in:
Andreas Pehrson 2022-02-01 23:12:22 +00:00
Родитель a7827eb63c
Коммит c4a76c10b9
1 изменённых файлов: 214 добавлений и 17 удалений

Просмотреть файл

@ -13,6 +13,7 @@ const statsExpectedByType = {
"ssrc",
"mediaType",
"kind",
"codecId",
"packetsReceived",
"packetsLost",
"packetsDiscarded",
@ -29,7 +30,6 @@ const statsExpectedByType = {
unimplemented: [
"mediaTrackId",
"transportId",
"codecId",
"associateStatsId",
"sliCount",
"packetsRepaired",
@ -52,19 +52,14 @@ const statsExpectedByType = {
"ssrc",
"mediaType",
"kind",
"codecId",
"packetsSent",
"bytesSent",
"remoteId",
],
optional: ["nackCount"],
localVideoOnly: ["framesEncoded", "firCount", "pliCount", "qpSum"],
unimplemented: [
"mediaTrackId",
"transportId",
"codecId",
"sliCount",
"targetBitrate",
],
unimplemented: ["mediaTrackId", "transportId", "sliCount", "targetBitrate"],
deprecated: ["isRemote"],
},
"remote-inbound-rtp": {
@ -75,6 +70,7 @@ const statsExpectedByType = {
"ssrc",
"mediaType",
"kind",
"codecId",
"packetsLost",
"jitter",
"localId",
@ -83,7 +79,6 @@ const statsExpectedByType = {
unimplemented: [
"mediaTrackId",
"transportId",
"codecId",
"packetsDiscarded",
"associateStatsId",
"sliCount",
@ -105,23 +100,32 @@ const statsExpectedByType = {
"ssrc",
"mediaType",
"kind",
"codecId",
"packetsSent",
"bytesSent",
"localId",
"remoteTimestamp",
],
optional: ["nackCount"],
unimplemented: [
"mediaTrackId",
"transportId",
"codecId",
"sliCount",
"targetBitrate",
],
unimplemented: ["mediaTrackId", "transportId", "sliCount", "targetBitrate"],
deprecated: ["isRemote"],
},
csrc: { skip: true },
codec: { skip: true },
codec: {
expected: [
"timestamp",
"type",
"id",
"payloadType",
"transportId",
"mimeType",
"clockRate",
"sdpFmtpLine",
],
optional: ["codecType", "channels"],
unimplemented: [],
deprecated: [],
},
"peer-connection": { skip: true },
"data-channel": { skip: true },
track: { skip: true },
@ -349,6 +353,21 @@ function pedanticChecks(report) {
ok(stat.kind == stat.mediaType, "kind equals legacy mediaType");
// codecId
ok(stat.codecId, `${stat.type}.codecId has a value`);
ok(report.has(stat.codecId), `codecId ${stat.codecId} exists in report`);
is(
report.get(stat.codecId).type,
"codec",
`codecId ${stat.codecId} in report is codec type`
);
is(
report.get(stat.codecId).mimeType.slice(0, 5),
stat.kind,
`codecId ${stat.codecId} in report is for a mimeType of the same ` +
`media type as the referencing rtp stream stat`
);
if (isRemote) {
// local id
if (stat.localId) {
@ -630,6 +649,184 @@ function pedanticChecks(report) {
`${stat.type}.timestamp, and no older than 30 seconds. ` +
`difference=${ageSeconds}s`
);
} else if (stat.type == "codec") {
//
// Required fields
//
// mimeType & payloadType
switch (stat.mimeType) {
case "audio/opus":
is(stat.payloadType, 109, "codec.payloadType for opus");
break;
case "video/VP8":
is(stat.payloadType, 120, "codec.payloadType for VP8");
break;
case "video/VP9":
is(stat.payloadType, 121, "codec.payloadType for VP9");
break;
case "video/H264":
ok(
stat.payloadType == 97 || stat.payloadType == 126,
`codec.payloadType for H264 was ${stat.payloadType}, exp. 97 or 126`
);
break;
default:
ok(
false,
`Unexpected codec.mimeType ${stat.mimeType} for payloadType ` +
`${stat.payloadType}`
);
break;
}
// transportId
// (no transport stats yet)
ok(stat.transportId, "codec.transportId is set");
// clockRate
if (stat.mimeType.startsWith("audio")) {
is(stat.clockRate, 48000, "codec.clockRate for audio/opus");
} else if (stat.mimeType.startsWith("video")) {
is(stat.clockRate, 90000, "codec.clockRate for video");
}
// sdpFmtpLine
// (not technically mandated by spec, but expected here)
ok(stat.sdpFmtpLine, "codec.sdpFmtpLine is set");
const opusParams = [
"maxplaybackrate",
"maxaveragebitrate",
"usedtx",
"stereo",
"useinbandfec",
"cbr",
"ptime",
"minptime",
"maxptime",
];
const vpxParams = ["max-fs", "max-fr"];
const h264Params = [
"packetization-mode",
"level-asymmetry-allowed",
"profile-level-id",
"max-fs",
"max-cpb",
"max-dpb",
"max-br",
"max-mbps",
];
for (const param of stat.sdpFmtpLine.split(";")) {
const [key, value] = param.split("=");
if (stat.payloadType == 109) {
ok(
opusParams.includes(key),
`codec.sdpFmtpLine param ${key}=${value} for opus`
);
} else if (stat.payloadType == 120 || stat.payloadType == 121) {
ok(
vpxParams.includes(key),
`codec.sdpFmtpLine param ${key}=${value} for VPx`
);
} else if (stat.payloadType == 97 || stat.payloadType == 126) {
ok(
h264Params.includes(key),
`codec.sdpFmtpLine param ${key}=${value} for H264`
);
if (key == "packetization-mode") {
if (stat.payloadType == 97) {
is(value, "0", "codec.sdpFmtpLine: H264 (97) packetization-mode");
} else if (stat.payloadType == 126) {
is(
value,
"1",
"codec.sdpFmtpLine: H264 (126) packetization-mode"
);
}
}
if (key == "profile-level-id") {
is(value, "42e01f", "codec.sdpFmtpLine: H264 profile-level-id");
}
}
}
//
// Optional fields
//
// codecType
ok(
!Object.keys(stat).includes("codecType") ||
stat.codecType == "encode" ||
stat.codecType == "decode",
"codec.codecType (${codec.codecType}) is an expected value or absent"
);
let numRecvStreams = 0;
let numSendStreams = 0;
const counts = {
"inbound-rtp": 0,
"outbound-rtp": 0,
"remote-inbound-rtp": 0,
"remote-outbound-rtp": 0,
};
const [kind] = stat.mimeType.split("/");
report.forEach(other => {
if (other.type == "inbound-rtp" && other.kind == kind) {
numRecvStreams += 1;
} else if (other.type == "outbound-rtp" && other.kind == kind) {
numSendStreams += 1;
}
if (other.codecId == stat.id) {
counts[other.type] += 1;
}
});
const expectedCounts = {
encode: {
"inbound-rtp": 0,
"outbound-rtp": numSendStreams,
"remote-inbound-rtp": numSendStreams,
"remote-outbound-rtp": 0,
},
decode: {
"inbound-rtp": numRecvStreams,
"outbound-rtp": 0,
"remote-inbound-rtp": 0,
"remote-outbound-rtp": numRecvStreams,
},
absent: {
"inbound-rtp": numRecvStreams,
"outbound-rtp": numSendStreams,
"remote-inbound-rtp": numSendStreams,
"remote-outbound-rtp": numRecvStreams,
},
};
// Note that the logic above assumes at most one sender and at most one
// receiver was used to generate this stats report. If more senders or
// receivers are present, they'd be referring to not only this codec stat,
// skewing `numSendStreams` and `numRecvStreams` above.
// This could be fixed when we support `senderId` and `receiverId` in
// RTCOutboundRtpStreamStats and RTCInboundRtpStreamStats respectively.
for (const [key, value] of Object.entries(counts)) {
is(
value,
expectedCounts[stat.codecType || "absent"][key],
`codec.codecType ${stat.codecType || "absent"} ref from ${key} stat`
);
}
// channels
if (stat.mimeType.startsWith("audio")) {
ok(stat.channels, "codec.channels should exist for audio");
if (stat.channels) {
if (stat.sdpFmtpLine.includes("stereo=1")) {
is(stat.channels, 2, "codec.channels for stereo audio");
} else {
is(stat.channels, 1, "codec.channels for mono audio");
}
}
} else {
ok(!stat.channels, "codec.channels should not exist for video");
}
} else if (stat.type == "candidate-pair") {
info("candidate-pair is: " + JSON.stringify(stat));
//