AKStream.Next · 文档中心

RTC Web SDK for browsers, H5, and WebView

RTC Web SDK for browsers, H5, and WebView

Browser Web, mobile H5, Android WebView, and iOS WKWebView share @akstream/rtc-web-sdk for meeting logic. A WebView host still owns certificate trust, camera/microphone permission prompts, foreground/background behavior, and native screen sharing where available. Use HTTPS trusted by the real device; a localhost development exception does not extend to a phone's LAN address.

flowchart LR
    A["Backend provides join ticket"] --> B["SDK join and admission"]
    B --> C["Remote onRemoteTrack"]
    C --> D["Page calls attachRemoteMedia"]
    D --> E["SDK leave and view cleanup"]

Join receive-only first

Install the Web package from the same SDK delivery. Your backend returns serverUrl, roomId, and one-use accessTicket after business authorization. Do not place the ticket in a shareable page URL.

If your delivery contains an npm tarball, run npm install ./vendor/akstream-rtc-web-sdk.tgz from the application root. For a source package, retain its src, dist, and offline whiteboard resources rather than copying only the entry JS. A same-name public npm version is not proof of a matching delivery.

<div id="remote"></div>
<p id="meeting-status" role="status"></p>

The following runs in an ESM-bundled page. meeting is the join response from your backend:

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 = 'Waiting for host admission'

async function leaveMeeting() {
  await client.leave()
  for (const element of views.values()) element.remove()
  views.clear()
}

After admission, success means the client can see members and receive remote publications. Receive-only needs no local camera or microphone permission. Once this works, enable capture from a user gesture with startCameraAndMicrophone({audio:true,video:true}) and pass its stream to attachLocalPreview. Keep receive-only available if OS permission is denied.

Move from receive-only to real publishing

Put an Enable camera and microphone button behind a user gesture. Check current SDK capabilities and host restrictions, call startCameraAndMicrophone({audio:true,video:true}), then pass the returned MediaStream to attachLocalPreview(localVideo, stream). Mute the local preview element to prevent feedback. Ask a second device to confirm actual remote frames and sound; a local preview alone does not prove WHIP publishing worked.

Disable camera and microphone separately with setTrackEnabled('CameraVideo', false) and setTrackEnabled('MicrophoneAudio', false). Drive button labels from onLocalMediaState, not by flipping a local boolean after a click. Browsers may block remote audio autoplay: if video appears but sound is absent, first let the user activate playback with a page click, then inspect receive stats, output route, and volume.

Call startScreenShare() only from a user click and let the browser picker choose a screen or window. Camera, microphone, and screen are separate publications; stopScreenShare() stops only sharing. Two video publications from one member need separate views and publication IDs.

What the host page owns

  • Use publicationId to distinguish camera and screen, and subscriptionId to manage the view binding. A matching display name does not mean two streams are one card.
  • Update controls from SDK media-state callbacks. Screen sharing requires a user gesture and system picker.
  • Your app owns layout and appearance. The SDK provides media, status, recording data, and resource APIs. getContinuousRecording() and onContinuousRecordingChanged need a matching newer server and SDK build.
  • Route changes and teardown must release the same client. Creating duplicates leaves multiple sessions.

An Android WebView host must handle camera/microphone permission requests and approve only resources the user actually allowed. An iOS WKWebView host likewise owns media-capture decisions. Native hosts also manage foreground/background state, audio routing, and certificate trust. The Web SDK cannot bypass OS permissions or make a LAN HTTP page a secure production capture origin.

Web Demo shows one host implementation; it is not a production business identity system. Meeting recording and attachments require independent archive access, not a reused join ticket.

For every public AkRtcWebClient method, input/result and major callback, see Web/H5 methods. Host payloads are in room actions; post-meeting calls are in archive methods.

Where to look when it fails

Symptom First check Page response
Join returns Waiting Waiting-room policy and host admission Show waiting; do not start capture
401 or ticket consumed Whether the one-use ticket was replayed Ask the backend for a fresh ticket
Local preview but remote has no video Publish state, WHIP/ICE, media node, codec Keep local preview and show the publish error
Video arrives without sound Browser autoplay, audio packets, output route Offer a click to activate audio
Only physical WebView fails Host certificate, capture permissions, native routing Record OS and SDK errors separately