| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616 |
- import { useState, useEffect } from "react"
- import { Card, Button, Input, Form, message, Space, Typography, Divider, Alert } from "antd"
- import {
- InfoCircleOutlined,
- PlayCircleOutlined,
- StopOutlined,
- ReloadOutlined,
- EyeOutlined,
- EyeInvisibleOutlined,
- } from "@ant-design/icons"
- import { useNavigate } from "react-router-dom"
- import { genRandomUserId } from "@/common"
- import styles from "./index.module.scss"
- // 导入SDK核心模块
- import { SttSdk } from "../../../packages/stt-sdk-core/src"
- import type {
- ISttManagerAdapter,
- IRtmManagerAdapter,
- IRtcManagerAdapter,
- } from "../../../packages/stt-sdk-core/src/types"
- // 导入字幕显示组件
- import CaptionDisplay from "./components/caption-display"
- const { Title, Text } = Typography
- const SdkTestPage = () => {
- const nav = useNavigate()
- const [messageApi, contextHolder] = message.useMessage()
- const [form] = Form.useForm()
- const [sdk, setSdk] = useState<SttSdk | null>(null)
- const [sttManager, setSttManager] = useState<ISttManagerAdapter | null>(null)
- const [rtmManager, setRtmManager] = useState<IRtmManagerAdapter | null>(null)
- const [rtcManager, setRtcManager] = useState<IRtcManagerAdapter | null>(null)
- const [isSdkInitialized, setIsSdkInitialized] = useState(false)
- const [isSttManagerInitialized, setIsSttManagerInitialized] = useState(false)
- const [isRtcManagerJoined, setIsRtcManagerJoined] = useState(false)
- const [isTranscriptionActive, setIsTranscriptionActive] = useState(false)
- const [transcriptionStatus, setTranscriptionStatus] = useState<string>("idle")
- const [testResults, setTestResults] = useState<string[]>([])
- const [sttData, setSttData] = useState<any>({})
- const [captionVisible, setCaptionVisible] = useState(true)
- const [captionLanguages, setCaptionLanguages] = useState<string[]>(["live"])
- // 添加测试日志
- const addTestLog = (log: string) => {
- setTestResults((prev) => [...prev, `${new Date().toLocaleTimeString()}: ${log}`])
- }
- // 监听STT数据变化
- const onSttDataChanged = (data: any) => {
- console.log("[SDK Test] sttDataChanged:", data)
- setSttData(data)
- if (data.status === "start") {
- setIsTranscriptionActive(true)
- setTranscriptionStatus("active")
- addTestLog("📢 转录状态更新: 转录已开始")
- } else if (data.status === "end") {
- setIsTranscriptionActive(false)
- setTranscriptionStatus("stopped")
- addTestLog("📢 转录状态更新: 转录已停止")
- }
- // 如果有转录内容,记录到日志
- if (data.transcribe1 || data.transcribe2) {
- addTestLog(`📝 转录结果: ${data.transcribe1 || data.transcribe2}`)
- }
- // 如果有翻译内容,记录到日志
- if (data.translate1List?.length) {
- data.translate1List.forEach((text: string, index: number) => {
- if (text) {
- addTestLog(`🌐 翻译${index + 1}: ${text}`)
- }
- })
- }
- if (data.translate2List?.length) {
- data.translate2List.forEach((text: string, index: number) => {
- if (text) {
- addTestLog(`🌐 翻译${index + 2}: ${text}`)
- }
- })
- }
- }
- // 初始化SDK
- const initializeSdk = async (values: {
- appId: string
- certificate: string
- channel: string
- userName: string
- }) => {
- try {
- addTestLog("开始初始化SDK...")
- const newSdk = new SttSdk()
- await newSdk.initialize({
- appId: values.appId,
- certificate: values.certificate,
- logLevel: "info",
- })
- setSdk(newSdk)
- setIsSdkInitialized(true)
- addTestLog("✅ SDK初始化成功")
- // 创建管理器 - 先创建RTM管理器,然后创建STT管理器并传入RTM管理器
- const rtmManager = newSdk.createRtmManager()
- const sttManager = newSdk.createSttManager(rtmManager)
- const rtcManager = newSdk.createRtcManager()
- // 监听RTM管理器的事件
- rtmManager.on("sttDataChanged", onSttDataChanged)
- setSttManager(sttManager)
- setRtmManager(rtmManager)
- setRtcManager(rtcManager)
- addTestLog("✅ STT、RTM和RTC管理器创建成功")
- messageApi.success("SDK初始化成功")
- } catch (error) {
- addTestLog(`❌ SDK初始化失败: ${error}`)
- messageApi.error(`SDK初始化失败: ${error}`)
- }
- }
- // 初始化STT管理器(会自动加入RTM频道)
- const initializeSttManager = async () => {
- if (!sttManager || !form) return
- try {
- const values = form.getFieldsValue()
- addTestLog("开始初始化STT管理器...")
- await sttManager.init({
- userId: genRandomUserId(),
- channel: values.channel,
- userName: values.userName,
- })
- setIsSttManagerInitialized(true)
- addTestLog("✅ STT管理器初始化成功(已自动加入RTM频道)")
- messageApi.success("STT管理器初始化成功")
- } catch (error) {
- addTestLog(`❌ STT管理器初始化失败: ${error}`)
- messageApi.error(`STT管理器初始化失败: ${error}`)
- }
- }
- // 开始转录
- const startTranscription = async () => {
- if (!sttManager) return
- try {
- addTestLog("开始语音转录...")
- setTranscriptionStatus("starting")
- await sttManager.startTranscription({
- // languages: [{ source: "en-US", target: ["zh-CN"] }],
- languages: [{ source: "zh-CN" }],
- })
- setIsTranscriptionActive(true)
- setTranscriptionStatus("active")
- addTestLog("✅ 语音转录已开始")
- messageApi.success("语音转录已开始")
- } catch (error) {
- addTestLog(`❌ 语音转录启动失败: ${error}`)
- setTranscriptionStatus("error")
- messageApi.error(`语音转录启动失败: ${error}`)
- }
- }
- // 停止转录
- const stopTranscription = async () => {
- if (!sttManager) return
- try {
- addTestLog("停止语音转录...")
- setTranscriptionStatus("stopping")
- await sttManager.stopTranscription()
- setIsTranscriptionActive(false)
- setTranscriptionStatus("stopped")
- addTestLog("✅ 语音转录已停止")
- messageApi.success("语音转录已停止")
- } catch (error) {
- addTestLog(`❌ 语音转录停止失败: ${error}`)
- setTranscriptionStatus("error")
- messageApi.error(`语音转录停止失败: ${error}`)
- }
- }
- // 查询转录状态
- const queryTranscription = async () => {
- if (!sttManager) return
- try {
- addTestLog("查询转录状态...")
- const result = await sttManager.queryTranscription()
- addTestLog(`📊 转录状态查询结果: ${JSON.stringify(result)}`)
- messageApi.info("转录状态查询完成,查看日志了解详情")
- } catch (error) {
- addTestLog(`❌ 转录状态查询失败: ${error}`)
- messageApi.error(`转录状态查询失败: ${error}`)
- }
- }
- // 加入RTC频道
- const joinRtcChannel = async () => {
- if (!rtcManager) return
- try {
- const values = form.getFieldsValue()
- addTestLog("开始加入RTC频道...")
- await rtcManager.join({
- channel: values.channel,
- userId: genRandomUserId(),
- })
- setIsRtcManagerJoined(true)
- addTestLog("✅ RTC频道加入成功")
- messageApi.success("RTC频道加入成功")
- } catch (error) {
- addTestLog(`❌ RTC频道加入失败: ${error}`)
- messageApi.error(`RTC频道加入失败: ${error}`)
- }
- }
- // 创建音视频轨道
- const createRtcTracks = async () => {
- if (!rtcManager) return
- try {
- addTestLog("开始创建音视频轨道...")
- await rtcManager.createTracks()
- addTestLog("✅ 音视频轨道创建成功")
- messageApi.success("音视频轨道创建成功")
- } catch (error) {
- addTestLog(`❌ 音视频轨道创建失败: ${error}`)
- messageApi.error(`音视频轨道创建失败: ${error}`)
- }
- }
- // 发布音视频流
- const publishRtcStream = async () => {
- if (!rtcManager) return
- try {
- addTestLog("开始发布音视频流...")
- await rtcManager.publish()
- addTestLog("✅ 音视频流发布成功")
- messageApi.success("音视频流发布成功")
- } catch (error) {
- addTestLog(`❌ 音视频流发布失败: ${error}`)
- messageApi.error(`音视频流发布失败: ${error}`)
- }
- }
- // 清理资源
- const cleanup = async () => {
- try {
- addTestLog("开始清理资源...")
- if (isTranscriptionActive && sttManager) {
- await stopTranscription()
- }
- if (sttManager) {
- await sttManager.destroy()
- setSttManager(null)
- setIsSttManagerInitialized(false)
- }
- if (rtmManager) {
- rtmManager.off("sttDataChanged", onSttDataChanged)
- await rtmManager.destroy()
- setRtmManager(null)
- }
- if (sdk) {
- await sdk.destroy()
- setSdk(null)
- setIsSdkInitialized(false)
- }
- addTestLog("✅ 资源清理完成")
- setTranscriptionStatus("idle")
- messageApi.success("资源清理完成")
- } catch (error) {
- addTestLog(`❌ 资源清理失败: ${error}`)
- messageApi.error(`资源清理失败: ${error}`)
- }
- }
- // 切换字幕显示
- const toggleCaptionVisibility = () => {
- setCaptionVisible(!captionVisible)
- addTestLog(`📺 字幕显示: ${!captionVisible ? "开启" : "关闭"}`)
- }
- // 切换语言显示
- const toggleLanguage = (language: string) => {
- setCaptionLanguages((prev) => {
- if (prev.includes(language)) {
- return prev.filter((lang) => lang !== language)
- } else {
- return [...prev, language]
- }
- })
- addTestLog(`🌐 语言显示: ${captionLanguages.includes(language) ? "关闭" : "开启"} ${language}`)
- }
- // 返回主应用
- const goToMainApp = () => {
- nav("/home")
- }
- // 组件卸载时清理资源
- useEffect(() => {
- return () => {
- if (sdk) {
- cleanup()
- }
- }
- }, [sdk])
- return (
- <div className={styles.sdkTestPage}>
- {contextHolder}
- <div className={styles.header}>
- <Title level={2}>🎯 SDK 功能测试页面</Title>
- <Text type="secondary">测试 STT SDK 核心功能的集成和兼容性</Text>
- </div>
- <div className={styles.content}>
- <div className={styles.leftPanel}>
- <Card title="🔧 SDK 配置" className={styles.configCard}>
- <Form
- form={form}
- layout="vertical"
- initialValues={{
- appId: import.meta.env.VITE_AGORA_APP_ID || "",
- certificate: import.meta.env.VITE_AGORA_APP_CERTIFICATE || "",
- channel: `test-channel-${Date.now()}`,
- userName: `TestUser-${Date.now()}`,
- }}
- >
- <Form.Item
- label="App ID"
- name="appId"
- rules={[{ required: true, message: "请输入 App ID" }]}
- >
- <Input placeholder="请输入 Agora App ID" />
- </Form.Item>
- <Form.Item
- label="Certificate"
- name="certificate"
- rules={[{ required: true, message: "请输入 Certificate" }]}
- >
- <Input placeholder="请输入 Agora Certificate" />
- </Form.Item>
- <Form.Item
- label="频道名称"
- name="channel"
- rules={[{ required: true, message: "请输入频道名称" }]}
- >
- <Input placeholder="请输入频道名称" />
- </Form.Item>
- <Form.Item
- label="用户名称"
- name="userName"
- rules={[{ required: true, message: "请输入用户名称" }]}
- >
- <Input placeholder="请输入用户名称" />
- </Form.Item>
- <Form.Item>
- <Space>
- <Button
- type="primary"
- onClick={() => initializeSdk(form.getFieldsValue())}
- disabled={isSdkInitialized}
- >
- {isSdkInitialized ? "✅ 已初始化" : "初始化 SDK"}
- </Button>
- <Button onClick={cleanup} danger disabled={!isSdkInitialized}>
- 清理资源
- </Button>
- </Space>
- </Form.Item>
- </Form>
- </Card>
- <Card title="🚀 功能测试" className={styles.testCard}>
- <Space direction="vertical" style={{ width: "100%" }}>
- <Button
- onClick={initializeSttManager}
- disabled={!isSdkInitialized || isSttManagerInitialized}
- block
- >
- {isSttManagerInitialized ? "✅ STT管理器已初始化" : "初始化STT管理器"}
- </Button>
- <Divider />
- <Button
- type="primary"
- icon={<PlayCircleOutlined />}
- onClick={startTranscription}
- disabled={!isSttManagerInitialized || isTranscriptionActive}
- block
- >
- {isTranscriptionActive ? "转录进行中..." : "开始转录"}
- </Button>
- <Button
- danger
- icon={<StopOutlined />}
- onClick={stopTranscription}
- disabled={!isTranscriptionActive}
- block
- >
- 停止转录
- </Button>
- <Button
- icon={<ReloadOutlined />}
- onClick={queryTranscription}
- disabled={!isTranscriptionActive}
- block
- >
- 查询状态
- </Button>
- <Divider />
- <Button
- onClick={joinRtcChannel}
- disabled={!isSdkInitialized || isRtcManagerJoined}
- block
- >
- {isRtcManagerJoined ? "✅ RTC频道已加入" : "加入RTC频道"}
- </Button>
- <Button onClick={createRtcTracks} disabled={!isRtcManagerJoined} block>
- 创建音视频轨道
- </Button>
- <Button
- type="primary"
- onClick={publishRtcStream}
- disabled={!isRtcManagerJoined}
- block
- >
- 发布音视频流
- </Button>
- </Space>
- <Divider />
- <Alert
- message="转录状态"
- description={
- <div>
- <div>
- {transcriptionStatus === "idle"
- ? "等待开始转录"
- : transcriptionStatus === "starting"
- ? "正在启动转录..."
- : transcriptionStatus === "active"
- ? "转录进行中"
- : transcriptionStatus === "stopping"
- ? "正在停止转录..."
- : transcriptionStatus === "stopped"
- ? "转录已停止"
- : "状态异常"}
- </div>
- {sttData.status && (
- <div style={{ marginTop: 8, fontSize: "12px", color: "#666" }}>
- <div>任务ID: {sttData.taskId || "未设置"}</div>
- <div>
- 开始时间:{" "}
- {sttData.startTime
- ? new Date(sttData.startTime).toLocaleTimeString()
- : "未开始"}
- </div>
- <div>
- 持续时间: {sttData.duration ? `${sttData.duration / 1000}秒` : "未设置"}
- </div>
- </div>
- )}
- </div>
- }
- type={
- transcriptionStatus === "active"
- ? "success"
- : transcriptionStatus === "error"
- ? "error"
- : "info"
- }
- showIcon
- />
- </Card>
- </div>
- <div className={styles.rightPanel}>
- <Card title="📋 测试日志" className={styles.logCard}>
- <div className={styles.logContainer}>
- {testResults.length === 0 ? (
- <Text type="secondary">暂无测试日志,请开始测试...</Text>
- ) : (
- testResults.map((log, index) => (
- <div key={index} className={styles.logEntry}>
- {log}
- </div>
- ))
- )}
- </div>
- {testResults.length > 0 && (
- <Button onClick={() => setTestResults([])} size="small" style={{ marginTop: 16 }}>
- 清空日志
- </Button>
- )}
- </Card>
- <Card title="💡 使用说明" className={styles.infoCard}>
- <Space direction="vertical" style={{ width: "100%" }}>
- <Text type="secondary">
- <InfoCircleOutlined /> 测试步骤:
- </Text>
- <ol>
- <li>填写 App ID 和 Certificate</li>
- <li>点击"初始化 SDK"</li>
- <li>初始化 STT 管理器和加入 RTM 频道</li>
- <li>开始/停止转录测试</li>
- <li>加入 RTC 频道并测试音频传输</li>
- <li>查看测试日志了解详细结果</li>
- </ol>
- <Button type="link" onClick={goToMainApp}>
- 返回主应用
- </Button>
- </Space>
- </Card>
- <Card
- title={
- <Space>
- <span>📺 实时转录显示</span>
- <Button
- type="text"
- size="small"
- icon={captionVisible ? <EyeOutlined /> : <EyeInvisibleOutlined />}
- onClick={toggleCaptionVisibility}
- >
- {captionVisible ? "隐藏" : "显示"}
- </Button>
- </Space>
- }
- className={styles.captionCard}
- extra={
- <Space>
- <Button
- size="small"
- type={captionLanguages.includes("live") ? "primary" : "default"}
- onClick={() => toggleLanguage("live")}
- >
- 原文
- </Button>
- <Button
- size="small"
- type={captionLanguages.includes("translate1") ? "primary" : "default"}
- onClick={() => toggleLanguage("translate1")}
- >
- 翻译1
- </Button>
- <Button
- size="small"
- type={captionLanguages.includes("translate2") ? "primary" : "default"}
- onClick={() => toggleLanguage("translate2")}
- >
- 翻译2
- </Button>
- </Space>
- }
- >
- <CaptionDisplay
- visible={captionVisible}
- captionLanguages={captionLanguages}
- sttData={sttData}
- />
- </Card>
- </div>
- </div>
- </div>
- )
- }
- export default SdkTestPage
|