Go 客户端 API 说明
使用 Go 客户端 SDK 连接设备、接收解码或编码音视频帧、保存文件并收发消息。尚未安装时先看 Go 客户端 SDK 接入。自行部署 RTC 节点请使用服务端 Go SDK。
Client 与 Connection
client, err := tirtc.NewClient(tirtc.ClientOptions{
AppID: appID, AccessKeyID: accessKeyID, AccessKeySecret: accessKeySecret,
CacheDir: cacheDir, // 可写绝对路径
})
connection, err := client.NewConnection(tirtc.ConnOptions{
OnStateChanged: func(state tirtc.ConnState, err error) {},
})上述为签名示意;实际代码逐步检查 err,见下方完整示例。使用一对 Access Key 托管鉴权,同一进程一个 RTC Client。Connect(ctx, deviceID) 等待初次连接结果;成功返回后才订阅或发送,后续断线通过 OnStateChanged 通知。
| 操作 | 方法 |
|---|---|
| 连接 / 断开 / 状态 | Connect(ctx, deviceID) error、Disconnect() error、State() ConnState |
| 音频订阅 | SubscribeAudio(id) error、UnsubscribeAudio(id) error |
| 视频订阅与关键帧 | SubscribeVideo(id) error、UnsubscribeVideo(id) error、RequestVideoKeyframe(id) error |
| 命令 | SendCommand(commandID uint32, data []byte) error;ConnOptions.OnCommand 接收 |
| 流消息 | SendStreamMessage(id uint8, timestamp time.Duration, data []byte) error;ConnOptions.OnStreamMessage 接收 |
| 关闭 | Close() error |
消息接收函数在创建连接时传入:
OnCommand func(commandID uint32, data []byte)
OnStreamMessage func(streamID uint8, timestamp time.Duration, data []byte)Stream ID 为 0..15,设备端发送、Output 绑定和订阅必须使用同一编号;同方向音频和视频使用不同编号。示例 10 / 11 不是保留值。自定义命令字从 0x2001 起;流消息时间戳使用非负整毫秒,最大为 uint32 毫秒。
接收第一帧视频
将以下代码放入 cmd/app/main.go。环境中设置 TIRTC_APP_ID、TIRTC_ACCESS_KEY_ID、TIRTC_SECRET_KEY_ID、TIRTC_DEVICE_ID,设备发送视频流 11。按接入指南使用 tirtc-build 构建后运行;收到帧会输出尺寸,30 秒无帧则报错。
package main
import (
"context"
"errors"
"fmt"
"log"
"os"
"path/filepath"
"time"
tirtc "github.com/tangeai/tirtc-client-go/v2"
)
// 在业务协程释放;回调正在退出时只对 ErrInUse 做有界重试。
func closeResource(closeFn func() error) error {
for attempt := 0; attempt < 100; attempt++ {
err := closeFn()
if !errors.Is(err, tirtc.ErrInUse) { return err }
time.Sleep(10 * time.Millisecond)
}
return tirtc.ErrInUse
}
func receive() (result error) {
cache, err := filepath.Abs(".tirtc-cache")
if err != nil { return err }
client, err := tirtc.NewClient(tirtc.ClientOptions{
AppID: os.Getenv("TIRTC_APP_ID"),
AccessKeyID: os.Getenv("TIRTC_ACCESS_KEY_ID"),
AccessKeySecret: os.Getenv("TIRTC_SECRET_KEY_ID"), CacheDir: cache,
})
if err != nil { return err }
var conn *tirtc.Conn
var video *tirtc.VideoOutput
defer func() {
// 子对象未成功关闭时保留父对象,不继续销毁。
if video != nil {
if err := closeResource(video.Close); err != nil {
result = errors.Join(result, err); return
}
}
if conn != nil {
if err := closeResource(conn.Close); err != nil {
result = errors.Join(result, err); return
}
}
result = errors.Join(result, closeResource(client.Close))
}()
failures := make(chan error, 1)
report := func(err error) {
if err != nil { select { case failures <- err: default: } }
}
conn, err = client.NewConnection(tirtc.ConnOptions{
OnStateChanged: func(state tirtc.ConnState, err error) { report(err) },
})
if err != nil { return err }
first := make(chan tirtc.VideoFrame, 1)
video, err = tirtc.NewVideoOutput(tirtc.VideoOutputOptions{
OnFrame: func(frame tirtc.VideoFrame) {
select { case first <- frame: default: }
},
OnError: report,
})
if err != nil { return err }
if err = video.Attach(conn, 11); err != nil { return err }
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err = conn.Connect(ctx, os.Getenv("TIRTC_DEVICE_ID")); err != nil { return err }
if err = conn.SubscribeVideo(11); err != nil { return err }
if err = conn.RequestVideoKeyframe(11); err != nil { return err }
select {
case frame := <-first:
fmt.Printf("video: %dx%d\n", frame.Width, frame.Height)
return nil
case err := <-failures:
return err
case <-time.After(30 * time.Second):
return fmt.Errorf("30 秒内未收到视频帧")
}
}
func main() {
if err := receive(); err != nil { log.Fatal(err) }
}这是运行后退出的首帧示例。常驻应用清理失败时,应保留对象引用,在业务控制流程处理错误或重试;不要直接开始下一轮 Client。
Output 与 Frame
| 需要的数据 | 创建方法及回调参数 |
|---|---|
| 解码音频 | NewAudioOutput(AudioOutputOptions{OnFrame: func(AudioFrame)}) |
| 解码视频 | NewVideoOutput(VideoOutputOptions{OnFrame: func(VideoFrame)}) |
| 编码音频 | NewEncodedAudioOutput(EncodedAudioOutputOptions{OnFrame: func(EncodedAudioFrame)}) |
| 编码视频 | NewEncodedVideoOutput(EncodedVideoOutputOptions{OnFrame: func(EncodedVideoFrame)}) |
四类构造器均返回 (output, error);Output 独立创建,调用 Attach(conn, id) 绑定,Detach() 解绑,最终 Close()。可同时使用解码和编码 Output。回调应快速返回,业务队列保持有界。SDK 不创建播放窗口、不驱动扬声器,也不提供音视频输入。
AudioFrame.Data 和 VideoFrame.Planes[].Data 是 Go slice,可在回调后持有;视频按各 Plane 的 Stride 读取。结合帧的格式、尺寸、采样率、声道和时间戳解释数据,不要假设固定格式。
MP4 与 JPEG
conn.StartRecording(tirtc.StartRecordingOptions{VideoStreamID: 11, AudioStreamID: &audioID})返回任务与错误。可选音频 ID 必须与视频不同;先订阅并确认设备正在送流。task.Stop()返回RecordingFile与错误,文件有Path、Duration。- 解码视频 Output 的
TakeSnapshot()返回SnapshotFile与错误;需要已有可用视频帧。 - 用标准
os/io将临时文件复制到业务目录,成功后调用file.Delete()。先显式停止录制并领取文件,再关闭连接。
释放与错误处理
先停止录制,再关闭或解绑 Output,关闭 Connection,最后关闭 Client。仍有 Output 绑定或录制任务时,Conn.Close() 返回 ErrInUse;Client.Close() 会处理连接并解绑 Output,应用仍需最终关闭这些 Output。
不要在回调中关闭同一对象或其父对象;通知业务协程清理。Close() 遇到正在执行的回调可能返回 ErrInUse,使用有界重试,并保留失败时的对象引用。
通过 errors.Is(err, tirtc.ErrInUse) 判断错误类别,或用 errors.As 取得 *tirtc.Error 的 Code,再用 tirtc.ErrorName(code) 查错误名。排查时保留操作名、错误和 client.UploadLogs() 返回的日志 ID。