|
@@ -105,18 +105,16 @@ const _sfc_main = {
|
|
showStartRecordingButton: false,
|
|
showStartRecordingButton: false,
|
|
showRetryButton: false,
|
|
showRetryButton: false,
|
|
// 控制重试按钮显示
|
|
// 控制重试按钮显示
|
|
- lastVideoToRetry: null,
|
|
|
|
|
|
+ lastVideoToRetry: null
|
|
// 存储上次失败的视频URL,用于重试
|
|
// 存储上次失败的视频URL,用于重试
|
|
- stopIOSRecording: null,
|
|
|
|
- // 存储iOS特殊停止录制函数
|
|
|
|
- cameraInitialized: false,
|
|
|
|
- iosDevice: false
|
|
|
|
};
|
|
};
|
|
},
|
|
},
|
|
mounted() {
|
|
mounted() {
|
|
this.playDigitalHumanVideo();
|
|
this.playDigitalHumanVideo();
|
|
this.checkAudioPermission();
|
|
this.checkAudioPermission();
|
|
this.initCamera();
|
|
this.initCamera();
|
|
|
|
+ this.checkIOSCameraRecordPermission();
|
|
|
|
+ this.checkAndFixRenderingIssues();
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
if (this.cameraStream && !this.useMiniProgramCameraComponent) {
|
|
if (this.cameraStream && !this.useMiniProgramCameraComponent) {
|
|
this.testAudioInput();
|
|
this.testAudioInput();
|
|
@@ -130,18 +128,44 @@ const _sfc_main = {
|
|
// 初始化相机
|
|
// 初始化相机
|
|
async initCamera() {
|
|
async initCamera() {
|
|
const systemInfo = common_vendor.index.getSystemInfoSync();
|
|
const systemInfo = common_vendor.index.getSystemInfoSync();
|
|
- this.iosDevice = systemInfo.platform === "ios";
|
|
|
|
- console.log("当前设备平台:", systemInfo.platform, "是否iOS:", this.iosDevice);
|
|
|
|
const isMiniProgram = systemInfo.uniPlatform === "mp-weixin" || systemInfo.uniPlatform === "mp-alipay" || systemInfo.uniPlatform === "mp-baidu";
|
|
const isMiniProgram = systemInfo.uniPlatform === "mp-weixin" || systemInfo.uniPlatform === "mp-alipay" || systemInfo.uniPlatform === "mp-baidu";
|
|
if (isMiniProgram) {
|
|
if (isMiniProgram) {
|
|
this.useMiniProgramCameraComponent = true;
|
|
this.useMiniProgramCameraComponent = true;
|
|
- await this.checkMiniProgramPermissions();
|
|
|
|
- setTimeout(() => {
|
|
|
|
- if (!this.cameraContext) {
|
|
|
|
- this.cameraContext = common_vendor.index.createCameraContext();
|
|
|
|
- console.log("相机上下文创建完成");
|
|
|
|
|
|
+ this.cameraContext = common_vendor.index.createCameraContext();
|
|
|
|
+ common_vendor.index.getSetting({
|
|
|
|
+ success: (res) => {
|
|
|
|
+ if (!res.authSetting["scope.record"]) {
|
|
|
|
+ common_vendor.index.authorize({
|
|
|
|
+ scope: "scope.record",
|
|
|
|
+ success: () => {
|
|
|
|
+ console.log("录音权限已获取");
|
|
|
|
+ },
|
|
|
|
+ fail: (err) => {
|
|
|
|
+ console.error("录音权限获取失败:", err);
|
|
|
|
+ this.showPermissionDialog("录音");
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ if (!res.authSetting["scope.camera"]) {
|
|
|
|
+ common_vendor.index.authorize({
|
|
|
|
+ scope: "scope.camera",
|
|
|
|
+ success: () => {
|
|
|
|
+ console.log("相机权限已获取");
|
|
|
|
+ },
|
|
|
|
+ fail: (err) => {
|
|
|
|
+ console.error("相机权限获取失败:", err);
|
|
|
|
+ this.showPermissionDialog("相机");
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ const systemInfo2 = common_vendor.index.getSystemInfoSync();
|
|
|
|
+ if (systemInfo2.platform === "ios") {
|
|
|
|
+ if (!res.authSetting["scope.camera"] || !res.authSetting["scope.record"]) {
|
|
|
|
+ console.log("iOS需要同时获取相机和录音权限");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- }, 500);
|
|
|
|
|
|
+ });
|
|
} else {
|
|
} else {
|
|
try {
|
|
try {
|
|
const constraints = {
|
|
const constraints = {
|
|
@@ -519,131 +543,150 @@ const _sfc_main = {
|
|
}
|
|
}
|
|
this.showStopRecordingButton = true;
|
|
this.showStopRecordingButton = true;
|
|
},
|
|
},
|
|
- // 修改小程序环境下的录制方法
|
|
|
|
|
|
+ // 添加一个新方法:重置相机组件
|
|
|
|
+ resetCamera() {
|
|
|
|
+ console.log("重置相机组件");
|
|
|
|
+ this.useMiniProgramCameraComponent = false;
|
|
|
|
+ if (this.cameraContext) {
|
|
|
|
+ this.cameraContext = null;
|
|
|
|
+ }
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ this.useMiniProgramCameraComponent = true;
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ this.cameraContext = common_vendor.index.createCameraContext();
|
|
|
|
+ console.log("相机组件已重置");
|
|
|
|
+ }, 500);
|
|
|
|
+ }, 500);
|
|
|
|
+ },
|
|
|
|
+ // 修改 startMiniProgramRecording 方法
|
|
startMiniProgramRecording() {
|
|
startMiniProgramRecording() {
|
|
- console.log("开始小程序录像,相机初始化状态:", this.cameraInitialized);
|
|
|
|
- if (!this.cameraInitialized) {
|
|
|
|
- common_vendor.index.showToast({
|
|
|
|
- title: "相机正在初始化,请稍候",
|
|
|
|
- icon: "none"
|
|
|
|
- });
|
|
|
|
|
|
+ console.log("开始小程序录制方法");
|
|
|
|
+ const systemInfo = common_vendor.index.getSystemInfoSync();
|
|
|
|
+ const isIOS = systemInfo.platform === "ios";
|
|
|
|
+ if (isIOS) {
|
|
|
|
+ this.resetCamera();
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
- this.startMiniProgramRecording();
|
|
|
|
|
|
+ this.actualStartRecording(isIOS);
|
|
}, 1e3);
|
|
}, 1e3);
|
|
- return;
|
|
|
|
|
|
+ } else {
|
|
|
|
+ this.actualStartRecording(isIOS);
|
|
}
|
|
}
|
|
|
|
+ },
|
|
|
|
+ // 添加新方法:实际开始录制
|
|
|
|
+ actualStartRecording(isIOS) {
|
|
if (!this.cameraContext) {
|
|
if (!this.cameraContext) {
|
|
this.cameraContext = common_vendor.index.createCameraContext();
|
|
this.cameraContext = common_vendor.index.createCameraContext();
|
|
- console.log("创建相机上下文");
|
|
|
|
|
|
+ console.log("创建新的相机上下文");
|
|
}
|
|
}
|
|
- const options = {
|
|
|
|
- timeout: 6e4,
|
|
|
|
- // 最长录制60秒
|
|
|
|
- success: () => {
|
|
|
|
- console.log("小程序录像开始成功");
|
|
|
|
- this.isRecording = true;
|
|
|
|
- },
|
|
|
|
- fail: (err) => {
|
|
|
|
- console.error("小程序录像开始失败:", err);
|
|
|
|
- if (this.iosDevice && !this.hasRetriedIOS) {
|
|
|
|
- this.hasRetriedIOS = true;
|
|
|
|
- console.log("iOS设备录制失败,尝试使用备用方法");
|
|
|
|
- setTimeout(() => {
|
|
|
|
- this.cameraContext = common_vendor.index.createCameraContext();
|
|
|
|
- this.cameraContext.startRecord({
|
|
|
|
- success: () => {
|
|
|
|
- console.log("iOS备用方法录像开始成功");
|
|
|
|
- this.isRecording = true;
|
|
|
|
- },
|
|
|
|
- fail: (retryErr) => {
|
|
|
|
- console.error("iOS备用方法录像失败:", retryErr);
|
|
|
|
- common_vendor.index.showToast({
|
|
|
|
- title: "录制失败,请检查相机权限",
|
|
|
|
- icon: "none"
|
|
|
|
- });
|
|
|
|
- this.proceedToNextQuestion();
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- }, 500);
|
|
|
|
|
|
+ common_vendor.index.getSetting({
|
|
|
|
+ success: (res) => {
|
|
|
|
+ const hasRecordAuth = res.authSetting["scope.record"];
|
|
|
|
+ const hasCameraAuth = res.authSetting["scope.camera"];
|
|
|
|
+ if (!hasRecordAuth || !hasCameraAuth) {
|
|
|
|
+ console.warn("缺少必要权限,请求权限");
|
|
|
|
+ this.requestMiniProgramPermissions();
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- common_vendor.index.showToast({
|
|
|
|
- title: "录制失败,请检查相机权限",
|
|
|
|
- icon: "none"
|
|
|
|
- });
|
|
|
|
|
|
+ if (isIOS) {
|
|
|
|
+ console.log("iOS: 检查相机状态");
|
|
|
|
+ const options = {
|
|
|
|
+ timeout: 3e4,
|
|
|
|
+ // 减少超时时间
|
|
|
|
+ quality: "low",
|
|
|
|
+ // 降低质量
|
|
|
|
+ compressed: true,
|
|
|
|
+ success: () => {
|
|
|
|
+ console.log("iOS录制开始成功");
|
|
|
|
+ },
|
|
|
|
+ fail: (err) => {
|
|
|
|
+ console.error("iOS录制失败:", err);
|
|
|
|
+ this.useAlternativeRecordingMethod();
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ try {
|
|
|
|
+ console.log("尝试开始录制");
|
|
|
|
+ this.recorder = this.cameraContext.startRecord(options);
|
|
|
|
+ } catch (e) {
|
|
|
|
+ console.error("开始录制异常:", e);
|
|
|
|
+ this.useAlternativeRecordingMethod();
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ const options = {
|
|
|
|
+ timeout: 6e4,
|
|
|
|
+ quality: "medium",
|
|
|
|
+ compressed: true,
|
|
|
|
+ success: () => {
|
|
|
|
+ console.log("Android录制开始成功");
|
|
|
|
+ },
|
|
|
|
+ fail: (err) => {
|
|
|
|
+ console.error("Android录制失败:", err);
|
|
|
|
+ common_vendor.index.showToast({
|
|
|
|
+ title: "录制失败,请检查相机权限",
|
|
|
|
+ icon: "none"
|
|
|
|
+ });
|
|
|
|
+ this.proceedToNextQuestion();
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ this.recorder = this.cameraContext.startRecord(options);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ // 添加新方法:使用替代录制方法
|
|
|
|
+ useAlternativeRecordingMethod() {
|
|
|
|
+ console.log("使用替代录制方法");
|
|
|
|
+ common_vendor.index.showActionSheet({
|
|
|
|
+ itemList: ["使用相册中的视频", "跳过此问题"],
|
|
|
|
+ success: (res) => {
|
|
|
|
+ if (res.tapIndex === 0) {
|
|
|
|
+ common_vendor.index.chooseVideo({
|
|
|
|
+ sourceType: ["album"],
|
|
|
|
+ maxDuration: 60,
|
|
|
|
+ camera: "front",
|
|
|
|
+ success: (res2) => {
|
|
|
|
+ console.log("选择视频成功:", res2.tempFilePath);
|
|
|
|
+ this.isRecording = false;
|
|
|
|
+ this.showStopRecordingButton = false;
|
|
|
|
+ this.uploadRecordedVideo(res2.tempFilePath);
|
|
|
|
+ },
|
|
|
|
+ fail: () => {
|
|
|
|
+ console.log("用户取消选择视频");
|
|
|
|
+ this.proceedToNextQuestion();
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ console.log("用户选择跳过问题");
|
|
|
|
+ this.proceedToNextQuestion();
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ fail: () => {
|
|
|
|
+ console.log("操作取消");
|
|
this.proceedToNextQuestion();
|
|
this.proceedToNextQuestion();
|
|
}
|
|
}
|
|
- };
|
|
|
|
- if (this.iosDevice) {
|
|
|
|
- delete options.timeout;
|
|
|
|
- console.log("使用iOS专用录制参数");
|
|
|
|
- } else {
|
|
|
|
- options.quality = "medium";
|
|
|
|
- options.compressed = true;
|
|
|
|
- options.timeoutCallback = () => {
|
|
|
|
- console.log("录制超时自动停止");
|
|
|
|
- this.stopRecordingAnswer();
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
- console.log("调用startRecord,参数:", JSON.stringify(options));
|
|
|
|
- this.recorder = this.cameraContext.startRecord(options);
|
|
|
|
|
|
+ });
|
|
},
|
|
},
|
|
// 添加新方法:请求小程序权限
|
|
// 添加新方法:请求小程序权限
|
|
- async checkMiniProgramPermissions() {
|
|
|
|
- return new Promise((resolve) => {
|
|
|
|
- common_vendor.index.getSetting({
|
|
|
|
- success: (res) => {
|
|
|
|
- const hasRecordAuth = res.authSetting["scope.record"];
|
|
|
|
- const hasCameraAuth = res.authSetting["scope.camera"];
|
|
|
|
- const authPromises = [];
|
|
|
|
- if (!hasRecordAuth) {
|
|
|
|
- authPromises.push(
|
|
|
|
- new Promise((resolveAuth) => {
|
|
|
|
- common_vendor.index.authorize({
|
|
|
|
- scope: "scope.record",
|
|
|
|
- success: () => {
|
|
|
|
- console.log("录音权限已获取");
|
|
|
|
- resolveAuth();
|
|
|
|
- },
|
|
|
|
- fail: (err) => {
|
|
|
|
- console.error("录音权限获取失败:", err);
|
|
|
|
- this.showPermissionDialog("录音");
|
|
|
|
- resolveAuth();
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- if (!hasCameraAuth) {
|
|
|
|
- authPromises.push(
|
|
|
|
- new Promise((resolveAuth) => {
|
|
|
|
- common_vendor.index.authorize({
|
|
|
|
- scope: "scope.camera",
|
|
|
|
- success: () => {
|
|
|
|
- console.log("相机权限已获取");
|
|
|
|
- resolveAuth();
|
|
|
|
- },
|
|
|
|
- fail: (err) => {
|
|
|
|
- console.error("相机权限获取失败:", err);
|
|
|
|
- this.showPermissionDialog("相机");
|
|
|
|
- resolveAuth();
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- }
|
|
|
|
- if (authPromises.length > 0) {
|
|
|
|
- Promise.all(authPromises).then(() => {
|
|
|
|
- resolve();
|
|
|
|
- });
|
|
|
|
- } else {
|
|
|
|
- resolve();
|
|
|
|
|
|
+ requestMiniProgramPermissions() {
|
|
|
|
+ common_vendor.index.authorize({
|
|
|
|
+ scope: "scope.record",
|
|
|
|
+ success: () => {
|
|
|
|
+ console.log("录音权限已获取");
|
|
|
|
+ common_vendor.index.authorize({
|
|
|
|
+ scope: "scope.camera",
|
|
|
|
+ success: () => {
|
|
|
|
+ console.log("相机权限已获取");
|
|
|
|
+ this.startMiniProgramRecording();
|
|
|
|
+ },
|
|
|
|
+ fail: (err) => {
|
|
|
|
+ console.error("相机权限获取失败:", err);
|
|
|
|
+ this.showPermissionDialog("相机");
|
|
}
|
|
}
|
|
- },
|
|
|
|
- fail: () => {
|
|
|
|
- resolve();
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ fail: (err) => {
|
|
|
|
+ console.error("录音权限获取失败:", err);
|
|
|
|
+ this.showPermissionDialog("录音");
|
|
|
|
+ }
|
|
});
|
|
});
|
|
},
|
|
},
|
|
// 修改浏览器环境下的录制方法
|
|
// 修改浏览器环境下的录制方法
|
|
@@ -658,8 +701,6 @@ const _sfc_main = {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
try {
|
|
try {
|
|
- const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
|
|
|
- console.log("当前设备是否为iOS:", isIOS);
|
|
|
|
const hasAudio = this.cameraStream.getAudioTracks().length > 0;
|
|
const hasAudio = this.cameraStream.getAudioTracks().length > 0;
|
|
if (!hasAudio) {
|
|
if (!hasAudio) {
|
|
console.warn("警告:媒体流中没有音频轨道,尝试重新获取带音频的媒体流");
|
|
console.warn("警告:媒体流中没有音频轨道,尝试重新获取带音频的媒体流");
|
|
@@ -687,18 +728,18 @@ const _sfc_main = {
|
|
videoElement.srcObject = combinedStream;
|
|
videoElement.srcObject = combinedStream;
|
|
videoElement.muted = true;
|
|
videoElement.muted = true;
|
|
}
|
|
}
|
|
- this.setupMediaRecorder(combinedStream, isIOS);
|
|
|
|
|
|
+ this.setupMediaRecorder(combinedStream);
|
|
} else {
|
|
} else {
|
|
console.warn("仍然无法获取音频轨道");
|
|
console.warn("仍然无法获取音频轨道");
|
|
- this.setupMediaRecorder(this.cameraStream, isIOS);
|
|
|
|
|
|
+ this.setupMediaRecorder(this.cameraStream);
|
|
}
|
|
}
|
|
}).catch((err) => {
|
|
}).catch((err) => {
|
|
console.error("获取音频失败:", err);
|
|
console.error("获取音频失败:", err);
|
|
- this.setupMediaRecorder(this.cameraStream, isIOS);
|
|
|
|
|
|
+ this.setupMediaRecorder(this.cameraStream);
|
|
});
|
|
});
|
|
} else {
|
|
} else {
|
|
console.log("检测到音频轨道,直接使用");
|
|
console.log("检测到音频轨道,直接使用");
|
|
- this.setupMediaRecorder(this.cameraStream, isIOS);
|
|
|
|
|
|
+ this.setupMediaRecorder(this.cameraStream);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
} catch (error) {
|
|
console.error("浏览器录制失败:", error);
|
|
console.error("浏览器录制失败:", error);
|
|
@@ -709,16 +750,11 @@ const _sfc_main = {
|
|
this.proceedToNextQuestion();
|
|
this.proceedToNextQuestion();
|
|
}
|
|
}
|
|
},
|
|
},
|
|
- // 修改 setupMediaRecorder 方法,添加 isIOS 参数
|
|
|
|
- setupMediaRecorder(stream, isIOS) {
|
|
|
|
|
|
+ // 修改 setupMediaRecorder 方法
|
|
|
|
+ setupMediaRecorder(stream) {
|
|
const videoTracks = stream.getVideoTracks();
|
|
const videoTracks = stream.getVideoTracks();
|
|
const audioTracks = stream.getAudioTracks();
|
|
const audioTracks = stream.getAudioTracks();
|
|
console.log("设置MediaRecorder - 视频轨道:", videoTracks.length, "音频轨道:", audioTracks.length);
|
|
console.log("设置MediaRecorder - 视频轨道:", videoTracks.length, "音频轨道:", audioTracks.length);
|
|
- if (isIOS) {
|
|
|
|
- console.log("检测到iOS设备,使用替代录制方法");
|
|
|
|
- this.setupIOSRecording(stream);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
let mimeType = "";
|
|
let mimeType = "";
|
|
const supportedTypes = [
|
|
const supportedTypes = [
|
|
"video/webm;codecs=vp9,opus",
|
|
"video/webm;codecs=vp9,opus",
|
|
@@ -798,109 +834,6 @@ const _sfc_main = {
|
|
console.error("开始录制失败:", e);
|
|
console.error("开始录制失败:", e);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
- // 添加新方法:为iOS设备设置特殊录制方法
|
|
|
|
- setupIOSRecording(stream) {
|
|
|
|
- console.log("设置iOS特殊录制方法");
|
|
|
|
- const videoElement = this.$refs.userCameraVideo;
|
|
|
|
- const canvas = document.createElement("canvas");
|
|
|
|
- const ctx = canvas.getContext("2d");
|
|
|
|
- canvas.width = 640;
|
|
|
|
- canvas.height = 480;
|
|
|
|
- this.recordedFrames = [];
|
|
|
|
- this.recordingStartTime = Date.now();
|
|
|
|
- this.frameCount = 0;
|
|
|
|
- const audioTracks = stream.getAudioTracks();
|
|
|
|
- let audioRecorder = null;
|
|
|
|
- let audioChunks = [];
|
|
|
|
- if (audioTracks.length > 0) {
|
|
|
|
- const audioStream = new MediaStream([audioTracks[0]]);
|
|
|
|
- try {
|
|
|
|
- audioRecorder = new MediaRecorder(audioStream);
|
|
|
|
- audioRecorder.ondataavailable = (e) => {
|
|
|
|
- if (e.data.size > 0) {
|
|
|
|
- audioChunks.push(e.data);
|
|
|
|
- }
|
|
|
|
- };
|
|
|
|
- audioRecorder.start(1e3);
|
|
|
|
- } catch (e) {
|
|
|
|
- console.warn("iOS音频录制器创建失败:", e);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- const captureFrame = () => {
|
|
|
|
- if (!this.isRecording)
|
|
|
|
- return;
|
|
|
|
- try {
|
|
|
|
- ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
|
|
|
|
- const imageData = canvas.toDataURL("image/jpeg", 0.8);
|
|
|
|
- this.recordedFrames.push({
|
|
|
|
- image: imageData,
|
|
|
|
- timestamp: Date.now() - this.recordingStartTime
|
|
|
|
- });
|
|
|
|
- this.frameCount++;
|
|
|
|
- if (this.frameCount % 15 === 0) {
|
|
|
|
- console.log(`已捕获 ${this.frameCount} 帧`);
|
|
|
|
- }
|
|
|
|
- this.frameCapturingId = requestAnimationFrame(captureFrame);
|
|
|
|
- } catch (e) {
|
|
|
|
- console.error("捕获视频帧失败:", e);
|
|
|
|
- }
|
|
|
|
- };
|
|
|
|
- this.frameCapturingId = requestAnimationFrame(captureFrame);
|
|
|
|
- this.stopIOSRecording = async () => {
|
|
|
|
- console.log("停止iOS录制,已捕获帧数:", this.recordedFrames.length);
|
|
|
|
- if (this.frameCapturingId) {
|
|
|
|
- cancelAnimationFrame(this.frameCapturingId);
|
|
|
|
- this.frameCapturingId = null;
|
|
|
|
- }
|
|
|
|
- if (audioRecorder && audioRecorder.state !== "inactive") {
|
|
|
|
- audioRecorder.stop();
|
|
|
|
- }
|
|
|
|
- common_vendor.index.showLoading({
|
|
|
|
- title: "正在处理视频...",
|
|
|
|
- mask: true
|
|
|
|
- });
|
|
|
|
- try {
|
|
|
|
- if (audioRecorder) {
|
|
|
|
- await new Promise((resolve) => {
|
|
|
|
- audioRecorder.onstop = resolve;
|
|
|
|
- if (audioRecorder.state === "inactive")
|
|
|
|
- resolve();
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- const videoBlob = await this.createVideoFromFrames(this.recordedFrames, audioChunks);
|
|
|
|
- const fileName = `answer_${this.currentVideoIndex}_${Date.now()}.mp4`;
|
|
|
|
- const file = new File([videoBlob], fileName, { type: "video/mp4" });
|
|
|
|
- common_vendor.index.hideLoading();
|
|
|
|
- this.uploadRecordedVideo(file);
|
|
|
|
- } catch (error) {
|
|
|
|
- console.error("iOS视频处理失败:", error);
|
|
|
|
- common_vendor.index.hideLoading();
|
|
|
|
- common_vendor.index.showToast({
|
|
|
|
- title: "视频处理失败",
|
|
|
|
- icon: "none"
|
|
|
|
- });
|
|
|
|
- this.proceedToNextQuestion();
|
|
|
|
- }
|
|
|
|
- };
|
|
|
|
- },
|
|
|
|
- // 添加新方法:从帧创建视频
|
|
|
|
- async createVideoFromFrames(frames, audioChunks) {
|
|
|
|
- console.log("从帧创建视频,帧数:", frames.length);
|
|
|
|
- const videoData = {
|
|
|
|
- frames: frames.slice(0, Math.min(frames.length, 300)),
|
|
|
|
- // 限制帧数以减小大小
|
|
|
|
- audioBlob: audioChunks.length > 0 ? await new Blob(audioChunks, { type: "audio/webm" }).arrayBuffer() : null,
|
|
|
|
- metadata: {
|
|
|
|
- duration: frames.length > 0 ? frames[frames.length - 1].timestamp : 0,
|
|
|
|
- frameCount: frames.length,
|
|
|
|
- hasAudio: audioChunks.length > 0,
|
|
|
|
- deviceInfo: navigator.userAgent,
|
|
|
|
- timestamp: Date.now()
|
|
|
|
- }
|
|
|
|
- };
|
|
|
|
- const jsonString = JSON.stringify(videoData);
|
|
|
|
- return new Blob([jsonString], { type: "application/json" });
|
|
|
|
- },
|
|
|
|
// 添加新方法:停止录制用户回答
|
|
// 添加新方法:停止录制用户回答
|
|
stopRecordingAnswer() {
|
|
stopRecordingAnswer() {
|
|
console.log("停止录制用户回答");
|
|
console.log("停止录制用户回答");
|
|
@@ -911,48 +844,56 @@ const _sfc_main = {
|
|
}
|
|
}
|
|
common_vendor.index.hideLoading();
|
|
common_vendor.index.hideLoading();
|
|
this.showStopRecordingButton = false;
|
|
this.showStopRecordingButton = false;
|
|
- const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
|
|
|
const systemInfo = common_vendor.index.getSystemInfoSync();
|
|
const systemInfo = common_vendor.index.getSystemInfoSync();
|
|
const isMiniProgram = systemInfo.uniPlatform && systemInfo.uniPlatform.startsWith("mp-");
|
|
const isMiniProgram = systemInfo.uniPlatform && systemInfo.uniPlatform.startsWith("mp-");
|
|
if (isMiniProgram) {
|
|
if (isMiniProgram) {
|
|
this.stopMiniProgramRecording();
|
|
this.stopMiniProgramRecording();
|
|
- } else if (isIOS && this.stopIOSRecording) {
|
|
|
|
- this.stopIOSRecording();
|
|
|
|
} else {
|
|
} else {
|
|
this.stopBrowserRecording();
|
|
this.stopBrowserRecording();
|
|
}
|
|
}
|
|
},
|
|
},
|
|
- // 添加新方法:停止小程序录制
|
|
|
|
|
|
+ // 修改 stopMiniProgramRecording 方法
|
|
stopMiniProgramRecording() {
|
|
stopMiniProgramRecording() {
|
|
- console.log("停止小程序录像");
|
|
|
|
if (!this.cameraContext) {
|
|
if (!this.cameraContext) {
|
|
console.error("相机上下文不存在");
|
|
console.error("相机上下文不存在");
|
|
this.proceedToNextQuestion();
|
|
this.proceedToNextQuestion();
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- common_vendor.index.showLoading({
|
|
|
|
- title: "正在处理视频...",
|
|
|
|
- mask: true
|
|
|
|
- });
|
|
|
|
- this.cameraContext.stopRecord({
|
|
|
|
|
|
+ const systemInfo = common_vendor.index.getSystemInfoSync();
|
|
|
|
+ const isIOS = systemInfo.platform === "ios";
|
|
|
|
+ const stopOptions = {
|
|
success: (res) => {
|
|
success: (res) => {
|
|
console.log("小程序录像停止成功:", res);
|
|
console.log("小程序录像停止成功:", res);
|
|
- common_vendor.index.hideLoading();
|
|
|
|
- const tempFilePath = res.tempVideoPath || res.tempThumbPath || res.tempFilePath;
|
|
|
|
- if (tempFilePath) {
|
|
|
|
- console.log("获取到视频文件路径:", tempFilePath);
|
|
|
|
- this.uploadRecordedVideo(tempFilePath);
|
|
|
|
- } else {
|
|
|
|
|
|
+ const tempFilePath = res.tempVideoPath;
|
|
|
|
+ if (!tempFilePath) {
|
|
console.error("未获取到视频文件路径");
|
|
console.error("未获取到视频文件路径");
|
|
common_vendor.index.showToast({
|
|
common_vendor.index.showToast({
|
|
title: "录制失败,未获取到视频文件",
|
|
title: "录制失败,未获取到视频文件",
|
|
icon: "none"
|
|
icon: "none"
|
|
});
|
|
});
|
|
this.proceedToNextQuestion();
|
|
this.proceedToNextQuestion();
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ if (isIOS) {
|
|
|
|
+ common_vendor.index.getFileInfo({
|
|
|
|
+ filePath: tempFilePath,
|
|
|
|
+ success: () => {
|
|
|
|
+ this.uploadRecordedVideo(tempFilePath);
|
|
|
|
+ },
|
|
|
|
+ fail: (err) => {
|
|
|
|
+ console.error("视频文件不存在:", err);
|
|
|
|
+ common_vendor.index.showToast({
|
|
|
|
+ title: "录制失败,视频文件不存在",
|
|
|
|
+ icon: "none"
|
|
|
|
+ });
|
|
|
|
+ this.proceedToNextQuestion();
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ this.uploadRecordedVideo(tempFilePath);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
fail: (err) => {
|
|
fail: (err) => {
|
|
- common_vendor.index.hideLoading();
|
|
|
|
console.error("小程序录像停止失败:", err);
|
|
console.error("小程序录像停止失败:", err);
|
|
common_vendor.index.showToast({
|
|
common_vendor.index.showToast({
|
|
title: "录制失败",
|
|
title: "录制失败",
|
|
@@ -960,7 +901,8 @@ const _sfc_main = {
|
|
});
|
|
});
|
|
this.proceedToNextQuestion();
|
|
this.proceedToNextQuestion();
|
|
}
|
|
}
|
|
- });
|
|
|
|
|
|
+ };
|
|
|
|
+ this.cameraContext.stopRecord(stopOptions);
|
|
},
|
|
},
|
|
// 添加新方法:停止浏览器录制
|
|
// 添加新方法:停止浏览器录制
|
|
stopBrowserRecording() {
|
|
stopBrowserRecording() {
|
|
@@ -975,7 +917,6 @@ const _sfc_main = {
|
|
// 修改上传录制的视频方法
|
|
// 修改上传录制的视频方法
|
|
uploadRecordedVideo(fileOrPath) {
|
|
uploadRecordedVideo(fileOrPath) {
|
|
console.log("准备上传视频:", typeof fileOrPath === "string" ? fileOrPath : fileOrPath.name);
|
|
console.log("准备上传视频:", typeof fileOrPath === "string" ? fileOrPath : fileOrPath.name);
|
|
- const isIOSSpecialFormat = typeof fileOrPath !== "string" && fileOrPath.type === "application/json";
|
|
|
|
common_vendor.index.showLoading({
|
|
common_vendor.index.showLoading({
|
|
title: "正在上传...",
|
|
title: "正在上传...",
|
|
mask: true
|
|
mask: true
|
|
@@ -1009,9 +950,6 @@ const _sfc_main = {
|
|
formData.append("question_id", questionId);
|
|
formData.append("question_id", questionId);
|
|
formData.append("video_duration", 0);
|
|
formData.append("video_duration", 0);
|
|
formData.append("has_audio", "true");
|
|
formData.append("has_audio", "true");
|
|
- if (isIOSSpecialFormat) {
|
|
|
|
- formData.append("is_ios_format", "true");
|
|
|
|
- }
|
|
|
|
const xhr = new XMLHttpRequest();
|
|
const xhr = new XMLHttpRequest();
|
|
xhr.open("POST", `${common_config.apiBaseUrl}/api/system/upload/`, true);
|
|
xhr.open("POST", `${common_config.apiBaseUrl}/api/system/upload/`, true);
|
|
xhr.timeout = 12e4;
|
|
xhr.timeout = 12e4;
|
|
@@ -1241,11 +1179,30 @@ const _sfc_main = {
|
|
// 处理相机错误
|
|
// 处理相机错误
|
|
handleCameraError(e) {
|
|
handleCameraError(e) {
|
|
console.error("相机错误:", e);
|
|
console.error("相机错误:", e);
|
|
- common_vendor.index.showToast({
|
|
|
|
- title: "相机初始化失败,请检查权限设置",
|
|
|
|
- icon: "none"
|
|
|
|
- });
|
|
|
|
- this.tryFallbackOptions();
|
|
|
|
|
|
+ const systemInfo = common_vendor.index.getSystemInfoSync();
|
|
|
|
+ const isIOS = systemInfo.platform === "ios";
|
|
|
|
+ if (isIOS) {
|
|
|
|
+ console.log("iOS相机错误,尝试重新初始化");
|
|
|
|
+ common_vendor.index.showToast({
|
|
|
|
+ title: "相机初始化中...",
|
|
|
|
+ icon: "loading",
|
|
|
|
+ duration: 2e3
|
|
|
|
+ });
|
|
|
|
+ this.resetCamera();
|
|
|
|
+ if (this.isRecording) {
|
|
|
|
+ this.isRecording = false;
|
|
|
|
+ this.showStopRecordingButton = false;
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ this.useAlternativeRecordingMethod();
|
|
|
|
+ }, 1e3);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ common_vendor.index.showToast({
|
|
|
|
+ title: "相机初始化失败,请检查权限设置",
|
|
|
|
+ icon: "none"
|
|
|
|
+ });
|
|
|
|
+ this.tryFallbackOptions();
|
|
|
|
+ }
|
|
},
|
|
},
|
|
// 添加新方法:尝试备用选项
|
|
// 添加新方法:尝试备用选项
|
|
tryFallbackOptions() {
|
|
tryFallbackOptions() {
|
|
@@ -1508,12 +1465,55 @@ const _sfc_main = {
|
|
};
|
|
};
|
|
});
|
|
});
|
|
},
|
|
},
|
|
- // 添加相机初始化完成的处理方法
|
|
|
|
- handleCameraInit() {
|
|
|
|
- console.log("相机初始化完成");
|
|
|
|
- this.cameraInitialized = true;
|
|
|
|
- if (!this.cameraContext && this.useMiniProgramCameraComponent) {
|
|
|
|
- this.cameraContext = common_vendor.index.createCameraContext();
|
|
|
|
|
|
+ // 修改 checkIOSCameraRecordPermission 方法
|
|
|
|
+ checkIOSCameraRecordPermission() {
|
|
|
|
+ const systemInfo = common_vendor.index.getSystemInfoSync();
|
|
|
|
+ if (systemInfo.platform !== "ios")
|
|
|
|
+ return;
|
|
|
|
+ common_vendor.index.getSetting({
|
|
|
|
+ success: (res) => {
|
|
|
|
+ if (!res.authSetting["scope.camera"]) {
|
|
|
|
+ common_vendor.index.authorize({
|
|
|
|
+ scope: "scope.camera",
|
|
|
|
+ success: () => {
|
|
|
|
+ console.log("iOS相机权限已获取");
|
|
|
|
+ },
|
|
|
|
+ fail: (err) => {
|
|
|
|
+ console.error("iOS相机权限获取失败:", err);
|
|
|
|
+ this.showPermissionDialog("相机");
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ if (!res.authSetting["scope.record"]) {
|
|
|
|
+ common_vendor.index.authorize({
|
|
|
|
+ scope: "scope.record",
|
|
|
|
+ success: () => {
|
|
|
|
+ console.log("iOS录音权限已获取");
|
|
|
|
+ },
|
|
|
|
+ fail: (err) => {
|
|
|
|
+ console.error("iOS录音权限获取失败:", err);
|
|
|
|
+ this.showPermissionDialog("录音");
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ // 添加新方法:检查并修复渲染问题
|
|
|
|
+ checkAndFixRenderingIssues() {
|
|
|
|
+ try {
|
|
|
|
+ if (typeof u !== "undefined" && u) {
|
|
|
|
+ if (!u.currentQuestion) {
|
|
|
|
+ console.log("修复: 创建缺失的currentQuestion对象");
|
|
|
|
+ u.currentQuestion = {};
|
|
|
|
+ }
|
|
|
|
+ if (u.currentQuestion && typeof u.currentQuestion.isImportant === "undefined") {
|
|
|
|
+ console.log("修复: 设置缺失的isImportant属性");
|
|
|
|
+ u.currentQuestion.isImportant = false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } catch (e) {
|
|
|
|
+ console.log("防御性检查异常:", e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -1534,22 +1534,21 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
} : {}, {
|
|
} : {}, {
|
|
i: $data.useMiniProgramCameraComponent
|
|
i: $data.useMiniProgramCameraComponent
|
|
}, $data.useMiniProgramCameraComponent ? {
|
|
}, $data.useMiniProgramCameraComponent ? {
|
|
- j: common_vendor.o((...args) => $options.handleCameraError && $options.handleCameraError(...args)),
|
|
|
|
- k: common_vendor.o((...args) => $options.handleCameraInit && $options.handleCameraInit(...args))
|
|
|
|
|
|
+ j: common_vendor.o((...args) => $options.handleCameraError && $options.handleCameraError(...args))
|
|
} : {}, {
|
|
} : {}, {
|
|
- l: $data.loading
|
|
|
|
|
|
+ k: $data.loading
|
|
}, $data.loading ? {} : {}, {
|
|
}, $data.loading ? {} : {}, {
|
|
- m: $data.showDebugInfo
|
|
|
|
|
|
+ l: $data.showDebugInfo
|
|
}, $data.showDebugInfo ? common_vendor.e({
|
|
}, $data.showDebugInfo ? common_vendor.e({
|
|
- n: $data.assistantResponse
|
|
|
|
|
|
+ m: $data.assistantResponse
|
|
}, $data.assistantResponse ? {
|
|
}, $data.assistantResponse ? {
|
|
- o: common_vendor.t($data.assistantResponse)
|
|
|
|
|
|
+ n: common_vendor.t($data.assistantResponse)
|
|
} : {}, {
|
|
} : {}, {
|
|
- p: $data.audioTranscript
|
|
|
|
|
|
+ o: $data.audioTranscript
|
|
}, $data.audioTranscript ? {
|
|
}, $data.audioTranscript ? {
|
|
- q: common_vendor.t($data.audioTranscript)
|
|
|
|
|
|
+ p: common_vendor.t($data.audioTranscript)
|
|
} : {}, {
|
|
} : {}, {
|
|
- r: common_vendor.f($data.processedResponses, (item, index, i0) => {
|
|
|
|
|
|
+ q: common_vendor.f($data.processedResponses, (item, index, i0) => {
|
|
return common_vendor.e({
|
|
return common_vendor.e({
|
|
a: item.role
|
|
a: item.role
|
|
}, item.role ? {
|
|
}, item.role ? {
|
|
@@ -1563,19 +1562,19 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
});
|
|
});
|
|
})
|
|
})
|
|
}) : {}, {
|
|
}) : {}, {
|
|
- s: $data.showStopRecordingButton
|
|
|
|
|
|
+ r: $data.showStopRecordingButton
|
|
}, $data.showStopRecordingButton ? {
|
|
}, $data.showStopRecordingButton ? {
|
|
- t: common_vendor.o((...args) => $options.stopRecordingAnswer && $options.stopRecordingAnswer(...args))
|
|
|
|
|
|
+ s: common_vendor.o((...args) => $options.stopRecordingAnswer && $options.stopRecordingAnswer(...args))
|
|
} : {}, {
|
|
} : {}, {
|
|
- v: $data.isRecording
|
|
|
|
|
|
+ t: $data.isRecording
|
|
}, $data.isRecording ? {} : {}, {
|
|
}, $data.isRecording ? {} : {}, {
|
|
- w: $data.showStartRecordingButton
|
|
|
|
|
|
+ v: $data.showStartRecordingButton
|
|
}, $data.showStartRecordingButton ? {
|
|
}, $data.showStartRecordingButton ? {
|
|
- x: common_vendor.o((...args) => $options.handleStartRecordingClick && $options.handleStartRecordingClick(...args))
|
|
|
|
|
|
+ w: common_vendor.o((...args) => $options.handleStartRecordingClick && $options.handleStartRecordingClick(...args))
|
|
} : {}, {
|
|
} : {}, {
|
|
- y: $data.showRetryButton
|
|
|
|
|
|
+ x: $data.showRetryButton
|
|
}, $data.showRetryButton ? {
|
|
}, $data.showRetryButton ? {
|
|
- z: common_vendor.o((...args) => $options.retryVideoUpload && $options.retryVideoUpload(...args))
|
|
|
|
|
|
+ y: common_vendor.o((...args) => $options.retryVideoUpload && $options.retryVideoUpload(...args))
|
|
} : {});
|
|
} : {});
|
|
}
|
|
}
|
|
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-464e78c6"]]);
|
|
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-464e78c6"]]);
|