通过实时字幕功能( startSubtitle )将房间内指定用户的语音转为字幕后,字幕数据会以二进制消息格式返回。你可以解析返回的字幕数据,进一步处理或存储,比如实时显示在前端。本文将介绍如何解析字幕,包括字幕回调格式以及字幕解析的示例代码。
返回的字幕回调格式如下:
参数名 | 类型 | 描述 |
---|---|---|
message | String | Base 64 编码的二进制消息内容。格式参看二进制消息格式。 |
signature | String | 鉴权签名。可与startSubtitle 接口中传入的signature 字段值进行对比以进行鉴权验证。 |
二进制消息格式如下:
参数名 | 类型 | 描述 |
---|---|---|
magic number | binary | 消息格式,固定为 subc 。 |
length | binary | 字幕消息长度,单位为 bytes。存放方式为大端序。 |
subtitle_message | binary | 字幕消息详细信息。格式参看subtitle_message 格式。 |
subtitle_message 格式:
参数名 | 类型 | 描述 |
---|---|---|
type | String | 消息格式,固定为 subtitle 。 |
data | data | 字幕详细信息。 |
data
参数名 | 类型 | 描述 |
---|---|---|
text | String | 字幕文本。 |
language | String | 字幕语言。 |
userId | String | 说话人 ID。 |
sequence | Int | 字幕序号。 |
definite | Boolean | 字幕是否为完整的一句话。
|
paragraph | Boolean | 字幕是否为一段完整的文本。
|
你可以参考以下示例代码对回调信息中的message
内容进行解析。
const ( subtitleHeader = "subc" exampleSignature = "example_signature" ) type RtsMessage struct { Message string `json:"message"` Signature string `json:"signature"` } type subc struct { Type string `json:"type"` Data []Data `json:"data"` } type Data struct { Definite bool `json:"definite"` Paragraph bool `json:"paragraph"` Language string `json:"language"` Sequence int `json:"sequence"` Text string `json:"text"` UserID string `json:"userId"` } func HandleSubtitle(c *gin.Context) { msg := &RtsMessage{} if err := c.BindJSON(&msg); err != nil { fmt.Printf("BindJson failed,err:%v\n", err) return } if msg.Signature != exampleSignature { fmt.Printf("Signature not match\n") return } subc, err := Unpack(msg.Message) if err != nil { fmt.Printf("Unpack failed,err:%v\n", err) return } fmt.Println(subc) //业务逻辑 c.String(200, "ok") } func Unpack(msg string) (*subc, error) { data, err := base64.StdEncoding.DecodeString(msg) if err != nil { return nil, fmt.Errorf("DecodeString failed,err:%v", err) } if len(data) < 8 { return nil, fmt.Errorf("Data invalid") } dataHeader := string(data[:4]) if dataHeader != subtitleHeader { return nil, fmt.Errorf("Header not match") } dataSize := binary.BigEndian.Uint32(data[4:8]) if dataSize+8 != uint32(len(data)) { return nil, fmt.Errorf("Size not match") } subData := data[8:] subc := &subc{} err = json.Unmarshal(subData, subc) if err != nil { return nil, fmt.Errorf("Unmarshal failed,err:%v\n", err) } return subc, nil } func main() { r := gin.Default() r.POST("/example_domain/vertc/subtitle", HandleSubtitle) r.Run() }
参数名 | 类型 | 描述 |
---|---|---|
uid | String | 消息发送者 ID。 |
message | String | Base 64 编码的二进制消息内容。与服务端返回二进制消息格式相同,详细参看二进制消息格式。 |
你可以参考以下示例代码对回调信息中的 message
内容进行解析。
//定义结构体 struct SubtitleMsgData { bool definite; std::string language; bool paragraph; int sequence; std::string text; std::string userId; }; //回调事件 void onRoomBinaryMessageReceived(const char* uid, int size, const uint8_t* message) { std::string subtitles; bool ret = Unpack(message, size, subtitles); if(ret) { ParseData(subtitles); } } //拆包校验 bool Unpack(const uint8_t *message, int size, std::string& subtitles) { int kSubtitleHeaderSize = 8; if(size < kSubtitleHeaderSize) { return false; } // magic number "subc" if(static_cast<uint32_t>((static_cast<uint32_t>(message[0]) << 24) | (static_cast<uint32_t>(message[1]) << 16) | (static_cast<uint32_t>(message[2]) << 8) | static_cast<uint32_t>(message[3])) != 0x73756276U) { return false; } uint32_t length = static_cast<uint32_t>((static_cast<uint32_t>(message[4]) << 24) | (static_cast<uint32_t>(message[5]) << 16) | (static_cast<uint32_t>(message[6]) << 8) | static_cast<uint32_t>(message[7])); if(size - kSubtitleHeaderSize != length) { return false; } if(length) { subtitles.assign((char*)message + kSubtitleHeaderSize, length); } else { subtitles = ""; } return true; } //解析 void ParseData(const std::string& msg) { // 解析 JSON 字符串 nlohmann::json json_data = nlohmann::json::parse(subtitles); // 存储解析后的数据 std::vector<SubtitleMsgData> subtitles; // 遍历 JSON 数据并填充结构体 for (const auto& item : json_data["data"]) { SubtitleMsgData subData; subData.definite = item["definite"]; subData.language = item["language"]; subData.paragraph = item["paragraph"]; subData.sequence = item["sequence"]; subData.text = item["text"]; subData.userId = item["userId"]; subtitles.push_back(subData); } }