Skip to content

语音对讲

客户端在已建立的 TiRTC 连接上采集本地麦克风,并把声音发送给设备端,适用于门铃对讲等场景。

如果你还没完成客户端工程接入、连接或远端音视频播放,先看对应客户端的 SDK 接入页(例如 WebFlutterReact Native)、建立与设备端的连接音视频流传输与播放

前提条件

  • 客户端已经完成 TiRtc.initialize(...)
  • 连接已经建立,状态进入 connected / CONNECTED
  • 应用已经完成系统麦克风权限申请。Android 需要 RECORD_AUDIO;iOS 需要麦克风权限;Flutter 和 React Native 按目标平台申请对应权限。
  • 设备端已经准备好接收客户端本地音频,并和客户端约定相同的 stream_id

与设备端约定对讲使用的音频 stream_id

语音对讲使用客户端本地麦克风输入。客户端的示例和 DevTools CLI 默认使用:

用途stream_id
客户端麦克风对讲14

14 是示例取值;也可以约定其他 stream_id,取值范围是 015

启动语音对讲

基本顺序是:创建 TiRtcAudioInput -> 设置采集参数 -> 绑定连接和本地音频 stream_id -> 调用 start()

dart
final TiRtcAudioInput talkbackInput = TiRtcAudioInput();

Future<int> startTalkback(TiRtcConn conn) async {
  talkbackInput.onStateChanged = (TiRtcInputState state) {
    debugPrint('talkback state=$state');
  };
  talkbackInput.onError = (int code, String? message) {
    debugPrint('talkback error=$code message=${message ?? ""}');
  };

  int code = await talkbackInput.setOptions(
    const TiRtcAudioInputOptions(
      codec: TiRtcAudioCodec.g711a,
      sampleRate: TiRtcAudioSampleRate.rate16k,
      channels: TiRtcAudioChannelCount.mono,
      aecMode: TiRtcAudioAecMode.enabled,
    ),
  );
  if (code != 0) return code;

  code = await talkbackInput.attach(connection: conn, streamId: 14);
  if (code != 0) return code;

  return talkbackInput.start();
}

Future<void> stopTalkback(TiRtcConn conn) async {
  await talkbackInput.stop();
  await talkbackInput.detach(connection: conn);
  await talkbackInput.dispose();
}
tsx
import {
  TiRtcAudioAecMode,
  TiRtcAudioChannelCount,
  TiRtcAudioCodec,
  TiRtcAudioInput,
  TiRtcAudioSampleRate,
  TiRtcConn,
} from 'tirtc-av-react-native';

const talkbackInput = new TiRtcAudioInput();

async function startTalkback(conn: TiRtcConn): Promise<number> {
  talkbackInput.onStateChanged = (state) => {
    console.info(`talkback state=${state}`);
  };
  talkbackInput.onError = (code, message) => {
    console.warn(`talkback error=${code} message=${message ?? ''}`);
  };

  let code = await talkbackInput.setOptions({
    codec: TiRtcAudioCodec.g711a,
    sampleRate: TiRtcAudioSampleRate.rate16k,
    channels: TiRtcAudioChannelCount.mono,
    aecMode: TiRtcAudioAecMode.enabled,
  });
  if (code !== 0) return code;

  code = await talkbackInput.attach(conn, 14);
  if (code !== 0) return code;

  return talkbackInput.start();
}

async function stopTalkback(conn: TiRtcConn): Promise<void> {
  await talkbackInput.stop();
  await talkbackInput.detach(conn);
  await talkbackInput.dispose();
}
kotlin
val talkbackInput = TiRtcAudioInput()

fun startTalkback(conn: TiRtcConn): Int {
  talkbackInput.onStateChanged = TiRtcInputStateListener { state ->
    Log.i("TiRTC", "talkback state=$state")
  }
  talkbackInput.onError = TiRtcInputErrorListener { code, message ->
    Log.w("TiRTC", "talkback error=$code message=$message")
  }

  var code = talkbackInput.setOptions(
    TiRtcAudioInputOptions(
      codec = TiRtcAudioCodec.G711A,
      sampleRate = TiRtcAudioSampleRate.RATE_16K,
      channels = TiRtcAudioChannelCount.MONO,
      aecMode = 1,
    ),
  )
  if (code != 0) return code

  code = talkbackInput.attach(conn, 14)
  if (code != 0) return code

  return talkbackInput.start()
}

