
本文旨在解决使用J*aScript MediaRecorder进行实时录音,并通过Base64编码传输至PHP服务器保存为`.ogg`文件时,文件损坏无法播放的问题。核心问题在于`MediaRecorder`的媒体类型配置不当,以及服务器端对音频数据块的处理方式错误(覆盖而非追加)。教程将详细阐述正确的客户端配置和服务器端文件追加策略,并提供完整的代码示例。
MediaRecorder API允许我们录制用户的音频和视频流。它通过ondata*ailable事件周期性地提供媒体数据块(e.data),这些数据块通常是媒体流的一部分,而非完整的、可独立播放的文件。为了将这些数据块组合成一个可播放的媒体文件,我们需要在客户端或服务器端进行适当的处理。
在将数据发送到服务器进行保存时,常见的流程是:
然而,在这个过程中,有两个关键环节容易导致最终文件损坏。
原始代码中,开发者尝试在创建Blob对象时指定媒体类型:
const blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });这种做法是错误的。MediaRecorder在开始录制时,就需要知道它应该以何种格式和编码器来处理媒体流。Blob构造函数中的type参数仅用于标识Blob的MIME类型,并不会改变其内部数据的实际编码格式。
正确做法是,在MediaRecorder的构造函数中指定媒体类型和编码器。这样,MediaRecorder才会按照指定的格式生成e.data数据块。
// ...
n*igator.mediaDevices.getUserMedia ({ audio: true })
.then(function(stream) {
// 在这里定义 MediaRecorder 的选项
const mrOptions = { mimeType: 'audio/ogg; codecs=opus' };
mediaRecorder = new MediaRecorder(stream, mrOptions); // 将选项传递给构造函数
mediaRecorder.start(2000); // 每2秒触发一次 ondata*ailable 事件
mediaRecorder.ondata*ailable = 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格式编码的。
腾讯AI 开放平台
腾讯AI开放平台
381
查看详情
原始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改为追加操作是首要且必要的步骤。如果即使追加后文件仍然无法完美播放,则需要考虑上述更复杂的流媒体处理方案。
结合上述两点修正,以下是优化后的客户端J*aScript和服务器端PHP代码:
<script>
var mediaRecorder = null;
let chunks = [];
if (n*igator.mediaDevices && n*igator.mediaDevices.getUserMedia) {
console.log('getUserMedia supported.');
n*igator.mediaDevices.getUserMedia (
{
audio: true
})
.then(function(stream) {
// 1. 在 MediaRecorder 构造函数中指定媒体类型和编码器
const mrOptions = { mimeType: 'audio/ogg; codecs=opus' };
mediaRecorder = new MediaRecorder(stream, mrOptions);
// 每2秒触发一次 ondata*ailable 事件,发送数据块
mediaRecorder.start(2000);
mediaRecorder.ondata*ailable = 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 {
con
sole.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中文网其它相关文章!
# 多维
# seo建站收录
# 北京企业做推广的网站
# seo统计埋点
# 网站建设实施
# 沛县网站推广怎么样
# 扬州网站推广系统
# 封开营销网络推广有哪些
# 探店营销推广方案怎么写
# 天津外链seo
# 厦门seo服务中心
# 可播放
# 播放器
# 几个
# 保存文件
# 流媒体
# php
# 而非
# 发送到
# 腾讯
# 客户端
# red
# stream
# win
# ai
# app
# 浏览器
# 编码
# js
# java
# javascript
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
优化推广96088 】
【
技术知识133117 】
【
IDC资讯59369 】
【
网络运营7196 】
【
IT资讯61894 】
相关推荐:
Flexbox布局中Stencil组件宽度不显示问题解析与:host尺寸控制
poki官网最新入口 poki小游戏大全入口
《环球网校》设置报考省市方法
德邦快递会员怎么开通
MongoDB聚合管道:高效统计列表中各项的文档数量
火狐浏览器如何刷新修复浏览器 火狐浏览器“重置Firefox”功能详解
PHP与SQL实践:高效实现数据复制与特定列值修改
pubmed数据库官方主页_pubmed学术论文查找官网直达
J*aScript字符串_Unicode处理
CSS过渡与滚动滚动事件结合应用_scroll与transition动画
三星A55应用闪退排查步骤_Samsung A55稳定性优化技巧
《edge浏览器》关闭翻译功能方法
百度小说看书时如何翻页_百度小说手动翻页与自动翻页设置
微信网页版在线登录 微信网页版在线使用入口
哔哩哔哩在线观看入口 B站官网免费进入
优化响应式标题底部边框:CSS实现技巧与最佳实践
向往的生活小游戏启动处_向往的生活小游戏立即启动
《爱笔思画x》魔棒工具抠图教程
Scipy Sparse CSR 矩阵非零元素行级遍历的最佳实践
漫蛙manwa漫画官网链接_漫蛙manwa最新可用网址推荐
背部总是隐隐作痛怎么回事 背痛如何改善
嘀嗒顺风车如何开具电子发票
J*a里如何处理ArithmeticException并防止除零_算术异常防护策略解析
在Peewee中处理PostgreSQL记录重复:一站式数据摄取教程
PHP页面重载后变量状态保持:实现用户档案连续浏览的教程
歌词怎么展示在|直播|间视频号?有什么注意事项?
《大学搜题酱》官网地址登录
铁路12306入口 铁路12306官网版入口登录网址
海外搜索引擎推广效果怎么样,怎么分析效果!
大熊猫抓取竹子的“大拇指”其实是什么?蚂蚁庄园课堂今天答案最新11月30日
Golang如何测试结构体方法_Golang reflect方法测试与调用技巧
Lar*el Eloquent:高效删除多对多关系中无关联子记录的父模型
如何使用 Optional 类型并满足 Pylint 的类型检查
J*aScript实现网页表单实时输入字段比较与验证教程
VS Code源代码管理(SCM)视图的进阶使用技巧
VS Code中的Tailwind CSS IntelliSense插件使用技巧
漫蛙漫画直连入口 _ manwa官方备用入口实时检测
视频号视频怎么提取文案?提取的文案如何优化与使用?
QQ邮箱手机版网页版 QQ邮箱登录入口地址
QQ阅读小说搜索入口地址_QQ阅读小说搜索入口地址搜索在线阅读
抖音如何解除|直播|权限绑定_抖音关闭并解绑|直播|功能的方法
米侠浏览器插件无法启用怎么办 米侠浏览器扩展兼容性修复
疯狂小鸟微信小游戏入口 疯狂小鸟网页版秒玩
使用TinyButStrong生成HTML并结合Dompdf创建PDF教程
优化Leaflet弹出层图片显示:条件渲染策略
腾讯QQ邮箱官方入口 QQ邮箱网页版登录平台
mysql镜像配置如何设置用户权限组_mysql镜像配置用户组与权限分级管理方法
顺丰速运官网查询入口 顺丰物流查询官网入口链接
《淘票票》添加到苹果钱包教程
如何在Golang中处理表单文件上传_Golang 表单文件上传示例
2025-12-09
运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。