HTML + JavaScript 实现网页录制音频与下载

news2024/9/23 17:12:53

HTML + JavaScript 实现网页录制音频与下载

  • HTML + JavaScript 实现网页录制音频与下载
    • 简介
      • getUserMedia
      • MediaRecorder
      • 获取和处理音频流
      • 实现音频的录制和播放
      • 音频效果的处理
      • 实时语音通话的应用
      • 兼容性和 Latency 问题
    • 项目代码
    • 运行实例
    • 参考
    • 源码下载

HTML + JavaScript 实现网页录制音频与下载

简介

在这个数字化的时代,网页端的音频处理能力已经成为一个非常热门的需求。本文将详细介绍如何利用 getUserMedia 和 MediaRecorder 这两个强大的 API,实现网页端音频的录制、处理和播放等功能。

getUserMedia

getUserMedia 和 MediaRecorder 是 HTML5 中两个非常重要的 API,用于访问设备媒体输入流并对其进行操作。

getUserMedia 允许网页端访问用户设备的媒体输入设备,比如摄像头和麦克风。通过该 API,在获得用户授权后,我们可以获取这些媒体流的数据,并用于各种网页应用场景中。

典型的使用方式如下:

// 请求获取音频流
navigator.mediaDevices.getUserMedia({
  audio: true
})
.then(stream => {
  // 在此处理音频流
})

getUserMedia 接受一个 constraints 对象作为参数,通过设置配置来请求获取指定的媒体类型,常见的配置有:

  • audio:Boolean 值,是否获取音频输入。
  • video:Boolean 值,是否获取视频输入。
  • 以及更详细的各种音视频参数设置。

MediaRecorder

MediaRecorder API 可以获取由 getUserMedia 生成的媒体流,并对其进行编码和封装,输出可供播放和传输的媒体文件。

典型的用法如下:

// 获取媒体流
const stream = await navigator.mediaDevices.getUserMedia({ audio: true })

// 创建 MediaRecorder 实例 
const mediaRecorder = new MediaRecorder(stream);

// 注册数据可用事件,以获取编码后的媒体数据块
mediaRecorder.ondataavailable = event => {
  audioChunks.push(event.data);
}

// 开始录制
mediaRecorder.start();

// 录制完成后停止
mediaRecorder.stop(); 

// 将录制的数据组装成 Blob
const blob = new Blob(audioChunks, {
  type: 'audio/mp3' 
});

简单来说,getUserMedia 获取输入流,MediaRecorder 对流进行编码和处理,两者结合就可以实现强大的音视频处理能力。

获取和处理音频流

了解了基本 API 使用方法后,我们来看看如何获取和处理音频流。

首先需要调用 getUserMedia 来获取音频流,典型的配置是:

const stream = await navigator.mediaDevices.getUserMedia({
  audio: {
    channelCount: 2,  
    sampleRate: 44100,
    sampleSize: 16,
    echoCancellation: true 
  }
});

我们可以指定声道数、采样率、采样大小等参数来获取音频流。

PS:这似乎不管用。

使用 navigator.mediaDevices.enumerateDevices() 可以获得所有可用的媒体设备列表,这样我们就可以提供设备选择功能给用户,而不仅仅是默认设备。

举例来说,如果我们想要让用户选择要使用的录音设备:

// 1. 获取录音设备列表
const audioDevices = await navigator.mediaDevices.enumerateDevices();

const mics = audioDevices.filter(d => d.kind === 'audioinput');

// 2. 提供设备选择 UI 供用户选择
const selectedMic = mics[0]; 

// 3. 根据选择配置进行获取流
const constraints = {
  audio: {
    deviceId: selectedMic.deviceId
  }  
};

const stream = await navigator.mediaDevices.getUserMedia(constraints);

这样我们就可以获得用户选择的设备录音了。

获得原始音频流后,我们可以利用 Web Audio API 对其进行处理。

例如添加回声效果:

