yangg 2 месяцев назад
Родитель
Сommit
a687fa32f3

+ 10 - 1
common/config.js

@@ -4,4 +4,13 @@
 export const apiBaseUrl = 'http://192.168.66.187:8083';
 
 // You can add other global configuration settings here
-export const appVersion = '1.0.0'; 
+export const appVersion = '1.0.0';
+
+// 添加文件上传相关配置
+// export const uploadConfig = {
+//   maxRetries: 3,           // 最大重试次数
+//   retryDelay: 1000,        // 重试延迟(毫秒)
+//   chunkSize: 1024 * 1024,  // 分块上传大小(1MB)
+//   timeout: 60000,          // 上传超时时间(毫秒)
+//   validateChecksum: true   // 是否验证校验和
+// }; 

+ 129 - 17
pages/identity-verify/identity-verify.vue

@@ -40,7 +40,9 @@
                 flash="off" 
                 class="user-camera-video"
                 @error="handleCameraError"
-                :record-audio="true">
+                :record-audio="true"
+                frame-size="medium"
+                resolution="medium">
         </camera>
         <!-- 在H5/App环境中使用video元素 -->
         <video v-else
@@ -276,7 +278,7 @@ export default {
       } else {
         // H5/App环境使用浏览器的API
         try {
-          // 请求摄像头和麦克风权限并获取媒体流
+          // 请求摄像头和麦克风权限并获取媒体流,添加更多约束
           const constraints = {
             audio: {
               echoCancellation: true,
@@ -284,8 +286,9 @@ export default {
               autoGainControl: true
             },
             video: {
-              width: { ideal: 640 },
-              height: { ideal: 480 },
+              width: { ideal: 640, max: 1280 }, // 控制视频宽度
+              height: { ideal: 480, max: 720 }, // 控制视频高度
+              frameRate: { ideal: 15, max: 24 }, // 控制帧率
               facingMode: 'user'
             }
           };
@@ -790,9 +793,11 @@ export default {
             return;
           }
           
-          // 开始录像,确保同时录制音频
+          // 开始录像,添加质量控制参数
           const options = {
             timeout: 60000, // 最长录制60秒
+            quality: 'medium', // 可选值:'low', 'medium', 'high'
+            compressed: true, // 是否压缩视频
             success: () => {
               console.log('小程序录像开始成功');
             },
@@ -805,7 +810,6 @@ export default {
               this.proceedToNextQuestion();
             },
             timeoutCallback: () => {
-              // 录制超时自动停止时的回调
               console.log('录制超时自动停止');
               this.stopRecordingAnswer();
             }
@@ -951,11 +955,11 @@ export default {
         }
       }
       
-      // 创建MediaRecorder实例,设置较高的音频比特率
+      // 创建MediaRecorder实例,设置较低的比特率以减小文件大小
       const options = {
         mimeType: mimeType || '',
-        audioBitsPerSecond: 128000,
-        videoBitsPerSecond: 2500000
+        audioBitsPerSecond: 64000,    // 降低音频比特率
+        videoBitsPerSecond: 1000000,  // 降低视频比特率到1Mbps
       };
       
       try {
@@ -978,7 +982,7 @@ export default {
       };
       
       // 监听录制停止事件
-      this.mediaRecorder.onstop = () => {
+      this.mediaRecorder.onstop = async () => {
         console.log('MediaRecorder停止,数据块数量:', this.recordedChunks.length);
         
         if (this.recordedChunks.length === 0) {
@@ -994,14 +998,36 @@ export default {
         // 创建Blob对象
         const mimeType = this.mediaRecorder.mimeType || 'video/webm';
         const blob = new Blob(this.recordedChunks, { type: mimeType });
-        console.log('创建Blob,大小:', blob.size, '类型:', mimeType);
+        console.log('创建Blob,原始大小:', blob.size, '类型:', mimeType);
         
-        // 将Blob转换为File对象
-        const fileName = `answer_${this.currentVideoIndex}_${Date.now()}.webm`;
-        const file = new File([blob], fileName, { type: mimeType });
+        // 显示压缩中提示
+        uni.showLoading({
+          title: '正在处理视频...',
+          mask: true
+        });
         
-        // 上传文件
-        this.uploadRecordedVideo(file);
+        try {
+          // 压缩视频
+          const compressedBlob = await this.compressVideo(blob);
+          
+          // 将Blob转换为File对象
+          const fileName = `answer_${this.currentVideoIndex}_${Date.now()}.webm`;
+          const file = new File([compressedBlob], fileName, { type: mimeType });
+          
+          // 隐藏压缩提示
+          uni.hideLoading();
+          
+          // 上传文件
+          this.uploadRecordedVideo(file);
+        } catch (error) {
+          console.error('视频处理失败:', error);
+          uni.hideLoading();
+          
+          // 如果压缩失败,使用原始视频
+          const fileName = `answer_${this.currentVideoIndex}_${Date.now()}.webm`;
+          const file = new File([blob], fileName, { type: mimeType });
+          this.uploadRecordedVideo(file);
+        }
       };
       
       // 监听错误
@@ -1135,7 +1161,7 @@ export default {
         // 使用XMLHttpRequest直接上传
         const xhr = new XMLHttpRequest();
         xhr.open('POST', `${apiBaseUrl}/api/system/upload/`, true);
-        
+        xhr.timeout = 120000; // 30秒超时
         xhr.onload = () => {
           uni.hideLoading();
           if (xhr.status === 200) {
@@ -1692,6 +1718,92 @@ export default {
           icon: 'none'
         });
       }
+    },
+
+    // 添加一个新方法用于压缩视频
+    async compressVideo(videoBlob) {
+      // 如果视频大小小于某个阈值,直接返回原视频
+      if (videoBlob.size < 5 * 1024 * 1024) { // 小于5MB
+        return videoBlob;
+      }
+      
+      console.log('开始压缩视频,原始大小:', videoBlob.size);
+      
+      // 创建一个视频元素来加载视频
+      const videoElement = document.createElement('video');
+      videoElement.muted = true;
+      videoElement.autoplay = false;
+      
+      // 创建一个canvas元素用于绘制视频帧
+      const canvas = document.createElement('canvas');
+      const ctx = canvas.getContext('2d');
+      
+      // 设置视频源
+      videoElement.src = URL.createObjectURL(videoBlob);
+      
+      return new Promise((resolve) => {
+        videoElement.onloadedmetadata = () => {
+          // 设置canvas尺寸为视频的一半
+          const width = Math.floor(videoElement.videoWidth / 2);
+          const height = Math.floor(videoElement.videoHeight / 2);
+          canvas.width = width;
+          canvas.height = height;
+          
+          // 创建MediaRecorder来录制canvas
+          const stream = canvas.captureStream(15); // 15fps
+          
+          // 如果原视频有音轨,添加到新流中
+          if (videoElement.captureStream) {
+            const originalStream = videoElement.captureStream();
+            const audioTracks = originalStream.getAudioTracks();
+            if (audioTracks.length > 0) {
+              stream.addTrack(audioTracks[0]);
+            }
+          }
+          
+          // 设置较低的比特率
+          const options = {
+            mimeType: 'video/webm;codecs=vp8,opus',
+            audioBitsPerSecond: 64000,
+            videoBitsPerSecond: 800000 // 800kbps
+          };
+          
+          const mediaRecorder = new MediaRecorder(stream, options);
+          const chunks = [];
+          
+          mediaRecorder.ondataavailable = (e) => {
+            if (e.data.size > 0) {
+              chunks.push(e.data);
+            }
+          };
+          
+          mediaRecorder.onstop = () => {
+            const compressedBlob = new Blob(chunks, { type: 'video/webm' });
+            console.log('视频压缩完成,压缩后大小:', compressedBlob.size);
+            resolve(compressedBlob);
+          };
+          
+          // 开始播放视频并录制
+          videoElement.onplay = () => {
+            mediaRecorder.start(100);
+            
+            // 绘制视频帧到canvas
+            const drawFrame = () => {
+              if (videoElement.paused || videoElement.ended) {
+                mediaRecorder.stop();
+                return;
+              }
+              
+              ctx.drawImage(videoElement, 0, 0, width, height);
+              requestAnimationFrame(drawFrame);
+            };
+            
+            drawFrame();
+          };
+          
+          videoElement.play();
+        };
+      });
     }
   }
 }

+ 94 - 9
unpackage/dist/dev/mp-weixin/pages/identity-verify/identity-verify.js

@@ -167,8 +167,12 @@ const _sfc_main = {
               autoGainControl: true
             },
             video: {
-              width: { ideal: 640 },
-              height: { ideal: 480 },
+              width: { ideal: 640, max: 1280 },
+              // 控制视频宽度
+              height: { ideal: 480, max: 720 },
+              // 控制视频高度
+              frameRate: { ideal: 15, max: 24 },
+              // 控制帧率
               facingMode: "user"
             }
           };
@@ -548,6 +552,10 @@ const _sfc_main = {
           const options = {
             timeout: 6e4,
             // 最长录制60秒
+            quality: "medium",
+            // 可选值:'low', 'medium', 'high'
+            compressed: true,
+            // 是否压缩视频
             success: () => {
               console.log("小程序录像开始成功");
             },
@@ -676,8 +684,10 @@ const _sfc_main = {
       }
       const options = {
         mimeType: mimeType || "",
-        audioBitsPerSecond: 128e3,
-        videoBitsPerSecond: 25e5
+        audioBitsPerSecond: 64e3,
+        // 降低音频比特率
+        videoBitsPerSecond: 1e6
+        // 降低视频比特率到1Mbps
       };
       try {
         this.mediaRecorder = new MediaRecorder(stream, options);
@@ -693,7 +703,7 @@ const _sfc_main = {
           console.log(`收到数据块: ${event.data.size} 字节`);
         }
       };
-      this.mediaRecorder.onstop = () => {
+      this.mediaRecorder.onstop = async () => {
         console.log("MediaRecorder停止,数据块数量:", this.recordedChunks.length);
         if (this.recordedChunks.length === 0) {
           console.error("没有录制到数据");
@@ -706,10 +716,24 @@ const _sfc_main = {
         }
         const mimeType2 = this.mediaRecorder.mimeType || "video/webm";
         const blob = new Blob(this.recordedChunks, { type: mimeType2 });
-        console.log("创建Blob,大小:", blob.size, "类型:", mimeType2);
-        const fileName = `answer_${this.currentVideoIndex}_${Date.now()}.webm`;
-        const file = new File([blob], fileName, { type: mimeType2 });
-        this.uploadRecordedVideo(file);
+        console.log("创建Blob,原始大小:", blob.size, "类型:", mimeType2);
+        common_vendor.index.showLoading({
+          title: "正在处理视频...",
+          mask: true
+        });
+        try {
+          const compressedBlob = await this.compressVideo(blob);
+          const fileName = `answer_${this.currentVideoIndex}_${Date.now()}.webm`;
+          const file = new File([compressedBlob], fileName, { type: mimeType2 });
+          common_vendor.index.hideLoading();
+          this.uploadRecordedVideo(file);
+        } catch (error) {
+          console.error("视频处理失败:", error);
+          common_vendor.index.hideLoading();
+          const fileName = `answer_${this.currentVideoIndex}_${Date.now()}.webm`;
+          const file = new File([blob], fileName, { type: mimeType2 });
+          this.uploadRecordedVideo(file);
+        }
       };
       this.mediaRecorder.onerror = (event) => {
         console.error("MediaRecorder错误:", event.error);
@@ -810,6 +834,7 @@ const _sfc_main = {
         formData.append("has_audio", "true");
         const xhr = new XMLHttpRequest();
         xhr.open("POST", `${common_config.apiBaseUrl}/api/system/upload/`, true);
+        xhr.timeout = 12e4;
         xhr.onload = () => {
           common_vendor.index.hideLoading();
           if (xhr.status === 200) {
@@ -1242,6 +1267,66 @@ const _sfc_main = {
           icon: "none"
         });
       }
+    },
+    // 添加一个新方法用于压缩视频
+    async compressVideo(videoBlob) {
+      if (videoBlob.size < 5 * 1024 * 1024) {
+        return videoBlob;
+      }
+      console.log("开始压缩视频,原始大小:", videoBlob.size);
+      const videoElement = document.createElement("video");
+      videoElement.muted = true;
+      videoElement.autoplay = false;
+      const canvas = document.createElement("canvas");
+      const ctx = canvas.getContext("2d");
+      videoElement.src = URL.createObjectURL(videoBlob);
+      return new Promise((resolve) => {
+        videoElement.onloadedmetadata = () => {
+          const width = Math.floor(videoElement.videoWidth / 2);
+          const height = Math.floor(videoElement.videoHeight / 2);
+          canvas.width = width;
+          canvas.height = height;
+          const stream = canvas.captureStream(15);
+          if (videoElement.captureStream) {
+            const originalStream = videoElement.captureStream();
+            const audioTracks = originalStream.getAudioTracks();
+            if (audioTracks.length > 0) {
+              stream.addTrack(audioTracks[0]);
+            }
+          }
+          const options = {
+            mimeType: "video/webm;codecs=vp8,opus",
+            audioBitsPerSecond: 64e3,
+            videoBitsPerSecond: 8e5
+            // 800kbps
+          };
+          const mediaRecorder = new MediaRecorder(stream, options);
+          const chunks = [];
+          mediaRecorder.ondataavailable = (e) => {
+            if (e.data.size > 0) {
+              chunks.push(e.data);
+            }
+          };
+          mediaRecorder.onstop = () => {
+            const compressedBlob = new Blob(chunks, { type: "video/webm" });
+            console.log("视频压缩完成,压缩后大小:", compressedBlob.size);
+            resolve(compressedBlob);
+          };
+          videoElement.onplay = () => {
+            mediaRecorder.start(100);
+            const drawFrame = () => {
+              if (videoElement.paused || videoElement.ended) {
+                mediaRecorder.stop();
+                return;
+              }
+              ctx.drawImage(videoElement, 0, 0, width, height);
+              requestAnimationFrame(drawFrame);
+            };
+            drawFrame();
+          };
+          videoElement.play();
+        };
+      });
     }
   }
 };

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
unpackage/dist/dev/mp-weixin/pages/identity-verify/identity-verify.wxml


+ 1 - 1
unpackage/dist/dev/mp-weixin/project.config.json

@@ -8,7 +8,7 @@
     "urlCheck": false,
     "es6": true,
     "postcss": false,
-    "minified": true,
+    "minified": false,
     "newFeature": true,
     "bigPackageSizeSupport": true,
     "babelSetting": {

Некоторые файлы не были показаны из-за большого количества измененных файлов