fun stopTalkback(conn: TiRtcConn) {
  talkbackInput.stop()
  talkbackInput.detach(conn)
  talkbackInput.dispose()
}
ts
import { TiRtcAudioInput, TiRtcAudioCodec, TiRtcAudioSampleRate } from 'tirtc-av/Index';

const talkbackInput = new TiRtcAudioInput();

async function startTalkback(conn: TiRtcConn): Promise<number> {
  let code = await talkbackInput.setOptions({
    codec: TiRtcAudioCodec.g711a,
    sampleRate: TiRtcAudioSampleRate.rate16k,
    channels: 1,
    aecMode: 1,
  });
  if (code !== 0) return code;

  code = await talkbackInput.attach({ connection: conn, streamId: 14 });
  if (code !== 0) return code;

  return talkbackInput.start();
}

async function stopTalkback(conn: TiRtcConn): Promise<void> {
  await talkbackInput.stop();
  await talkbackInput.detach({ connection: conn });
  await talkbackInput.dispose();
}
swift
final class Talkback: NSObject, TiRtcAudioInputDelegate {
    private let input = TiRtcAudioInput()

    override init() {
        super.init()
        input.delegate = self
    }

    func start(conn: TiRtcConn) -> Int32 {
        let options = TiRtcAudioInputOptions()
        options.codec = .g711a
        options.sampleRate = .rate16k
        options.channels = .mono
        options.aecMode = 1

        var code = input.setOptions(options)
        if code != 0 { return code }

        code = input.attach(connection: conn, streamId: 14)
        if code != 0 { return code }

        return input.start()
    }

    func stop(conn: TiRtcConn) {
        _ = input.stop()
        _ = input.detach(connection: conn)
        input.dispose()
    }

    func audioInput(_ input: TiRtcAudioInput, didChangeState state: TiRtcInputState) {
        print("talkback state=\(state.rawValue)")
    }

    func audioInput(_ input: TiRtcAudioInput, didFailWithCode code: Int32, message: String?) {
        print("talkback error=\(code) message=\(message ?? "")")
    }
}
js
const talkbackInput = TiRtcAudioInput({
  connection,
  streamId: 14,
});

async function startTalkback() {
  talkbackInput.setOptions({ sampleRate: 8000 });

  await talkbackInput.start(); // 在点击按钮等用户手势里调用,浏览器会在这里申请麦克风权限
  await talkbackInput.attach();
}

async function stopTalkback() {
  talkbackInput.stop();
  await talkbackInput.detach();
}

确认语音已送到设备端

TiRtcAudioInput.start() 返回 0 只表示客户端已经开始采集和发送请求。真正跑通语音对讲,需要同时看到两个结果:

  • 客户端输入状态进入 running / RUNNING,并且没有触发输入错误回调。
  • 你在客户端说话时,设备端能收到或播出对应 stream_id 的音频。使用 C SDK 接收时,可在 TIRTCCALLBACKS.on_audio 中检查 pFi->stream_id,示例默认是 14

常见问题

打开麦克风失败

优先检查:

  • 应用是否已经获得系统麦克风权限。
  • 连接是否已经进入 connected。
  • 本地对讲使用的 stream_id 是否和设备端接收配置一致。
  • TiRtcAudioInput 是否还绑定着旧连接或旧 stream_id;更换绑定前先 stop()detach()

设备端听不到客户端声音

优先检查:

  • 客户端是否调用了 TiRtcAudioInput.start() 并拿到返回值 0
  • 设备端是否在接收客户端约定的本地音频 stream_id,例如示例默认 14
  • 客户端系统输入设备是否正常,麦克风是否被其他应用独占。
  • 如果使用 DevTools CLI 验证语音对讲,CLI 是否用 --input system --output both 启动。

相关文档

TiRTC 开发文档