AI 助手
扣子 AI 帮助与支持
你好,我是 扣子 文档问答助手 🎉 你在阅读当前文档的过程中,无论对文档概念的解释,还是文档内容方面的疑问,都可以随时向我提问,我会全力为你解答
推荐问题
扣子 3.0 都有什么新特性?
扣子和扣子编程有什么区别?
扣子如何收费?
文档反馈
低代码项目
动态
快速开始
智能体
工作流
应用
资源
发布
模型
多人协作
推广与变现

集成音视频 Realtime iOS SDK

扣子编程 Realtime SDK iOS 版基于火山引擎 RTC SDK 进行封装,支持将扣子编程的智能体功能与火山引擎 RTC SDK 集成,实现与智能体的实时音视频通话。扣子编程提示 Swift 和 Objective-C 语言的示例项目源码,你可以参考示例项目源码,快速从零开始构建一个简单的音视频通话应用。

准备工作

开始集成 Realtime SDK 前,请确保开发环境满足以下要求:

操作 说明

发布智能体

已成功搭建并发布智能体为 API 服务。搭建步骤可参考搭建可视化智能体搭建低延时语音助手,发布步骤请参见发布为 API 服务

说明

若需使用视频理解能力,请为智能体配置一个视觉模型,例如豆包视觉理解模型。

获取访问密钥

获取访问密钥,用于身份认证与鉴权。

  • 体验或调试场景:建议生成短期的个人访问令牌(PAT),以快速完成 Realtime SDK 的整体流程。个人访问令牌的获取方法请参见添加个人访问令牌
  • 线上环境:在线上环境中,应使用服务访问令牌(SAT)或 OAuth 鉴权方案,各鉴权方式的详细说明请参考鉴权方式概述

说明

扣子编程 SDK 封装了多种鉴权方式,能够有效简化鉴权流程,你可以参考鉴权示例代码实现不同方式的 OAuth 认证,以获取和管理访问扣子编程 API 所需的令牌。

准备开发环境

  • macOS 电脑,且可以正常访问互联网。
  • Xcode 14.1 或以上版本。本示例采用 **** 16.2 版本。
  • Apple 开发者账号,开发阶段可以使用个人免费账号。
  • iOS 11.0 或以上版本真机设备,且可以正常访问互联网。本示例采用 iOS 18.1。
  • 安装 CocoaPods。

跑通示例项目源码

示例项目目录结构

音视频通话 iOS 示例项目的目录结构如下:

├── Podfile
├── coze-swift
│   ├── AppDelegate.swift
│   ├── Assets.xcassets
│   ├── Base.lproj
│   ├── Config                        //配置 API 相关信息
│   ├── Info.plist
│   ├── Models                       //创建房间相关数据结构
│   ├── SceneDelegate.swift
│   ├── Services                       //API 请求封装
│   └── ViewController.swift           //主界面
├── coze-swift.xcodeproj
└── coze-swift.xcworkspace

├── Podfile
├── Pods
├── coze-objc
│   ├── AppDelegate.h
│   ├── AppDelegate.m
│   ├── Assets.xcassets
│   ├── Base.lproj
│   ├── Config
│   ├── Info.plist
│   ├── Models
│   ├── SceneDelegate.h
│   ├── SceneDelegate.m
│   ├── Services
│   ├── ViewController.h
│   ├── ViewController.m
│   └── main.m
├── coze-objc.xcodeproj
└── coze-objc.xcworkspace

