Allow remote WebRTC playback using STUN server from RTSPtoWeb integration (#13942)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Allen Porter 2022-10-03 18:10:57 -07:00 committed by GitHub
parent 70d4fe1285
commit c77e5dee84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 45 additions and 2 deletions

View File

@ -7,7 +7,9 @@ import {
TemplateResult, TemplateResult,
} from "lit"; } from "lit";
import { customElement, property, state, query } from "lit/decorators"; import { customElement, property, state, query } from "lit/decorators";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { handleWebRtcOffer, WebRtcAnswer } from "../data/camera"; import { handleWebRtcOffer, WebRtcAnswer } from "../data/camera";
import { fetchWebRtcSettings } from "../data/rtsp_to_webrtc";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import "./ha-alert"; import "./ha-alert";
@ -86,7 +88,8 @@ class HaWebRtcPlayer extends LitElement {
private async _startWebRtc(): Promise<void> { private async _startWebRtc(): Promise<void> {
this._error = undefined; this._error = undefined;
const peerConnection = new RTCPeerConnection(); const configuration = await this._fetchPeerConfiguration();
const peerConnection = new RTCPeerConnection(configuration);
// Some cameras (such as nest) require a data channel to establish a stream // Some cameras (such as nest) require a data channel to establish a stream
// however, not used by any integrations. // however, not used by any integrations.
peerConnection.createDataChannel("dataSendChannel"); peerConnection.createDataChannel("dataSendChannel");
@ -102,12 +105,25 @@ class HaWebRtcPlayer extends LitElement {
); );
await peerConnection.setLocalDescription(offer); await peerConnection.setLocalDescription(offer);
let candidates = ""; // Build an Offer SDP string with ice candidates
const iceResolver = new Promise<void>((resolve) => {
peerConnection.addEventListener("icecandidate", async (event) => {
if (!event.candidate) {
resolve(); // Gathering complete
return;
}
candidates += `a=${event.candidate.candidate}\r\n`;
});
});
await iceResolver;
const offer_sdp = offer.sdp! + candidates;
let webRtcAnswer: WebRtcAnswer; let webRtcAnswer: WebRtcAnswer;
try { try {
webRtcAnswer = await handleWebRtcOffer( webRtcAnswer = await handleWebRtcOffer(
this.hass, this.hass,
this.entityid, this.entityid,
offer.sdp! offer_sdp
); );
} catch (err: any) { } catch (err: any) {
this._error = "Failed to start WebRTC stream: " + err.message; this._error = "Failed to start WebRTC stream: " + err.message;
@ -138,6 +154,23 @@ class HaWebRtcPlayer extends LitElement {
this._peerConnection = peerConnection; this._peerConnection = peerConnection;
} }
private async _fetchPeerConfiguration(): Promise<RTCConfiguration> {
if (!isComponentLoaded(this.hass!, "rtsp_to_webrtc")) {
return {};
}
const settings = await fetchWebRtcSettings(this.hass!);
if (!settings || !settings.stun_server) {
return {};
}
return {
iceServers: [
{
urls: [`stun:${settings.stun_server!}`],
},
],
};
}
private _cleanUp() { private _cleanUp() {
if (this._remoteStream) { if (this._remoteStream) {
this._remoteStream.getTracks().forEach((track) => { this._remoteStream.getTracks().forEach((track) => {

View File

@ -0,0 +1,10 @@
import { HomeAssistant } from "../types";
export interface WebRtcSettings {
stun_server?: string;
}
export const fetchWebRtcSettings = async (hass: HomeAssistant) =>
hass.callWS<WebRtcSettings>({
type: "rtsp_to_webrtc/get_settings",
});