@gjsify/webrtc 0.52.0 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/lib/esm/get-user-media.js +1 -1
- package/lib/esm/gst-init.js +3 -1
- package/lib/esm/register/media-devices.js +1 -1
- package/lib/esm/rtc-rtp-sender.js +1 -1
- package/lib/types/get-user-media.spec.d.ts +2 -0
- package/lib/types/register/media-devices.d.ts +1 -1
- package/package.json +10 -9
package/README.md
CHANGED
|
@@ -14,6 +14,12 @@ npm install @gjsify/webrtc
|
|
|
14
14
|
yarn add @gjsify/webrtc
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
The GStreamer side comes from the system: `webrtcbin` (gst-plugins-bad) and libnice's
|
|
18
|
+
GStreamer plugin for ICE. On Fedora `dnf install gstreamer1-plugins-bad-free libnice-gstreamer1`,
|
|
19
|
+
on Debian/Ubuntu `apt install gstreamer1.0-plugins-bad gstreamer1.0-nice`, on macOS
|
|
20
|
+
`brew install gstreamer libnice-gstreamer`. The native bridge (`@gjsify/webrtc-native`)
|
|
21
|
+
ships prebuilt for `linux-{x64,arm64,ppc64,s390x,riscv64}` and `darwin-{arm64,x64}`.
|
|
22
|
+
|
|
17
23
|
## Usage
|
|
18
24
|
|
|
19
25
|
```typescript
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";import{Gst as e,ensureGstInit as t}from"./gst-init.js";import{MediaStreamTrack as n}from"./media-stream-track.js";import{MediaStream as r}from"./media-stream.js";async function getUserMedia(i){if(t(),!i.audio&&!i.video)throw TypeError(`Failed to execute 'getUserMedia': At least one of audio or video must be requested`);let a=[];if(i.audio){let t=typeof i.audio==`object`?i.audio:{},r=_createAudioSource(),o=new e.Pipeline;o.add(r);let s=_buildAudioCaps(t);if(s){let t=e.ElementFactory.make(`capsfilter`,null);t.caps=e.Caps.from_string(s),o.add(t),r.link(t)}a.push(new n({kind:`audio`,label:r.name??`audio`,_gst:{source:r,pipeline:o}}))}if(i.video){let t=typeof i.video==`object`?i.video:{},r=_createVideoSource(),o=new e.Pipeline;o.add(r);let s=_buildVideoCaps(t);if(s){let t=e.ElementFactory.make(`capsfilter`,null);t.caps=e.Caps.from_string(s),o.add(t),r.link(t)}a.push(new n({kind:`video`,label:r.name??`video`,_gst:{source:r,pipeline:o}}))}return new r(a)}function _buildAudioCaps(e){let t=[];return e.sampleRate!=null&&t.push(`rate=${Math.trunc(e.sampleRate)}`),e.channelCount!=null&&t.push(`channels=${Math.trunc(e.channelCount)}`),t.length===0?null:`audio/x-raw,${t.join(`,`)}`}function _buildVideoCaps(e){let t=[];return e.width!=null&&t.push(`width=${Math.trunc(e.width)}`),e.height!=null&&t.push(`height=${Math.trunc(e.height)}`),e.frameRate!=null&&t.push(`framerate=${Math.trunc(e.frameRate)}/1`),t.length===0?null:`video/x-raw,${t.join(`,`)}`}function _probeSource(t,n){let r=e.ElementFactory.make(t,null);if(!r)return null;let i=e.ElementFactory.make(n,null),a=e.ElementFactory.make(`fakesink`,null);if(!i||!a)return r;let o=new e.Pipeline;o.add(r),o.add(i),o.add(a),r.link(i),i.link(a);let s=o.set_state(e.State.PLAYING);return s!==e.StateChangeReturn.FAILURE&&(s=o.get_state(500*Number(e.MSECOND))[0]),o.set_state(e.State.NULL),r.unlink(i),o.remove(r),s===e.StateChangeReturn.FAILURE?null:r}const i=new Map;function _chooseSource(t,n,r,a){let o=i.get(t);if(o)return e.ElementFactory.make(o,null);for(let e of n){let n=_probeSource(e,r);if(n)return i.set(t,e),n}return i.set(t,a),e.ElementFactory.make(a,null)}function
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{Gst as e,ensureGstInit as t}from"./gst-init.js";import{MediaStreamTrack as n}from"./media-stream-track.js";import{MediaStream as r}from"./media-stream.js";async function getUserMedia(i){if(t(),!i.audio&&!i.video)throw TypeError(`Failed to execute 'getUserMedia': At least one of audio or video must be requested`);let a=[];if(i.audio){let t=typeof i.audio==`object`?i.audio:{},r=_createAudioSource(t),o=new e.Pipeline;o.add(r);let s=_buildAudioCaps(t);if(s){let t=e.ElementFactory.make(`capsfilter`,null);t.caps=e.Caps.from_string(s),o.add(t),r.link(t)}a.push(new n({kind:`audio`,label:r.name??`audio`,_gst:{source:r,pipeline:o}}))}if(i.video){let t=typeof i.video==`object`?i.video:{},r=_createVideoSource(t),o=new e.Pipeline;o.add(r);let s=_buildVideoCaps(t);if(s){let t=e.ElementFactory.make(`capsfilter`,null);t.caps=e.Caps.from_string(s),o.add(t),r.link(t)}a.push(new n({kind:`video`,label:r.name??`video`,_gst:{source:r,pipeline:o}}))}return new r(a)}function _buildAudioCaps(e){let t=[];return e.sampleRate!=null&&t.push(`rate=${Math.trunc(e.sampleRate)}`),e.channelCount!=null&&t.push(`channels=${Math.trunc(e.channelCount)}`),t.length===0?null:`audio/x-raw,${t.join(`,`)}`}function _buildVideoCaps(e){let t=[];return e.width!=null&&t.push(`width=${Math.trunc(e.width)}`),e.height!=null&&t.push(`height=${Math.trunc(e.height)}`),e.frameRate!=null&&t.push(`framerate=${Math.trunc(e.frameRate)}/1`),t.length===0?null:`video/x-raw,${t.join(`,`)}`}function _probeSource(t,n){let r=e.ElementFactory.make(t,null);if(!r)return null;let i=e.ElementFactory.make(n,null),a=e.ElementFactory.make(`fakesink`,null);if(!i||!a)return r;let o=new e.Pipeline;o.add(r),o.add(i),o.add(a),r.link(i),i.link(a);let s=o.set_state(e.State.PLAYING);return s!==e.StateChangeReturn.FAILURE&&(s=o.get_state(500*Number(e.MSECOND))[0]),o.set_state(e.State.NULL),r.unlink(i),o.remove(r),s===e.StateChangeReturn.FAILURE?null:r}const i=new Map;function _chooseSource(t,n,r,a){let o=i.get(t);if(o)return e.ElementFactory.make(o,null);for(let e of n){let n=_probeSource(e,r);if(n)return i.set(t,e),n}return i.set(t,a),e.ElementFactory.make(a,null)}const a=[`device`,`target_object`,`path`],o=new Set;function _bindDevice(e,t,n){if(n){for(let e of a)if(e in t){t[e]=n;return}o.has(e)||(o.add(e),console.warn(`getUserMedia: deviceId ${JSON.stringify(n)} ignored — ${t.name} has no device property, so the ${e} source is the host default. getSupportedConstraints() reports deviceId: true.`))}}function _createAudioSource(e){let t=_chooseSource(`audio`,[`pipewiresrc`,`pulsesrc`,`autoaudiosrc`],`audioconvert`,`audiotestsrc`);try{t.is_live=!0}catch{}return i.get(`audio`)===`audiotestsrc`&&(t.wave=0),_bindDevice(`audio`,t,e.deviceId),t}function _createVideoSource(e){let t=_chooseSource(`video`,[`pipewiresrc`,`v4l2src`,`autovideosrc`],`videoconvert`,`videotestsrc`);return i.get(`video`)===`videotestsrc`&&(t.is_live=!0,t.pattern=0),_bindDevice(`video`,t,e.deviceId),t}export{getUserMedia};
|
package/lib/esm/gst-init.js
CHANGED
|
@@ -5,7 +5,9 @@ plugin — verify with: ls /usr/lib/gstreamer-1.0/libgstwebrtc.so
|
|
|
5
5
|
Otherwise: install the plugin from source, or use a distro that ships it.`:`GStreamer element "webrtcbin" not available. Install gst-plugins-bad:
|
|
6
6
|
Fedora: dnf install gstreamer1-plugins-bad-free gstreamer1-plugins-bad-free-extras
|
|
7
7
|
Ubuntu/Debian: apt install gstreamer1.0-plugins-bad
|
|
8
|
-
Alpine/postmarketOS: apk add gst-plugins-bad
|
|
8
|
+
Alpine/postmarketOS: apk add gst-plugins-bad
|
|
9
|
+
macOS (Homebrew): brew install gstreamer`),e.ElementFactory.find(`nicesrc`)||throwNotSupported(`GStreamer "nice" plugin (libnice-gstreamer) not available — required by webrtcbin.
|
|
9
10
|
Fedora: dnf install libnice-gstreamer1
|
|
10
11
|
Ubuntu/Debian: apt install gstreamer1.0-nice
|
|
12
|
+
macOS: brew install libnice-gstreamer
|
|
11
13
|
Verify with: gst-inspect-1.0 nicesrc`)}function throwNotSupported(e){throw new n(e,`NotSupportedError`)}export{e as Gst,ensureGstInit,ensureWebrtcbinAvailable,excludeUnboundedAutodetectCandidates};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{MediaDevices as e}from"../media-devices.js";const t=globalThis;t.navigator
|
|
1
|
+
import{MediaDevices as e}from"../media-devices.js";import"@gjsify/node-globals/register/navigator";const t=globalThis;t.navigator.mediaDevices===void 0&&(t.navigator.mediaDevices=new e);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";import{Gst as e}from"./gst-init.js";import{getRtcpCname as t}from"./gst-utils.js";import{asCapsFilterElement as n,asRtpPayloaderElement as r,asValveElement as i,asVp8EncElement as a,asWebRtcBin as o}from"./internal/gst-types.js";import{getRtpCapabilities as s}from"./rtp-capabilities.js";import{parseMediaSectionParams as c}from"./sdp-params.js";import{RTCDTMFSender as l}from"./rtc-dtmf-sender.js";import{TeeMultiplexer as u}from"./tee-multiplexer.js";let d=0;var RTCRtpSender=class{_gstSender;_track=null;_lastParams=null;_pipeline=null;_webrtcbin=null;_mlineIndex=-1;_elements=[];_valve=null;_linked=!1;_teeSrcPad=null;_getStatsForTrack=null;_transport=null;_dtmf=null;_kind=null;_transceiver=null;_onPipelineChanged=null;constructor(e,t,n){this._gstSender=e,this._pipeline=t??null,this._webrtcbin=n??null}get track(){return this._track}get dtmf(){if((this._track?.kind??this._kind)!==`audio`)return null;if(!this._dtmf){let e=new l;e._isStopped=()=>this._transceiver?.stopped??!1,e._getCurrentDirection=()=>this._transceiver?.currentDirection??null,this._dtmf=e}return this._dtmf}get transport(){return this._transport}_setTrack(e){e===null&&this._linked&&this._teardownPipeline(),this._track=e}_setMlineIndex(e){this._mlineIndex=e}_wirePipeline(t){if(this._linked||!this._pipeline||!this._webrtcbin)return;let o=t._gstSource;if(!o)return;let s=t,c;if(s._gstTee&&s._gstPipeline&&s._gstPipeline!==this._pipeline){let t=s._gstPipeline,n=s._gstTee;this._webrtcbin.get_parent()===this._pipeline&&(this._pipeline.set_state(e.State.NULL),this._pipeline.remove(this._webrtcbin)),t.add(this._webrtcbin),this._webrtcbin.sync_state_with_parent(),this._pipeline=t,this._onPipelineChanged?.(t);let r=n.request_pad_simple?n.request_pad_simple(`src_%u`):n.get_request_pad(`src_%u`);this._teeSrcPad=r,c=null}else if(s._teeMultiplexer){let e=s._teeMultiplexer.requestSrcPad();this._teeSrcPad=e,c=null}else if(s._gstPipeline&&s._gstPipeline!==this._pipeline&&s._senderConsumers>0){let t=s._gstPipeline,n=o.get_static_pad(`src`),r=n?.get_peer?.();r&&n.unlink(r),o.set_state(e.State.NULL),t.remove(o),this._pipeline.add(o),s._gstPipeline=this._pipeline;let i=new u(this._pipeline,o);if(s._teeMultiplexer=i,r){let
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{Gst as e}from"./gst-init.js";import{getRtcpCname as t}from"./gst-utils.js";import{asCapsFilterElement as n,asRtpPayloaderElement as r,asValveElement as i,asVp8EncElement as a,asWebRtcBin as o}from"./internal/gst-types.js";import{getRtpCapabilities as s}from"./rtp-capabilities.js";import{parseMediaSectionParams as c}from"./sdp-params.js";import{RTCDTMFSender as l}from"./rtc-dtmf-sender.js";import{TeeMultiplexer as u}from"./tee-multiplexer.js";let d=0;function releaseDownstreamStreamingThread(t){if(!t)return;let n=e.Event.new_flush_start();t.direction===e.PadDirection.SRC?t.push_event(n):t.send_event(n)}var RTCRtpSender=class{_gstSender;_track=null;_lastParams=null;_pipeline=null;_webrtcbin=null;_mlineIndex=-1;_elements=[];_valve=null;_linked=!1;_teeSrcPad=null;_getStatsForTrack=null;_transport=null;_dtmf=null;_kind=null;_transceiver=null;_onPipelineChanged=null;constructor(e,t,n){this._gstSender=e,this._pipeline=t??null,this._webrtcbin=n??null}get track(){return this._track}get dtmf(){if((this._track?.kind??this._kind)!==`audio`)return null;if(!this._dtmf){let e=new l;e._isStopped=()=>this._transceiver?.stopped??!1,e._getCurrentDirection=()=>this._transceiver?.currentDirection??null,this._dtmf=e}return this._dtmf}get transport(){return this._transport}_setTrack(e){e===null&&this._linked&&this._teardownPipeline(),this._track=e}_setMlineIndex(e){this._mlineIndex=e}_wirePipeline(t){if(this._linked||!this._pipeline||!this._webrtcbin)return;let o=t._gstSource;if(!o)return;let s=t,c;if(s._gstTee&&s._gstPipeline&&s._gstPipeline!==this._pipeline){let t=s._gstPipeline,n=s._gstTee;this._webrtcbin.get_parent()===this._pipeline&&(this._pipeline.set_state(e.State.NULL),this._pipeline.remove(this._webrtcbin)),t.add(this._webrtcbin),this._webrtcbin.sync_state_with_parent(),this._pipeline=t,this._onPipelineChanged?.(t);let r=n.request_pad_simple?n.request_pad_simple(`src_%u`):n.get_request_pad(`src_%u`);this._teeSrcPad=r,c=null}else if(s._teeMultiplexer){let e=s._teeMultiplexer.requestSrcPad();this._teeSrcPad=e,c=null}else if(s._gstPipeline&&s._gstPipeline!==this._pipeline&&s._senderConsumers>0){let t=s._gstPipeline,n=o.get_static_pad(`src`),r=n?.get_peer?.();r&&(releaseDownstreamStreamingThread(n),n.unlink(r)),o.set_state(e.State.NULL),t.remove(o),this._pipeline.add(o),s._gstPipeline=this._pipeline;let i=new u(this._pipeline,o);if(s._teeMultiplexer=i,r){let t=i.requestSrcPad();t&&t.link(r),r.send_event(e.Event.new_flush_stop(!0))}let a=i.requestSrcPad();this._teeSrcPad=a,c=null}else{let t=s._gstPipeline;t&&t!==this._pipeline&&(o.set_state(e.State.NULL),t.remove(o),s._gstPipeline=this._pipeline),o.get_parent()!==this._pipeline&&this._pipeline.add(o),c=o}let l=i(e.ElementFactory.make(`valve`,null));l.drop=!t.enabled,this._valve=l,this._pipeline.add(l);let d=[l],f;if(t.kind===`audio`){let t=e.ElementFactory.make(`audioconvert`,null),i=e.ElementFactory.make(`audioresample`,null),a=e.ElementFactory.make(`opusenc`,null),o=r(e.ElementFactory.make(`rtpopuspay`,null));o.pt=111;let s=n(e.ElementFactory.make(`capsfilter`,null));s.caps=e.Caps.from_string(`application/x-rtp,media=audio,encoding-name=OPUS,clock-rate=48000,payload=111`),d.push(t,i,a,o,s);for(let e of d)this._pipeline.add(e);if(this._teeSrcPad){let e=l.get_static_pad(`sink`);this._teeSrcPad.link(e)}else c&&c.link(l);l.link(t),t.link(i),i.link(a),a.link(o),o.link(s),f=s}else{let t=e.ElementFactory.make(`videoconvert`,null),i=e.ElementFactory.make(`videoscale`,null),o=a(e.ElementFactory.make(`vp8enc`,null));o.deadline=1,o.keyframe_max_dist=60;let s=r(e.ElementFactory.make(`rtpvp8pay`,null));s.pt=96;let u=n(e.ElementFactory.make(`capsfilter`,null));u.caps=e.Caps.from_string(`application/x-rtp,media=video,encoding-name=VP8,clock-rate=90000,payload=96`),d.push(t,i,o,s,u);for(let e of d)this._pipeline.add(e);if(this._teeSrcPad){let e=l.get_static_pad(`sink`);this._teeSrcPad.link(e)}else c&&c.link(l);l.link(t),t.link(i),i.link(o),o.link(s),s.link(u),f=u}let p=this._mlineIndex>=0?`sink_${this._mlineIndex}`:`sink_%u`,m=this._webrtcbin.request_pad_simple?this._webrtcbin.request_pad_simple(p):this._webrtcbin.get_request_pad(p);m&&f.get_static_pad(`src`).link(m);let h=this._teeSrcPad?d:[o,...d];for(let e of h)e.sync_state_with_parent();this._elements=h,this._linked=!0,t._senderConsumers++,t._setEnableCallback(e=>{this._valve&&(this._valve.drop=!e)})}_teardownPipeline(){if(this._linked){if(this._track&&(this._track._setEnableCallback(null),this._track._senderConsumers=Math.max(0,this._track._senderConsumers-1)),releaseDownstreamStreamingThread(this._valve?.get_static_pad(`sink`)),this._teeSrcPad&&this._track?._teeMultiplexer){try{this._track._teeMultiplexer.releaseSrcPad(this._teeSrcPad)}catch{}this._teeSrcPad=null}for(let t of[...this._elements].reverse())try{t.set_state(e.State.NULL),this._pipeline?.remove(t)}catch{}this._elements=[],this._valve=null,this._linked=!1}}getParameters(){let e=this._negotiatedParams();this._lastParams?(this._lastParams.codecs=e.codecs,this._lastParams.headerExtensions=e.headerExtensions,this._lastParams.rtcp=e.rtcp):this._lastParams={transactionId:String(++d),encodings:[],...e};let t=this._lastParams;return{...t,encodings:[...t.encodings],codecs:t.codecs.map(e=>({...e})),headerExtensions:t.headerExtensions.map(e=>({...e})),rtcp:{...t.rtcp}}}_negotiatedParams(){let e={},n={codecs:[],headerExtensions:[],rtcp:e};if(!this._webrtcbin)return n;let r=t(this._webrtcbin);r!==void 0&&(e.cname=r);let i=o(this._webrtcbin),a=(i.current_remote_description??i.current_local_description)?.sdp?.as_text?.()??``;if(!a)return n;let s=this._track?.kind??this._kind,l=c(a,{mid:this._transceiver?.mid??void 0,mlineIndex:this._mlineIndex>=0?this._mlineIndex:void 0,kind:s??void 0});return l?(n.codecs=l.codecs,n.headerExtensions=l.headerExtensions,e.reducedSize=l.reducedSize,n):n}async setParameters(e){if(!this._lastParams)throw new DOMException(`getParameters must be called before setParameters`,`InvalidStateError`);if(e.transactionId!==this._lastParams.transactionId)throw new DOMException(`transactionId mismatch`,`InvalidModificationError`);this._lastParams=null}async replaceTrack(t){if(t===null){this._teardownPipeline(),this._track=null;return}if(this._track!==null&&t.kind!==this._track.kind)throw TypeError(`Cannot replace track with different kind`);let n=t;if(this._linked&&n._gstSource){let t=this._elements[0],r=n._gstSource,i=n._gstPipeline;i&&i!==this._pipeline&&(r.set_state(e.State.NULL),i.remove(r),n._gstPipeline=this._pipeline),t.set_state(e.State.NULL),this._valve&&t.unlink(this._valve),this._pipeline?.remove(t),this._pipeline?.add(r),this._valve&&r.link(this._valve),r.sync_state_with_parent(),this._elements[0]=r}else n._gstSource&&this._wirePipeline(t);this._track&&this._track._setEnableCallback(null),this._track=t,this._linked&&t._setEnableCallback(e=>{this._valve&&(this._valve.drop=!e)})}async getStats(){if(this._getStatsForTrack&&this._track)return this._getStatsForTrack(this._track);let{RTCStatsReport:e}=await import(`./rtc-stats-report.js`);return new e}setStreams(...e){}static getCapabilities(e){return s(e)}};export{RTCRtpSender};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import '@gjsify/node-globals/register/navigator';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/webrtc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.0",
|
|
4
4
|
"description": "W3C WebRTC API for GJS using GStreamer webrtcbin as the peer-connection backend",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "lib/esm/index.js",
|
|
@@ -61,22 +61,23 @@
|
|
|
61
61
|
"peer-connection"
|
|
62
62
|
],
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@gjsify/buffer": "^0.
|
|
65
|
-
"@gjsify/dom-events": "^0.
|
|
66
|
-
"@gjsify/dom-exception": "^0.
|
|
67
|
-
"@gjsify/
|
|
68
|
-
"@gjsify/
|
|
64
|
+
"@gjsify/buffer": "^0.54.0",
|
|
65
|
+
"@gjsify/dom-events": "^0.54.0",
|
|
66
|
+
"@gjsify/dom-exception": "^0.54.0",
|
|
67
|
+
"@gjsify/node-globals": "^0.54.0",
|
|
68
|
+
"@gjsify/webcrypto": "^0.54.0",
|
|
69
|
+
"@gjsify/webrtc-native": "^0.54.0"
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
72
|
+
"@girs/gio-2.0": "5.4.0",
|
|
71
73
|
"@girs/gjs": "5.4.0",
|
|
72
74
|
"@girs/glib-2.0": "5.4.0",
|
|
73
75
|
"@girs/gobject-2.0": "5.4.0",
|
|
74
76
|
"@girs/gst-1.0": "5.4.0",
|
|
75
77
|
"@girs/gstsdp-1.0": "5.4.0",
|
|
76
78
|
"@girs/gstwebrtc-1.0": "5.4.0",
|
|
77
|
-
"@gjsify/
|
|
78
|
-
"@gjsify/
|
|
79
|
-
"@gjsify/unit": "^0.52.0",
|
|
79
|
+
"@gjsify/cli": "^0.54.0",
|
|
80
|
+
"@gjsify/unit": "^0.54.0",
|
|
80
81
|
"@types/node": "^25.9.2",
|
|
81
82
|
"typescript": "^6.0.3"
|
|
82
83
|
},
|