配置并跑通示例项目源码

  1. 克隆音视频通话 iOS 示例项目源码到本地。
    git clone https://github.com/coze-dev/coze-ios
    
  2. 在项目根目录下运行以下命令,初始化并安装项目依赖。
    pod install
    
  3. 在配置文件中设置 accessToken 和 botid。
    1. 在项目根目录下,复制coze-swift/Config/APIConfig.swift.template文件为。
    2. 编辑APIConfig.swift配置文件,配置 accessTokenbotId
    import Foundation
    
    struct APIConfig {
        static let baseURL = "https://api.coze.cn"
        static let accessToken = "pat_sWaR9V4Yr8***" // 替换为从 https://www.coze.cn/open/oauth/pats 获取到的token
        static let botId = "74415109067667***" // 替换为自己的bot ID
        static let voiceId:String? = nil // 音色ID,可选
    } 
    

    关键参数说明如下:

    • accessToken:请替换为准备工作中获取的访问密钥,用于身份认证与鉴权。
    • botId:智能体 ID,获取方法如下:
      进入智能体的开发页面,开发页面 URL 中 bot 参数后的数字即为智能体 ID。例如, URL 为https://www.coze.cn/space/341****/bot/73428668*****,则智能体 ID 为73428668*****
    • voiceId:音色 ID。可选。你可以调用查看音色列表 API 查看当前可用的音色列表,也可以使用系统音色列表
    1. 在项目根目录下,复制 coze-objc/Config/APIConfig.h.template 文件为coze-objc/Config/APIConfig.h
    2. 编辑APIConfig.h配置文件,配置 API_ACCESS_TOKENAPI_BOT_ID
      #ifndef APIConfig_h
      #define APIConfig_h
      
      #define API_BASE_URL @"https://api.coze.cn" // 替换为从 https://www.coze.cn/open/oauth/pats 获取到的token
      #define API_ACCESS_TOKEN @"pat_KWQlw2nvTlLTMISAz***"
      #define API_BOT_ID @"74281773215***"
      #define API_VOICE_ID nil
      
      #endif /* APIConfig_h */
      

    关键参数说明如下:

    • API_ACCESS_TOKEN:请替换为准备工作中获取的访问密钥,用于身份认证与鉴权。
    • API_BOT_ID:智能体 ID,获取方法如下:
      进入智能体的开发页面,开发页面 URL 中 bot 参数后的数字即为智能体 ID。例如, URL 为https://www.coze.cn/space/341****/bot/73428668*****,则智能体 ID 为73428668*****
    • API_VOICE_ID:音色 ID。可选。你可以调用查看音色列表 API 查看当前可用的音色列表,也可以使用系统音色列表
  4. 编译并运行项目。
    1. 使用 Xcode 打开coze-swift.xcworkspacecoze-objc.xcworkspace
    2. 在 Xcode 中连接并选择你的 iOS 真机设备,单击 XCode 窗口左上角的运行按钮(或使用 Command ⌘ + R 快捷键)。
    3. 在 iOS 设备上打开 Demo 应用时,在弹窗中选择开启麦克风和摄像头权限。
    4. 体验 Demo 效果,界面类似如下:

      Image

    5. 在音视频通话界面单击连接,连接成功后,你可以与智能体开始实时语音对话。你还可以体验打开或关闭摄像头、静音或取消静音、打断语音通话。

实现音视频通话

步骤一:(可选)新建项目

本步骤为如何创建一个新项目,如集成到已有项目,请跳过该步骤。

  1. 打开 Xcode,单击 Create New Project… 新建项目。
  2. 在项目模板页选择 iOS > App,单击 Next
  3. 配置项目信息,单击 Next。
    Image
    参数 说明
    Product Name 输入项目的名称,该名称将用于标识应用程序。
    Team 选择开发团队。如果尚未登录 Apple 账户,请点击 Add account… 按钮,并按照提示完成登录。登录完成后,即可从下拉菜单中选择您的 Apple 账户作为开发团队。
    Organization Identifier 输入组织标识符,通常为反向域名格式(例如 com.yourcompany )。
    Interface 选择 Storyboard,用于定义用户界面布局。
    Language 选择 SwiftObjective-C,作为项目开发的主要编程语言。
  4. 选择项目的存储位置,单击 Create 完成项目创建。

步骤二:配置签名

选中项目,进入 TARGETS > coze-objc > Signing & Capabilities,选择 Automatically manage signing

Image

步骤三:配置权限

音视频通话需要使用麦克风、摄像头和网络权限,操作步骤如下:

  1. 在 Xcode 中,打开项目的 Info.plist 文件。如果该文件不可编辑,可以通过鼠标右键选择 Info,然后选择 Open As > Source Code,以源代码形式查看和编辑。
  2. Info.plist 文件中,新增以下配置内容,明确告知系统和用户应用程序需要访问的权限及其用途。
    Image
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
      <dict>
        <key>UIApplicationSceneManifest</key>
        <dict>
          <key>UIApplicationSupportsMultipleScenes</key>
          <false />
          <key>UISceneConfigurations</key>
          <dict>
            <key>UIWindowSceneSessionRoleApplication</key>
            <array>
              <dict>
                <key>UISceneConfigurationName</key>
                <string>Default Configuration</string>
                <key>UISceneDelegateClassName</key>
                <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
                <key>UISceneStoryboardFile</key>
                <string>Main</string>
              </dict>
            </array>
          </dict>
        </dict>
    
        <!-- 新增配置 -->
        <key>NSCameraUsageDescription</key>
        <string>需要访问您的相机以进行视频通话</string>
    
        <key>NSMicrophoneUsageDescription</key>
        <string>需要访问您的麦克风以进行语音通话</string>
    
        <key>NSAppTransportSecurity</key>
        <string>需要网络权限</string>
        <dict>
          <key>NSAllowsArbitraryLoads</key>
          <true />
        </dict>
        <!-- 新增配置  -->
    
      </dict>
    </plist>
    