// 创建音频环境
const audioContext = new AudioContext();

// 创建流源节点
const source = audioContext.createMediaStreamSource(stream);

// 创建回声效果节点
const echo = audioContext.createConvolver();

// 连接处理链
source.connect(echo);
echo.connect(audioContext.destination);

// 加载回声冲击响应并应用
const impulseResponse = await fetch('impulse.wav');
const buffer = await impulseResponse.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(buffer);

echo.buffer = audioBuffer;

通过这样的音频处理链,我们就可以在录音时添加回声、混响等音效了。

实现音频的录制和播放

录制音频的步骤:

  1. 调用 getUserMedia 获取音频流。
  2. 创建 MediaRecorder 实例,传入音频流。
  3. 注册数据可用回调,以获取编码后的音频数据块。
  4. 调用 recorder.start() 开始录制。
  5. 录制完成后调用 recorder.stop()。

代码:

let recorder;
let audioChunks = [];

// 开始录音 handler
const startRecording = async () => {

  const stream = await navigator.mediaDevices.getUserMedia({
    audio: true
  });

  recorder = new MediaRecorder(stream);

  recorder.ondataavailable = event => {
    audioChunks.push(event.data);
  };

  recorder.start();

} 

// 停止录音 handler
const stopRecording = () => {
  if(recorder.state === "recording") {
    recorder.stop();
  }
}

录音完成后,我们可以将音频数据组装成一个 Blob 对象,然后赋值给一个 <audio> 元素的 src 属性进行播放。

代码:

// 录音停止后
const blob = new Blob(audioChunks, { type: 'audio/ogg' }); 

const audioURL = URL.createObjectURL(blob);

const player = document.querySelector('audio');
player.src = audioURL;

// 调用播放
player.play();

这样就可以播放刚刚录制的音频了。

后续也可以添加下载功能等。

音频效果的处理

利用 Web Audio API,我们可以添加各种音频效果,进行音频处理。

例如添加回声效果:

const audioContext = new AudioContext();

// 原始音频节点
const source = audioContext.createMediaStreamSource(stream);

// 回声效果节点
const echo = audioContext.createConvolver();

// 连接处理链
source.connect(echo);
echo.connect(audioContext.destination);

// 加载冲击响应作为回声效果
const impulseResponse = await fetch('impulse.wav');
const arrayBuffer = await impulseResponse.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);

echo.buffer = audioBuffer;

这样在录制时音频流就会经过回声效果处理了。

此外,我们还可以添加混响、滤波、均衡器、压缩等多种音频效果,使得网页端也能处理出专业级的音频作品。

实时语音通话的应用

利用 getUserMedia 和 WebRTC 技术,我们还可以在网页端实现实时的点对点语音通话。

简述流程如下:

  1. 通过 getUserMedia 获取本地音视频流。
  2. 创建 RTCPeerConnection 实例。
  3. 将本地流添加到连接上。
  4. 交换 ICE 候选信息,建立连接。
  5. 当检测到连接后,渲染远端用户的音视频流。

这样就可以实现类似 Skype 的网页端语音通话功能了。

代码:

// 1. 获取本地流
const localStream = await navigator.mediaDevices.getUserMedia({
  audio: true,
  video: true
});

// 2. 创建连接对象
const pc = new RTCPeerConnection();

// 3. 添加本地流
localStream.getTracks().forEach(track => pc.addTrack(track, localStream)); 

// 4. 交换 ICE 等信令,处理 ONADDSTREAM 等事件

// ...

// 5. 收到远端流,渲染到页面
pc.ontrack = event => {
  remoteVideo.srcObject = event.streams[0];
}

获取本地输入流后,经过编码和传输就可以实现语音聊天了。

兼容性和 Latency 问题

