let sharedStream: MediaStream | null = null; /** * 获得摄像头的视频流,可能抛出异常 * 获得的流只能通过本文件中的 closeMediaStream 关闭 */ export async function getMediaStream(): Promise { if (sharedStream) { logger({ cnl: ["server"], act: "共享sharedStream" }); return sharedStream; } if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { logger({ cnl: ["local", "server"], pgu: "AUTO", act: "检查navigator.mediaDevices.getUserMedia", key: "不可能的事情发生了", }); } logger({ cnl: ["server"], pgu: "AUTO", act: "启动摄像头" }); logger({ cnl: ["server"], pgu: "AUTO", act: "摄像头打开", dtl: "摄像头getSupportedConstraints", ext: { getSupportedConstraints: navigator.mediaDevices.getSupportedConstraints && JSON.stringify(navigator.mediaDevices.getSupportedConstraints()), }, }); try { const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "user", aspectRatio: 4 / 3, // @ts-expect-error 是支持的属性,应该是typescript的类型还没包含进来 resizeMode: "crop-and-scale", width: 640, height: 480, }, }); if (!stream) { logger({ cnl: ["server"], act: "摄像头打开失败", dtl: "没有可用的视频流", key: "不可能的事情发生了", }); } // logger detail info { const vt0 = stream.getVideoTracks()[0]; if (vt0 && vt0.getCapabilities && vt0.getSettings) { logger({ cnl: ["local", "server"], pgu: "AUTO", ext: { getCapabilities: vt0.getCapabilities(), getConstraints: vt0.getConstraints && vt0.getConstraints(), getSettings: vt0.getSettings && vt0.getSettings(), }, }); } else { logger({ cnl: ["local", "server"], pgu: "AUTO", dtl: "stream.getVideoTracks()[0] failed", }); } } sharedStream = stream; return stream; } catch (error) { console.log(error); logger({ cnl: ["server"], act: "摄像头打开失败", dtl: "无法启用摄像头", stk: error instanceof Error ? error.message : error + "", ext: { isError: error instanceof Error }, }); if (!(error instanceof Error)) throw new Error("not an error"); let errMsg; if (error.name || error.message) { errMsg = `${error.name} ${error.message}`; } else { errMsg = error; } if (error.name === "NotReadableError") { $message.error("无法启用摄像头: " + error.name + " 请重试!"); } else if ( error.name === "NotFoundError" || error.name === "DevicesNotFoundError" ) { $message.error( "无法启用摄像头: " + error.name + " 没有找到合适的摄像头!请重试或更换摄像头!" ); } else { $message.error("无法启用摄像头: " + error.name + errMsg); } const errorMsgLog = errMsg + (typeof errMsg === "object" ? JSON.stringify(errMsg) : ""); logger({ cnl: ["server"], act: "摄像头打开失败", dtl: "无法启用摄像头", ejn: JSON.stringify(error), stk: error.stack, ext: { errorName: error.name, errorMessage: error.message, errorMsgLog, }, }); throw error; } } export function closeMediaStream() { logger({ cnl: ["server"], act: "关闭stream" }); sharedStream?.getTracks().forEach(function (track) { track.stop(); }); sharedStream = null; }