序言
这篇文章主要用于记录几个项目中客户端代码的性能优化以及恶性bug的修复。
修改 WebRTC 编码器
2022年5月30日
在测试中发现,软件在进行视频通话时,CPU 负载较高,经过检测发现是由于 WebRTC 默认使用 VP8 的编码器、解码器。为了降低 CPU 负载,我允许用户自行选择使用 CPU 或是 GPU 进行渲染。当使用 GPU 进行视频渲染时,会使用 H264 的编码器进行实现。
// NOTE: 支持的编码器
const senderCodecs = RTCRtpSender.getCapabilities('video')?.codecs as RTCRtpCodecCapability[];
const receiverCodecs = RTCRtpReceiver.getCapabilities('video')?.codecs as RTCRtpCodecCapability[];
(() => {
const senderH264Index = senderCodecs?.findIndex(
(c) =>
c.mimeType === 'video/H264' &&
c.sdpFmtpLine ===
'level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f'
);
const senderH264 = (senderCodecs as Array<RTCRtpCodecCapability>)[
senderH264Index ? senderH264Index : 0
];
senderCodecs?.splice(senderH264Index ? senderH264Index : 0, 1);
senderCodecs?.unshift(senderH264);
const receiverH264Index = receiverCodecs?.findIndex(
(c) =>
c.mimeType === 'video/H264' &&
c.sdpFmtpLine ===
'level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f'
);
const receiverH264 = (receiverCodecs as Array<RTCRtpCodecCapability>)[
receiverH264Index ? receiverH264Index : 0
];
receiverCodecs?.splice(receiverH264Index ? receiverH264Index : 0, 1);
receiverCodecs?.unshift(receiverH264);
})();
export {
senderCodecs, receiverCodecs };
将编码器进行保存,如果有需要则使用 H264 的编码器/解码器。
用户缺失部分多媒体设备
2022年6月2日
由于我们的 WebRTC 默认用户上传音视频两条轨道,一旦用户不具备某种设备可能会导致整套流程无法走下去。为了缓解这种情况,我将获取媒体流失败的情况进行了 try...catch
操作,并将返回一个默认的媒体流。
let defaultVideoWidget: HTMLVideoElement | undefined;
function getDefaultStream(): Promise<MediaStream> {
return new Promise((resolve) => {
if (defaultVideoWidget) {
resolve((defaultVideoWidget as any).captureStream(1) as MediaStream);
} else {
defaultVideoWidget = document.createElement('video');
defaultVideoWidget.autoplay = true;
defaultVideoWidget.src = '../electronAssets/null.mp4';
defaultVideoWidget.loop = true;
defaultVideoWidget.onloadedmetadata = () => {
resolve((defaultVideoWidget as any).captureStream(1) as MediaStream);
};
}
});
}
消息提示音 Bug 修复
2022年6月4日
由于我们的消息提示音使用了 AudioContext 实现,其中具有大量的异步操作。在最初的开发中我没有意识到这样的异步操作带来的问题,在后续的测试中才发现异步操作导致停止播放很难正常起到作用。因此,针对异步操作,我将消息提示音的代码修改为了下方的版本:
// Prompt.ts
export const AUDIO_TYPE = {
MESSAGE_RECEIVED: 'info',
WEBRTC_CALLING: 'call',
WEBRTC_ANSWERING: 'answer',
};
export const buildPropmt = function (audioType: string, loop = false) {
const audioContext = new AudioContext();
let source = audioContext.createBufferSource();
const audio = require(`./audios/${
audioType}.aac`);
let abortController = new AbortController();
let abortSignal = abortController.signal;
const startAudioPropmt = () => {
if (source.buffer) {
source.stop();
source = audioContext.createBufferSource();
}
fetch(audio.default, {
signal: abortSignal,
})
.then((res) => {
return res.arrayBuffer();
})
.then((arrayBuffer) => {
return audioContext.decodeAudioData(arrayBuffer, (decodeData) => {
return decodeData;
});
})
.then((audioBuffer) => {
stopAudioPropmt();
source.buffer = audioBuffer;
source.loop = loop;
source.connect(audioContext.destination);
source.start(0);
})
.catch((message) => {
console.log(message);
});
};
const stopAudioPropmt = () => {
if (source.buffer) {
source.stop();
source = audioContext.createBufferSource();
} else {
abortController.abort();
abortController = new AbortController();
abortSignal = abortController.signal;
}
};
return [startAudioPropmt, stopAudioPropmt];
};
现在会优先终止异步操作,防止操作失效导致程序的后续使用中出现无法修复的 bug 。