
本文旨在解决使用JavaScript MediaRecorder进行实时录音,并通过Base64编码传输至PHP服务器保存为`.ogg`文件时,文件损坏无法播放的问题。核心问题在于`MediaRecorder`的媒体类型配置不当,以及服务器端对音频数据块的处理方式错误(覆盖而非追加)。教程将详细阐述正确的客户端配置和服务器端文件追加策略,并提供完整的代码示例。
MediaRecorder API允许我们录制用户的音频和视频流。它通过ondataavailable事件周期性地提供媒体数据块(e.data),这些数据块通常是媒体流的一部分,而非完整的、可独立播放的文件。为了将这些数据块组合成一个可播放的媒体文件,我们需要在客户端或服务器端进行适当的处理。
在将数据发送到服务器进行保存时,常见的流程是:
然而,在这个过程中,有两个关键环节容易导致最终文件损坏。
立即学习“PHP免费学习笔记(深入)”;
原始代码中,开发者尝试在创建Blob对象时指定媒体类型:
const blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });这种做法是错误的。MediaRecorder在开始录制时,就需要知道它应该以何种格式和编码器来处理媒体流。Blob构造函数中的type参数仅用于标识Blob的MIME类型,并不会改变其内部数据的实际编码格式。
正确做法是,在MediaRecorder的构造函数中指定媒体类型和编码器。这样,MediaRecorder才会按照指定的格式生成e.data数据块。
// ...
navigator.mediaDevices.getUserMedia ({ audio: true })
.then(function(stream) {
// 在这里定义 MediaRecorder 的选项
const mrOptions = { mimeType: 'audio/ogg; codecs=opus' };
mediaRecorder = new MediaRecorder(stream, mrOptions); // 将选项传递给构造函数
mediaRecorder.start(2000); // 每2秒触发一次 ondataavailable 事件
mediaRecorder.ondataavailable = function(e) {
chunks.push(e.data);
// 创建 Blob 时,可以引用 MediaRecorder 实际使用的 mimeType
const blob = new Blob(chunks, { type : mediaRecorder.mimeType });
chunks = []; // 清空 chunks,准备接收下一个数据块
// ... 后续处理
};
})
// ...通过在MediaRecorder构造函数中设置mimeType,我们确保了e.data数据块本身就是以audio/ogg; codecs=opus格式编码的。
原始PHP代码使用file_put_contents("r.ogg", base64_decode($_POST["data"]));来保存数据。file_put_contents函数在默认情况下会覆盖目标文件的全部内容。由于MediaRecorder会周期性地发送数据块,每次服务器收到数据时都会覆盖之前的内容,导致最终文件只包含最后一个数据块,这显然不是一个完整的、可播放的音频文件。
要解决这个问题,我们需要将每次收到的数据追加到文件中,而不是覆盖。
<?php
if(isset($_POST["data"]))
{
// 使用 FILE_APPEND 标志,将数据追加到文件末尾
file_put_contents("r.ogg", base64_decode($_POST["data"]), FILE_APPEND);
exit;
}
?>重要注意事项: 虽然使用FILE_APPEND可以解决文件覆盖问题,但简单地将原始的Ogg Opus数据块追加到文件中,不一定能保证生成一个完全符合Ogg容器规范的、可播放的.ogg文件。Ogg文件格式包含复杂的页结构、头部信息和数据流管理。MediaRecorder生成的e.data块可能只是原始的Opus编码数据,或者是不完整的Ogg页。直接拼接这些块可能会导致文件结构损坏,或者播放器无法正确解析。
对于更健壮的实时流媒体保存方案,通常需要:
然而,对于本教程的目标——修复用户现有代码中的直接问题,将file_put_contents改为追加操作是首要且必要的步骤。如果即使追加后文件仍然无法完美播放,则需要考虑上述更复杂的流媒体处理方案。
结合上述两点修正,以下是优化后的客户端JavaScript和服务器端PHP代码:
<script>
var mediaRecorder = null;
let chunks = [];
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
console.log('getUserMedia supported.');
navigator.mediaDevices.getUserMedia (
{
audio: true
})
.then(function(stream) {
// 1. 在 MediaRecorder 构造函数中指定媒体类型和编码器
const mrOptions = { mimeType: 'audio/ogg; codecs=opus' };
mediaRecorder = new MediaRecorder(stream, mrOptions);
// 每2秒触发一次 ondataavailable 事件,发送数据块
mediaRecorder.start(2000);
mediaRecorder.ondataavailable = function(e) {
chunks.push(e.data);
// 2. 创建 Blob 时,使用 MediaRecorder 实际的 mimeType
const blob = new Blob(chunks, { type : mediaRecorder.mimeType });
chunks = []; // 清空 chunks,准备接收下一个数据块
var reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function() {
var data = reader.result.split(";base64,")[1];
requestp2("a.php", "data="+encodeURIComponent(data));
}
};
// 可以添加停止录音的逻辑,例如:
// setTimeout(() => {
// mediaRecorder.stop();
// console.log('Recording stopped.');
// }, 10000); // 录制10秒后停止
})
.catch(function(err) {
console.log('The following getUserMedia error occurred: ' + err);
});
} else {
console.log('getUserMedia not supported on your browser!');
}
function requestp2(path, data)
{
var http = new XMLHttpRequest();
http.open('POST', path, true);
http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
http.send(data);
}
</script>将此代码保存为 a.php (或您在 requestp2 函数中指定的任何文件名)。
<?php
if(isset($_POST["data"]))
{
// 1. 解码 Base64 数据
$decodedData = base64_decode($_POST["data"]);
// 2. 将数据追加到文件末尾,而不是覆盖
// 注意:首次写入时,如果文件不存在,file_put_contents 会创建它。
// 每次后续写入都会追加。
if ($decodedData !== false) {
file_put_contents("r.ogg", $decodedData, FILE_APPEND);
} else {
error_log("Failed to decode base64 data.");
}
exit;
}
?>修复MediaRecorder实时录音至PHP保存文件损坏的问题,关键在于两点:首先,确保在MediaRecorder构造函数中正确指定媒体类型和编码器,使其生成符合预期格式的数据块;其次,在服务器端使用FILE_APPEND模式将接收到的数据块追加到文件中,而非覆盖。虽然直接追加可能对某些复杂媒体格式的完整性有局限,但它解决了文件损坏的核心原因,为进一步的媒体处理奠定了基础。
以上就是修复MediaRecorder实时录音至PHP保存文件损坏问题的详细内容,更多请关注php中文网其它相关文章!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号