Skip to content

Commit

Permalink
Merge branch 'v1-migration' into nx/release
Browse files Browse the repository at this point in the history
  • Loading branch information
Santiago-Souto committed Oct 30, 2024
2 parents 9ad9e2f + aba0bc1 commit b10c68b
Show file tree
Hide file tree
Showing 8 changed files with 1,620 additions and 1,649 deletions.
1 change: 0 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ classDiagram
+setStereo(sdp) string
+setDTX(sdp) string
+setVideoBitrate(sdp, bitrate) string
+setSimulcast(sdp, codec) string
}
class Logger {
<<Singleton>>
Expand Down
2,853 changes: 1,435 additions & 1,418 deletions package-lock.json

Large diffs are not rendered by default.

17 changes: 12 additions & 5 deletions packages/millicast-sdk/src/PeerConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,6 @@ export default class PeerConnection extends EventEmitter {
}
this.sessionDescription.sdp = SdpParser.setMultiopus(this.sessionDescription.sdp, mediaStream)
}

if (!options.disableVideo && options.simulcast) {
this.sessionDescription.sdp = SdpParser.setSimulcast(this.sessionDescription.sdp, options.codec)
}
if (options.absCaptureTime) {
this.sessionDescription.sdp = SdpParser.setAbsoluteCaptureTime(this.sessionDescription.sdp)
}
Expand Down Expand Up @@ -518,13 +514,24 @@ const addMediaStreamToPeer = (peer: RTCPeerConnection, mediaStream: MediaStream,

if (track.kind === 'video') {
initOptions.direction = !options.disableVideo ? 'sendonly' : 'inactive'
const encodings = []

if (options.scalabilityMode && new UserAgent().isChrome()) {
logger.debug(`Video track with scalability mode: ${options.scalabilityMode}.`)
initOptions.sendEncodings = [{ scalabilityMode: options.scalabilityMode } as RTCRtpEncodingParameters]
encodings.push({ scalabilityMode: options.scalabilityMode } as RTCRtpEncodingParameters)
} else if (options.scalabilityMode) {
logger.warn('SVC is only supported in Google Chrome')
}
if (options.simulcast) {
encodings.push(
{ rid: 'f', scaleResolutionDownBy: 1.0 },
{ rid: 'h', scaleResolutionDownBy: 2.0 },
{ rid: 'q', scaleResolutionDownBy: 4.0 }
)
}
if (encodings.length > 0) {
initOptions.sendEncodings = encodings
}
}

peer.addTransceiver(track, initOptions)
Expand Down
41 changes: 33 additions & 8 deletions packages/millicast-sdk/src/View.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
DRMOptions,
MetadataObject,
SEIUserUnregisteredData,
ViewerEvents,
} from './types/View.types.js'
import { DRMProfile } from './types/Director.types'
import { DecodedJWT, Media } from './types/BaseWebRTC.types'
Expand Down Expand Up @@ -89,11 +90,39 @@ export default class View extends BaseWebRTC {
private eventQueue: RTCTrackEvent[] = []
private stopReemitingWebRTCPeerInstanceEvents: (() => void) | null = null
private stopReemitingSignalingInstanceEvents: (() => void) | null = null
private events: { [K in keyof ViewerEvents]: Array<(payload: ViewerEvents[K]) => void> } = {}
protected override options: ViewConnectOptions | null = null
constructor(tokenGenerator: TokenGeneratorCallback, autoReconnect = true) {
super(tokenGenerator, logger, autoReconnect)
}

override on<K extends keyof ViewerEvents>(eventName: K, listener: (payload: ViewerEvents[K]) => void): this {
if (!this.events[eventName]) {
this.events[eventName] = []
}
this.events[eventName].push(listener)
return this
}

override off<K extends keyof ViewerEvents>(eventName: K, listener: (payload: ViewerEvents[K]) => void): this {
const listeners = this.events[eventName]
if (listeners) {
const idx = listeners.indexOf(listener)
if (idx >= 0) {
listeners.splice(idx, 1)
}
}
return this
}

override emit<K extends keyof ViewerEvents>(eventName: K, payload: ViewerEvents[K]): boolean {
if (this.events[eventName]) {
this.events[eventName].forEach((listener) => listener(payload))
return true
}
return false
}

/**
* @typedef {Object} LayerInfo
* @property {String} encodingId - rid value of the simulcast encoding of the track (default: automatic selection)
Expand Down Expand Up @@ -302,16 +331,12 @@ export default class View extends BaseWebRTC {
await webRTCPeerInstance.createRTCPeer(this.options?.peerConfig)
// Stop emiting events from the previous instances
this.stopReemitingWebRTCPeerInstanceEvents?.()
this.stopReemitingSignalingInstanceEvents?.()
// And start emitting from the new ones
this.stopReemitingWebRTCPeerInstanceEvents = reemit(
webRTCPeerInstance,
this,
Object.values(webRTCEvents).filter((e) => e !== webRTCEvents.track)
)
this.stopReemitingSignalingInstanceEvents = reemit(signalingInstance, this, [
signalingEvents.broadcastEvent,
])

if (this.options?.metadata) {
if (!this.worker) {
Expand Down Expand Up @@ -345,8 +370,6 @@ export default class View extends BaseWebRTC {
}
}
this.emit('metadata', metadata)
// FIXME : Remove in v0.3.0
this.emit('onMetadata', metadata)
}
}
}
Expand All @@ -363,18 +386,20 @@ export default class View extends BaseWebRTC {
if (event.data.sourceId === null) {
switch (event.name) {
case 'active':
this.emit('broadcastEvent', event)
this.isMainStreamActive = true
while (this.eventQueue.length > 0) {
this.onTrackEvent(this.eventQueue.shift() as RTCTrackEvent)
}
break
return
case 'inactive':
this.isMainStreamActive = false
break
default:
break
}
}
this.emit('broadcastEvent', event)
})

const options = { ...(this.options as ViewConnectOptions), stereo: true }
Expand Down Expand Up @@ -480,7 +505,7 @@ export default class View extends BaseWebRTC {
)
}
}
this.emit(webRTCEvents.track, trackEvent)
this.emit('track', trackEvent)
}

getDRMConfiguration(mediaId: string) {
Expand Down
113 changes: 113 additions & 0 deletions packages/millicast-sdk/src/types/View.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,116 @@ export interface MetadataObject {
unregistered: SEIUserUnregisteredData
timecode: Date
}

/**
* Broadcast event
*/
export type BroadcastEventName = ViewServerEvent

export interface BroadcastEvent {
name: BroadcastEventName
}

/**
* Active Event
*/
export interface TrackInfo {
trackId: string
media: Media
}

export interface ActiveEventPayload {
streamId: string
sourceId: string | null
tracks: TrackInfo[]
encryption?: EncryptionParameters
}

export interface ActiveEvent extends BroadcastEvent {
name: Extract<BroadcastEventName, 'active'>
data: ActiveEventPayload
}


/**
* Inactive Event
*/
export interface InactiveEventPayload {
streamId: string
sourceId: string | null
}

export interface InactiveEvent extends BroadcastEvent {
name: Extract<BroadcastEventName, 'inactive'>
data: InactiveEventPayload
}


/**
* ViewerCount Event
*/
export interface ViewerCountEventPayload {
viewerCount: number
}

export interface ViewerCountEvent extends BroadcastEvent {
name: Extract<BroadcastEventName, 'viewercount'>
data: ViewerCountEventPayload
}


/**
* Layers Event
*/
export interface LayersEventPayload {
medias: LayersMediaCollection
}

export interface LayersMediaCollection {
[key: string]: LayerMedia
}

export interface LayerMedia {
active: Array<LayerMediaInfo>
inactive: Array<LayerMediaInfo>
layers: Array<Layer>
}

export interface LayerMediaInfo {
id: string
simulcastIdx: number
totalBytes: number
numPackets: number
bitrate: number
totalBitrate: number
width: number
height: number
layers: Array<Layer>
}

export interface Layer extends Omit<LayerMediaInfo, 'id' | 'layers'> {
encodingId: string
spatialLayerId: number
temporalLayerId: number
}

export interface LayersEvent extends BroadcastEvent {
name: Extract<BroadcastEventName, 'layers'>
data: LayersEventPayload
}

/**
* Metadata Event
*/
export type MetadataEvent = MetadataObject

/**
* Events declaration of Viewers that user could listen to
*/
export interface ViewerEvents {
'broadcastEvent'?: BroadcastEvent
'track'?: RTCTrackEvent
'metadata'?: MetadataEvent
// TODO: elaborate error type
'error'?: Error
}
91 changes: 1 addition & 90 deletions packages/millicast-sdk/src/utils/SdpParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,95 +39,6 @@ const headerExtensionIdUppperRange = Array.from(
* @description Simplify SDP parser.
*/
const SdpParser = {
/**
* @function
* @name setSimulcast
* @description Parse SDP for support simulcast.
* **Only available in Chromium based browsers.**
* @param {String} sdp - Current SDP.
* @param {String} codec - Codec.
* @returns {String} SDP parsed with simulcast support.
* @example SdpParser.setSimulcast(sdp, 'h264')
*/
setSimulcast(sdp = '', codec = '') : string {
logger.info('Setting simulcast. Codec: ', codec)
const browserData = new UserAgent()
if (!browserData.isChromium()) {
logger.warn(
'Your browser does not appear to support Simulcast. For a better experience, use a Chromium based browser.'
)
return sdp
}
if (codec !== 'h264' && codec !== 'vp8') {
logger.warn(
`Your selected codec ${codec} does not appear to support Simulcast. To broadcast using simulcast, please use H.264 or VP8.`
)
return sdp
}
// Check if there is video available to set simulcast
if (!/m=video/.test(sdp)) {
logger.warn('There is no available video for simulcast to be enabled.')
return sdp
}

try {
const reg1 = new RegExp('m=video.*?a=ssrc:(\\d*) cname:(.+?)\\r\\n', 's')
const reg2 = new RegExp('m=video.*?a=ssrc:(\\d*) msid:(.+?)\\r\\n', 's')
// Get ssrc and cname and msid
const res1 = reg1.exec(sdp) ?? []
const ssrc = res1[1]
const cname = res1[2]
const res2 = reg2.exec(sdp) ?? []
const msid = res2[2]
// Add simulcasts ssrcs
const num = 2
const ssrcs = [ssrc]
for (let i = 0; i < num; ++i) {
// Create new ssrcs
const ssrc = 100 + i * 2
const rtx = ssrc + 1
// Add to ssrc list
ssrcs.push(ssrc.toString())
// Add sdp stuff
sdp +=
'a=ssrc-group:FID ' +
ssrc +
' ' +
rtx.toString() +
'\r\n' +
'a=ssrc:' +
ssrc.toString() +
' cname:' +
cname +
'\r\n' +
'a=ssrc:' +
ssrc.toString() +
' msid:' +
msid +
'\r\n' +
'a=ssrc:' +
rtx.toString() +
' cname:' +
cname +
'\r\n' +
'a=ssrc:' +
rtx.toString() +
' msid:' +
msid +
'\r\n'
}
// Add SIM group
sdp += 'a=ssrc-group:SIM ' + ssrcs.join(' ') + '\r\n'

logger.info('Simulcast setted')
logger.debug('Simulcast SDP: ', sdp)
return sdp
} catch (e) {
logger.error('Error setting SDP for simulcast: ', e)
throw e
}
},

/**
* @function
* @name setStereo
Expand Down Expand Up @@ -441,7 +352,7 @@ const SdpParser = {

// Checks if mediaStream has more than 2 audio channels.
const hasAudioMultichannel = (mediaStream: MediaStream) => {
return mediaStream.getAudioTracks().some((value) => value.getSettings().channelCount as number > 2)
return mediaStream.getAudioTracks().some((value) => (value.getSettings().channelCount as number) > 2)
}

export default SdpParser
Loading

0 comments on commit b10c68b

Please sign in to comment.