# audio-demo
**Repository Path**: BinGo2014_admin/audio-demo
## Basic Information
- **Project Name**: audio-demo
- **Description**: 基于浏览器的单向喊话案例,WebRTC, 前端采集数据传输到后端,后端通过ffmpeg使用rtmp方式进行推流,另外一端实现拉流,播放语音
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-09-26
- **Last Updated**: 2023-09-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 浏览器口播方案
## 1 方案一: WebRTC实现
百度百科:
> WebRTC (Web Real-Time Communications) 是一项[实时通讯](https://baike.baidu.com/item/实时通讯/2895640?fromModule=lemma_inlink)技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间[点对点](https://baike.baidu.com/item/点对点/7452984?fromModule=lemma_inlink)(Peer-to-Peer)的连接,实现[视频流](https://baike.baidu.com/item/视频流/9392168?fromModule=lemma_inlink)和(或)[音频流](https://baike.baidu.com/item/音频流/7390786?fromModule=lemma_inlink)或者其他任意数据的传输。WebRTC 包含的这些标准使用户在无需安装任何插件或者第三方的软件的情况下,创建点对点(Peer-to-Peer)的数据分享和[电话会议](https://baike.baidu.com/item/电话会议/2519220?fromModule=lemma_inlink)成为可能。
简单来说WebRTC是一种浏览器端直接推流的方式
## 2 方案二:前端采集音频数据,推送至后端
前端使用 `jussbuca-pro-talk` 采集音频数据,通过`websocket`推送到后端,后端通过`ffmpeg`进行解码、编码,然后使用`rtmp`推流出去。
> 前端页面需要`localhost`或`https`才能支持浏览器开启电脑话筒,所以上面的页面是部署在`nginx`中的
后端`websocket`接收数据:
```java
@OnMessage
public void onMessage(byte[] message, Session session) {
try {
log.info("websocket onMessage sessionId:{}", session.getId());
ContextUtil.getBean(SpeakService.class).speak(session, message);
} catch (Exception e) {
log.error("websocket onMessage error sessionId:{}, message:{}", session.getId(), message);
}
}
```
核心数据处理:
```java
@Slf4j
@Service
public class AudioCloudPlayerImpl implements AudioCloudPlayer {
private final BlockingQueue queue = new LinkedBlockingQueue<>();
@Override
public void init() {
}
@Override
public void start(List deviceNoList) {
new Thread(this::speak).start();
}
@SneakyThrows
@Override
public void pushStream(byte[] voice) {
queue.put(AudioData.builder()
.data(voice)
.build());
}
@Override
public void stop() {
}
public void speak() {
try {
// 创建命名管道
File pipe = File.createTempFile("audio_test_", ".pipe");
pipe.delete();
Runtime.getRuntime().exec("mkfifo " + pipe.getAbsolutePath());
// 构造命令行
// G711是一个音频编码标准,常用于数字电话和语音传输。它有两个变种:μ - law和A - law。μ - law 通常在北美和日本使用,而 A - law 在其他国家和地区使用。
// 对于ffmpeg来说,G711a(即A - law)的编码标记为pcm_alaw,G711u(即μ - law)的编码标记为pcm_mulaw。
// 所以,如果你需要把 G711a 格式转换为 aac 格式,可以使用以下命令:ffmpeg -f alaw -ar 8000 -i input.alaw -c:a aac output.aac
String ffmpegCommand = "ffmpeg -re -f alaw -ar 16000 -ac 1 -i " + pipe.getAbsolutePath() + " -c:a aac -b:a 128k -f flv rtmp://192.168.42.93:1935/live";
// 启动FFmpeg进程
log.info("执行ffmpeg命令:{}", ffmpegCommand);
Process process = Runtime.getRuntime().exec(ffmpegCommand);
// 开始新的线程为FFmpeg提供数据
new Thread() {
public void run() {
try {
// 開啟管道並寫入數據
OutputStream outputStream = new FileOutputStream(pipe);
byte[] audioData;
while (true) {
// 以下是伪代码,须替换为你的音频数据
audioData = queue.take().getData();
log.info("向管道写数据length:{}", audioData.length);
// 将音频数据写入管道
outputStream.write(audioData);
}
} catch (Exception ex) {
// 处理异常
ex.printStackTrace();
}
}
}.start();
// 根据需要处理FFmpeg的标准输出和错误信息
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
log.info(line);
}
// 确保处理 FFmpeg进程退出
process.waitFor();
} catch (Exception e) {
log.error("speak error: ", e);
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class AudioData {
private byte[] data;
}
}
```
**核心的原理:**
`mkfifo`创建一个管道,将前端传递过来的数据写入管道,然后`ffmpeg`读取管道数据,进行解码、编码,然后推流。