一、功能描述
注意:说明:
二、V2TIMStreamElem 属性
流式消息的 Elem 类型为 V2TIM_ELEM_TYPE_STREAM(值为 11),对应的 Elem 类为 V2TIMStreamElem。
V2TIMStreamElem 属性说明如下:
| 属性 | 含义 | 说明 |
| markdown | 流式消息文本内容 | 随着分片到达逐步累加的文本内容,只读。 |
| data | 流式消息二进制数据 | 随着分片到达逐步累加的二进制数据,只读。 |
| isStreamEnded | 流式消息是否结束 | 为 YES/true 时表示服务端已完成所有内容推送。 |
注意:说明:
三、添加高级消息监听
流式消息通过高级消息监听器 V2TIMAdvancedMsgListener 接收和更新,需要事先调用 addAdvancedMsgListener 添加监听。
示例代码如下:
JavaSwiftObjective-CC++ArkTSV2TIMManager.getMessageManager().addAdvancedMsgListener(new V2TIMAdvancedMsgListener() { @Override public void onRecvNewMessage(V2TIMMessage msg) { // 收到新消息 } @Override public void onRecvMessageModified(V2TIMMessage msg) { // 消息内容被修改(流式消息内容更新时触发) }});V2TIMManager.shared.addAdvancedMsgListener(listener: self)[[V2TIMManager sharedInstance] addAdvancedMsgListener:self];V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(this);import { V2TIMManager, V2TIMAdvancedMsgListener, V2TIMMessage } from '@tencentcloud/imsdk';const advancedMsgListener: V2TIMAdvancedMsgListener = { onRecvNewMessage: (msg: V2TIMMessage) => { // 收到新消息 }, onRecvMessageModified: (msg: V2TIMMessage) => { // 消息内容被修改(流式消息内容更新时触发) }};V2TIMManager.getMessageManager().addAdvancedMsgListener(advancedMsgListener);
四、接收流式消息
当收到一条新的流式消息时,会通过 onRecvNewMessage 回调通知。此时消息中的 streamElem 内容可能还不完整(isStreamEnded 为 NO/false)。
示例代码如下:
JavaSwiftObjective-CC++ArkTS@Overridepublic void onRecvNewMessage(V2TIMMessage msg) { if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_STREAM) { V2TIMStreamElem streamElem = msg.getStreamElem(); // 在 UI 上展示消息,后续内容会通过 onRecvMessageModified 持续更新 }}func onRecvNewMessage(_ msg: V2TIMMessage) { if msg.elemType == .ELEM_TYPE_STREAM { if let streamElem = msg.streamElem { // 在 UI 上展示消息,后续内容会通过 onRecvMessageModified 持续更新 } }}- (void)onRecvNewMessage:(V2TIMMessage *)msg { if (msg.elemType == V2TIM_ELEM_TYPE_STREAM) { V2TIMStreamElem *streamElem = msg.streamElem; // 在 UI 上展示消息,后续内容会通过 onRecvMessageModified 持续更新 }}void OnRecvNewMessage(const V2TIMMessage &message) override { if (message.elemList.Size() > 0) { V2TIMElem *elem = message.elemList[0]; if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_STREAM) { auto *streamElem = static_cast<V2TIMStreamElem *>(elem); // 在 UI 上展示消息,后续内容会通过 OnRecvMessageModified 持续更新 } }}const advancedMsgListener: V2TIMAdvancedMsgListener = { onRecvNewMessage: (msg: V2TIMMessage) => { if (msg.elemType === V2TIMElemType.V2TIM_ELEM_TYPE_STREAM) { const streamElem = msg.streamElem; // 在 UI 上展示消息,后续内容会通过 onRecvMessageModified 持续更新 } }};
五、监听流式消息内容更新
SDK 收到新的分片数据后,会自动将内容累加到 streamElem 中,并通过 onRecvMessageModified 回调通知上层。客户端只需在该回调中读取最新的 markdown / data 内容并刷新 UI 即可。
注意:说明:
示例代码如下:
JavaSwiftObjective-CC++ArkTS@Overridepublic void onRecvMessageModified(V2TIMMessage msg) { if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_STREAM) { V2TIMStreamElem streamElem = msg.getStreamElem(); // 更新 UI 上展示的内容 updateMessageUI(msg.getMsgID(), streamElem.getMarkdown()); if (streamElem.isStreamEnded()) { // 流式消息已结束,可以做最终处理 } }}func onRecvMessageModified(_ msg: V2TIMMessage) { if msg.elemType == .ELEM_TYPE_STREAM { if let streamElem = msg.streamElem { // 更新 UI 上展示的内容 updateMessageUI(msgID: msg.msgID, content: streamElem.markdown) if streamElem.isStreamEnded { // 流式消息已结束,可以做最终处理 } } }}- (void)onRecvMessageModified:(V2TIMMessage *)msg { if (msg.elemType == V2TIM_ELEM_TYPE_STREAM) { V2TIMStreamElem *streamElem = msg.streamElem; // 更新 UI 上展示的内容 [self updateMessageUI:msg.msgID withContent:streamElem.markdown]; if (streamElem.isStreamEnded) { // 流式消息已结束,可以做最终处理 } }}void OnRecvMessageModified(const V2TIMMessage &message) override { if (message.elemList.Size() > 0) { V2TIMElem *elem = message.elemList[0]; if (elem->elemType == V2TIMElemType::V2TIM_ELEM_TYPE_STREAM) { auto *streamElem = static_cast<V2TIMStreamElem *>(elem); // 更新 UI 上展示的内容 // streamElem->markdown 为当前已累加的完整文本 if (streamElem->isStreamEnded) { // 流式消息已结束,可以做最终处理 } } }}const advancedMsgListener: V2TIMAdvancedMsgListener = { onRecvMessageModified: (msg: V2TIMMessage) => { if (msg.elemType === V2TIMElemType.V2TIM_ELEM_TYPE_STREAM) { const streamElem = msg.streamElem; if (!streamElem) { return; } // 更新 UI 上展示的内容 updateMessageUI(msg.msgID, streamElem.markdown); if (streamElem.isStreamEnded) { // 流式消息已结束,可以做最终处理 } } }};
六、历史消息中的流式消息
当拉取历史消息时:
·若消息中包含未完成的流式消息,SDK 会自动恢复拉流操作,并通过 onRecvMessageModified 回调继续更新内容,客户端无需额外处理。
·若消息中的流式消息已全部拉取完毕,拉取历史消息后可直接展示完整内容,且不会再次触发 onRecvMessageModified 回调。
七、V2TIMTTSVoiceFormat 属性
流式消息转语音前,可设置语音格式,对应类为 V2TIMTTSVoiceFormat,属性说明如下:
| 属性 | 含义 | 说明 |
| voiceID | 音色 ID | 可选。参考文档 TTS 精品音色,选择合适的 voice_id。 |
| speed | 语速 | 可选。在 [0.5, 2.0] 之间取值,默认 1.0。 |
八、启动 TTS
startTTS 用于对当前流式消息发起实时 TTS 语音合成。调用成功表示 TTS 请求已成功发起,合成后的音频分片会通过高级消息监听器的 onRecvTTSResponse 回调返回。
JavaSwiftObjective-CC++ArkTSV2TIMStreamElem streamElem = message.getStreamElem();if (streamElem == null) { return;}V2TIMTTSVoiceFormat voiceFormat = new V2TIMTTSVoiceFormat();voiceFormat.setVoiceID("VOICE_ID");voiceFormat.setSpeed(1.0f);streamElem.startTTS(voiceFormat, new V2TIMCallback() { @Override public void onSuccess() {} @Override public void onError(int code, String desc) {}});guard let streamElem = message.streamElem else { return}let voiceFormat = V2TIMTTSVoiceFormat()voiceFormat.voiceID = "VOICE_ID"voiceFormat.speed = 1.0streamElem.startTTS(voiceFormat: voiceFormat, succ: { // TTS 请求发起成功。}, fail: { code, desc in // TTS 请求发起失败。})V2TIMStreamElem *streamElem = message.streamElem;if (streamElem == nil) { return;}V2TIMTTSVoiceFormat *voiceFormat = [[V2TIMTTSVoiceFormat alloc] init];voiceFormat.voiceID = @"VOICE_ID";voiceFormat.speed = 1.0;[streamElem startTTS:voiceFormat succ:^{ // TTS 请求发起成功。} fail:^(int code, NSString *desc) { // TTS 请求发起失败。}];class TTSCallback : public V2TIMCallback {public: void OnSuccess() override {} void OnError(int errorCode, const V2TIMString &errorMessage) override {}};V2TIMString msgID = "MSG_ID";V2TIMString streamMsgID = "STREAM_MSG_ID";V2TIMTTSVoiceFormat voiceFormat;voiceFormat.voiceID = "VOICE_ID";voiceFormat.speed = 1.0f;TTSCallback callback;V2TIMManager::GetInstance()->GetMessageManager()->StartTTS( msgID, streamMsgID, 0, voiceFormat, &callback);import { V2TIMManager, V2TIMTTSVoiceFormat } from '@tencentcloud/imsdk';import { BusinessError } from '@kit.BasicServicesKit';const voiceFormat = new V2TIMTTSVoiceFormat();voiceFormat.voiceID = 'VOICE_ID';voiceFormat.speed = 1.0;V2TIMManager.getMessageManager() .startTTS(message.msgID, streamMsgID, 0, voiceFormat) .then(() => { // TTS 请求发起成功。 }) .catch((error: BusinessError) => { // TTS 请求发起失败。 });
九、停止 TTS
stopTTS 用于停止当前 TTS 语音合成任务。停止后,业务侧应同步停止本地解码和播放流程。
JavaSwiftObjective-CC++ArkTSV2TIMStreamElem streamElem = message.getStreamElem();if (streamElem == null) { return;}streamElem.stopTTS(new V2TIMCallback() { @Override public void onSuccess() {} @Override public void onError(int code, String desc) {}});guard let streamElem = message.streamElem else { return}streamElem.stopTTS(succ: { // TTS 停止成功。}, fail: { code, desc in // TTS 停止失败。})V2TIMStreamElem *streamElem = message.streamElem;if (streamElem == nil) { return;}[streamElem stopTTS:^{ // TTS 停止成功。} fail:^(int code, NSString *desc) { // TTS 停止失败。}];class TTSCallback : public V2TIMCallback {public: void OnSuccess() override {} void OnError(int errorCode, const V2TIMString &errorMessage) override {}};V2TIMString msgID = "MSG_ID";V2TIMString streamMsgID = "STREAM_MSG_ID";TTSCallback callback;V2TIMManager::GetInstance()->GetMessageManager()->StopTTS( msgID, streamMsgID, &callback);import { V2TIMManager } from '@tencentcloud/imsdk';import { BusinessError } from '@kit.BasicServicesKit';V2TIMManager.getMessageManager() .stopTTS(message.msgID, streamMsgID) .then(() => { // TTS 停止成功。 }) .catch((error: BusinessError) => { // TTS 停止失败。 });
十、接收 TTS 语音合成音频分片
onRecvTTSResponse 用于接收 TTS 语音合成音频分片。每次回调只携带当前 chunk 的 Opus 音频分片列表,音频格式为 Opus / 16K / Mono,不是完整拼接结果。onRecvTTSResponse 通过高级消息监听器 V2TIMAdvancedMsgListener 接收和更新,需要事先调用 addAdvancedMsgListener 添加监听。
JavaSwiftObjective-CC++ArkTSV2TIMManager.getMessageManager().addAdvancedMsgListener(new V2TIMAdvancedMsgListener() { @Override public void onRecvTTSResponse(V2TIMTTSResponse response) { if (response == null || !message.getMsgID().equals(response.getMsgID())) { return; } List<byte[]> audioDataList = response.getAudioDataList(); boolean isFinished = response.isFinished(); // 业务侧在这里解码并播放 audioDataList。 }});V2TIMManager.sharedInstance().addAdvancedMsgListener(listener: self)func onRecvTTSResponse(response: V2TIMTTSResponse) { guard response.msgID == message.msgID else { return } let audioDataList = response.audioDataList ?? [] let isFinished = response.isFinished // 业务侧在这里解码并播放 audioDataList。}[[V2TIMManager sharedInstance] addAdvancedMsgListener:self];- (void)onRecvTTSResponse:(V2TIMTTSResponse *)response { if (![response.msgID isEqualToString:message.msgID]) { return; } NSArray<NSData *> *audioDataList = response.audioDataList; BOOL isFinished = response.isFinished; // 业务侧在这里解码并播放 audioDataList。}class TTSListener : public V2TIMAdvancedMsgListener {public: explicit TTSListener(const V2TIMString &targetMsgID) : targetMsgID_(targetMsgID) {} void OnRecvTTSResponse(const V2TIMTTSResponse &response) override { if (response.msgID != targetMsgID_) { return; } V2TIMBufferVector audioDataList = response.audioDataList; bool isFinished = response.isFinished; // 业务侧在这里解码并播放 audioDataList。 }private: V2TIMString targetMsgID_;};TTSListener listener("MSG_ID");V2TIMManager::GetInstance()->GetMessageManager()->AddAdvancedMsgListener(&listener);import { V2TIMManager, V2TIMAdvancedMsgListener, V2TIMTTSResponse } from '@tencentcloud/imsdk';const advancedMsgListener: V2TIMAdvancedMsgListener = { onRecvTTSResponse: (response: V2TIMTTSResponse) => { if (response.msgID !== message.msgID) { return; } const audioDataList: ArrayBuffer[] = response.audioDataList; const isFinished: boolean = response.isFinished; // 业务侧在这里解码并播放 audioDataList。 }};V2TIMManager.getMessageManager().addAdvancedMsgListener(advancedMsgListener);