index.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <script setup lang="ts">
  2. import { emitter } from "@/utils/mitt";
  3. import { useLoader } from "@pureadmin/utils";
  4. import { CanvasRenderer } from "./canvasRenderer";
  5. import { ref, onMounted, onBeforeUnmount } from "vue";
  6. defineOptions({
  7. name: "VideoFrame"
  8. });
  9. const num = 200;
  10. const curImg = ref("");
  11. const renderer = ref();
  12. const captureUtil = ref();
  13. const loading = ref(false);
  14. const { loadScript } = useLoader();
  15. const { VITE_PUBLIC_PATH } = import.meta.env;
  16. const getPath = path => `${VITE_PUBLIC_PATH}wasm/${path}`;
  17. const src = getPath("index.js");
  18. const workerPath = getPath("capture.worker.js");
  19. const wasmPath = getPath("capture.worker.wasm");
  20. loadScript({
  21. src
  22. }).then(mgs => {
  23. if (mgs[0].message === "加载成功") {
  24. // @ts-expect-error
  25. captureUtil.value = cheetahCapture.initCapture({
  26. workerPath,
  27. wasmPath
  28. });
  29. }
  30. });
  31. onMounted(() => {
  32. renderer.value = new CanvasRenderer("canvas-container");
  33. emitter.on("imageInfo", info => (curImg.value = info.img.src));
  34. });
  35. function beforeUpload(file) {
  36. curImg.value = "";
  37. loading.value = true;
  38. renderer.value.clearImages();
  39. // api参考 https://github.com/wanwu/cheetah-capture#api
  40. captureUtil.value.then(res => {
  41. res.capture({
  42. // 视频文件
  43. file,
  44. // 抽取指定数目的帧图片,传递`number`是按照数目抽帧,传递数组是指定抽帧的时间,单位毫秒(抽帧策略:https://github.com/wanwu/cheetah-capture/issues/6#issuecomment-1634384486)
  45. info: 16,
  46. // 当抽帧结果变化的回调
  47. onChange: (list, { url }) => {
  48. renderer.value.addImage(url, num * list.url.length, 0, num, num);
  49. },
  50. // 当抽帧结束并成功的回调
  51. onSuccess: () => {
  52. renderer.value.addListener();
  53. // 默认选中第一张
  54. renderer.value.drawTick({ offsetX: num / 2, offsetY: num / 2 });
  55. loading.value = false;
  56. },
  57. // 当抽帧过程出现错误的回调
  58. onError: () => {
  59. loading.value = false;
  60. }
  61. });
  62. });
  63. return false;
  64. }
  65. onBeforeUnmount(() => {
  66. // 解绑`imageInfo`公共事件,防止多次触发
  67. emitter.off("imageInfo");
  68. });
  69. </script>
  70. <template>
  71. <el-card shadow="never">
  72. <template #header>
  73. <div class="card-header">
  74. <span class="font-medium">
  75. <p>
  76. 基于自定义编译
  77. <el-link
  78. href="https://github.com/FFmpeg/FFmpeg"
  79. target="_blank"
  80. style="margin: 0 4px 5px; font-size: 16px"
  81. >
  82. FFmpeg
  83. </el-link>
  84. 的截帧工具,支持MP4、MOV、AVI、WebM、MKV等主流格式,支持
  85. H.264(AVC)、H.265(HEVC)、MPEG-2、MPEG-4、VP8、VP9、WMV3编码格式
  86. </p>
  87. 当然还可以支持更多视频格式,只要FFmpeg支持的,按理都能支持,您也可参考
  88. <el-link
  89. href="https://github.com/wanwu/cheetah-capture"
  90. target="_blank"
  91. style="margin: 0 4px 5px; font-size: 16px"
  92. >
  93. cheetah-capture
  94. </el-link>
  95. <el-link
  96. href="https://github.com/jordiwang/web-capture"
  97. target="_blank"
  98. style="margin: 0 4px 5px; font-size: 16px"
  99. >
  100. web-capture
  101. </el-link>
  102. 修改并编译wasm等文件(强烈推荐在Ubuntu系统进行编译)
  103. <p>
  104. mac系统推荐安装
  105. <el-link
  106. href="https://github.com/utmapp/UTM"
  107. target="_blank"
  108. style="margin: 0 4px 5px; font-size: 16px"
  109. >
  110. UTM
  111. </el-link>
  112. 虚拟机,windows系统推荐安装VMware虚拟机
  113. </p>
  114. <p>
  115. 当然这只是一个视频帧截取工具,如果您想要更多操作可以研究下
  116. <el-link
  117. href="https://ffmpegwasm.netlify.app/"
  118. target="_blank"
  119. style="margin: 0 4px 5px; font-size: 16px"
  120. >
  121. ffmpeg.wasm
  122. </el-link>
  123. ,它是基于 FFmpeg 的纯 WebAssembly / JavaScript
  124. 工具,可以在浏览器内进行视频和音频录制、转换和流式传输等,不过通过一些实践,对于时长较长的视频性能还是不太行,不过用于时长较短的短视频还是可以上生产的
  125. </p>
  126. </span>
  127. <el-link
  128. class="mt-2"
  129. href="https://github.com/pure-admin/vue-pure-admin/blob/main/src/views/able/video-frame"
  130. target="_blank"
  131. >
  132. 代码位置 src/views/able/video-frame
  133. </el-link>
  134. </div>
  135. </template>
  136. <div class="flex flex-wrap">
  137. <el-upload
  138. drag
  139. :show-file-list="false"
  140. accept=".mp4,.mov,.avi,.webm,.mkv"
  141. :before-upload="beforeUpload"
  142. >
  143. <div class="el-upload__text">
  144. 可拖拽上传视频(默认截取16张帧图片,可在代码中自行修改)
  145. </div>
  146. </el-upload>
  147. <el-image
  148. v-if="curImg"
  149. :src="curImg"
  150. :preview-src-list="Array.of(curImg)"
  151. class="w-[180px] h-[180px] ml-2 rounded-[6px]"
  152. />
  153. </div>
  154. <div
  155. id="canvas-container"
  156. v-loading="loading"
  157. element-loading-text="温馨提示:可左右拖拽图片并单击选取所需的帧图片"
  158. class="w-full h-[200px] overflow-hidden mt-6"
  159. />
  160. </el-card>
  161. </template>
  162. <style lang="scss" scoped>
  163. ::v-deep(.el-upload-dragger) {
  164. display: flex;
  165. align-items: center;
  166. height: 180px;
  167. }
  168. </style>