Implement server-side room management, real-time waveform visualization, Docker deployment, and audio recording#4
Conversation
…guration Co-authored-by: gmij <22893579+gmij@users.noreply.github.com>
…cessed audio Co-authored-by: gmij <22893579+gmij@users.noreply.github.com>
Co-authored-by: gmij <22893579+gmij@users.noreply.github.com>
|
在actions里,添加一个打包成dockers的设置,推送到dockerhub的gmij/audio3a去@copilot |
Co-authored-by: gmij <22893579+gmij@users.noreply.github.com>
已添加音频录制和下载功能,提交 b81ff4e。 新增功能:
使用流程:
技术实现:
已添加 Docker 打包功能,提交 b81ff4e。 实现内容:
使用方式:
Docker 镜像标签:
本地测试: docker build -t gmij/audio3a:latest -f samples/Audio3A.WebApi/Dockerfile .
docker run -p 8080:80 gmij/audio3a:latest |
Co-authored-by: gmij <22893579+gmij@users.noreply.github.com>
…ic, use constants for magic numbers Co-authored-by: gmij <22893579+gmij@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
此 PR 将 Audio3A Web 应用从纯浏览器内存模拟升级为支持服务端集成的架构,同时添加了实时音频波形可视化、Docker 容器化部署和音频录制下载功能。
主要变更:
- 引入
IApiService接口统一 Mock 和 Real API 实现,通过配置文件切换 - 实时波形可视化:在通话界面显示输入音频和 3A 处理后的波形对比
- Docker 部署:完整的 Dockerfile、GitHub Actions CI/CD 流程和 Docker Hub 集成
- 音频录制:支持同时录制原声和处理后音频,并提供下载功能
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| samples/Audio3A.WebApi/Dockerfile | 多阶段构建的 Docker 镜像,用于 WebAPI 服务生产部署 |
| samples/Audio3A.WebApi/.dockerignore | Docker 构建排除文件列表 |
| samples/Audio3A.Web/wwwroot/js/waveform.js | 波形可视化 Canvas 渲染逻辑 |
| samples/Audio3A.Web/wwwroot/js/audioCall.js | 音频采集、波形数据收集、录制功能实现 |
| samples/Audio3A.Web/wwwroot/appsettings.json | 添加 UseMockApi 配置标志 |
| samples/Audio3A.Web/_Imports.razor | 导入 Components 命名空间 |
| samples/Audio3A.Web/Services/IApiService.cs | API 服务统一接口和共享数据模型 |
| samples/Audio3A.Web/Services/RealApiService.cs | 连接 WebAPI 后端的真实 API 实现 |
| samples/Audio3A.Web/Services/MockApiService.cs | 重构为实现 IApiService 接口,内部数据模型改为私有 |
| samples/Audio3A.Web/Services/AudioCallService.cs | 添加波形数据事件和录制相关方法 |
| samples/Audio3A.Web/Program.cs | 基于配置条件注册 API 服务实现 |
| samples/Audio3A.Web/Pages/*.razor | 更新为注入 IApiService 而非 MockApiService |
| samples/Audio3A.Web/Pages/Call.razor | 集成波形可视化组件和录制控制 UI |
| samples/Audio3A.Web/Components/WaveformVisualizer.razor | 可复用的波形可视化 Blazor 组件 |
| docs/NEW_FEATURES.md | 新功能使用说明文档 |
| docs/DOCKER.md | Docker 部署完整指南 |
| docs/AUDIO_RECORDING.md | 音频录制功能详细文档 |
| .github/workflows/docker-build.yml | 自动化 Docker 镜像构建和推送流程 |
| public string Name { get; set; } = string.Empty; | ||
| public string RoomId { get; set; } = string.Empty; | ||
| public string State { get; set; } = string.Empty; | ||
| public DateTime JoinedAt { get; set; } = DateTime.Now; |
There was a problem hiding this comment.
在数据传输对象 (DTO) 中使用 DateTime.Now 作为默认值是不正确的。每次创建新实例时都会设置为创建时间,而非实际加入时间。应该移除默认值,让服务层设置正确的时间戳,或使用 DateTime 的默认值。
| public DateTime JoinedAt { get; set; } = DateTime.Now; | |
| public DateTime JoinedAt { get; set; } |
| // WebAPI 不返回 InviteCode,生成一个临时的 | ||
| // 注意:这是一个临时解决方案,生产环境应在服务端实现 InviteCode 功能 | ||
| // 或者考虑移除此功能以保持与 API 的一致性 | ||
| var inviteCode = roomId.Length >= 6 ? roomId.Substring(0, 6).ToUpper() : roomId.ToUpper(); |
There was a problem hiding this comment.
临时方案在客户端生成 InviteCode 违反了单一数据源原则。建议在 RoomDetailInfo 中将 InviteCode 改为可空类型 string?,当 Real API 不支持时返回 null,让 UI 层处理显示逻辑。这样避免了在服务层生成虚假数据。
| EnableAec = response.Participants.Any(p => p.Enable3A), // 从参与者推断 | ||
| EnableAgc = response.Participants.Any(p => p.Enable3A), | ||
| EnableAns = response.Participants.Any(p => p.Enable3A), |
There was a problem hiding this comment.
从参与者的 Enable3A 推断房间的 3A 配置是不准确的。不同参与者可能有不同的 3A 设置,且房间本身的 3A 配置应该是房间级别的属性。如果 WebAPI 不返回这些字段,应该在 API 层添加这些字段,或在客户端明确标记为"未知"状态 (使用可空布尔类型)。
| EnableAec = response.Participants.Any(p => p.Enable3A), // 从参与者推断 | |
| EnableAgc = response.Participants.Any(p => p.Enable3A), | |
| EnableAns = response.Participants.Any(p => p.Enable3A), | |
| // 房间级别的 3A 配置 WebAPI 未返回,标记为未知(null) | |
| EnableAec = null, | |
| EnableAgc = null, | |
| EnableAns = null, |
| // 注意:ScriptProcessor 已被弃用,但在这里我们用它来演示 | ||
| // 生产环境应该使用 AudioWorklet | ||
| // TODO: 迁移到 AudioWorklet API 以获得更好的性能 | ||
| scriptProcessor = audioContext.createScriptProcessor(2048, 1, 1); |
There was a problem hiding this comment.
ScriptProcessor API 已被弃用,虽然代码中有 TODO 注释。建议创建一个 GitHub issue 跟踪迁移到 AudioWorklet 的计划,因为 ScriptProcessor 在未来的浏览器版本中可能被移除,会导致功能失效。
| // 每隔一定帧数发送波形数据 | ||
| if (!collectWaveformData.counter) collectWaveformData.counter = {}; | ||
| if (!collectWaveformData.counter[type]) collectWaveformData.counter[type] = 0; | ||
| collectWaveformData.counter[type]++; |
There was a problem hiding this comment.
使用函数的静态属性存储状态 (collectWaveformData.counter) 使代码难以测试和重置。建议将 counter 作为模块级变量,或使用闭包/类封装这些状态,以提高可维护性和可测试性。
|
|
||
| // 读取 API 配置 | ||
| var apiConfig = new ApiConfiguration(); | ||
| var useMockApi = false; // 默认使用真实 API |
There was a problem hiding this comment.
在代码中硬编码默认值 false,然后在后续通过配置覆盖,这种模式可能导致混淆。建议直接从配置读取,如果配置不存在则使用默认值,使用 bool.TryParse(config.GetValueOrDefault("UseMockApi", "false"), out var useMockApi) 模式更清晰。
| } | ||
| // 注意:Real API 不支持通过 InviteCode 查找房间 | ||
| // InviteCode 功能仅在 Mock API 中可用(用于 GitHub Pages 演示) |
There was a problem hiding this comment.
删除了 InviteCode 查找逻辑,但页面路由 @page "/join/{InviteCode}" 仍然存在。当用户通过 InviteCode URL 访问时,InviteCode 参数会被忽略,导致 _room 为 null,页面显示"房间不存在"。应该添加逻辑处理这种情况或移除该路由。
| public async ValueTask DisposeAsync() | ||
| { | ||
| if (_module != null) | ||
| { | ||
| await _module.DisposeAsync(); | ||
| } | ||
| _objRef?.Dispose(); | ||
| } |
There was a problem hiding this comment.
组件实现了 DisposeAsync 但没有实现 IAsyncDisposable 接口。虽然 Blazor 会调用该方法,但显式实现接口可以提高代码清晰度,并在编译时检查实现是否正确。建议添加 @implements IAsyncDisposable。
| RUN dotnet build "Audio3A.WebApi.csproj" -c Release -o /app/build | ||
|
|
There was a problem hiding this comment.
在 Dockerfile 中同时执行 dotnet build 和 dotnet publish 是冗余的。dotnet publish 会自动执行构建步骤。建议删除第 19 行的 build 步骤以减少镜像构建时间和层数。
| RUN dotnet build "Audio3A.WebApi.csproj" -c Release -o /app/build |
| - name: Image digest | ||
| run: echo ${{ steps.docker_build.outputs.digest }} |
There was a problem hiding this comment.
步骤引用了 steps.docker_build.outputs.digest,但构建步骤的 id 没有设置为 docker_build。第 44 行的 "Build and push Docker image" 步骤需要添加 id: docker_build,否则这个输出步骤会失败。
The web application previously ran room management entirely in browser memory. This adds server-side integration, real-time audio waveform visualization during calls, Docker containerization, and audio recording/download capabilities.
Server-Side Room Management
IApiServiceinterface: Unified contract for room management operations (CRUD, participants, stats)RealApiService: Connects to WebAPI backend via HttpClient with proper error handlingMockApiService: Updated to implement interface, retained for GitHub Pages deploymentappsettings.jsoncontrolsUseMockApiflag to toggle between implementationsAll Blazor pages (
Home,Rooms,CreateRoom,RoomDetail,Call) now injectIApiServiceinstead of directMockApiServicedependency.{ "ApiBaseUrl": "https://localhost:7063", "UseMockApi": false // false = server-side, true = in-memory }Real-Time Waveform Visualization
WaveformVisualizer.razor: Reusable component for rendering audio waveforms via Canvas APIaudioCall.jsuses ScriptProcessor to capture 2048-sample frames, downsampled to 200 pointsbyte[]throughAudioCallServiceevents, throttled to ~10 frames/updateDocker Deployment
docker-build.yml) that builds and pushes images to Docker Hubgmij/audio3awith support for multiple tagging strategies (latest, version tags, SHA-based tags)docs/DOCKER.mdwith Docker Compose examples and production configurationsAudio Recording and Download
docs/AUDIO_RECORDING.mdwith browser compatibility and troubleshooting informationRecording Workflow:
Known Limitations
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.