Skip to content
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

[WIP] Implement a simple exclusive filter by IP address #564

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/mini-widgets/MiniVideoRecorder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ watch(externalStreams, () => {
// Retrieve stream from the saved stream name, otherwise choose the first available stream as a fallback
const savedStream = savedStreamName ? availableStreams.value.find((s) => s.name === savedStreamName) : undefined

if (savedStream !== undefined && savedStream !== selectedStream.value && selectedStream.value === undefined) {
if (savedStream !== undefined && savedStream.id !== selectedStream.value?.id && selectedStream.value === undefined) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unrelated to the patch, just noticed that was wrong.

console.debug!('[WebRTC] trying to set stream...')
updateCurrentStream(savedStream)
}
Expand Down
53 changes: 51 additions & 2 deletions src/components/widgets/VideoPlayer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@
hide-details
return-object
/>
<v-text-field
v-model="selectedICEIPs"
label="Selected IP Address"
class="uri-input my-3"
validate-on="lazy input"
density="compact"
variant="outlined"
type="input"
hint="IP Address of the Vehicle to be used for the WebRTC ICE Routing, usually, the IP of the tether/cabled interface. Blank means any route. E.g: 192.168.2.2"
:rules="[isValidHostAddress]"
/>
<v-banner-text>Saved stream name: "{{ widget.options.streamName }}"</v-banner-text>
<v-banner-text>Signaller Status: {{ signallerStatus }}</v-banner-text>
<v-banner-text>Stream Status: {{ streamStatus }}</v-banner-text>
Expand Down Expand Up @@ -66,6 +77,30 @@ import type { Stream } from '@/libs/webrtc/signalling_protocol'
import { useMainVehicleStore } from '@/stores/mainVehicle'
import type { Widget } from '@/types/widgets'

// FIXME: Why can't I just import a property from another component?
// import isValidHostAddress from '@/views/ConfigurationGeneralView.vue'
const isValidHostAddress = (value: string): boolean | string => {
if (value.length >= 255) {
return 'Address is too long'
}

// Regexes from https://stackoverflow.com/a/106223/3850957
const ipRegex = new RegExp(
rafaellehmkuhl marked this conversation as resolved.
Show resolved Hide resolved
'^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
)
const hostnameRegex = new RegExp(
'^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$'
)

if (ipRegex.test(value) || hostnameRegex.test(value)) {
return true
}

return 'Invalid host address. Should be an IP address or a hostname'
}

const mainVehicleStore = useMainVehicleStore()

const { rtcConfiguration, webRTCSignallingURI } = useMainVehicleStore()

console.debug('[WebRTC] Using webrtc-adapter for', adapter.browserDetails)
Expand All @@ -79,10 +114,15 @@ const props = defineProps<{

const widget = toRefs(props).widget

const selectedICEIPsField = ref<string | undefined>()
const selectedICEIPs = ref<string | undefined>()
const selectedStream = ref<Stream | undefined>()
const videoElement = ref<HTMLVideoElement | undefined>()
const webRTCManager = new WebRTCManager(webRTCSignallingURI.val, rtcConfiguration)
const { availableStreams, mediaStream, signallerStatus, streamStatus } = webRTCManager.startStream(selectedStream)
const { availableStreams, mediaStream, signallerStatus, streamStatus } = webRTCManager.startStream(
selectedStream,
selectedICEIPs
)

onBeforeMount(() => {
// Set initial widget options if they don't exist
Expand Down Expand Up @@ -119,6 +159,15 @@ watch(mediaStream, async (newStream, oldStream) => {
})
})

watch(selectedICEIPsField, async (oldAddr, newAddr) => {
if (!newAddr || isValidHostAddress(newAddr)) {
return
}

selectedICEIPs.value = selectedICEIPsField.value
})


watch(selectedStream, () => (widget.value.options.streamName = selectedStream.value?.name))

watch(availableStreams, () => {
Expand All @@ -133,7 +182,7 @@ watch(availableStreams, () => {
? availableStreams.value.find((s) => s.name === savedStreamName)
: availableStreams.value.first()

if (savedStream !== undefined && savedStream !== selectedStream.value) {
if (savedStream !== undefined && savedStream.id !== selectedStream.value?.id) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unrelated to the patch, just noticed that was wrong.

console.debug!('[WebRTC] trying to set stream...')
selectedStream.value = savedStream
}
Expand Down
36 changes: 35 additions & 1 deletion src/composables/webRTC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export class WebRTCManager {
private streamName: string | undefined
private session: Session | undefined
private rtcConfiguration: RTCConfiguration
private selectedICEIPs: string | undefined

private hasEnded = false
private signaller: Signaller
Expand All @@ -51,6 +52,7 @@ export class WebRTCManager {
*
* @param {Connection.URI} webRTCSignallingURI
* @param {RTCConfiguration} rtcConfiguration
* @param {string[]} selectedICEIPs
*/
constructor(webRTCSignallingURI: Connection.URI, rtcConfiguration: RTCConfiguration) {
console.debug('[WebRTC] Trying to connect to signalling server.')
Expand Down Expand Up @@ -78,9 +80,13 @@ export class WebRTCManager {
/**
*
* @param { Ref<Stream | undefined> } selectedStream - Stream to receive stream from
* @param { Ref<string> } selectedICEIPs
* @returns { startStreamReturn }
*/
public startStream(selectedStream: Ref<Stream | undefined>): startStreamReturn {
public startStream(
selectedStream: Ref<Stream | undefined>,
selectedICEIPs: Ref<string | undefined>
): startStreamReturn {
watch(selectedStream, (newStream, oldStream) => {
if (newStream?.id === oldStream?.id) {
return
Expand All @@ -97,6 +103,28 @@ export class WebRTCManager {
}
})

watch(selectedICEIPs, (newIp, oldIp) => {
if (newIp === oldIp) {
return
}

const msg = `Selected IP changed from "${oldIp}" to "${newIp}".`
console.debug('[WebRTC] ' + msg)

this.selectedICEIPs = newIp

if (this.streamName !== undefined) {
this.stopSession(msg)
}

if (this.streamName !== undefined) {
this.startSession()
}
})

// FIXME: I want to assign this.selectedICEIPs when startStream is first called
// this.selectedICEIPs = selectedICEIPs.value

return {
availableStreams: this.availableStreams,
mediaStream: this.mediaStream,
Expand Down Expand Up @@ -294,13 +322,19 @@ export class WebRTCManager {
* @param {string} receivedSessionId
*/
private onSessionIdReceived(stream: Stream, producerId: string, receivedSessionId: string): void {
const selectedICEIPs = []
if (this.selectedICEIPs) {
selectedICEIPs.push(this.selectedICEIPs)
}

// Create a new Session with the received Session ID
this.session = new Session(
receivedSessionId,
this.consumerId!,
stream,
this.signaller,
this.rtcConfiguration,
selectedICEIPs,
(event: RTCTrackEvent): void => this.onTrackAdded(event),
(_sessionId, reason) => this.onSessionClosed(reason)
)
Expand Down
10 changes: 10 additions & 0 deletions src/libs/webrtc/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export class Session {
private ended: boolean
private signaller: Signaller
private peerConnection: RTCPeerConnection
private selectedICEIPs: string[]
public rtcConfiguration: RTCConfiguration
public onTrackAdded?: OnTrackAddedCallback
public onClose?: OnCloseCallback
Expand All @@ -27,6 +28,7 @@ export class Session {
* @param {Stream} stream - The Stream instance for which this Session will be created with, given by the signalling server
* @param {Signaller} signaller - The Signaller instance for this Session to use
* @param {RTCConfiguration} rtcConfiguration - Configuration for the RTC connection, such as Turn and Stun servers
* @param {string[]} selectedICEIPs - A whitelist for ICE IP addresses, ignored if empty
* @param {OnTrackAddedCallback} onTrackAdded - An optional callback for when a track is added to this session
* @param {OnCloseCallback} onClose - An optional callback for when this session closes
*/
Expand All @@ -36,6 +38,7 @@ export class Session {
stream: Stream,
signaller: Signaller,
rtcConfiguration: RTCConfiguration,
selectedICEIPs: string[] = [],
onTrackAdded?: OnTrackAddedCallback,
onClose?: OnCloseCallback
) {
Expand All @@ -48,6 +51,7 @@ export class Session {
this.signaller = signaller
this.rtcConfiguration = rtcConfiguration
this.ended = false
this.selectedICEIPs = selectedICEIPs

this.peerConnection = this.createRTCPeerConnection(rtcConfiguration)

Expand Down Expand Up @@ -165,6 +169,12 @@ export class Session {
* @param {RTCIceCandidateInit} candidate - The ICE candidate received from the signalling server
*/
public onIncomingICE(candidate: RTCIceCandidateInit): void {
// Ignores unwanted routes, useful, for example, to prevent WebRTC to chose the wrong route, like when the OS default is WiFi but you want to receive the video via tether because of reliability
if (candidate.candidate && !this.selectedICEIPs.some((address) => candidate.candidate!.includes(address))) {
console.debug(`[WebRTC] [Session] ICE candidate ignored: ${JSON.stringify(candidate, null, 4)}`)
return
}

rafaellehmkuhl marked this conversation as resolved.
Show resolved Hide resolved
this.peerConnection
.addIceCandidate(candidate)
.then(() => console.debug(`[WebRTC] [Session] ICE candidate added: ${JSON.stringify(candidate, null, 4)}`))
Expand Down
Loading