Chrome录制的RecordRTC视频无法在Safari及Chrome iOS播放问题
跨浏览器视频录制与播放解决方案
问题根源
你在MacBook Chrome中录制的视频无法在Safari(Mac/iPhone)和iPhone Chrome中播放,核心原因是Chrome默认使用的VP9编码WebM格式不被Safari支持。Safari对WebM格式的支持有限,尤其不兼容VP9编解码器,而H.264(AVC1)+ AAC是目前跨浏览器兼容性最好的音视频编码组合。
具体修复步骤
1. 替换UA嗅探为特性检测
放弃不可靠的UserAgent判断,直接使用浏览器原生的MediaRecorder.isTypeSupported()方法检测支持的MIME类型,确保选择的格式能被当前浏览器和目标播放环境兼容。
2. 统一使用H.264编码格式
优先选择video/mp4;codecs=avc1,aac(MP4容器+H.264视频+AAC音频),这是所有现代浏览器(包括Safari、Chrome、Firefox)都支持的格式。如果浏览器不支持MP4录制,则降级到video/webm;codecs=h264。
3. 修复代码中的语法错误
saveRecording函数中存在字符串拼接错误:'video/'.videoFormat应改为'video/' + videoFormat,否则会导致上传的文件MIME类型无效。
完整修改后的代码
var startCameraBtn = document.querySelector('#start-camera-btn'); var startRecordingBtn = document.querySelector('#start-recording-btn'); var stopRecordingBtn = document.querySelector('#stop-recording-btn'); var retakeRecordingBtn = document.querySelector('#retake-recording-btn'); var saveRecordingBtn = document.querySelector('#save-recording-btn'); var publishJobBtn = document.querySelector('#publish-job-btn'); var video = document.querySelector('video'); var recorder; // globally accessible var blob; var mimeType; var videoFormat; // 初始化时检测最佳兼容格式 detectBestVideoFormat(); function captureCamera(callback) { navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then(function(camera) { callback(camera); console.log("Camera started"); }).catch(function(error) { alert('无法访问摄像头,请查看控制台日志。'); console.error(error); }); } function stopRecordingCallback() { video.src = video.srcObject = null; video.muted = false; video.volume = 1; blob = recorder.getBlob() video.src = URL.createObjectURL(blob); } function startCamera(){ this.style.display = "none"; saveRecordingBtn.style.display = "none"; startRecordingBtn.style.display = "block"; captureCamera(function(camera) { video.muted = true; video.volume = 0; video.srcObject = camera; recorder = RecordRTC(camera, { type: 'video', mimeType: mimeType, recorderType: MediaStreamRecorder, disableLogs: true, bitsPerSecond: 128000, audioBitsPerSecond: 128000, videoBitsPerSecond: 128000 }); recorder.camera = camera; }); } function startRecording(){ this.style.display = "none"; stopRecordingBtn.style.display = "block"; recorder.startRecording(); } function stopRecording(){ this.style.display = "none"; retakeRecordingBtn.style.display = "block"; saveRecordingBtn.style.display = "block"; recorder.stopRecording(stopRecordingCallback); } function saveRecording(){ this.innerHTML = '保存中...'; var fileName = getFileName(videoFormat); // 修复字符串拼接错误 var fileObject = new File([blob], fileName, { type: 'video/' + videoFormat }); uploadToPHPServer(fileObject, function(response, fileDownloadURL) { console.log(response); }); } startCameraBtn.onclick = startCamera; startRecordingBtn.onclick = startRecording; stopRecordingBtn.onclick = stopRecording; retakeRecordingBtn.onclick = startCamera; saveRecordingBtn.onclick = saveRecording; function uploadToPHPServer(blob, callback) { var formData = new FormData(); formData.append('video-filename', blob.name); formData.append('video-blob', blob); callback('正在上传录制文件到服务器。'); var upload_url = 'upload_video.php'; makeXMLHttpRequest(upload_url, formData, function(progress) { if (progress !== 'upload-ended') { callback(progress); return; } var initialURL = upload_url + blob.name; callback('上传完成', initialURL); }); } function makeXMLHttpRequest(url, data, callback) { var request = new XMLHttpRequest(); request.onreadystatechange = function() { if (request.readyState == 4 && request.status == 200) { if (request.responseText === 'success') { callback('upload-ended'); return; } saveRecordingBtn.innerHTML = request.responseText; publishJobBtn.style.display = "block"; return; } }; request.upload.onloadstart = function() { callback('PHP上传开始...'); }; request.upload.onprogress = function(event) { callback('PHP上传进度 ' + Math.round(event.loaded / event.total * 100) + "%"); }; request.upload.onload = function() { callback('PHP上传完成,获取文件URL中。'); }; request.upload.onerror = function(error) { callback('PHP上传失败。'); }; request.upload.onabort = function(error) { callback('PHP上传已终止。'); }; request.open('POST', url); request.send(data); } // 替换UA嗅探为特性检测 function detectBestVideoFormat() { // 优先检测MP4(H.264+AAC) if (MediaRecorder.isTypeSupported('video/mp4;codecs=avc1,aac')) { mimeType = 'video/mp4;codecs=avc1,aac'; videoFormat = 'mp4'; } // 降级到WebM+H.264 else if (MediaRecorder.isTypeSupported('video/webm;codecs=h264')) { mimeType = 'video/webm;codecs=h264'; videoFormat = 'webm'; } // 最后的兜底方案 else { mimeType = 'video/webm'; videoFormat = 'webm'; } console.log("当前使用视频格式: "+videoFormat+" MIME类型: "+mimeType); } function getFileName(fileExtension) { var d = new Date(); var year = d.getUTCFullYear(); var month = d.getUTCMonth(); var date = d.getUTCDate(); return 'RecordRTC-' + year + month + date + '-' + getRandomString() + '.' + fileExtension; } function getRandomString() { if (window.crypto && window.crypto.getRandomValues && navigator.userAgent.indexOf('Safari') === -1) { var a = window.crypto.getRandomValues(new Uint32Array(3)), token = ''; for (var i = 0, l = a.length; i < l; i++) { token += a[i].toString(36); } return token; } else { return (Math.random() * new Date().getTime()).toString(36).replace(/\./g, ''); } }
额外注意事项
- 服务器端兼容: 确保你的PHP服务器正确处理MP4文件的MIME类型(
video/mp4),避免返回错误的Content-Type导致播放失败。 - 测试覆盖: 在所有目标浏览器(Mac Chrome/Safari、iPhone Safari/Chrome)中测试录制和播放流程,确保兼容性。
内容的提问来源于stack exchange,提问作者nadwebdev
相关产品推荐
相关产品推荐

