修复无法播放bug

main
wang 2 months ago
parent 36c92b7faf
commit 70e431eebc

@ -675,16 +675,33 @@ def handle_send_audio_message(data):
room_id = data.get('room_id')
audio_data = data.get('audio_data') # Base64编码的音频数据
audio_duration = data.get('audio_duration', 0) # 音频持续时间(秒)
audio_mime_type = data.get('audio_mime_type', 'audio/webm') # 音频MIME类型
if not room_id or not audio_data:
logger.error("语音消息缺少必要参数: room_id或audio_data")
return
# 检查audio_data是否为有效的base64编码数据
if not audio_data.startswith('data:audio'):
logger.warning(f"接收到非标准格式的音频数据,前缀: {audio_data[:20]}")
# 检查和修复音频数据格式
try:
# 确保音频数据有正确的MIME类型前缀
if not audio_data.startswith('data:audio'):
logger.warning(f"接收到非标准格式的音频数据,尝试修复")
# 如果数据是base64但没有前缀添加前缀
if not audio_data.startswith('data:'):
# 从base64数据中移除可能存在的前缀
cleaned_data = audio_data.replace('data:audio/webm;base64,', '')
cleaned_data = cleaned_data.replace('data:audio/ogg;base64,', '')
cleaned_data = cleaned_data.replace('data:audio/wav;base64,', '')
cleaned_data = cleaned_data.replace('data:;base64,', '')
# 使用客户端提供的MIME类型
audio_data = f'data:{audio_mime_type};base64,{cleaned_data}'
logger.info(f"已修复音频数据格式使用MIME类型: {audio_mime_type}")
except Exception as e:
logger.error(f"修复音频数据格式时出错: {str(e)}")
logger.info(f"处理语音消息: 用户ID={user_id}, 房间ID={room_id}, 音频时长={audio_duration}秒, 数据长度={len(audio_data)}")
logger.info(f"处理语音消息: 用户ID={user_id}, 房间ID={room_id}, 音频时长={audio_duration}秒, MIME类型={audio_mime_type}, 数据长度={len(audio_data)}")
try:
# 获取用户和房间信息
@ -716,6 +733,7 @@ def handle_send_audio_message(data):
'message': audio_data,
'message_type': 'audio',
'audio_duration': audio_duration,
'audio_mime_type': audio_mime_type,
'created_at': message.created_at.isoformat()
}, room=str(room_id))
logger.info(f"语音消息已广播到房间: {room_id}")

File diff suppressed because one or more lines are too long

