‘liusuyi’
2023-08-26 76817b8c752b12030ab285bcb5b2effebfa9a248
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/**
 * Performs the actual SDP exchange.
 *
 * 1. Constructs the client's SDP offer
 * 2. Sends the SDP offer to the server,
 * 3. Awaits the server's offer.
 *
 * SDP describes what kind of media we can send and how the server and client communicate.
 *
 * https://developer.mozilla.org/en-US/docs/Glossary/SDP
 * https://www.ietf.org/archive/id/draft-ietf-wish-whip-01.html#name-protocol-operation
 */
export default async function negotiateConnectionWithClientOffer(
    peerConnection,
    endpoint
) {
    /** https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createOffer */
    const offer = await peerConnection.createOffer();
    /** https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/setLocalDescription */
    await peerConnection.setLocalDescription(offer);
    /** Wait for ICE gathering to complete */
    let ofr = await waitToCompleteICEGathering(peerConnection);
    if (!ofr) {
        throw Error("failed to gather ICE candidates for offer");
    }
    /**
     * As long as the connection is open, attempt to...
     */
    while (peerConnection.connectionState !== "closed") {
        /**
         * This response contains the server's SDP offer.
         * This specifies how the client should communicate,
         * and what kind of media client and server have negotiated to exchange.
         */
        let response = await postSDPOffer(endpoint, ofr.sdp);
        if (response.status === 201) {
            let answerSDP = await response.text();
            await peerConnection.setRemoteDescription(
                new RTCSessionDescription({ type: "answer", sdp: answerSDP })
            );
            return response.headers.get("Location");
        } else if (response.status === 405) {
            console.error("Update the URL passed into the WHIP or WHEP client");
        } else {
            const errorMessage = await response.text();
            console.error(errorMessage);
        }
        /** Limit reconnection attempts to at-most once every 5 seconds */
        await new Promise((r) => setTimeout(r, 5000));
    }
}
async function postSDPOffer(endpoint, data) {
    return await fetch(endpoint, {
        method: "POST",
        mode: "cors",
        headers: {
            "content-type": "application/sdp",
        },
        body: data,
    });
}
/**
 * Receives an RTCPeerConnection and waits until
 * the connection is initialized or a timeout passes.
 *
 * https://www.ietf.org/archive/id/draft-ietf-wish-whip-01.html#section-4.1
 * https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceGatheringState
 * https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/icegatheringstatechange_event
 */
async function waitToCompleteICEGathering(peerConnection) {
    return new Promise((resolve) => {
        /** Wait at most 1 second for ICE gathering. */
        setTimeout(function () {
            resolve(peerConnection.localDescription);
        }, 1000);
        peerConnection.onicegatheringstatechange = (ev) =>
            peerConnection.iceGatheringState === "complete" &&
            resolve(peerConnection.localDescription);
    });
}