尽管 getUserMedia 和 MediaRecorder 在现代浏览器中已经得到了较好的支持,但由于不同厂商和版本实现存在差异,在实际应用中还是需要注意一些兼容性问题:

  • 检测 API 支持情况,提供降级方案。
  • 注意不同浏览器对 Codec、采样率等参数支持的差异。
  • 封装浏览器差异,提供统一的 API。

此外,录音和播放也存在一定的延迟问题。我们需要针对 Latency 进行优化,比如使用更小的 buffer 大小,压缩数据包大小等方法。

项目代码

record.html:

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Record Page</title>
        <link rel="stylesheet" type="text/css" href="css/record.css">

    </head>

    <body>
        <div class="app">
            <audio controls class="audio-player"></audio>
            <button class="record-btn">录音</button>
            <a id="download" download="record.aac"></a>
        </div>
    </body>
    <script src="js/record.js"></script>

</html>

record.css:

.app {
    display: flex;
    justify-content: center;
    align-items: center;
}

.record-btn {
    margin: 0 10px;
}

record.js:

const recordBtn = document.querySelector(".record-btn")
const player = document.querySelector(".audio-player")
const download = document.querySelector('#download')
if (navigator.mediaDevices.getUserMedia) {
    let audioChunks = []
    // 约束属性
    const constraints = {
        // 音频约束
        audio: {
            sampleRate: 16000, // 采样率
            sampleSize: 16, // 每个采样点大小的位数
            channelCount: 1, // 通道数
            volume: 1, // 从 0(静音)到 1(最大音量)取值,被用作每个样本值的乘数
            echoCancellation: true, // 开启回音消除
            noiseSuppression: true, // 开启降噪功能
        },
        // 视频约束
        video: false
    }
    // 请求获取音频流
    navigator.mediaDevices.getUserMedia(constraints)
        .catch(err => serverLog("ERROR mediaDevices.getUserMedia: ${err}"))
        .then(stream => {// 在此处理音频流
            // 创建 MediaRecorder 实例
            const mediaRecorder = new MediaRecorder(stream)
            // 点击按钮
            recordBtn.onclick = () => {
                if (mediaRecorder.state === "recording") {
                    // 录制完成后停止
                    mediaRecorder.stop()
                    recordBtn.textContent = "录音结束"
                }
                else {
                    // 开始录制
                    mediaRecorder.start()
                    recordBtn.textContent = "录音中..."
                }
            }
            mediaRecorder.ondataavailable = e => {
                audioChunks.push(e.data)
            }
            // 结束事件
            mediaRecorder.onstop = e => {
                // 将录制的数据组装成 Blob(binary large object) 对象(一个不可修改的存储二进制数据的容器)
                const blob = new Blob(audioChunks, { type: "audio/aac" })
                audioChunks = []
                const audioURL = window.URL.createObjectURL(blob)
                // 赋值给一个 <audio> 元素的 src 属性进行播放
                player.src = audioURL
                // 添加下载功能
                download.innerHTML = '下载'
                download.href = audioURL
            }
        },
            () => {
                console.error("授权失败!");
            }
        );
} else {
    console.error("该浏览器不支持 getUserMedia!");
}

运行实例

打开 record.html,首先获取麦克风权限:

在这里插入图片描述

点击“允许”。

在这里插入图片描述

页面有一个 audio-player 和一个 buttom。

点击“录音”按钮,就开始录音了。

再点一次按钮,停止录音,数据传回给 audio-player,可以在网页上播放录音。

在这里插入图片描述

点击“下载”,可以下载录制的音频。

PS:音频文件名称设置为 record.aac,文件格式为 WebM,音频格式为 opus,单声道,采样率 48kHz,位深 32bit。

参考

  1. Blob 的所有 Type 类型
  2. getUserMedia() 音频约束

源码下载

CSDN:Web Record.zip

GitHub:Web-Record

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1315778.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WEB渗透—PHP反序列化(三)