@ -194,16 +194,14 @@
/>
<button @click="sendMessage" :disabled="!newMessage.trim()" class="send-button">发送</button>
<!-- 语音消息按钮 -->
<!-- 语音消息按钮 - 改为开关形式 -->
<button
@mousedown="startRecording"
@mouseup="stopRecording"
@mouseleave="cancelRecording"
@click="toggleRecording"
class="voice-button"
:class="{ 'recording': isRecording }"
title="按住说话"
:title="isRecording ? '点击结束录音' : '点击开始录音'"
>
<i class="mic-icon">🎤</i>
<i class="mic-icon">{{ isRecording ? '⏹️' : '🎤' }}</i>
<span v-if="isRecording" class="recording-text">... {{ recordingDuration }}s</span>
</button>
</div>
@ -480,59 +478,8 @@ export default {
}
})
// WebRTC
socket.on('voice_chat_event', (data) => {
console.log('收到语音聊天事件:', data)
const { type, user_id, username } = data
switch (type) {
case 'user_joined': {
//
addNotification(`${username} 加入了语音聊天`)
//
if (!rtcParticipants.value.find(p => p.id === user_id)) {
rtcParticipants.value.push({
id: user_id,
username: username,
muted: false
})
}
//
if (rtcActive.value && localStream.value) {
//
sendSignal(user_id, 'new_peer', {})
}
break
}
case 'user_left': {
//
addNotification(`${username} 离开了语音聊天`)
//
rtcParticipants.value = rtcParticipants.value.filter(p => p.id !== user_id)
//
closePeerConnection(user_id)
break
}
case 'mute_change': {
//
const mutedStatus = data.muted
const participant = rtcParticipants.value.find(p => p.id === user_id)
if (participant) {
participant.muted = mutedStatus
addNotification(`${username} ${mutedStatus ? '已静音' : '已取消静音'}`)
}
break
}
}
})
}
//
@ -646,10 +593,37 @@ export default {
}
})
// 使
const options = {
mimeType: 'audio/webm;codecs=opus',
audioBitsPerSecond: 32000 //
//
let options = {};
let mimeType = '';
//
if (MediaRecorder.isTypeSupported('audio/webm;codecs=opus')) {
mimeType = 'audio/webm;codecs=opus';
console.log('使用 audio/webm;codecs=opus 格式录音');
}
else if (MediaRecorder.isTypeSupported('audio/webm')) {
mimeType = 'audio/webm';
console.log('使用 audio/webm 格式录音');
}
else if (MediaRecorder.isTypeSupported('audio/ogg;codecs=opus')) {
mimeType = 'audio/ogg;codecs=opus';
console.log('使用 audio/ogg;codecs=opus 格式录音');
}
else if (MediaRecorder.isTypeSupported('audio/mp4')) {
mimeType = 'audio/mp4';
console.log('使用 audio/mp4 格式录音');
}
else {
console.log('使用默认音频格式录音');
}
//
if (mimeType) {
options = {
mimeType: mimeType,
audioBitsPerSecond: 32000 //
};
}
//
@ -682,7 +656,7 @@ export default {
console.log('录音实际时长:', actualDuration, '秒')
// Blob
const audioBlob = new Blob(audioChunks.value, { type: 'audio/webm' })
const audioBlob = new Blob(audioChunks.value, { type: mimeType || 'audio/webm' })
//
const tempId = 'temp-' + Date.now()
@ -706,12 +680,14 @@ export default {
reader.readAsDataURL(audioBlob)
reader.onloadend = () => {
const base64Audio = reader.result
console.log('音频格式:', mimeType || 'unknown', '数据前缀:', base64Audio.substring(0, 50))
//
socket.emit('send_audio_message', {
room_id: currentRoom.value.id,
audio_data: base64Audio,
audio_duration: actualDuration
audio_duration: actualDuration,
audio_mime_type: mimeType || 'audio/webm'
})
//
@ -744,6 +720,15 @@ export default {
}
}
//
const toggleRecording = () => {
if (isRecording.value) {
stopRecording()
} else {
startRecording()
}
}
//
const stopRecording = () => {
if (!mediaRecorder.value || mediaRecorder.value.state === 'inactive') {
@ -799,24 +784,53 @@ export default {
console.log('准备播放语音消息:', message.id, '类型:', typeof message.message);
//
let audioSource = null;
const mimeType = message.audio_mime_type || 'audio/webm';
if (message.localAudioUrl && message.localAudioUrl.startsWith('blob:')) {
// 使Blob URL
audioPlayer.value.src = message.localAudioUrl;
audioSource = message.localAudioUrl;
console.log('使用本地缓存的Blob URL播放');
} else if (message.message.startsWith('blob:')) {
// 使Blob URL
audioPlayer.value.src = message.message;
audioSource = message.message;
console.log('使用Blob URL播放:', message.message);
} else if (message.message.startsWith('data:audio')) {
// base64
audioPlayer.value.src = message.message;
audioSource = message.message;
console.log('使用base64数据播放');
} else {
//
console.warn('未知格式的音频消息,尝试直接使用:', message.message.substring(0, 50) + '...');
audioPlayer.value.src = message.message;
console.warn('未知格式的音频消息,尝试修复格式');
//
try {
// base64
if (typeof message.message === 'string') {
// base64
const base64Regex = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
const cleanedData = message.message.replace(/^data:.*?;base64,/, '');
if (base64Regex.test(cleanedData)) {
// MIME
audioSource = `data:${mimeType};base64,` + cleanedData;
console.log(`修复为带头部的base64数据使用MIME类型: ${mimeType}`);
} else {
// base64使
audioSource = message.message;
}
} else {
audioSource = message.message;
}
} catch (formatErr) {
console.error('修复音频格式失败:', formatErr);
audioSource = message.message;
}
}
//
audioPlayer.value.src = audioSource;
//
currentPlayingId.value = message.id;
isPlaying.value = true;
@ -830,8 +844,39 @@ export default {
//
audioPlayer.value.onerror = (e) => {
console.error('音频播放出错:', e);
alert('播放语音失败,可能是格式不支持或数据已损坏');
const errorDetails = e.target.error ? `错误代码: ${e.target.error.code}` : '未知错误';
console.error('音频播放出错:', errorDetails);
// 使MIME
if (!message.retryCount) {
message.retryCount = 1;
console.log('尝试使用备用MIME类型重新播放');
//
audioPlayer.value.src = '';
// MIME
const alternativeMimeTypes = ['audio/webm', 'audio/ogg', 'audio/mp4', 'audio/wav'];
const currentMimeType = mimeType;
// MIME
let newMimeType = alternativeMimeTypes.find(type => type !== currentMimeType) || 'audio/webm';
// 使MIME
message.audio_mime_type = newMimeType;
//
setTimeout(() => playAudio(message), 100);
return;
}
//
if (message.isLocal) {
alert('播放本地录制的语音失败,这可能是暂时性问题,语音消息仍会发送给其他用户');
} else {
alert('播放语音失败,可能是格式不支持或数据已损坏');
}
currentPlayingId.value = null;
isPlaying.value = false;
};
@ -930,7 +975,7 @@ export default {
// 1.
await getMediaDevices()
// 2.
// 2.
const stream = await navigator.mediaDevices.getUserMedia({
audio: {
deviceId: selectedAudioInput.value ? { exact: selectedAudioInput.value } : undefined,
@ -941,6 +986,12 @@ export default {
video: false
})
//
stream.getAudioTracks().forEach(track => {
track.enabled = false
})
isMuted.value = true
localStream.value = stream
// 3.
@ -953,7 +1004,7 @@ export default {
// 5. WebRTC
rtcActive.value = true
addNotification('您已加入语音聊天')
addNotification('您已加入语音聊天(已静音)')
isLoadingRTC.value = false
} catch (err) {
@ -1345,6 +1396,7 @@ export default {
audioPlayer,
isRecording,
recordingDuration,
rtcActive,
localStream,
peerConnections,
mediaDevices,
@ -1360,7 +1412,7 @@ export default {
joinRoom,
leaveRoom,
sendMessage,
startRecording,
toggleRecording,
stopRecording,
cancelRecording,
playAudio,
@ -1369,7 +1421,7 @@ export default {
isCurrentUser,
formatTime,
toggleVoiceChat,
rtcActive
toggleMute
}
}
}
@ -1836,9 +1888,6 @@ export default {
.recording-text {
position: absolute;
left: -120px;
top: 50%;
transform: translateY(-50%);
white-space: nowrap;
font-size: 12px;
color: #e74c3c;
@ -1848,6 +1897,9 @@ export default {
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
z-index: 10;
left: -120px;
top: 50%;
transform: translateY(-50%);
}
/* 在空间不足时的备选位置 */

Loading…
Cancel
Save