This example builds on the basic WHIPClient publish flow and focuses on how audio/video UI selections become concrete media constraints and an acquired MediaStream.
At a high level:
- configure capture settings in
r5-publish-settings(including advanced audio options) - generate a requested
MediaStreamConstraintsobject - acquire media with
navigator.mediaDevices.getUserMedia(...) - publish with
WHIPClient.initWithStream(...) - compare requested constraints vs applied track settings in the UI
- WHIP publishing with explicit media constraint control
- advanced microphone constraint handling (
sampleRate,sampleSize,channelCount,echoCancellation,noiseSuppression,autoGainControl) - resolution/device/publish option controls that feed capture + publish setup
- side-by-side visibility of:
- what was requested
- what the browser/device actually applied
The flow is:
r5-publish-settingsreads current UI state (device picks, toggles, resolution, advanced audio fields).buildMediaConstraints()composes aMediaStreamConstraintsobject.refreshStream()callsacquireUserMediaStream()which runsgetUserMedia(constraints).- On success, the new stream is attached to preview and emitted via
publish-settings-updated. startPublish()retrieves that stream (refreshStream()return value) and callsWHIPClient.initWithStream(config, mediaStream).
The publisher is initialized with a stream that already reflects your selected constraints, rather than relying on a default SDK-captured stream.
This example intentionally teaches a key WebRTC concept: requested constraints are hints/requirements, but the final track settings may differ.
The Media Summary panel shows both:
- Requested: built from your current form values (
buildMediaConstraints()) - Applied (track.getSettings): read from the acquired audio/video tracks (
readAppliedTrackSettings())
So if a browser or device cannot fully honor a request (for example a specific sample rate), the panel makes that visible immediately.
The same summary is also logged before publish so you can inspect it in the Log panel during test runs.
The snippet below is intentionally simplified for learning and does not include every helper used in the testbed:
// If integrating as a script dependency from a CDN:
const sdk = window.red5prosdk
const { WHIPClient } = sdk
// OR, If integrating as a module from NPM install:
// import { WHIPClient } from 'red5pro-webrtc-sdk'
const requestedConstraints: MediaStreamConstraints = {
video: { width: { ideal: 1280 }, height: { ideal: 720 } },
audio: {
sampleRate: 48000,
sampleSize: 16,
channelCount: 2,
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true,
},
}
const mediaStream = await navigator.mediaDevices.getUserMedia(requestedConstraints)
const audioSettings = mediaStream.getAudioTracks()[0]?.getSettings()
const videoSettings = mediaStream.getVideoTracks()[0]?.getSettings()
console.log('Requested:', requestedConstraints)
console.log('Applied audio:', audioSettings)
console.log('Applied video:', videoSettings)
const publisher = new WHIPClient()
await publisher.initWithStream(
{
endpoint,
streamName,
mediaElementId: 'publisher-video',
connectionParams,
streamMode: 'live',
},
mediaStream
)
await publisher.publish()Use this sequence in your own webapp:
- Build a publish form for audio/video toggles, device selections, and advanced audio fields.
- Generate constraints from form state every time settings change.
- Refresh preview stream with
getUserMediaso users can validate before publish. - Read
track.getSettings()from the acquired stream. - Render a "Requested vs Applied" panel so differences are transparent.
- Pass the acquired stream into
initWithStream(...)and publish.
Media constraints are independent from deployment mode. Endpoint and connection setup follows the standard WHIP pattern.
endpointtargets the server directly (origin).connectionParamsis optional unless your server/plugins require extra values (for example authentication credentials).
Example shape:
const endpoint = `https://${host}:443/live/whip/${streamName}`
const connectionParams = {
// optional plugin/query params
}endpointshould target Stream Manager proxy routing (not a hardcoded origin/edge).connectionParamsis where Stream Manager-related values (region/nodeGroup/transcoder/auth metadata) are commonly supplied.
Example shape:
const endpoint = `https://${host}/as/v1/proxy/whip/${app}/${streamName}`
const connectionParams = {
// e.g. region, nodeGroup, strict matching flags, authentication credentials, etc.
}- publish flow:
startPublish() - media summary rendering:
updateMediaSummary() - summary logging:
logMediaSummary() - publish settings UI wiring:
r5-publish-settingswithaudio-advancedinindex.html - requested constraint builder:
buildMediaConstraints()insrc/components/r5-publish-settings/index.ts - audio advanced mapping:
buildAudioConstraint()insrc/components/r5-publish-settings/index.ts - applied track settings capture:
readAppliedTrackSettings()insrc/components/r5-publish-settings/index.ts
Pair this with a WHEP-focused playback example to compare publish-time capture constraints with subscribe-time playback behavior.