Web渗透—PHP反序列化 课程学习分享&#xff08;课程非本人制作&#xff0c;仅提供学习分享&#xff09; 靶场下载地址&#xff1a;GitHub - mcc0624/php_ser_Class: php反序列化靶场课程&#xff0c;基于课程制作的靶场 课程地址&#xff1a;PHP反序列化漏洞学习_哔哩…

dockerfile创建镜像 lNMP+wordpress

dockerfile创建镜像 lNMPwordpress nginx dockernginx mysql dockermysql php dockerphp nginx vim nginx.conf vim Dockerfile docker network create --subnet172.17.0.0/16 --opt "com.docker.network.bridge.name""docker1" mynetwork docker buil…

Redis设计与实现之对象处理机制

目录 一、前言 二、对象处理机制 1、redisObject 数据结构&#xff0c;以及 Redis 的数据类型 2、 命令的类型检查和多态 3、对象共享 4、引用计数以及对象的销毁 三、对象的处理 1、Redis是如何处理字符串对象的&#xff1f; 2、Redis是如何处理列表对象的&#xff1f…

Arduino中以太网Udp通信

目录 1、测试硬件 2、程序 &#xff08;0&#xff09;头文件添加 &#xff08;1&#xff09;变量定义 &#xff08;2&#xff09;初始化程序 &#xff08;3&#xff09;循环执行程序 3、程序下载 &#xff08;1&#xff09;开发板控制器和端口号选择 &#xff08;2&am…

java基础知识④:设计模式

目录 一、设计模式 1️⃣创建型设计模式&#xff08;常用&#xff1a;单例、工厂、抽象工厂&#xff09; 2️⃣结构型设计模式&#xff08;常用&#xff1a;适配器、装饰者、外观、代理&#xff09; 3️⃣行为型设计模式&#xff08;常用&#xff1a;观察者、策略、模板方法、命…

windows下redis 设置开机自启动

1&#xff0c;在redis的目录下执行&#xff08;执行后就作为windows服务了&#xff09; redis-server --service-install redis.windows.conf 2&#xff0c;安装好后需要手动启动redis redis-server --service-start 3&#xff0c;停止服务 redis-server --service-stop

springMVC 学习总结(四) 拦截器及统一异常处理

一.拦截器 1.拦截器与过滤器的区别 拦截器 Interceptor 和 过滤器 Filter类似&#xff0c;主要用于拦截用户请求并作出一定处理操作&#xff0c; 但两则也有不同之处&#xff0c;如过滤器只在Servlet前后起作用&#xff0c;是Servlet规范定义的&#xff0c;是Servlt容器才支…

邮政快递查询,邮政快递单号查询,根据更新量筛选出需要的单号

批量查询邮政快递单号的物流信息&#xff0c;并根据物流更新量将需要的单号筛选出来。 所需工具&#xff1a; 一个【快递批量查询高手】软件 邮政快递单号若干 操作步骤&#xff1a; 步骤1&#xff1a;运行【快递批量查询高手】软件&#xff0c;第一次使用的伙伴记得先注册&…

stm32H库的内部FLASH读写操作与结构体数组数据写入与读取

stm32H库的内部FLASH读写操作与结构体数组数据写入与读取 1.软硬件准备2.关于STM32的Flash的一些说明3.实验结果 参考博主-STM32系列(HAL库)——内部FLASH读写实验 1.软硬件准备 软件&#xff1a;CubeMX、SSCOM&#xff08;串口调试助手&#xff09; 硬件&#xff1a;SMT32F…

SQL进阶理论篇(七):B+树的查询及存储机制

文章目录 简介数据库中的存储结构数据库中的页结构从数据页来看B树的查询过程总结参考文献 简介 我们之前已经了解过数据库的B树索引和Hash索引&#xff0c;这些索引信息以及数据记录都是保存在文件里的&#xff0c;确切的说是存储在页结构中。 本节&#xff0c;从我们将了解…

Agilent安捷伦34972A数据采集仪34908A采集卡

