camera.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. let sharedStream: MediaStream | null = null;
  2. /**
  3. * 获得摄像头的视频流,可能抛出异常
  4. * 获得的流只能通过本文件中的 closeMediaStream 关闭
  5. */
  6. export async function getMediaStream(): Promise<MediaStream> {
  7. if (sharedStream) {
  8. logger({ cnl: ["server"], act: "共享sharedStream" });
  9. return sharedStream;
  10. }
  11. if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
  12. logger({
  13. cnl: ["local", "server"],
  14. pgu: "AUTO",
  15. act: "检查navigator.mediaDevices.getUserMedia",
  16. key: "不可能的事情发生了",
  17. });
  18. }
  19. logger({ cnl: ["server"], pgu: "AUTO", act: "启动摄像头" });
  20. logger({
  21. cnl: ["server"],
  22. pgu: "AUTO",
  23. act: "摄像头打开",
  24. dtl: "摄像头getSupportedConstraints",
  25. ext: {
  26. getSupportedConstraints:
  27. navigator.mediaDevices.getSupportedConstraints &&
  28. JSON.stringify(navigator.mediaDevices.getSupportedConstraints()),
  29. },
  30. });
  31. try {
  32. const stream = await navigator.mediaDevices.getUserMedia({
  33. video: {
  34. facingMode: "user",
  35. aspectRatio: 4 / 3,
  36. // @ts-expect-error 是支持的属性,应该是typescript的类型还没包含进来
  37. resizeMode: "crop-and-scale",
  38. width: 640,
  39. height: 480,
  40. },
  41. });
  42. if (!stream) {
  43. logger({
  44. cnl: ["server"],
  45. act: "摄像头打开失败",
  46. dtl: "没有可用的视频流",
  47. key: "不可能的事情发生了",
  48. });
  49. }
  50. // logger detail info
  51. {
  52. const vt0 = stream.getVideoTracks()[0];
  53. if (vt0 && vt0.getCapabilities && vt0.getSettings) {
  54. logger({
  55. cnl: ["local", "server"],
  56. pgu: "AUTO",
  57. ext: {
  58. getCapabilities: vt0.getCapabilities(),
  59. getConstraints: vt0.getConstraints && vt0.getConstraints(),
  60. getSettings: vt0.getSettings && vt0.getSettings(),
  61. },
  62. });
  63. } else {
  64. logger({
  65. cnl: ["local", "server"],
  66. pgu: "AUTO",
  67. dtl: "stream.getVideoTracks()[0] failed",
  68. });
  69. }
  70. }
  71. sharedStream = stream;
  72. return stream;
  73. } catch (error) {
  74. console.log(error);
  75. logger({
  76. cnl: ["server"],
  77. act: "摄像头打开失败",
  78. dtl: "无法启用摄像头",
  79. stk: error instanceof Error ? error.message : error + "",
  80. ext: { isError: error instanceof Error },
  81. });
  82. if (!(error instanceof Error)) throw new Error("not an error");
  83. let errMsg;
  84. if (error.name || error.message) {
  85. errMsg = `${error.name} ${error.message}`;
  86. } else {
  87. errMsg = error;
  88. }
  89. if (error.name === "NotReadableError") {
  90. $message.error("无法启用摄像头: " + error.name + " 请重试!");
  91. } else if (
  92. error.name === "NotFoundError" ||
  93. error.name === "DevicesNotFoundError"
  94. ) {
  95. $message.error(
  96. "无法启用摄像头: " +
  97. error.name +
  98. " 没有找到合适的摄像头!请重试或更换摄像头!"
  99. );
  100. } else {
  101. $message.error("无法启用摄像头: " + error.name + errMsg);
  102. }
  103. const errorMsgLog =
  104. errMsg + (typeof errMsg === "object" ? JSON.stringify(errMsg) : "");
  105. logger({
  106. cnl: ["server"],
  107. act: "摄像头打开失败",
  108. dtl: "无法启用摄像头",
  109. ejn: JSON.stringify(error),
  110. stk: error.stack,
  111. ext: {
  112. errorName: error.name,
  113. errorMessage: error.message,
  114. errorMsgLog,
  115. },
  116. });
  117. throw error;
  118. }
  119. }
  120. export function closeMediaStream() {
  121. logger({ cnl: ["server"], act: "关闭stream" });
  122. sharedStream?.getTracks().forEach(function (track) {
  123. track.stop();
  124. });
  125. sharedStream = null;
  126. }