# Web、H5 与 WebView 使用 RTC SDK Web、移动 H5、Android WebView 和 iOS WKWebView 的会议核心使用同一个 `@akstream/rtc-web-sdk`。WebView 宿主仍要负责证书信任、相机/麦克风授权、前后台和需要的原生共享能力。正式服务地址使用可被设备信任的 HTTPS;`localhost` 的开发例外不能照搬到手机局域网地址。 ```mermaid flowchart LR A["业务后端给入会票据"] --> B["SDK join 与准入"] B --> C["远端 onRemoteTrack"] C --> D["页面 attachRemoteMedia"] D --> E["离会时 SDK leave 并移除视图"] ``` ## 先做只看入会 从同一批 SDK 交付物安装 Web 包。`serverUrl`、`roomId`、一次性 `accessTicket` 由你的业务后端在用户通过授权后返回,票据不要放在可分享的网页 URL。 交付方提供 npm tarball 时,在应用根目录执行 `npm install ./vendor/akstream-rtc-web-sdk.tgz`;使用源码包时保留包内 `src`、`dist` 和离线白板资源,不要只复制入口 JS。公共 npm 上同名包的版本号不能代替本次交付核对。 ```html
``` 下面的代码放在有 ESM 打包器的页面模块中,`meeting` 是业务后端返回的入会信息: ```js import { AkRtcWebClient } from '@akstream/rtc-web-sdk' const remote = document.getElementById('remote') const status = document.getElementById('meeting-status') const views = new Map() const client = new AkRtcWebClient(meeting.serverUrl, { onState: (state, detail) => { status.textContent = detail || state }, onRemoteTrack: ({ subscriptionId, track }) => { const element = document.createElement(track.kind === 'audio' ? 'audio' : 'video') element.autoplay = true if (element instanceof HTMLVideoElement) element.playsInline = true remote.append(element) views.set(subscriptionId, element) void client.attachRemoteMedia(subscriptionId, element) }, onRemoteTrackRemoved: id => { const element = views.get(id) if (element) { client.detachRemoteMedia(id, element); element.remove() } views.delete(id) } }) const joined = await client.join(meeting.roomId, meeting.accessTicket, { platform: 'pc-web' }) if (joined.admissionStatus === 'Waiting') status.textContent = '等待主持人准入' // 页面离开时调用,并移除自己创建的视图。 async function leaveMeeting() { await client.leave() for (const element of views.values()) element.remove() views.clear() } ``` 成功时,准入后能看到房间成员与远端发布;只看模式无需申请本地摄像头和麦克风。等只看成功后,再由用户点击开启采集:`startCameraAndMicrophone({audio:true,video:true})`,并把返回的流交给 `attachLocalPreview`。拒绝系统授权时保留只看能力,不要显示“已开麦”。 ## 从只看切到真实发布 页面放一个带用户手势的“开启摄像头和麦克风”按钮。点击时先判断 SDK 返回的实际会议能力和主持限制,再调用 `startCameraAndMicrophone({audio:true,video:true})`;将返回的 `MediaStream` 交给 `attachLocalPreview(localVideo, stream)`。播放器上的本地预览应静音,避免扬声器回授。随后请第二台设备确认确实收到了视频帧与声音;只看本机预览不足以证明 WHIP 已发布成功。 关闭摄像头和麦克风分别使用 `setTrackEnabled('CameraVideo', false)`、`setTrackEnabled('MicrophoneAudio', false)`。按钮文字以 `onLocalMediaState` 的实际状态更新,不要点击后自己翻转布尔值。对方的音频元素可能受浏览器自动播放策略限制;若看得到画面却无声,先要求用户点击页面完成播放激活,再检查接收统计、输出设备与音量。 屏幕共享只能从用户点击事件调用 `startScreenShare()`,并由用户在浏览器选择屏幕或窗口。摄像头、麦克风和屏幕是不同发布;停止共享只调用 `stopScreenShare()`。一个成员同时发两路视频时,每一路都要有独立视图和 `publicationId`。 ## 页面负责什么 - 每个远端发布用自己的 `publicationId` 区分摄像头和屏幕,用 `subscriptionId` 管视图绑定;同名成员不能把两路合成一张卡片。 - 静音、相机和共享状态以 SDK 回调的实际结果更新按钮。共享屏幕需要用户手势和系统选择器。 - 页面布局和视觉效果由业务应用决定;SDK 提供媒体、状态、录制数据与资源接口。录制状态可用 `getContinuousRecording()` 查询,并通过 `onContinuousRecordingChanged` 接收变更,但新接口需服务端与 SDK 同批更新。 - 离会、路由切换和页面销毁应走同一清理路径;重复创建客户端会造成多条会话。 Android WebView 需要宿主处理相机/麦克风权限请求并只授权用户实际允许的资源;iOS WKWebView 同样需要宿主处理媒体采集权限。原生宿主还负责前后台、音频路由和平台证书信任。Web SDK 不会绕过系统授权,也不能把局域网 HTTP 页面当成安全的正式采集环境。 Web Demo 展示了一种宿主写法,不是生产业务身份系统。需要下载会议录像与附件时,使用[独立档案授权](recording.md),不能复用入会票据。 公开的 `AkRtcWebClient` 方法、入参出参及全部重要事件见[Web/H5 函数参考](web-api.md);主持人控制 payload 见[会议动作](room-actions.md),会后档案方法见[档案 SDK](archive-api.md)。 ## 失败时从哪里查 | 现象 | 先检查 | 页面应怎样处理 | |---|---|---| | 入会返回 `Waiting` | 房间等候室与主持准入 | 只显示等待,不提前开启采集 | | 401 或票据已消费 | 是否复用了同一张入会票据 | 向业务后端取新票,不循环重放 | | 本机有预览,对方没视频 | 发布状态、WHIP/ICE、媒体节点和编码 | 保留本地预览,显示发布失败原因 | | 接收有画面没声音 | 浏览器播放限制、接收音频包、输出设备 | 给用户一个“点击开启声音”入口 | | WebView 只在真机失败 | 宿主证书、系统采集授权、原生音频路由 | 分别记录系统权限和 SDK 错误,避免归因混乱 |