附加功能: 3插槽LXI数据采集单元&#xff0c;带6位数字数字多用表(22位)和8个插件模块可供选择(单独出售) 测量11种不同的输入信号(无外部信号调理)&#xff0c;包括热电偶、RTD和热敏电阻的温度&#xff1b;DC/交流伏特或电流&#xff1b;2线或4线电阻&#xff1b;频率和周期…

FindMy技术用于滑雪板

随着冬季运动的日益普及&#xff0c;滑雪板作为滑雪运动的重要器材&#xff0c;也变得越来越受欢迎。在各大雪场和户外运动场所&#xff0c;人们纷纷挥舞着滑雪板&#xff0c;畅享冬季运动的乐趣。 在滑雪过程中&#xff0c;由于雪场的复杂环境和运动的高速性&#xff0c;很容易…

数据结构之排序

目录 ​ 1.常见的排序算法 2.插入排序 直接插入排序 希尔排序 3.交换排序 冒泡排序 快速排序 hoare版本 挖坑法 前后指针法 非递归实现 4.选择排序 直接选择排序 堆排序 5.归并排序 6.排序总结 一起去&#xff0c;更远的远方 1.常见的排序算法 排序&#xff1a;所…

Linux学习笔记-Ubuntu下ssh服务器连接异常Connection reset

文章目录 一、问题问题现象1.1 连接重置无法访问的的问题1.2 查看服务器连接状态1.3 使用调试模式查看的信息 二、临时解决方法三、从根源解决问题3.1 问题分析3.2 服务器的ssh日志3.3 修改ssh配置禁止root登录3.4 配置允许所有ip访问3.5 修改认证方法 角色&#xff1a;百世经纶…

自动化访客互动:提升网站效益与用户体验的关键优势

在激烈的市场竞争环境中&#xff0c;想抢占市场&#xff0c;获得收益并不容易。每一个订单的完成都要经过一定的销售周期&#xff0c;所以企业可以根据销售周期每个阶段的特点进行优化&#xff0c;留住客户。其中&#xff0c;企业可以在与客户在线互动的过程中&#xff0c;让互…

缓存一致性几种解决方案

文章目录 一、理论知识1、概述2、坏的方案2.1 先写 MySQL&#xff0c;再写 Redis2.2 先写 Redis&#xff0c;再写 MySQL2.3 先删除 Redis&#xff0c;再写 MySQL 3、好的方案3.1 先删除 Redis&#xff0c;再写 MySQL&#xff0c;再删除 Redis3.2 先写 MySQL&#xff0c;再删除 …

离散数学知识点-期末复习

目录 一、利用真值表求主析取范式、主合取范式 1.例题 二、推理证明 1.推理规则 2.例题 三、符号化命题 四、有穷集的计数 1.包含互斥原理 2.例题 ​1.文氏图法 2.包含互斥原理法 五、关系的闭包 1.三种闭包 2.Warshall算法 3.例题 六、等价关系 1.定义 2.…

杰卡德的故事

三个男人分别是杰卡德距离 杰卡德相似系数和杰卡德系数 杰卡德相似系数和杰卡德距离是互为相反数的。 杰卡德系数和杰卡德距离是不是一回事 感觉是一回事

Linux--Docker容器(最新)

这里写目录标题 安装Docker安装指令配置加速器 Docker简介名词解释作用run命令解读 操作常见命令命令的别名 数据卷简介数据卷命令使用 本地目录挂载问题发现问题解决二级目录二级目录 安装Docker 安装指令 如下文档 https://b11et3un53m.feishu.cn/wiki/Rfocw7ctXij2RBkShcu…

TrustGeo代码理解(五)sublayers.py

代码链接:https://github.com/ICDM-UESTC/TrustGeo 一、导入模块 import torch import torch.nn as nn import torch.nn.functional as F 这段代码是一个简单的神经网络的定义,用于深度学习任务。 1、import torch:导入 PyTorch 库,提供张量(tensor)等深度学习操作的…