今天写项目时,有一个需求:通过前端实现用户语音、系统扬声器语音的输入与输出,第一个还是比较好实现的,问问GPT就会给你生成代码,问题的难点就在于第2条:如何获取电脑扬声器的输出,问gpt,gpt说不可以实现,在经过查阅大量资料和不断的尝试后,我终于找到了问题的解决办法:通过访问屏幕媒体流来获取数据,间接的拿到音频数据!
js已经内置好了方法,直接调用即可,不需要下载什么插件啥的,下面分别解释一下获取麦克风的权限,声音录制与播放,还有系统扬声器声音的录制与播放:
1-麦克风权限:
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
让我们逐步解析这行代码的各个部分:
navigator.mediaDevices
:
navigator
是Web API中的一个全局对象,它提供了关于浏览器的信息,并允许脚本执行一些浏览器级别的操作,比如页面跳转或查询浏览器设置。mediaDevices
是navigator
对象的一个属性,它提供了一个接口,允许用户访问连接到设备的媒体输入和输出设备,如摄像头和麦克风。getUserMedia()
方法:
getUserMedia()
是mediaDevices
接口中的一个方法,用于请求用户媒体的访问权限。这个方法返回一个Promise
,这是因为请求用户媒体是一个异步操作,需要等待用户授权或拒绝。- 这个方法接受一个配置对象作为参数,用于指定需要访问的媒体类型。在你的例子中,配置对象是
{ audio: true }
,表示请求访问用户的音频设备(通常是麦克风)。- 配置对象
{ audio: true }
:
- 这个对象指定了
getUserMedia()
方法的参数,即请求访问用户的音频输入设备。如果还需要访问视频输入设备(如摄像头),可以添加video: true
或者指定更详细的视频约束(如分辨率、帧率等)。await
关键字 :
- 由于
getUserMedia()
方法返回一个Promise
,你可以使用await
关键字等待这个Promise
解析完成。await
只能在异步函数(即声明为async
的函数)内部使用。- 使用
await
可以让代码看起来像是同步执行的,即等待getUserMedia()
的Promise
解析后再继续执行后续代码。如果Promise
被拒绝(例如,用户拒绝访问请求),则await
表达式会抛出一个异常,这通常需要被try...catch
语句捕获。const stream = ...
:
- 如果
getUserMedia()
成功,它会解析为一个MediaStream
对象,这个对象代表了用户媒体设备的实时音频/视频流。- 在这个例子中,这个
MediaStream
对象被赋值给了一个名为stream
的常量。你可以使用这个stream
对象来播放音频或视频,或者将其发送到服务器进行进一步处理。综上所述,这行代码的作用是异步请求访问用户的音频设备(麦克风),并将获取到的音频流存储在
stream
变量中。这是实现Web应用中音频录制功能的基础步骤之一。2-系统扬声器权限
const stream = await navigator.mediaDevices.getDisplayMedia({audio: true});
可以看到,和上面方法做对比仅仅是把getUserMedia****换成了 getDisplayMedia。
后边的这段代码本来的功能其实是为了获取屏幕数据的,但是再参数里设置的audio:ture就可以神奇的获取到电脑音频数据了,随后再提取音频流进行保存:
screenMediaRecorder = new MediaRecorder(stream);
screenMediaRecorder.ondataavailable = event => {
audioChunks.push(event.data);
};
就成功的拿到了音频数据了。
3 -获取电脑音频操作步骤:

第一步:运行代码,然后浏览器就会弹出这个窗口
第二步:选择"整个屏幕"这样的话就可以获得整个屏幕的声音输出
第三步:再次点击窗口里的整个屏幕
第三步:点击同时分享系统音频
第四步:点击分享
4-源码分享
源码放在这了,有需要的朋友自取,记得点赞收藏支持博主
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>语音录制与播放</title>
<style>
.button-row {
display: flex;
justify-content: space-around;
margin-bottom: 10px;
}
button {
padding: 10px 20px;
font-size: 16px;
margin: 5px;
}
</style>
</head>
<body>
<div class="button-row">
<button id="startRecord">1-开始录制语音</button>
<button id="stopRecord">2-停止录制语音</button>
<button id="playRecord">3-播放录制的声音</button>
</div>
<div class="button-row">
<button id="startScreenRecord">4-获取系统扬声器声音</button>
<button id="stopScreenRecord">5-停止获取系统扬声器声音</button>
<button id="playScreenRecord">6-播放系统扬声器声音</button>
</div>
<audio id="audioPlayback" controls style="display:none;"></audio>
<audio id="audioPlayback2" controls style="display:none;"></audio>
<script>
let mediaRecorder;
let screenMediaRecorder;
let audioChunks = [];
document.getElementById('startRecord').addEventListener('click', async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.ondataavailable = event => {
audioChunks.push(event.data);
};
mediaRecorder.onstop = () => {
const audioBlob = new Blob(audioChunks, { 'type': 'audio/wav' });
const audioUrl = URL.createObjectURL(audioBlob);
document.getElementById('audioPlayback').src = audioUrl;
document.getElementById('audioPlayback').style.display = 'block';
audioChunks = []; // Reset for next recording
mediaRecorder = null;
stream.getTracks().forEach(track => {
track.stop();
});
};
mediaRecorder.start();
} catch (err) {
console.error("获取用户语音失败: ", err);
}
});
document.getElementById('stopRecord').addEventListener('click', () => {
if (mediaRecorder) {
mediaRecorder.stop();
}
});
document.getElementById('playRecord').addEventListener('click', () => {
const audioPlayback = document.getElementById('audioPlayback');
if (audioPlayback.paused) {
audioPlayback.play();
} else {
audioPlayback.pause();
}
});
document.getElementById('startScreenRecord').addEventListener('click', async()=> {
try {
// 请求访问用户的屏幕媒体流
const stream = await navigator.mediaDevices.getDisplayMedia({audio: true});
screenMediaRecorder = new MediaRecorder(stream);
screenMediaRecorder.ondataavailable = event => {
audioChunks.push(event.data);
};
screenMediaRecorder.onstop = () => {
const audioBlob = new Blob(audioChunks, { 'type': 'audio/wav' });
const audioUrl = URL.createObjectURL(audioBlob);
document.getElementById('audioPlayback2').src = audioUrl;
document.getElementById('audioPlayback2').style.display = 'block';
audioChunks = []; // Reset for next recording
screenMediaRecorder = null;
stream.getTracks().forEach(track => {
track.stop();
});
};
screenMediaRecorder.start();
} catch (err) {
console.error("获取用户语音失败: ", err);
}
});
document.getElementById('stopScreenRecord').addEventListener('click', () => {
if (screenMediaRecorder) {
screenMediaRecorder.stop();
}
});
document.getElementById('playScreenRecord').addEventListener('click', () => {
const audioPlayback = document.getElementById('audioPlayback2');
if (audioPlayback.paused) {
audioPlayback.play();
} else {
audioPlayback.pause();
}
});
</script>
</body>
</html>