步骤四:集成 Realtime SDK

本文以通过 CocoaPods 直接集成为例。如需手动集成,请参考火山引擎 RTC 文档。

  1. 在终端窗口执行如下命令安装 CocoaPods
    sudo gem install cocoapods
    
  2. 进入项目根目录,执行如下命令,创建 Podfile 文件。
    pod init
    
  3. 打开 Podfile 文件,并将其内容替换为以下代码。建议使用 VSCode 或其他代码编辑器打开项目文件。

    说明

    请将 **3.58.1.19400 **替换为 RTC 的目标版本号,具体版本信息可在下载 RTC SDK 页面查看。

    # Uncomment the next line to define a global platform for your project
    # platform :ios, '9.0'
    source 'https://cdn.cocoapods.org/'
    source 'https://github.com/volcengine/volcengine-specs.git'
    
    target 'coze-swift' do
      # Comment the next line if you don't want to use dynamic frameworks
      use_frameworks!
      pod 'VolcEngineRTC', '3.58.1.19400'
    
      # Pods for RTCDemo-Swift
    
    end
    
  4. 在终端窗口执行 pod install 命令安装 VolcEngineRTC 相关库。
    安装完成后,项目文件夹中将生成一个 coze-objc.xcworkspace 文件。使用 Xcode 打开该文件,以进行后续开发操作。
    Image
  5. 编译与运行。
    编译项目,检查是否存在错误。在 Xcode 中,连接并选择 iOS 真机设备。单击 XCode 窗口左上角的运行按钮(或使用 Command ⌘ + R 快捷键)。

    说明

    仅支持在 iOS 真机设备上运行,不支持在模拟器运行。

    如果编译过程中提示错误,请参见常见问题

步骤五:实现音视频通话

说明

本文以 ViewController.m 中的实现步骤为例,讲解如何实现一个基本的音视频通话功能。本示例并未覆盖全部代码内容,如需查看完整代码,请从音视频通话 iOS 示例项目获取完整代码。

引入头文件

ViewController.swiftViewController.h 中引入必要的模块,并声明相关属性和方法。

import UIKit
import VolcEngineRTC

class ViewController: UIViewController, ByteRTCVideoDelegate, ByteRTCRoomDelegate {
    var rtcVideo: ByteRTCVideo?
    var rtcRoom: ByteRTCRoom?
    private var roomInfo: RoomData?
}

#import <UIKit/UIKit.h>
#import <VolcEngineRTC/VolcEngineRTC.h>
#import "ApiResponse.h"

NS_ASSUME_NONNULL_BEGIN

@interface ViewController : UIViewController <ByteRTCVideoDelegate, ByteRTCRoomDelegate, UITableViewDelegate, UITableViewDataSource>
@end

NS_ASSUME_NONNULL_END

创建用户界面

ViewController 中实现 createUI 方法,创建本地视频预览视图,定义控制按钮。

func createUI() {
    let width = self.view.bounds.size.width
    let height = self.view.bounds.size.height
    
    // 本地预览视图占上半部分
    let previewHeight = height * 0.4
    localView.frame = CGRect(x: 0, y: 0, width: width, height: previewHeight)
    self.view.addSubview(localView)
    
    // 按钮区域
    let buttonY = previewHeight + 10
    let buttonWidth = (width - 30) / 2
    
    joinButton.frame = CGRect(x: 10, y: buttonY, width: buttonWidth, height: 44)
    cameraButton.frame = CGRect(x: width - buttonWidth - 10, y: buttonY, width: buttonWidth, height: 44)
    
    self.view.addSubview(joinButton)
    self.view.addSubview(cameraButton)
}

