Giter Site home page Giter Site logo

whip-whap-js's Introduction

WHIP-WHAP-JS: WebRTC WHIP and WHAP using Perfect Negotiation

Features

  • Does NOT encapsulate RTCPeerConnection
  • Small, simple code base
  • Auto-retries WHIP/WHAP on WebRTC failure, or when iceConnectionState is "disconnected"
  • Uses Perfect Negotiation for WHIP and WHAP as from the Jan-Ivar Bruaroey article here

WHIP

WHIP stands for WebRTC-HTTP ingestion protocol It's an IETF draft, and you can learn more here: https://github.com/wish-wg/webrtc-http-ingest-protocol

WHAP

WHIP stands for WebRTC-HTTP access protocol And this is the only place to learn about it for now. It's similar to WHIP, but intended for receiving audio/video from WebRTC servers. See the example below.

LICENSE AND PULL REQUEST TERMS

  • MIT license
  • Not part of license, but note regarding pull requests and submissions:
  • You agree any submissions or pull requests to this project are granted under the terms of the MIT open source license.

WHIP Example

WHIP example: send camera to WHIP server.

let url = '/whip'
let whipwhap = await import('https://cdn.jsdelivr.net/npm/whip-whap-js')
document.write('<video id="video1" autoplay controls muted width="1024" allowfullscreen/>')
let pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] })
pc.addEventListener('iceconnectionstatechange', whipwhap.handleIceStateChange)
pc.addEventListener('negotiationneeded', ev => whipwhap.handleNegotiationNeeded(ev, url))
let gum = await navigator.mediaDevices.getUserMedia({ audio: true, video: true })
pc.addTransceiver(gum.getVideoTracks()[0], { 'direction': 'sendonly' })
pc.addTransceiver(gum.getAudioTracks()[0], { 'direction': 'sendonly' })
let video1 = document.getElementById('video1')
video1.srcObject = gum
video1.play()

WHAP Example

WHAP example: receive video/audio from WebRTC server to HTML video element.

let url = '/whap'
let whipwhap = await import('https://cdn.jsdelivr.net/npm/whip-whap-js')
document.write('<video id="video1" autoplay controls muted width="1024" allowfullscreen/>')
let pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] })
pc.addTransceiver('video', { 'direction': 'recvonly' }) // build sdp
pc.addTransceiver('audio', { 'direction': 'recvonly' }) // build sdp
pc.addEventListener('iceconnectionstatechange', whipwhap.handleIceStateChange)
pc.addEventListener('negotiationneeded', ev => whipwhap.handleNegotiationNeeded(ev, url))
let video1 = document.getElementById('video1')
pc.ontrack = ev => video1.srcObject = ev.streams[0]

Location Header

Server should return a Location header on success, it could be used on DELETE

let location = null

pc.addEventListener('negotiationneeded', async ev => {
    location = await whipwhap.handleNegotiationNeeded(ev, url))
}

// to stop
if (locatioin) {
    fetch(location, {method: 'DELETE'})
}

When used in CORS, Access-Control-Expose-Headers: Location header is needed.

Functions

whip-whap-js

WHIP WHAP module.

whip-whap-js~handleNegotiationNeeded(event, url)

Event handler for 'negotiationneeded' event.

Kind: inner method of whip-whap-js

Param Type
event Event
url string

whip-whap-js~handleIceStateChange(event)

Event handler for 'iceconnectionstatechange' event.

Kind: inner method of whip-whap-js

Param Type
event Event

whip-whap-js's People

Contributors

cameronelliott avatar lminiero avatar seven1240 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

seven1240

whip-whap-js's Issues

Expect 201 not 202 from WHIP server

Hi,

thanks for sharing a new WHIP client implementation! As part of the IETF 112 Hackathon, I'm tinkering with other WHIP implementations I've become aware of, so I wanted to test yours too.

I tried your example by putting the snippet in a web page and setting the url to a Simple WHIP Server endpoint:

let url = 'http://localhost:7080/whip/endpoint/test'
let whipwhap = await import('https://cdn.jsdelivr.net/npm/whip-whap-js')
document.write('<video id="video1" autoplay controls muted width="1024" allowfullscreen/>')
let pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] })
pc.addEventListener('iceconnectionstatechange', whipwhap.handleIceStateChange)
pc.addEventListener('negotiationneeded', ev => whipwhap.handleNegotiationNeeded(ev, url))
let gum = await navigator.mediaDevices.getUserMedia({ audio: true, video: true })
pc.addTransceiver(gum.getVideoTracks()[0], { 'direction': 'sendonly' })
pc.addTransceiver(gum.getAudioTracks()[0], { 'direction': 'sendonly' })
let video1 = document.getElementById('video1')
video1.srcObject = gum
video1.play()

but that ends in an ICE failure in Janus. Checking the webrtc-internals, I see the code above never calls setRemoteDescription (which I thought was done automatically by whipwhap.handleNegotiationNeeded(ev, url) but doesn't seem to be the case), which means STUN always fails since the browser is not aware of the server ICE credentials.

Not sure if I'm doing something wrong or the example snippet needs fixing? Thanks!

restartICE problem

The js is nice and easy to use.

I hit a problem when my server has problem setup ICE, restartICE triggers negotiationneeded and in that callback it POST to /api/whap which create a Location: /api/whap/new-uuid, so when restartICE being called repeatedly it caused a lot of new-uuid generated on the server.

Question:

  • should negotiationneeded returns the response.headers.get('Location') so it's possible to DELETE it later?
  • should we pass a state and Location URL into negotiationneeded so when the second time it's being triggered it calls PATCH to the Location URL to update SDP instead of calling POST which means create a new one?

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.