-
Notifications
You must be signed in to change notification settings - Fork 263
User Timestamp Parsing #1812
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
User Timestamp Parsing #1812
Changes from all commits
41e5ec1
4aab3e5
e3c48d3
df6c1d8
9db706b
aec3263
7db8729
6e16a9d
04ce0ed
45820bd
b37b19b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ import { ConnectionState } from '../room/Room'; | |
| import { DeviceUnsupportedError } from '../room/errors'; | ||
| import { EngineEvent, ParticipantEvent, RoomEvent } from '../room/events'; | ||
| import type RemoteTrack from '../room/track/RemoteTrack'; | ||
| import RemoteVideoTrack from '../room/track/RemoteVideoTrack'; | ||
| import type { Track } from '../room/track/Track'; | ||
| import type { VideoCodec } from '../room/track/options'; | ||
| import { mimeTypeToVideoCodecString } from '../room/track/utils'; | ||
|
|
@@ -221,6 +222,15 @@ export class E2EEManager | |
| encryptFuture.resolve(data as EncryptDataResponseMessage['data']); | ||
| } | ||
| break; | ||
| case 'packetTrailer': | ||
| this.handleUserTimestamp( | ||
| data.trackId, | ||
| data.participantIdentity, | ||
| data.timestampUs, | ||
| data.frameId, | ||
| data.rtpTimestamp, | ||
| ); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
|
|
@@ -231,6 +241,32 @@ export class E2EEManager | |
| this.emit(EncryptionEvent.EncryptionError, ev.error, undefined); | ||
| }; | ||
|
|
||
| private handleUserTimestamp( | ||
| trackId: string, | ||
| participantIdentity: string, | ||
| timestampUs: number, | ||
| frameId?: number, | ||
| rtpTimestamp?: number, | ||
| ) { | ||
| if (!this.room) { | ||
| return; | ||
|
Comment on lines
+244
to
+252
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: Similar to my last comment - I don't know a ton about the user timestamp feature, but my suspicion is that putting |
||
| } | ||
| const participant = this.room.getParticipantByIdentity(participantIdentity); | ||
| if (!participant) { | ||
| return; | ||
| } | ||
| for (const pub of participant.trackPublications.values()) { | ||
| if ( | ||
| pub.track && | ||
| pub.track.mediaStreamID === trackId && | ||
| pub.track instanceof RemoteVideoTrack | ||
| ) { | ||
| pub.track.setUserTimestamp(timestampUs, rtpTimestamp, frameId); | ||
| return; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public setupEngine(engine: RTCEngine) { | ||
| engine.on(EngineEvent.RTPVideoMapUpdate, (rtpMap) => { | ||
| this.postRTPMap(rtpMap); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,10 +4,17 @@ import { EventEmitter } from 'events'; | |
| import type TypedEventEmitter from 'typed-emitter'; | ||
| import { workerLogger } from '../../logger'; | ||
| import type { VideoCodec } from '../../room/track/options'; | ||
| import { stripPacketTrailerFromEncodedFrame } from '../../packet_trailer/PacketTrailerTransformer'; | ||
| import { ENCRYPTION_ALGORITHM, IV_LENGTH, UNENCRYPTED_BYTES } from '../constants'; | ||
| import { CryptorError, CryptorErrorReason } from '../errors'; | ||
| import { type CryptorCallbacks, CryptorEvent } from '../events'; | ||
| import type { DecodeRatchetOptions, KeyProviderOptions, KeySet, RatchetResult } from '../types'; | ||
| import type { | ||
| DecodeRatchetOptions, | ||
| KeyProviderOptions, | ||
| KeySet, | ||
| RatchetResult, | ||
| PacketTrailerMessage, | ||
| } from '../types'; | ||
| import { deriveKeys, isVideoFrame, needsRbspUnescaping, parseRbsp, writeRbsp } from '../utils'; | ||
| import type { ParticipantKeyHandler } from './ParticipantKeyHandler'; | ||
| import { processNALUsForEncryption } from './naluUtils'; | ||
|
|
@@ -454,6 +461,32 @@ export class FrameCryptor extends BaseFrameCryptor { | |
| encodedFrame: RTCEncodedVideoFrame | RTCEncodedAudioFrame, | ||
| controller: TransformStreamDefaultController, | ||
| ) { | ||
| // Always attempt to strip LKTS packet trailer before any e2ee | ||
| // processing. On the send side, the trailer is appended *after* encryption, | ||
| // so it must be removed *before* decryption. | ||
| if (isVideoFrame(encodedFrame) && encodedFrame.data.byteLength > 0) { | ||
| try { | ||
| const packetTrailerResult = stripPacketTrailerFromEncodedFrame( | ||
| encodedFrame as RTCEncodedVideoFrame, | ||
| ); | ||
| if (packetTrailerResult !== undefined && this.trackId && this.participantIdentity) { | ||
| const msg: PacketTrailerMessage = { | ||
| kind: 'packetTrailer', | ||
| data: { | ||
| trackId: this.trackId, | ||
| participantIdentity: this.participantIdentity, | ||
| timestampUs: packetTrailerResult.timestampUs, | ||
| frameId: packetTrailerResult.frameId, | ||
| rtpTimestamp: packetTrailerResult.rtpTimestamp, | ||
| }, | ||
| }; | ||
| postMessage(msg); | ||
| } | ||
| } catch { | ||
| // Best-effort: never break media pipeline if timestamp parsing fails. | ||
| } | ||
| } | ||
|
|
||
|
Comment on lines
+464
to
+489
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: Is the Is there a reason it has to run in a web worker context (ie, maybe it is super computationally expensive / etc)? Is this feature supposed to work if e2ee is disabled? |
||
| if ( | ||
| !this.isEnabled() || | ||
| // skip for decryption for empty dtx frames | ||
|
|
||
Check warning
Code scanning / CodeQL
Unsafe HTML constructed from library input Medium
Copilot Autofix
AI 2 days ago
In general, to fix unsafe HTML construction you should either (1) avoid
innerHTMLentirely and build the DOM withdocument.createElementandtextContent/innerText, or (2) rigorously escape/sanitize any untrusted values before inserting them into HTML. Since this is demo UI code and we want to preserve functionality while minimizing changes, the best fix here is to ensure that any untrusted dynamic value interpolated into the HTML string is passed through a simple HTML-escaping helper before being inserted intoinnerHTML.Concretely, in
examples/demo/demo.tswe should define a smallescapeHtmlfunction near the rendering helpers that replaces&,<,>,", and'with their corresponding HTML entities. Then, insiderenderParticipant, we should compute a safe version of the participant identity, e.g.const safeIdentity = escapeHtml(identity);, and usesafeIdentityinstead ofidentityin every interpolated position within thediv.innerHTMLtemplate. This keeps the structure of the HTML unchanged (IDs, class names, etc.) but ensures that any untrusted characters are escaped and can no longer break out of their intended context to execute scripts. This one change addresses all CodeQL variants that trace through participant/track data into theinnerHTMLtemplate.