- (void)createUI {
  CGFloat width = self.view.bounds.size.width;
  CGFloat height = self.view.bounds.size.height;

  // 本地预览视图
  CGFloat previewHeight = height * 0.4;
  self.localView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, previewHeight)];
  self.localView.backgroundColor = [UIColor lightGrayColor];
  [self.view addSubview:self.localView];

  // 按钮区域
  CGFloat buttonY = previewHeight + 10;
  CGFloat buttonWidth = (width - 30) / 2;

  // 创建连接按钮
  self.joinButton = [UIButton buttonWithType:UIButtonTypeCustom];
  self.joinButton.frame = CGRectMake(10, buttonY, buttonWidth, 44);
  [self.joinButton setTitle:@"连接" forState:UIControlStateNormal];
  [self.joinButton addTarget:self action:@selector(connectButtonTapped) forControlEvents:UIControlEventTouchUpInside];
  
  // 创建其他控制按钮...
  
  [self.view addSubview:self.joinButton];
}

创建房间并获取房间信息

调用扣子编程的创建房间接口创建房间,通过 NetworkService 获取房间信息。

do {
    // 获取房间信息
    let response = try await NetworkService.shared.createRoom(
        botId: APIConfig.botId,
        voiceId: APIConfig.voiceId
    )
    
    // 异常处理
    if response.code != 0 {
        throw NSError(
            domain: "", code: Int(response.code),
            userInfo: [NSLocalizedDescriptionKey: response.msg])
    }
    roomInfo = response.data
} catch {
    print("连接失败: \(error)")
}

[[NetworkService shared] createRoomWithBotId:API_BOT_ID
                                   voiceId:API_VOICE_ID
                                completion:^(RoomResponse *response, NSError *error) {
    if (error || response.code != 0) {
        // 处理错误...
    } else {
        self.roomInfo = response.data;
        [self buildRTCEngine];
        [self bindLocalRenderView];
        [self joinRoom];
    }
}];

说明

创建房间后返回的 Token,其默认有效期为 3 分钟,如果 3 分钟内没有用户加入房间,或者用户静音 3 分钟,智能体将自动退出房间,房间随即被释放,后续若要再次和智能体对话,则需重新创建房间。

创建 RTC 引擎

创建并初始化 RTC 引擎。

func buildRTCEngine() {
    guard let roomInfo = self.roomInfo else { return }
    
    self.rtcVideo = ByteRTCVideo.createRTCVideo(
        roomInfo.app_id, delegate: self, parameters: [:])
}

- (void)buildRTCEngine {
  if (!self.roomInfo) {
    return;
  }

  self.rtcVideo = [ByteRTCVideo createRTCVideo:self.roomInfo.app_id
                                    delegate:self
                                  parameters:@{}];
  [self.rtcVideo startAudioCapture];
}

采集音视频并设置本端渲染

  1. 调用 startAudioCapture() 开启麦克风音频流采集,调用 startVideoCapture() 开启摄像头视频流采集。
  2. 调用 setLocalVideoCanvas() 方法设置本端视频渲染视图。
    // 开启音频采集
    self.rtcVideo?.startAudioCapture()
    
    // 开启视频采集(可选)
    self.rtcVideo?.startVideoCapture()
    

    // 开始音频采集
    [self.rtcVideo startAudioCapture];
    
    // 开始视频采集
    [self.rtcVideo startVideoCapture];
    
    - (void)bindLocalRenderView {
      ByteRTCVideoCanvas *canvas = [[ByteRTCVideoCanvas alloc] init];
    
    //设置本端视频渲染视图
      canvas.view = self.localView;
      canvas.renderMode = ByteRTCRenderModeHidden;
      [self.rtcVideo setLocalVideoCanvas:ByteRTCStreamIndexMain withCanvas:canvas];
    }
    

