# 自定义音频采集和渲染
# 简介
实时音频传输过程中,JC SDK 会启动默认的音频模块进行采集和渲染。当遇到不支持系统标准 API 的音频设备的时候,或者希望使用自己已经拥有的音频模块进行音频采集和渲染的时候,您可以选择在音频被采集前自定义音频采集,也可以选择在音频播放前自定义音频渲染。
# 前提条件
在自定义音频采集和渲染前,确保已经初始化了 JCMediaDevice 模块。
# 自定义音频采集
参照下述步骤,在音频输入前,修改音频输入源和采样率等参数:
- 发起通话前,将 JCMediaDevice (opens new window) 中 audioParam (opens new window) 的 autoStartAudioInputDevice (opens new window) 设置为
false
。这种情况下才可以获取音频输入数据。 - 调用 inputCustomAudioData (opens new window) 自定义音频采集。 建议在一对一通话状态为连接中时或者加入频道成功后,在工作线程中不断调用此方法传入音频数据。
- 在结束通话的时候,停止工作线程,结束音频采集。
import { JCMediaDevice, JCCall, JCMediaChannel } from "@juphoon/jcsdk";
import { ByteBuffer } from "@ohos/util";
// 在开启前将 autoStartAudioInputDevice 属性设为 false
function turnOffAutoStartAudioInputDevice(mediaDevice: JCMediaDevice): void {
mediaDevice.audioParam.autoStartAudioInputDevice = false;
}
// 自定义音频采集任务
class CustomAudioInputTask {
private isRunning: boolean = false;
private mediaDevice: JCMediaDevice;
private call: JCCall | null;
private mediaChannel: JCMediaChannel | null;
constructor(
mediaDevice: JCMediaDevice,
call: JCCall | null = null,
mediaChannel: JCMediaChannel | null = null
) {
this.mediaDevice = mediaDevice;
this.call = call;
this.mediaChannel = mediaChannel;
}
// 开始自定义音频采集
public start(): void {
if (this.isRunning) {
return;
}
this.isRunning = true;
this.runAudioInputTask();
}
// 停止自定义音频采集
public stop(): void {
this.isRunning = false;
}
// 执行自定义音频采集
private async runAudioInputTask(): Promise<void> {
const sampleRateHz = 8000;
const channels = 1;
// 示例:创建自定义音频数据
const text = "自定义音频数据示例";
const encoder = new TextEncoder();
const value = encoder.encode(text);
// 创建直接缓冲区
const buffer = new ByteBuffer(value.length);
buffer.writeBytes(value);
buffer.flip();
// 循环输入音频数据
while (
this.isRunning &&
((this.call && this.call.getActiveCallItem() !== null) ||
(this.mediaChannel &&
this.mediaChannel.getState() === JCMediaChannel.STATE_JOINED))
) {
this.mediaDevice.inputCustomAudioData(
sampleRateHz,
channels,
buffer,
0,
0,
0
);
// 等待10ms
await new Promise<void>((resolve) => {
setTimeout(resolve, 10);
});
}
this.isRunning = false;
}
}
# 自定义音频渲染
参照下述步骤,在音频播放前,修改音频输出参数:
- 发起通话前,将 JCMediaDevice (opens new window) 中 audioParam (opens new window) 的 autoStartAudioOutputDevice (opens new window) 设置为
false
。这种情况下才可以获取音频输出数据。 - 调用 getAudioOutputData (opens new window) 自定义音频渲染。建议在一对一通话状态为连接中时或者加入频道成功后,在工作线程中不断调用此方法渲染音频数据。
- 在结束通话的时候,停止工作线程,结束音频渲染。
import { JCMediaDevice, JCCall, JCMediaChannel } from "@juphoon/jcsdk";
import { ByteBuffer } from "@ohos/util";
// 在开启前将 autoStartAudioOutputDevice 属性设为 false
function turnOffAutoStartAudioOutputDevice(mediaDevice: JCMediaDevice): void {
mediaDevice.audioParam.autoStartAudioOutputDevice = false;
}
// 自定义音频渲染任务
class CustomAudioOutputTask {
private isRunning: boolean = false;
private mediaDevice: JCMediaDevice;
private call: JCCall | null;
private mediaChannel: JCMediaChannel | null;
constructor(
mediaDevice: JCMediaDevice,
call: JCCall | null = null,
mediaChannel: JCMediaChannel | null = null
) {
this.mediaDevice = mediaDevice;
this.call = call;
this.mediaChannel = mediaChannel;
}
// 开始自定义音频渲染
public start(): void {
if (this.isRunning) {
return;
}
this.isRunning = true;
this.runAudioOutputTask();
}
// 停止自定义音频渲染
public stop(): void {
this.isRunning = false;
}
// 执行自定义音频渲染
private async runAudioOutputTask(): Promise<void> {
const sampleRateHz = 8000;
const channels = 1;
// 创建用于获取音频输出数据的缓冲区
// 计算每10ms的数据大小: 采样率 * 每个样本字节数(短整型2字节) * 通道数 / 100
const bufferSize = Math.floor((sampleRateHz / 100) * 2 * channels);
const buffer = new ByteBuffer(bufferSize);
// 循环获取并处理音频数据
while (
this.isRunning &&
((this.call && this.call.getActiveCallItem() !== null) ||
(this.mediaChannel &&
this.mediaChannel.getState() === JCMediaChannel.STATE_JOINED))
) {
// 获取音频输出数据
this.mediaDevice.getAudioOutputData(sampleRateHz, channels, buffer);
// 在这里对buffer中的音频数据进行处理和渲染
// 例如:播放到扬声器、进行音频处理等
// 等待10ms,与音频帧率匹配
await new Promise<void>((resolve) => {
setTimeout(resolve, 10);
});
}
this.isRunning = false;
}
}