UNPKG

@observertc/samples-encoder

Version:

ObserveRTC Library for Encoding Samples

433 lines (390 loc) 15.7 kB
import { MediaPlayoutStatsEncoder } from "./MediaPlayoutEncoder"; import { CertificateEncoder } from "./CertificateEncoder"; import { ClientSampleEncoder } from "./ClientSampleEncoder"; import { CodecStatsEncoder } from "./CodecStatsEncoder"; import { DataChannelEncoder } from "./DataChannelEncoder"; import { IceCandidateEncoder } from "./IceCandidateEncoder"; import { IceCandidatePairEncoder } from "./IceCandidatePairStatsEncoder"; import { IceTransportEncoder } from "./IceTransportEncoder"; import { InboundRtpEncoder } from "./InboundRtpEncoder"; import { InboundTrackSampleEncoder } from "./InboundTrackEncoder"; import { PeerConnectionSample as InputClientSample, PeerConnectionSample } from "./InputSamples"; import { MediaSourceStatsEncoder } from "./MediaSourceEncoder"; import { OutboundRtpEncoder } from "./OutboundRtpEncoder"; import { OutboundTrackSampleEncoder } from "./OutboundTrackEncoder"; import { ClientSample_PeerConnectionSample } from "./OutputSamples"; import { PeerConnectionTransportEncoder } from "./PeerConnectionTransportEncoder"; import { RemoteInboundRtpEncoder } from "./RemoteInboundRtpEncoder"; import { RemoteOutboundRtpEncoder } from "./RemoteOutboundRtpEncoder"; import { AttachmentEncoder, Encoder, NumberToNumberEncoder, OneTimePassStringToUint8ArrayEncoder, OneTimePassUuidToByteArrayEncoder, StringToStringEncoder, stringToBytesArray, uuidToByteArray } from "./utils"; export class PeerConnectionSampleEncoder implements Encoder<PeerConnectionSample, ClientSample_PeerConnectionSample>{ private readonly _peerConnectionId: Uint8Array; private _visited = false; private _attachmentsEncoder: AttachmentEncoder; private _scoreEncoder = new NumberToNumberEncoder(); private _scoreReasonsEncoder = new StringToStringEncoder(); private _inboundTracksEncoders = new Map<string, InboundTrackSampleEncoder>(); private _outboundTracksEncoders = new Map<string, OutboundTrackSampleEncoder>(); private _codecStatsEncoders = new Map<string, CodecStatsEncoder>(); private _inboundRtpEncoders = new Map<number, InboundRtpEncoder>(); private _remoteInboundRtpEncoders = new Map<number, RemoteInboundRtpEncoder>(); private _outboundRtpEncoders = new Map<number, OutboundRtpEncoder>(); private _remoteOutboundRtpEncoders = new Map<number, RemoteOutboundRtpEncoder>(); private _mediaSourceEncoders = new Map<string, MediaSourceStatsEncoder>(); private _mediaPlayoutEncoders = new Map<string, MediaPlayoutStatsEncoder>(); private _peerConnectionTransportEncoders = new Map<string, PeerConnectionTransportEncoder>(); private _dataChannelEncoders = new Map<string, DataChannelEncoder>(); private _iceTransportEncoders = new Map<string, IceTransportEncoder>(); private _iceCandidateEncoders = new Map<string, IceCandidateEncoder>(); private _iceCandidatePairEncoders = new Map<string, IceCandidatePairEncoder>(); private _certificateEncoders = new Map<string, CertificateEncoder>(); public constructor( peerConnectionId: string, public parent: ClientSampleEncoder, ) { this._peerConnectionId = this.parent.settings.peerConnectionIdIsUuid ? uuidToByteArray(peerConnectionId) : stringToBytesArray(peerConnectionId); this._attachmentsEncoder = this.parent.attachmentEncoderFactory.createPeerConnectionSampleAttachmentEncoder(); } public get visited(): boolean { const result = this._visited; this._visited = false; return result; } public reset(): void { this._attachmentsEncoder.reset(); this._scoreEncoder.reset(); this._scoreReasonsEncoder.reset(); this._inboundTracksEncoders.forEach((encoder) => encoder.reset()); this._outboundTracksEncoders.forEach((encoder) => encoder.reset()); this._codecStatsEncoders.forEach((encoder) => encoder.reset()); this._inboundRtpEncoders.forEach((encoder) => encoder.reset()); this._remoteInboundRtpEncoders.forEach((encoder) => encoder.reset()); this._outboundRtpEncoders.forEach((encoder) => encoder.reset()); this._remoteOutboundRtpEncoders.forEach((encoder) => encoder.reset()); this._mediaSourceEncoders.forEach((encoder) => encoder.reset()); this._mediaPlayoutEncoders.forEach((encoder) => encoder.reset()); this._peerConnectionTransportEncoders.forEach((encoder) => encoder.reset()); this._dataChannelEncoders.forEach((encoder) => encoder.reset()); this._iceTransportEncoders.forEach((encoder) => encoder.reset()); this._iceCandidateEncoders.forEach((encoder) => encoder.reset()); this._iceCandidatePairEncoders.forEach((encoder) => encoder.reset()); this._certificateEncoders.forEach((encoder) => encoder.reset()); } public encode(sample: PeerConnectionSample): ClientSample_PeerConnectionSample { this._visited = true; const inboundTracks = sample.inboundTracks?.map(this._encodeInboundTracks.bind(this)); const outboundTracks = sample.outboundTracks?.map(this._encodeOutboundTracks.bind(this)); const codecs = sample.codecs?.map(this._encodeCodecStats.bind(this)); const inboundRtps = sample.inboundRtps?.map(this._encodeInboundRtp.bind(this)); const remoteInboundRtps = sample.remoteInboundRtps?.map(this._encodeRemoteInboundRtp.bind(this)); const outboundRtps = sample.outboundRtps?.map(this._encodeOutboundRtp.bind(this)); const remoteOutboundRtps = sample.remoteOutboundRtps?.map(this._encodeRemoteOutboundRtp.bind(this)); const mediaSources = sample.mediaSources?.map(this._encodeMediaSource.bind(this)); const mediaPlayouts = sample.mediaPlayouts?.map(this._encodeMediaPlayout.bind(this)); const peerConnectionTransports = sample.peerConnectionTransports?.map(this._encodePeerConnectionTransport.bind(this)); const dataChannels = sample.dataChannels?.map(this._encodeDataChannel.bind(this)); const iceTransports = sample.iceTransports?.map(this._encodeIceTransport.bind(this)); const iceCandidates = sample.iceCandidates?.map(this._encodeIceCandidate.bind(this)); const iceCandidatePairs = sample.iceCandidatePairs?.map(this._encodeIceCandidatePair.bind(this)); const certificates = sample.certificates?.map(this._encodeCertificate.bind(this)); return new ClientSample_PeerConnectionSample({ peerConnectionId: this._peerConnectionId, attachments: this._attachmentsEncoder.encode(sample.attachments), score: this._scoreEncoder.encode(sample.score), scoreReasons: this._scoreReasonsEncoder.encode(sample.scoreReasons), inboundTracks, outboundTracks, codecs, inboundRtps, remoteInboundRtps, outboundRtps, remoteOutboundRtps, mediaSources, mediaPlayouts, peerConnectionTransports, dataChannels, iceTransports, iceCandidates, iceCandidatePairs, certificates, }); } private _encodeCodecStats(input: Required<InputClientSample>['codecs'][number]) { let encoder = this._codecStatsEncoders.get(input.id); if (!encoder) { encoder = new CodecStatsEncoder( this.parent.attachmentEncoderFactory.createCodecStatsAttachmentEncoder() ); this._codecStatsEncoders.set(input.id, encoder); } return encoder.encode(input); } private _encodeInboundTracks(input: Required<InputClientSample>['inboundTracks'][number]) { let encoder = this._inboundTracksEncoders.get(input.id); if (!encoder) { encoder = new InboundTrackSampleEncoder( this.parent.attachmentEncoderFactory.createInboundTrackAttachmentEncoder() ); this._inboundTracksEncoders.set(input.id, encoder); } return encoder.encode(input); } private _encodeOutboundTracks(input: Required<InputClientSample>['outboundTracks'][number]) { let encoder = this._outboundTracksEncoders.get(input.id); if (!encoder) { encoder = new OutboundTrackSampleEncoder( this.parent.attachmentEncoderFactory.createOutboundTrackAttachmentEncoder() ); this._outboundTracksEncoders.set(input.id, encoder); } return encoder.encode(input); } private _encodeInboundRtp(input: Required<InputClientSample>['inboundRtps'][number]) { let encoder = this._inboundRtpEncoders.get(input.ssrc); if (!encoder) { const trackIdEncoder = this.parent.settings.trackIdIsUuid ? new OneTimePassUuidToByteArrayEncoder() : new OneTimePassStringToUint8ArrayEncoder(); encoder = new InboundRtpEncoder( input.ssrc, trackIdEncoder, this.parent.attachmentEncoderFactory.createInboundRtpAttachmentEncoder() ); this._inboundRtpEncoders.set(input.ssrc, encoder); } return encoder.encode(input); } private _encodeOutboundRtp(input: Required<InputClientSample>['outboundRtps'][number]) { let encoder = this._outboundRtpEncoders.get(input.ssrc); if (!encoder) { encoder = new OutboundRtpEncoder( input.ssrc, this.parent.attachmentEncoderFactory.createOutboundRtpAttachmentEncoder() ); this._outboundRtpEncoders.set(input.ssrc, encoder); } return encoder.encode(input); } private _encodeRemoteInboundRtp(input: Required<InputClientSample>['remoteInboundRtps'][number]) { let encoder = this._remoteInboundRtpEncoders.get(input.ssrc); if (!encoder) { encoder = new RemoteInboundRtpEncoder( input.ssrc, this.parent.attachmentEncoderFactory.createRemoteInboundRtpAttachmentEncoder() ); this._remoteInboundRtpEncoders.set(input.ssrc, encoder); } return encoder.encode(input); } private _encodeRemoteOutboundRtp(input: Required<InputClientSample>['remoteOutboundRtps'][number]) { let encoder = this._remoteOutboundRtpEncoders.get(input.ssrc); if (!encoder) { encoder = new RemoteOutboundRtpEncoder( input.ssrc, this.parent.attachmentEncoderFactory.createRemoteOutboundRtpAttachmentEncoder() ); this._remoteOutboundRtpEncoders.set(input.ssrc, encoder); } return encoder.encode(input); } private _encodeDataChannel(input: Required<InputClientSample>['dataChannels'][number]) { let encoder = this._dataChannelEncoders.get(input.id); if (!encoder) { encoder = new DataChannelEncoder( this.parent.attachmentEncoderFactory.createDataChannelAttachmentEncoder() ); this._dataChannelEncoders.set(input.id, encoder); } return encoder.encode(input); } private _encodeMediaSource(input: Required<InputClientSample>['mediaSources'][number]) { let encoder = this._mediaSourceEncoders.get(input.id); if (!encoder) { encoder = new MediaSourceStatsEncoder( this.parent.attachmentEncoderFactory.createMediaSourceAttachmentEncoder() ); this._mediaSourceEncoders.set(input.id, encoder); } return encoder.encode(input); } private _encodeMediaPlayout(input: Required<InputClientSample>['mediaPlayouts'][number]) { let encoder = this._mediaPlayoutEncoders.get(input.id); if (!encoder) { encoder = new MediaPlayoutStatsEncoder( this.parent.attachmentEncoderFactory.createMediaPlayoutAttachmentEncoder() ); this._mediaPlayoutEncoders.set(input.id, encoder); } return encoder.encode(input); } private _encodePeerConnectionTransport(input: Required<InputClientSample>['peerConnectionTransports'][number]) { let encoder = this._peerConnectionTransportEncoders.get(input.id); if (!encoder) { encoder = new PeerConnectionTransportEncoder( this.parent.attachmentEncoderFactory.createPeerConnectionTransportAttachmentEncoder() ); this._peerConnectionTransportEncoders.set(input.id, encoder); } return encoder.encode(input); } private _encodeIceTransport(input: Required<InputClientSample>['iceTransports'][number]) { let encoder = this._iceTransportEncoders.get(input.id); if (!encoder) { encoder = new IceTransportEncoder( this.parent.attachmentEncoderFactory.createIceTransportAttachmentEncoder() ); this._iceTransportEncoders.set(input.id, encoder); } return encoder.encode(input); } private _encodeIceCandidate(input: Required<InputClientSample>['iceCandidates'][number]) { let encoder = this._iceCandidateEncoders.get(input.id); if (!encoder) { encoder = new IceCandidateEncoder( this.parent.attachmentEncoderFactory.createIceCandidateAttachmentEncoder() ); this._iceCandidateEncoders.set(input.id, encoder); } return encoder.encode(input); } private _encodeIceCandidatePair(input: Required<InputClientSample>['iceCandidatePairs'][number]) { let encoder = this._iceCandidatePairEncoders.get(input.id); if (!encoder) { encoder = new IceCandidatePairEncoder( this.parent.attachmentEncoderFactory.createIceCandidatePairAttachmentEncoder() ); this._iceCandidatePairEncoders.set(input.id, encoder); } return encoder.encode(input); } private _encodeCertificate(input: Required<InputClientSample>['certificates'][number]) { let encoder = this._certificateEncoders.get(input.id); if (!encoder) { encoder = new CertificateEncoder( this.parent.attachmentEncoderFactory.createCertificateAttachmentEncoder() ); this._certificateEncoders.set(input.id, encoder); } return encoder.encode(input); } public checkVisitsAndClean() { let visited = false; for (const [id, encoder] of [...this._inboundTracksEncoders.entries()]) { if (!encoder.visited) { this._inboundTracksEncoders.delete(id); continue; } visited = true; } for (const [id, encoder] of [...this._outboundTracksEncoders.entries()]) { if (!encoder.visited) { this._outboundTracksEncoders.delete(id); continue; } visited = true; } for (const [id, encoder] of [...this._codecStatsEncoders.entries()]) { if (!encoder.visited) { this._codecStatsEncoders.delete(id); continue; } visited = true; } for (const [ssrc, encoder] of [...this._inboundRtpEncoders.entries()]) { if (!encoder.visited) { this._inboundRtpEncoders.delete(ssrc); continue; } visited = true; } for (const [ssrc, encoder] of [...this._remoteInboundRtpEncoders.entries()]) { if (!encoder.visited) { this._remoteInboundRtpEncoders.delete(ssrc); continue; } visited = true; } for (const [ssrc, encoder] of [...this._outboundRtpEncoders.entries()]) { if (!encoder.visited) { this._outboundRtpEncoders.delete(ssrc); continue; } visited = true; } for (const [ssrc, encoder] of [...this._remoteOutboundRtpEncoders.entries()]) { if (!encoder.visited) { this._remoteOutboundRtpEncoders.delete(ssrc); continue; } visited = true; } for (const [id, encoder] of [...this._mediaSourceEncoders.entries()]) { if (!encoder.visited) { this._mediaSourceEncoders.delete(id); continue; } visited = true; } for (const [id, encoder] of [...this._mediaPlayoutEncoders.entries()]) { if (!encoder.visited) { this._mediaPlayoutEncoders.delete(id); continue; } visited = true; } for (const [id, encoder] of [...this._peerConnectionTransportEncoders.entries()]) { if (!encoder.visited) { this._peerConnectionTransportEncoders.delete(id); continue; } visited = true; } for (const [id, encoder] of [...this._dataChannelEncoders.entries()]) { if (!encoder.visited) { this._dataChannelEncoders.delete(id); continue; } visited = true; } for (const [id, encoder] of [...this._iceTransportEncoders.entries()]) { if (!encoder.visited) { this._iceTransportEncoders.delete(id); continue; } visited = true; } for (const [id, encoder] of [...this._iceCandidateEncoders.entries()]) { if (!encoder.visited) { this._iceCandidateEncoders.delete(id); continue; } visited = true; } for (const [id, encoder] of [...this._iceCandidatePairEncoders.entries()]) { if (!encoder.visited) { this._iceCandidatePairEncoders.delete(id); continue; } visited = true; } for (const [id, encoder] of [...this._certificateEncoders.entries()]) { if (!encoder.visited) { this._certificateEncoders.delete(id); continue; } visited = true; } return visited; } }