创建并加入房间

  1. 调用 createRTCRoom 接口创建 RTC 房间。
  2. 调用 joinRoom 接口加入 RTC 房间。
    // 创建房间
    self.rtcRoom = self.rtcVideo?.createRTCRoom(roomInfo.room_id)
    self.rtcRoom?.delegate = self
    
    let userInfo = ByteRTCUserInfo()
    userInfo.userId = roomInfo.uid
    
    let roomCfg = ByteRTCRoomConfig()
    roomCfg.isAutoPublish = true
    roomCfg.isAutoSubscribeAudio = true
    roomCfg.isAutoSubscribeVideo = true
    
    // 加入房间
    self.rtcRoom?.joinRoom(roomInfo.token, userInfo: userInfo, roomConfig: roomCfg)
    

    - (void)joinRoom {
      self.rtcRoom = [self.rtcVideo createRTCRoom:self.roomInfo.room_id];
      self.rtcRoom.delegate = self;
    
      ByteRTCUserInfo *userInfo = [[ByteRTCUserInfo alloc] init];
      userInfo.userId = self.roomInfo.uid;
    
      ByteRTCRoomConfig *roomConfig = [[ByteRTCRoomConfig alloc] init];
      roomConfig.isAutoPublish = YES;
      roomConfig.isAutoSubscribeAudio = YES;
      roomConfig.isAutoSubscribeVideo = YES;
    
      [self.rtcRoom joinRoom:self.roomInfo.token
                    userInfo:userInfo
                  roomConfig:roomConfig];
    }
    

渲染本端视频流

调用 setLocalVideoCanvas 方法配置本端视频渲染视图。

func bindLocalRenderView() {
    let canvas = ByteRTCVideoCanvas.init()
    canvas.view = self.localView
    canvas.renderMode = .hidden
    self.rtcVideo?.setLocalVideoCanvas(.indexMain, withCanvas: canvas)
}

- (void)bindLocalRenderView {
  ByteRTCVideoCanvas *canvas = [[ByteRTCVideoCanvas alloc] init];
  canvas.view = self.localView;
  canvas.renderMode = ByteRTCRenderModeHidden;
  [self.rtcVideo setLocalVideoCanvas:ByteRTCStreamIndexMain withCanvas:canvas];
}

实时字幕显示

处理接收到的用户消息并实时显示字幕。

当收到 conversation.message.delta 事件时,系统会根据事件类型处理消息:

  • 如果是新消息,则通过messageList.append(message)将其添加到消息列表中。
  • 如果是消息的增量更新,则通过lastMessage += message对最后一条消息进行追加更新。

处理完成后,调用messageTableView.reloadData()方法,刷新 UITableView 并显示最新的字幕内容。

func addMessage(_ message: String, eventType: String) {
    DispatchQueue.main.async {
        if self.lastEventType == "conversation.message.delta" {
            if var lastMessage = self.messageList.last {
                lastMessage += message
                self.messageList[self.messageList.count - 1] = lastMessage
            }
        } else if eventType == "conversation.message.delta" {
            self.messageList.append(message)
        }
        
        self.lastEventType = eventType
        self.messageTableView.reloadData()
    }
}

监听 onUserMessageReceived 回调,将收到的 messageDict 用户消息更新到界面上。

- (void)rtcRoom:(ByteRTCRoom *)rtcRoom
    onUserMessageReceived:(NSString *)uid
                  message:(NSString *)message {
  NSError *error;
  NSData *jsonData = [message dataUsingEncoding:NSUTF8StringEncoding];
  NSDictionary *messageDict = [NSJSONSerialization JSONObjectWithData:jsonData
                                                            options:0
                                                              error:&error];

  if (!error) {
    NSString *eventType = messageDict[@"event_type"];
    if ([eventType isEqualToString:@"conversation.message.delta"]) {
      NSString *content = messageDict[@"data"][@"content"] ?: @"";
      [self addMessage:content eventType:eventType];
    }
  }
}

开启或关闭本端视频流采集

cameraButtonTapped 回调中,通过按钮的 isSelected 状态切换,调用 startVideoCapture()stopVideoCapture()方法,实现摄像头的启用或禁用功能。

@objc private func cameraButtonTapped() {
    cameraButton.isSelected = !cameraButton.isSelected
    
    if cameraButton.isSelected {
        self.rtcVideo?.startVideoCapture()
    } else {
        self.rtcVideo?.stopVideoCapture()
    }
}

- (void)cameraButtonTapped {
  self.cameraButton.selected = !self.cameraButton.selected;

  if (self.cameraButton.selected) {
    [self.rtcVideo startVideoCapture];
  } else {
    [self.rtcVideo stopVideoCapture];
  }
}

静音或取消静音

muteButtonTapped 回调中,根据按钮的选中状态调用 startAudioCapture()stopAudioCapture()方法,从而实现麦克风的启用或静音功能。

@objc private func muteButtonTapped() {
    muteButton.isSelected = !muteButton.isSelected
    
    if muteButton.isSelected {
        self.rtcVideo?.stopAudioCapture()
    } else {
        self.rtcVideo?.startAudioCapture()
    }
}

- (void)muteButtonTapped {
  self.muteButton.selected = !self.muteButton.selected;

  if (self.muteButton.selected) {
    [self.rtcVideo stopAudioCapture];
  } else {
    [self.rtcVideo startAudioCapture];
  }
}

打断通话

调用 sendUserMessage 方法,实现在语音通话过程中,用户可以打断通话。打断通话后,将停止音频流输出。

@objc private func interruptButtonTapped() {
    let message = [
        "id": "event_1",
        "event_type": "conversation.chat.cancel",
        "data": "{}"
    ]
    
    do {
        let jsonData = try JSONSerialization.data(withJSONObject: message)
        if let jsonString = String(data: jsonData, encoding: .utf8) {
            self.rtcRoom?.sendUserMessage(
                APIConfig.botId, 
                message: jsonString,
                config: ByteRTCMessageConfig.reliableOrdered)
        }
    } catch {
        print("Error creating JSON: \(error)")
    }
}

- (void)interruptButtonTapped {
  NSDictionary *message = @{
    @"id" : @"event_1",
    @"event_type" : @"conversation.chat.cancel",
    @"data" : @"{}"
  };

  NSError *error;
  NSData *jsonData = [NSJSONSerialization dataWithJSONObject:message
                                                   options:0
                                                     error:&error];

  if (!error) {
    NSString *jsonString = [[NSString alloc] initWithData:jsonData
                                               encoding:NSUTF8StringEncoding];
    [self.rtcRoom sendUserMessage:API_BOT_ID
                        message:jsonString
                         config:ByteRTCMessageConfigReliableOrdered];
  }
}

停止音视频通话

  1. 依次调用 leaveRoomdestroy 接口离开并销毁房间。
  2. 调用destroyRTCVideo 接口释放引擎资源,避免内存泄漏。
deinit {
    // 销毁房间
    self.rtcRoom?.leaveRoom()
    self.rtcRoom?.destroy()
    self.rtcRoom = nil
    
    // 销毁引擎
    ByteRTCVideo.destroyRTCVideo()
    self.rtcVideo = nil
}

- (void)dealloc {
  [self.rtcRoom leaveRoom];
  [self.rtcRoom destroy];
  self.rtcRoom = nil;
  [ByteRTCVideo destroyRTCVideo];
  self.rtcVideo = nil;
}

说明

在实现音视频通话后,如遇无声音、无画面、视频卡顿等问题时,您可以使用诊断工具快速排查和定位异常房间及用户,并获取异常根因分析、处理建议、分析报告等。

信令事件

事件类型

智能语音信令事件包括上行事件和下行事件。每个事件有 ID 和 EventType, 通过 EventType 可以区分具体的事件类型,每个事件类型对应的 Payload 在 Data 中,开发者可以按需去提取需要的内容。

  • 上行事件:设备端上报给服务端的事件。应用程序需要根据扣子编程提供的事件结构,在触发事件时填充字段内容并上报事件。设备端需要在监听到 Realtime SDK 的 onUserJoined 回调智能体进房后才能发送上行事件,可通过 Realtime SDK 的 sendUserMessage 发送,如果是嵌入式设备集成音视频,可通过 Realtime SDK 的 byte_rtc_rts_send_message 发送。详细信息可参考Realtime 上行事件
  • 下行事件:服务端下发给设备端的事件,可订阅 Realtime SDK的 onUserMessageReceived回调,如果是嵌入式设备集成音视频,可订阅 Realtime SDK 的 on_message_received回调,接收下行事件。应用程序需要解析下行事件,并根据业务需求进行下一步操作。详细信息可参考Realtime 下行事件

公共参数

智能语音信令事件的公共参数如下:

参数名称 类型 描述
id String 事件 ID,也就是事件的唯一标识。由客户端或服务端生成,在故障排查场景下用于定位具体的事件,便于排查问题。
event_type String 事件的类型。
data JSON 事件的详细信息,其中包含具体事件的业务字段。

信令事件的使用流程

信令事件的使用流程如下:

  1. 初始化。通过监听 session.created 事件,确认房间初始化完成。
  2. 发送请求。使用上行事件(如 conversation.message.create)向智能体发送消息。
  3. 处理响应。监听下行事件(如 conversation.message.delta)获取智能体的增量回复。
  4. 插件交互。在收到事件 conversation.chat.requires_action 后,执行插件操作并通过事件 conversation.chat.submit_tool_outputs 提交结果。
  5. 错误处理。通过监听 error 事件捕获和处理异常情况。

使用示例如下:

// 发送消息
let message = [
    "id": "event_1",
    "event_type": "conversation.message.create",
    "data": [
        "role": "user",
        "content_type": "text",
        "content": "讲一个笑话"
    ],
]

do {
    let jsonData = try JSONSerialization.data(withJSONObject: message)
    if let jsonString = String(data: jsonData, encoding: .utf8) {
        self.rtcRoom?.sendUserMessage(
            APIConfig.botId, message: jsonString,
            config: ByteRTCMessageConfig.reliableOrdered)
    }
} catch {
    print("Error creating JSON: \(error)")
}

// 发送消息
  NSDictionary *message = @{
    @"id" : @"event_1",
    @"event_type" : @"conversation.message.create",
    @"data" : @{
        @"role": @"user",
        @"content_type": @"text",
        @"content": @"讲一个笑话"
    }
  };

  NSError *error;
  NSData *jsonData = [NSJSONSerialization dataWithJSONObject:message
                                                     options:0
                                                       error:&error];

  if (!error) {
    NSString *jsonString = [[NSString alloc] initWithData:jsonData
                                                 encoding:NSUTF8StringEncoding];
    [self.rtcRoom sendUserMessage:API_BOT_ID
                          message:jsonString
                           config:ByteRTCMessageConfigReliableOrdered];
  }

信令事件的处理思路

在处理信令事件时,建议采用以下思路:

  • 监听所有事件:捕获客户端和服务端的所有交互,便于调试和监控。扣子编程推荐你监听所有的事件,你也可以根据实际的业务需求,监听不同类型的事件,例如客户端事件、服务端事件等。
  • 处理特定事件:根据业务需求,监听和处理特定的事件,例如连接成功、中断、断开、音频状态变化等。
  • 错误处理:捕获和处理错误事件,确保应用的稳定性和用户体验。

示例代码如下:

func rtcRoom(_ rtcRoom: ByteRTCRoom, onUserMessageReceived uid: String, message: String) {
    print("收到用户消息 - 用户ID: \(uid), 消息: \(message)")

    do {
        if let jsonData = message.data(using: .utf8) {
            let messageData = try JSONDecoder().decode(MessageData.self, from: jsonData)

            if messageData.event_type == "conversation.message.delta"
                || messageData.event_type == "conversation.message.completed"
            {
                let content = messageData.data?.content ?? ""
                self.addMessage(content, eventType: (messageData.event_type)!)
            }
        }
    } catch {
        print("JSON 解析错误: \(error)")
    }
}

// 监听所有事件
- (void)rtcRoom:(ByteRTCRoom *)rtcRoom
    onUserMessageReceived:(NSString *)uid
                  message:(NSString *)message {
  NSLog(@"收到用户消息 - 用户ID: %@, 消息: %@", uid, message);

  NSError *error;
  NSData *jsonData = [message dataUsingEncoding:NSUTF8StringEncoding];
  NSDictionary *messageDict = [NSJSONSerialization JSONObjectWithData:jsonData
                                                              options:0
                                                                error:&error];
}

常见问题

编译时提示 Sandbox: rsync.samba(xxxxx) deny(1)

Xcode 15 及以上版本如果提示该错误,请按以下步骤进行处理:

在 Xcode 中,选中项目,选择 TARGETS > 项目名称 > Build Settings,在 Build Options 区域,将 User Script Sandboxing 的值修改为 No

Image

编译时提示 No such module 'VolcEngineRTC'

请使用 iOS 真机进行测试,不支持使用模拟器运行。

Image

提示 doesn't match RTCDemo-Swift.app's iOS deployment target

Image

该错误可能是由于真机的 iOS 版本与 Xcode 的配置版本不一致导致的。解决方法如下:

在 Xcode 中,选中项目,选择 TARGETS > 项目名称 > General。在 Minimum Deployments 区域,将 iOS 版本号修改为与真机一致的版本。

Image