werift 0.23.0 → 0.24.1
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 +46 -0
- package/lib/common/src/binary.js +2 -3
- package/lib/common/src/binary.js.map +1 -1
- package/lib/common/src/transport.d.ts +22 -9
- package/lib/common/src/transport.js +152 -25
- package/lib/common/src/transport.js.map +1 -1
- package/lib/ice/src/candidate.d.ts +16 -1
- package/lib/ice/src/candidate.js +99 -15
- package/lib/ice/src/candidate.js.map +1 -1
- package/lib/ice/src/ice.d.ts +2 -0
- package/lib/ice/src/ice.js +175 -50
- package/lib/ice/src/ice.js.map +1 -1
- package/lib/ice/src/iceBase.d.ts +23 -2
- package/lib/ice/src/iceBase.js +63 -1
- package/lib/ice/src/iceBase.js.map +1 -1
- package/lib/ice/src/index.d.ts +2 -0
- package/lib/ice/src/index.js +2 -0
- package/lib/ice/src/index.js.map +1 -1
- package/lib/ice/src/internal/selectAddresses.d.ts +9 -0
- package/lib/ice/src/internal/selectAddresses.js +40 -0
- package/lib/ice/src/internal/selectAddresses.js.map +1 -0
- package/lib/ice/src/stun/const.d.ts +1 -26
- package/lib/ice/src/stun/const.js +15 -54
- package/lib/ice/src/stun/const.js.map +1 -1
- package/lib/ice/src/stun/message.d.ts +1 -25
- package/lib/ice/src/stun/message.js +15 -149
- package/lib/ice/src/stun/message.js.map +1 -1
- package/lib/ice/src/stun/protocol.d.ts +1 -1
- package/lib/ice/src/stun/protocol.js +2 -2
- package/lib/ice/src/stun/protocol.js.map +1 -1
- package/lib/ice/src/stun/tcpFrame.d.ts +5 -0
- package/lib/ice/src/stun/tcpFrame.js +26 -0
- package/lib/ice/src/stun/tcpFrame.js.map +1 -0
- package/lib/ice/src/stun/tcpProtocol.d.ts +51 -0
- package/lib/ice/src/stun/tcpProtocol.js +326 -0
- package/lib/ice/src/stun/tcpProtocol.js.map +1 -0
- package/lib/ice/src/stun/transaction.d.ts +2 -1
- package/lib/ice/src/stun/transaction.js +9 -3
- package/lib/ice/src/stun/transaction.js.map +1 -1
- package/lib/ice/src/turn/frame.d.ts +1 -0
- package/lib/ice/src/turn/frame.js +18 -0
- package/lib/ice/src/turn/frame.js.map +1 -0
- package/lib/ice/src/turn/protocol.d.ts +9 -7
- package/lib/ice/src/turn/protocol.js +32 -44
- package/lib/ice/src/turn/protocol.js.map +1 -1
- package/lib/ice/src/types/model.d.ts +1 -1
- package/lib/ice/src/types/model.js.map +1 -1
- package/lib/ice/src/utils.js +2 -31
- package/lib/ice/src/utils.js.map +1 -1
- package/lib/{ice → ice-server}/src/stun/attributes.d.ts +12 -5
- package/lib/{ice → ice-server}/src/stun/attributes.js +69 -66
- package/lib/ice-server/src/stun/attributes.js.map +1 -0
- package/lib/ice-server/src/stun/const.d.ts +28 -0
- package/lib/ice-server/src/stun/const.js +88 -0
- package/lib/ice-server/src/stun/const.js.map +1 -0
- package/lib/ice-server/src/stun/ip.d.ts +12 -0
- package/lib/ice-server/src/stun/ip.js +88 -0
- package/lib/ice-server/src/stun/ip.js.map +1 -0
- package/lib/ice-server/src/stun/message.d.ts +37 -0
- package/lib/ice-server/src/stun/message.js +190 -0
- package/lib/ice-server/src/stun/message.js.map +1 -0
- package/lib/ice-server/src/turn/auth.d.ts +1 -0
- package/lib/ice-server/src/turn/auth.js +10 -0
- package/lib/ice-server/src/turn/auth.js.map +1 -0
- package/lib/ice-server/src/turn/frame.d.ts +13 -0
- package/lib/ice-server/src/turn/frame.js +69 -0
- package/lib/ice-server/src/turn/frame.js.map +1 -0
- package/lib/index.mjs +2866 -946
- package/lib/nonstandard/index.mjs +1872 -455
- package/lib/rtp/src/codec/av1.js +11 -5
- package/lib/rtp/src/codec/av1.js.map +1 -1
- package/lib/rtp/src/codec/leb128.d.ts +5 -0
- package/lib/rtp/src/codec/leb128.js +24 -0
- package/lib/rtp/src/codec/leb128.js.map +1 -0
- package/lib/rtp/src/extra/container/mp4/container.d.ts +9 -6
- package/lib/rtp/src/extra/container/mp4/container.js +258 -173
- package/lib/rtp/src/extra/container/mp4/container.js.map +1 -1
- package/lib/rtp/src/extra/container/mp4/h264.d.ts +1 -0
- package/lib/rtp/src/extra/container/mp4/h264.js +13 -0
- package/lib/rtp/src/extra/container/mp4/h264.js.map +1 -1
- package/lib/rtp/src/extra/processor/mp4.d.ts +11 -7
- package/lib/rtp/src/extra/processor/mp4.js +115 -60
- package/lib/rtp/src/extra/processor/mp4.js.map +1 -1
- package/lib/rtp/src/extra/processor/mp4Callback.d.ts +2 -3
- package/lib/rtp/src/extra/processor/mp4Callback.js +5 -3
- package/lib/rtp/src/extra/processor/mp4Callback.js.map +1 -1
- package/lib/rtp/src/rtp/rtx.js +4 -6
- package/lib/rtp/src/rtp/rtx.js.map +1 -1
- package/lib/rtp/src/srtp/context/context.d.ts +2 -0
- package/lib/rtp/src/srtp/context/context.js +12 -12
- package/lib/rtp/src/srtp/context/context.js.map +1 -1
- package/lib/sctp/src/param.js +31 -15
- package/lib/sctp/src/param.js.map +1 -1
- package/lib/sctp/src/sctp.js +10 -9
- package/lib/sctp/src/sctp.js.map +1 -1
- package/lib/webrtc/src/dataChannel.d.ts +3 -2
- package/lib/webrtc/src/dataChannel.js +9 -2
- package/lib/webrtc/src/dataChannel.js.map +1 -1
- package/lib/webrtc/src/errors.d.ts +3 -0
- package/lib/webrtc/src/errors.js +11 -0
- package/lib/webrtc/src/errors.js.map +1 -0
- package/lib/webrtc/src/helper.d.ts +5 -1
- package/lib/webrtc/src/helper.js +33 -1
- package/lib/webrtc/src/helper.js.map +1 -1
- package/lib/webrtc/src/media/receiver/statistics.d.ts +4 -1
- package/lib/webrtc/src/media/receiver/statistics.js +22 -0
- package/lib/webrtc/src/media/receiver/statistics.js.map +1 -1
- package/lib/webrtc/src/media/rtpReceiver.d.ts +15 -2
- package/lib/webrtc/src/media/rtpReceiver.js +135 -20
- package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
- package/lib/webrtc/src/media/rtpSender.d.ts +32 -7
- package/lib/webrtc/src/media/rtpSender.js +179 -31
- package/lib/webrtc/src/media/rtpSender.js.map +1 -1
- package/lib/webrtc/src/media/rtpTransceiver.d.ts +13 -4
- package/lib/webrtc/src/media/rtpTransceiver.js +26 -5
- package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
- package/lib/webrtc/src/media/stats.d.ts +15 -1
- package/lib/webrtc/src/media/stats.js +55 -1
- package/lib/webrtc/src/media/stats.js.map +1 -1
- package/lib/webrtc/src/media/track.d.ts +2 -1
- package/lib/webrtc/src/media/track.js +7 -1
- package/lib/webrtc/src/media/track.js.map +1 -1
- package/lib/webrtc/src/nonstandard/dummyMedia.d.ts +19 -0
- package/lib/webrtc/src/nonstandard/dummyMedia.js +157 -0
- package/lib/webrtc/src/nonstandard/dummyMedia.js.map +1 -0
- package/lib/webrtc/src/nonstandard/navigator.d.ts +18 -8
- package/lib/webrtc/src/nonstandard/navigator.js +96 -32
- package/lib/webrtc/src/nonstandard/navigator.js.map +1 -1
- package/lib/webrtc/src/nonstandard/userMedia/packetizer.d.ts +16 -0
- package/lib/webrtc/src/nonstandard/userMedia/packetizer.js +360 -0
- package/lib/webrtc/src/nonstandard/userMedia/packetizer.js.map +1 -0
- package/lib/webrtc/src/nonstandard/userMedia.d.ts +71 -31
- package/lib/webrtc/src/nonstandard/userMedia.js +454 -108
- package/lib/webrtc/src/nonstandard/userMedia.js.map +1 -1
- package/lib/webrtc/src/peerConnection.d.ts +183 -50
- package/lib/webrtc/src/peerConnection.js +458 -109
- package/lib/webrtc/src/peerConnection.js.map +1 -1
- package/lib/webrtc/src/sctpManager.d.ts +1 -1
- package/lib/webrtc/src/sctpManager.js +27 -7
- package/lib/webrtc/src/sctpManager.js.map +1 -1
- package/lib/webrtc/src/sdp.d.ts +10 -7
- package/lib/webrtc/src/sdp.js +40 -44
- package/lib/webrtc/src/sdp.js.map +1 -1
- package/lib/webrtc/src/sdpManager.d.ts +4 -3
- package/lib/webrtc/src/sdpManager.js +50 -27
- package/lib/webrtc/src/sdpManager.js.map +1 -1
- package/lib/webrtc/src/secureTransportManager.d.ts +13 -4
- package/lib/webrtc/src/secureTransportManager.js +100 -22
- package/lib/webrtc/src/secureTransportManager.js.map +1 -1
- package/lib/webrtc/src/transceiverManager.d.ts +5 -4
- package/lib/webrtc/src/transceiverManager.js +61 -62
- package/lib/webrtc/src/transceiverManager.js.map +1 -1
- package/lib/webrtc/src/transport/dtls.d.ts +15 -4
- package/lib/webrtc/src/transport/dtls.js +91 -23
- package/lib/webrtc/src/transport/dtls.js.map +1 -1
- package/lib/webrtc/src/transport/ice.d.ts +21 -3
- package/lib/webrtc/src/transport/ice.js +159 -30
- package/lib/webrtc/src/transport/ice.js.map +1 -1
- package/lib/webrtc/src/transport/sctp.d.ts +1 -0
- package/lib/webrtc/src/transport/sctp.js +26 -22
- package/lib/webrtc/src/transport/sctp.js.map +1 -1
- package/lib/webrtc/src/utils.d.ts +12 -4
- package/lib/webrtc/src/utils.js +118 -16
- package/lib/webrtc/src/utils.js.map +1 -1
- package/package.json +7 -10
- package/lib/ice/src/stun/attributes.js.map +0 -1
- package/lib/rtp/src/extra/container/mp4/mp4box.d.ts +0 -5
- package/lib/rtp/src/extra/container/mp4/mp4box.js +0 -32
- package/lib/rtp/src/extra/container/mp4/mp4box.js.map +0 -1
|
@@ -1,11 +1,20 @@
|
|
|
1
|
+
// ../rtp/src/extra/container/mp4/container.ts
|
|
2
|
+
import {
|
|
3
|
+
EncodedAudioPacketSource,
|
|
4
|
+
EncodedPacket,
|
|
5
|
+
EncodedVideoPacketSource,
|
|
6
|
+
Mp4OutputFormat,
|
|
7
|
+
NullTarget,
|
|
8
|
+
Output
|
|
9
|
+
} from "mediabunny";
|
|
10
|
+
|
|
1
11
|
// ../common/src/binary.ts
|
|
2
12
|
import { randomBytes } from "crypto";
|
|
3
|
-
import { jspack } from "@shinyoshiaki/jspack";
|
|
4
13
|
function random16() {
|
|
5
|
-
return
|
|
14
|
+
return randomBytes(2).readUInt16BE(0);
|
|
6
15
|
}
|
|
7
16
|
function random32() {
|
|
8
|
-
return
|
|
17
|
+
return randomBytes(4).readUInt32BE(0);
|
|
9
18
|
}
|
|
10
19
|
function bufferXor(a, b) {
|
|
11
20
|
if (a.length !== b.length) {
|
|
@@ -464,7 +473,7 @@ var WeriftError = class extends Error {
|
|
|
464
473
|
var debug = Debug.debug;
|
|
465
474
|
|
|
466
475
|
// ../common/src/event.ts
|
|
467
|
-
var
|
|
476
|
+
var Event2 = class {
|
|
468
477
|
event = {
|
|
469
478
|
stack: [],
|
|
470
479
|
promiseStack: [],
|
|
@@ -570,7 +579,7 @@ var Event = class {
|
|
|
570
579
|
error
|
|
571
580
|
);
|
|
572
581
|
};
|
|
573
|
-
watch = (cb, timeLimit) => new Promise((
|
|
582
|
+
watch = (cb, timeLimit) => new Promise((resolve2, reject) => {
|
|
574
583
|
const timeout = timeLimit && setTimeout(() => {
|
|
575
584
|
reject("Event watch timeout");
|
|
576
585
|
}, timeLimit);
|
|
@@ -579,22 +588,22 @@ var Event = class {
|
|
|
579
588
|
if (done) {
|
|
580
589
|
if (timeout) clearTimeout(timeout);
|
|
581
590
|
unSubscribe();
|
|
582
|
-
|
|
591
|
+
resolve2(args);
|
|
583
592
|
}
|
|
584
593
|
});
|
|
585
594
|
});
|
|
586
|
-
asPromise = (timeLimit) => new Promise((
|
|
595
|
+
asPromise = (timeLimit) => new Promise((resolve2, reject) => {
|
|
587
596
|
const timeout = timeLimit && setTimeout(() => {
|
|
588
597
|
reject("Event asPromise timeout");
|
|
589
598
|
}, timeLimit);
|
|
590
599
|
this.once(
|
|
591
600
|
(...args) => {
|
|
592
601
|
if (timeout) clearTimeout(timeout);
|
|
593
|
-
|
|
602
|
+
resolve2(args);
|
|
594
603
|
},
|
|
595
604
|
() => {
|
|
596
605
|
if (timeout) clearTimeout(timeout);
|
|
597
|
-
|
|
606
|
+
resolve2([]);
|
|
598
607
|
},
|
|
599
608
|
(err5) => {
|
|
600
609
|
if (timeout) clearTimeout(timeout);
|
|
@@ -630,7 +639,8 @@ import {
|
|
|
630
639
|
createSocket as createSocket2
|
|
631
640
|
} from "dgram";
|
|
632
641
|
import * as net from "node:net";
|
|
633
|
-
import
|
|
642
|
+
import * as tls from "node:tls";
|
|
643
|
+
import { connect as connect2 } from "node:net";
|
|
634
644
|
var log = debug("werift-ice:packages/ice/src/transport.ts");
|
|
635
645
|
var UdpTransport = class _UdpTransport {
|
|
636
646
|
constructor(socketType, options = {}) {
|
|
@@ -717,11 +727,89 @@ var UdpTransport = class _UdpTransport {
|
|
|
717
727
|
});
|
|
718
728
|
};
|
|
719
729
|
var TcpTransport = class _TcpTransport {
|
|
730
|
+
type = "tcp";
|
|
731
|
+
stream;
|
|
720
732
|
constructor(addr) {
|
|
721
|
-
this.
|
|
733
|
+
this.stream = new StreamTransport(
|
|
734
|
+
"tcp",
|
|
735
|
+
() => connect2({ port: addr[1], host: addr[0] })
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
static async init(addr) {
|
|
739
|
+
const transport = new _TcpTransport(addr);
|
|
740
|
+
await transport.init();
|
|
741
|
+
return transport;
|
|
742
|
+
}
|
|
743
|
+
async init() {
|
|
744
|
+
await this.stream.waitForConnect();
|
|
745
|
+
}
|
|
746
|
+
get address() {
|
|
747
|
+
return this.stream.address;
|
|
748
|
+
}
|
|
749
|
+
get closed() {
|
|
750
|
+
return this.stream.closed;
|
|
751
|
+
}
|
|
752
|
+
get onData() {
|
|
753
|
+
return this.stream.onData;
|
|
754
|
+
}
|
|
755
|
+
set onData(handler) {
|
|
756
|
+
this.stream.onData = handler;
|
|
757
|
+
}
|
|
758
|
+
send = async (data, addr) => {
|
|
759
|
+
await this.stream.send(data, addr);
|
|
760
|
+
};
|
|
761
|
+
close = async () => {
|
|
762
|
+
await this.stream.close();
|
|
763
|
+
};
|
|
764
|
+
};
|
|
765
|
+
var TlsTransport = class _TlsTransport {
|
|
766
|
+
type = "tls";
|
|
767
|
+
stream;
|
|
768
|
+
constructor(addr, options = {}) {
|
|
769
|
+
this.stream = new StreamTransport(
|
|
770
|
+
"tls",
|
|
771
|
+
() => tls.connect({
|
|
772
|
+
...options,
|
|
773
|
+
host: addr[0],
|
|
774
|
+
port: addr[1]
|
|
775
|
+
})
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
static async init(addr, options = {}) {
|
|
779
|
+
const transport = new _TlsTransport(addr, options);
|
|
780
|
+
await transport.init();
|
|
781
|
+
return transport;
|
|
782
|
+
}
|
|
783
|
+
async init() {
|
|
784
|
+
await this.stream.waitForConnect();
|
|
785
|
+
}
|
|
786
|
+
get address() {
|
|
787
|
+
return this.stream.address;
|
|
788
|
+
}
|
|
789
|
+
get closed() {
|
|
790
|
+
return this.stream.closed;
|
|
791
|
+
}
|
|
792
|
+
get onData() {
|
|
793
|
+
return this.stream.onData;
|
|
794
|
+
}
|
|
795
|
+
set onData(handler) {
|
|
796
|
+
this.stream.onData = handler;
|
|
797
|
+
}
|
|
798
|
+
send = async (data, addr) => {
|
|
799
|
+
await this.stream.send(data, addr);
|
|
800
|
+
};
|
|
801
|
+
close = async () => {
|
|
802
|
+
await this.stream.close();
|
|
803
|
+
};
|
|
804
|
+
};
|
|
805
|
+
var StreamTransport = class {
|
|
806
|
+
constructor(type, createClient, connectEvent = type === "tls" ? "secureConnect" : "connect") {
|
|
807
|
+
this.createClient = createClient;
|
|
808
|
+
this.connectEvent = connectEvent;
|
|
809
|
+
this.type = type;
|
|
722
810
|
this.connect();
|
|
723
811
|
}
|
|
724
|
-
type
|
|
812
|
+
type;
|
|
725
813
|
connecting;
|
|
726
814
|
client;
|
|
727
815
|
onData = () => {
|
|
@@ -734,90 +822,88 @@ var TcpTransport = class _TcpTransport {
|
|
|
734
822
|
if (this.client) {
|
|
735
823
|
this.client.destroy();
|
|
736
824
|
}
|
|
825
|
+
const client = this.createClient();
|
|
826
|
+
this.client = client;
|
|
737
827
|
this.connecting = new Promise((r, f) => {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
828
|
+
const onConnect = () => {
|
|
829
|
+
client.off("error", onConnectError);
|
|
830
|
+
r();
|
|
831
|
+
};
|
|
832
|
+
const onConnectError = (error) => {
|
|
833
|
+
client.off(this.connectEvent, onConnect);
|
|
741
834
|
f(error);
|
|
742
|
-
}
|
|
835
|
+
};
|
|
836
|
+
client.once(this.connectEvent, onConnect);
|
|
837
|
+
client.once("error", onConnectError);
|
|
743
838
|
});
|
|
744
|
-
|
|
839
|
+
client.on("data", (data) => {
|
|
745
840
|
const addr = [
|
|
746
841
|
this.client.remoteAddress,
|
|
747
842
|
this.client.remotePort
|
|
748
843
|
];
|
|
749
844
|
this.onData(data, addr);
|
|
750
845
|
});
|
|
751
|
-
|
|
846
|
+
client.on("end", () => {
|
|
752
847
|
this.connect();
|
|
753
848
|
});
|
|
754
|
-
|
|
755
|
-
|
|
849
|
+
client.on("error", (error) => {
|
|
850
|
+
log(`${this.type} transport error`, error);
|
|
756
851
|
});
|
|
757
852
|
}
|
|
758
|
-
async
|
|
853
|
+
async waitForConnect() {
|
|
759
854
|
await this.connecting;
|
|
760
855
|
}
|
|
761
|
-
static async init(addr) {
|
|
762
|
-
const transport = new _TcpTransport(addr);
|
|
763
|
-
await transport.init();
|
|
764
|
-
return transport;
|
|
765
|
-
}
|
|
766
856
|
get address() {
|
|
767
857
|
return {};
|
|
768
858
|
}
|
|
769
859
|
send = async (data, addr) => {
|
|
860
|
+
void addr;
|
|
770
861
|
await this.connecting;
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
862
|
+
await new Promise((resolve2, reject) => {
|
|
863
|
+
this.client.write(data, (err5) => {
|
|
864
|
+
if (err5) {
|
|
865
|
+
reject(err5);
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
resolve2();
|
|
869
|
+
});
|
|
775
870
|
});
|
|
776
871
|
};
|
|
777
872
|
close = async () => {
|
|
778
873
|
this.closed = true;
|
|
779
|
-
this.client
|
|
874
|
+
this.client?.destroy();
|
|
780
875
|
};
|
|
781
876
|
};
|
|
782
877
|
|
|
783
|
-
// ../rtp/src/extra/container/mp4/mp4box.ts
|
|
784
|
-
import {
|
|
785
|
-
BoxParser,
|
|
786
|
-
ISOFile,
|
|
787
|
-
Log,
|
|
788
|
-
createFile,
|
|
789
|
-
DataStream
|
|
790
|
-
} from "mp4box";
|
|
791
|
-
import {
|
|
792
|
-
BoxParser as BoxParser2
|
|
793
|
-
} from "mp4box";
|
|
794
|
-
BoxParser2.dOpsBox.prototype.write = function(stream) {
|
|
795
|
-
this.size = 11;
|
|
796
|
-
this.writeHeader(stream);
|
|
797
|
-
stream.writeUint8(0);
|
|
798
|
-
stream.writeUint8(this.OutputChannelCount);
|
|
799
|
-
stream.writeUint16(this.PreSkip);
|
|
800
|
-
stream.writeUint32(this.InputSampleRate);
|
|
801
|
-
stream.writeUint16(0);
|
|
802
|
-
stream.writeUint8(0);
|
|
803
|
-
};
|
|
804
|
-
|
|
805
878
|
// ../rtp/src/extra/container/mp4/container.ts
|
|
806
879
|
var Mp4Container = class {
|
|
807
880
|
constructor(props) {
|
|
808
881
|
this.props = props;
|
|
809
|
-
this.#mp4 = new ISOFile();
|
|
810
|
-
this.#mp4.init();
|
|
811
882
|
}
|
|
812
|
-
#mp4;
|
|
813
883
|
#audioFrame;
|
|
884
|
+
#audioMeta;
|
|
885
|
+
#audioSource;
|
|
886
|
+
#currentFragment;
|
|
887
|
+
#ftyp;
|
|
888
|
+
#initializationEmitted = false;
|
|
889
|
+
#lastAudioDuration = 0;
|
|
890
|
+
#lastMoof;
|
|
891
|
+
#lastMoofTimestamp = 0;
|
|
892
|
+
#lastVideoDuration = 0;
|
|
893
|
+
#moov;
|
|
894
|
+
#onDataOperation = Promise.resolve();
|
|
895
|
+
#operation = Promise.resolve();
|
|
896
|
+
#output;
|
|
897
|
+
#startPromise;
|
|
898
|
+
#stopPromise;
|
|
899
|
+
#stopped = false;
|
|
814
900
|
#videoFrame;
|
|
815
|
-
|
|
901
|
+
#videoMeta;
|
|
902
|
+
#videoSource;
|
|
816
903
|
audioTrack;
|
|
904
|
+
frameBuffer = [];
|
|
905
|
+
onData = new Event2();
|
|
817
906
|
videoTrack;
|
|
818
|
-
#audioSegment = 0;
|
|
819
|
-
#videoSegment = 0;
|
|
820
|
-
onData = new Event();
|
|
821
907
|
get tracksReady() {
|
|
822
908
|
let ready = true;
|
|
823
909
|
if (this.props.track.audio && !this.audioTrack) {
|
|
@@ -836,145 +922,259 @@ var Mp4Container = class {
|
|
|
836
922
|
}
|
|
837
923
|
}
|
|
838
924
|
#init(frame) {
|
|
839
|
-
let codec = frame.codec.substring(0, 4);
|
|
840
|
-
if (codec == "opus") {
|
|
841
|
-
codec = "Opus";
|
|
842
|
-
}
|
|
843
|
-
const options = {
|
|
844
|
-
type: codec,
|
|
845
|
-
timescale: 1e6
|
|
846
|
-
};
|
|
847
|
-
if (isVideoConfig(frame)) {
|
|
848
|
-
options.width = frame.codedWidth;
|
|
849
|
-
options.height = frame.codedHeight;
|
|
850
|
-
} else {
|
|
851
|
-
options.channel_count = frame.numberOfChannels;
|
|
852
|
-
options.samplerate = frame.sampleRate;
|
|
853
|
-
options.hdlr = "soun";
|
|
854
|
-
}
|
|
855
|
-
if (!frame.description) throw new Error("missing frame description");
|
|
856
|
-
const desc = frame.description;
|
|
857
|
-
if (codec === "avc1") {
|
|
858
|
-
options.avcDecoderConfigRecord = desc;
|
|
859
|
-
} else if (codec === "hev1") {
|
|
860
|
-
options.hevcDecoderConfigRecord = desc;
|
|
861
|
-
} else if (codec === "Opus") {
|
|
862
|
-
const dops = new BoxParser.dOpsBox();
|
|
863
|
-
dops.parse(new DataStream(desc, 8, DataStream.LITTLE_ENDIAN));
|
|
864
|
-
options.description = dops;
|
|
865
|
-
} else {
|
|
866
|
-
throw new Error(`unsupported codec: ${codec}`);
|
|
867
|
-
}
|
|
868
|
-
const track = this.#mp4.addTrack(options);
|
|
869
|
-
if (track == void 0) {
|
|
870
|
-
throw new Error("failed to initialize MP4 track");
|
|
871
|
-
}
|
|
872
925
|
if (frame.track === "audio") {
|
|
873
|
-
|
|
926
|
+
if (isVideoConfig(frame)) {
|
|
927
|
+
throw new Error("audio track requires an audio decoder config");
|
|
928
|
+
}
|
|
929
|
+
if (frame.codec !== "opus") {
|
|
930
|
+
throw new Error(`unsupported codec: ${frame.codec}`);
|
|
931
|
+
}
|
|
932
|
+
this.#audioMeta = {
|
|
933
|
+
decoderConfig: {
|
|
934
|
+
codec: "opus",
|
|
935
|
+
description: frame.description != void 0 ? new Uint8Array(frame.description) : void 0,
|
|
936
|
+
numberOfChannels: frame.numberOfChannels,
|
|
937
|
+
sampleRate: frame.sampleRate
|
|
938
|
+
}
|
|
939
|
+
};
|
|
940
|
+
this.audioTrack = 1;
|
|
874
941
|
} else {
|
|
875
|
-
|
|
942
|
+
if (!isVideoConfig(frame)) {
|
|
943
|
+
throw new Error("video track requires a video decoder config");
|
|
944
|
+
}
|
|
945
|
+
if (!frame.codec.startsWith("avc1")) {
|
|
946
|
+
throw new Error(`unsupported codec: ${frame.codec}`);
|
|
947
|
+
}
|
|
948
|
+
if (frame.codedWidth == void 0 || frame.codedHeight == void 0) {
|
|
949
|
+
throw new Error("missing coded video dimensions");
|
|
950
|
+
}
|
|
951
|
+
this.#videoMeta = {
|
|
952
|
+
decoderConfig: {
|
|
953
|
+
codec: frame.codec,
|
|
954
|
+
codedHeight: frame.codedHeight,
|
|
955
|
+
codedWidth: frame.codedWidth,
|
|
956
|
+
description: frame.description != void 0 ? new Uint8Array(frame.description) : void 0,
|
|
957
|
+
displayAspectHeight: frame.displayAspectHeight,
|
|
958
|
+
displayAspectWidth: frame.displayAspectWidth
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
this.videoTrack = this.props.track.audio ? 2 : 1;
|
|
876
962
|
}
|
|
877
963
|
if (!this.tracksReady) {
|
|
878
964
|
return;
|
|
879
965
|
}
|
|
880
|
-
|
|
881
|
-
this.#
|
|
882
|
-
this.#
|
|
883
|
-
|
|
884
|
-
0
|
|
885
|
-
);
|
|
886
|
-
const data = new Uint8Array(buffer2);
|
|
887
|
-
const res = {
|
|
888
|
-
type: "init",
|
|
889
|
-
timestamp: 0,
|
|
890
|
-
duration: 0,
|
|
891
|
-
data,
|
|
892
|
-
kind: frame.track
|
|
893
|
-
};
|
|
894
|
-
this.onData.execute(res);
|
|
966
|
+
void this.#enqueueOperation(async () => {
|
|
967
|
+
await this.#ensureOutputStarted();
|
|
968
|
+
await this.#drainFrameBuffer();
|
|
969
|
+
});
|
|
895
970
|
}
|
|
896
|
-
frameBuffer = [];
|
|
897
971
|
#enqueue(frame) {
|
|
898
972
|
this.frameBuffer.push(frame);
|
|
899
973
|
if (!this.tracksReady) {
|
|
900
974
|
return;
|
|
901
975
|
}
|
|
902
|
-
|
|
903
|
-
this
|
|
976
|
+
void this.#enqueueOperation(async () => {
|
|
977
|
+
await this.#ensureOutputStarted();
|
|
978
|
+
await this.#drainFrameBuffer();
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
stop() {
|
|
982
|
+
if (this.#stopPromise) {
|
|
983
|
+
return this.#stopPromise;
|
|
904
984
|
}
|
|
985
|
+
this.#stopped = true;
|
|
986
|
+
this.#stopPromise = this.#enqueueOperation(async () => {
|
|
987
|
+
if (!this.tracksReady) {
|
|
988
|
+
this.frameBuffer = [];
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
await this.#ensureOutputStarted();
|
|
992
|
+
await this.#drainFrameBuffer();
|
|
993
|
+
await this.#flushPendingFrame("audio");
|
|
994
|
+
await this.#flushPendingFrame("video");
|
|
995
|
+
this.#audioSource?.close();
|
|
996
|
+
this.#videoSource?.close();
|
|
997
|
+
if (this.#output) {
|
|
998
|
+
await this.#output.finalize();
|
|
999
|
+
}
|
|
1000
|
+
await this.#flushOnData();
|
|
1001
|
+
});
|
|
1002
|
+
return this.#stopPromise;
|
|
1003
|
+
}
|
|
1004
|
+
async #drainFrameBuffer() {
|
|
1005
|
+
const frames = this.frameBuffer;
|
|
905
1006
|
this.frameBuffer = [];
|
|
1007
|
+
for (const frame of frames) {
|
|
1008
|
+
await this.#processFrame(frame);
|
|
1009
|
+
}
|
|
906
1010
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1011
|
+
async #ensureOutputStarted() {
|
|
1012
|
+
if (this.#startPromise) {
|
|
1013
|
+
await this.#startPromise;
|
|
1014
|
+
return;
|
|
911
1015
|
}
|
|
912
|
-
if (
|
|
913
|
-
|
|
914
|
-
this.#videoSegment += 1;
|
|
915
|
-
} else if (this.#videoSegment == 0) {
|
|
916
|
-
throw new Error("must start with keyframe");
|
|
917
|
-
}
|
|
918
|
-
} else {
|
|
919
|
-
this.#audioSegment += 1;
|
|
1016
|
+
if (!this.tracksReady) {
|
|
1017
|
+
return;
|
|
920
1018
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1019
|
+
const output = new Output({
|
|
1020
|
+
format: new Mp4OutputFormat({
|
|
1021
|
+
fastStart: "fragmented",
|
|
1022
|
+
minimumFragmentDuration: 0,
|
|
1023
|
+
onFtyp: (data) => {
|
|
1024
|
+
this.#ftyp = copyBytes(data);
|
|
1025
|
+
},
|
|
1026
|
+
onMdat: (data) => {
|
|
1027
|
+
if (!this.#lastMoof) {
|
|
1028
|
+
throw new Error("moof missing before mdat");
|
|
1029
|
+
}
|
|
1030
|
+
const fragment = this.#currentFragment;
|
|
1031
|
+
const timestamp = fragment ? fragment.timestamp : Math.round(this.#lastMoofTimestamp * 1e6);
|
|
1032
|
+
const duration = fragment?.duration ?? 0;
|
|
1033
|
+
const kind = fragment?.kind ?? this.#defaultKind();
|
|
1034
|
+
const type = fragment?.type ?? "key";
|
|
1035
|
+
const segment = concatBytes(this.#lastMoof, data);
|
|
1036
|
+
this.#currentFragment = void 0;
|
|
1037
|
+
this.#lastMoof = void 0;
|
|
1038
|
+
void this.#emitData({
|
|
1039
|
+
type,
|
|
1040
|
+
timestamp,
|
|
1041
|
+
duration,
|
|
1042
|
+
data: segment,
|
|
1043
|
+
kind
|
|
1044
|
+
});
|
|
1045
|
+
},
|
|
1046
|
+
onMoof: (data, _position, timestamp) => {
|
|
1047
|
+
this.#lastMoof = copyBytes(data);
|
|
1048
|
+
this.#lastMoofTimestamp = timestamp;
|
|
1049
|
+
},
|
|
1050
|
+
onMoov: (data) => {
|
|
1051
|
+
this.#moov = copyBytes(data);
|
|
1052
|
+
this.#emitInitializationSegment();
|
|
1053
|
+
}
|
|
1054
|
+
}),
|
|
1055
|
+
target: new NullTarget()
|
|
1056
|
+
});
|
|
1057
|
+
if (this.#audioMeta) {
|
|
1058
|
+
this.#audioSource = new EncodedAudioPacketSource("opus");
|
|
1059
|
+
output.addAudioTrack(this.#audioSource);
|
|
1060
|
+
}
|
|
1061
|
+
if (this.#videoMeta) {
|
|
1062
|
+
this.#videoSource = new EncodedVideoPacketSource("avc");
|
|
1063
|
+
output.addVideoTrack(this.#videoSource);
|
|
1064
|
+
}
|
|
1065
|
+
this.#output = output;
|
|
1066
|
+
this.#startPromise = output.start();
|
|
1067
|
+
await this.#startPromise;
|
|
1068
|
+
}
|
|
1069
|
+
#emitInitializationSegment() {
|
|
1070
|
+
if (this.#initializationEmitted || !this.#ftyp || !this.#moov) {
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
this.#initializationEmitted = true;
|
|
1074
|
+
void this.#emitData({
|
|
1075
|
+
type: "init",
|
|
1076
|
+
timestamp: 0,
|
|
1077
|
+
duration: 0,
|
|
1078
|
+
data: concatBytes(this.#ftyp, this.#moov),
|
|
1079
|
+
kind: this.#defaultKind()
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
#emitData(data) {
|
|
1083
|
+
const next = this.#onDataOperation.then(() => this.onData.execute(data));
|
|
1084
|
+
this.#onDataOperation = next;
|
|
1085
|
+
return next;
|
|
1086
|
+
}
|
|
1087
|
+
async #flushOnData() {
|
|
1088
|
+
await this.#onDataOperation;
|
|
1089
|
+
}
|
|
1090
|
+
#enqueueOperation(operation) {
|
|
1091
|
+
const next = this.#operation.then(operation);
|
|
1092
|
+
this.#operation = next;
|
|
1093
|
+
return next;
|
|
1094
|
+
}
|
|
1095
|
+
#defaultKind() {
|
|
1096
|
+
return this.props.track.video ? "video" : "audio";
|
|
1097
|
+
}
|
|
1098
|
+
async #flushPendingFrame(track) {
|
|
1099
|
+
const buffered = track === "audio" ? this.#audioFrame : this.#videoFrame;
|
|
1100
|
+
if (!buffered) {
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
const duration = track === "audio" ? this.#lastAudioDuration : this.#lastVideoDuration;
|
|
1104
|
+
await this.#writePacket(buffered, duration);
|
|
1105
|
+
if (track === "audio") {
|
|
1106
|
+
this.#audioFrame = void 0;
|
|
926
1107
|
} else {
|
|
927
|
-
|
|
1108
|
+
this.#videoFrame = void 0;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
async #processFrame(frame) {
|
|
1112
|
+
const buffered = frame.track === "audio" ? this.#audioFrame : this.#videoFrame;
|
|
1113
|
+
if (!buffered) {
|
|
1114
|
+
if (frame.track === "audio") {
|
|
928
1115
|
this.#audioFrame = frame;
|
|
929
|
-
|
|
1116
|
+
} else {
|
|
1117
|
+
this.#videoFrame = frame;
|
|
930
1118
|
}
|
|
1119
|
+
return;
|
|
931
1120
|
}
|
|
932
|
-
const
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
bufferFrame.copyTo(buffer2);
|
|
939
|
-
this.#mp4.addSample(track, buffer2, {
|
|
940
|
-
duration,
|
|
941
|
-
dts: bufferFrame.timestamp,
|
|
942
|
-
cts: bufferFrame.timestamp,
|
|
943
|
-
is_sync: bufferFrame.type == "key"
|
|
944
|
-
});
|
|
945
|
-
const stream = new DataStream(void 0, 0, DataStream.BIG_ENDIAN);
|
|
946
|
-
for (; ; ) {
|
|
947
|
-
const moof = this.#mp4.moofs.shift();
|
|
948
|
-
const mdat = this.#mp4.mdats.shift();
|
|
949
|
-
if (!moof && !mdat) break;
|
|
950
|
-
if (!moof) throw new Error("moof missing");
|
|
951
|
-
if (!mdat) throw new Error("mdat missing");
|
|
952
|
-
moof.write(stream);
|
|
953
|
-
mdat.write(stream);
|
|
954
|
-
}
|
|
955
|
-
const data = new Uint8Array(stream.buffer);
|
|
956
|
-
if (frame.track === "video") {
|
|
1121
|
+
const duration = Math.max(frame.timestamp - buffered.timestamp, 0);
|
|
1122
|
+
await this.#writePacket(buffered, duration);
|
|
1123
|
+
if (frame.track === "audio") {
|
|
1124
|
+
this.#audioFrame = frame;
|
|
1125
|
+
this.#lastAudioDuration = duration;
|
|
1126
|
+
} else {
|
|
957
1127
|
this.#videoFrame = frame;
|
|
1128
|
+
this.#lastVideoDuration = duration;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
async #writePacket(frame, duration) {
|
|
1132
|
+
const data = new Uint8Array(frame.byteLength);
|
|
1133
|
+
frame.copyTo(data.buffer);
|
|
1134
|
+
const packet = new EncodedPacket(
|
|
1135
|
+
data,
|
|
1136
|
+
frame.type,
|
|
1137
|
+
frame.timestamp / 1e6,
|
|
1138
|
+
duration / 1e6
|
|
1139
|
+
);
|
|
1140
|
+
if (frame.track === "audio") {
|
|
1141
|
+
if (!this.#audioSource || !this.#audioMeta) {
|
|
1142
|
+
throw new Error("audio track missing");
|
|
1143
|
+
}
|
|
1144
|
+
await this.#audioSource.add(packet, this.#audioMeta);
|
|
958
1145
|
} else {
|
|
959
|
-
this.#
|
|
1146
|
+
if (!this.#videoSource || !this.#videoMeta) {
|
|
1147
|
+
throw new Error("video track missing");
|
|
1148
|
+
}
|
|
1149
|
+
await this.#videoSource.add(packet, this.#videoMeta);
|
|
960
1150
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
duration
|
|
966
|
-
|
|
967
|
-
};
|
|
968
|
-
this.onData.execute(res);
|
|
1151
|
+
this.#rememberFragmentPacket(
|
|
1152
|
+
frame.track,
|
|
1153
|
+
frame.type,
|
|
1154
|
+
frame.timestamp,
|
|
1155
|
+
duration
|
|
1156
|
+
);
|
|
969
1157
|
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1158
|
+
#rememberFragmentPacket(kind, type, timestamp, duration) {
|
|
1159
|
+
const end = timestamp + duration;
|
|
1160
|
+
if (!this.#currentFragment) {
|
|
1161
|
+
this.#currentFragment = {
|
|
1162
|
+
kind,
|
|
1163
|
+
type,
|
|
1164
|
+
timestamp,
|
|
1165
|
+
duration
|
|
1166
|
+
};
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
if (kind === "video" && type === "key") {
|
|
1170
|
+
this.#currentFragment.type = "key";
|
|
1171
|
+
}
|
|
1172
|
+
const fragmentEnd = Math.max(
|
|
1173
|
+
this.#currentFragment.timestamp + this.#currentFragment.duration,
|
|
1174
|
+
end
|
|
1175
|
+
);
|
|
1176
|
+
this.#currentFragment.duration = fragmentEnd - this.#currentFragment.timestamp;
|
|
976
1177
|
}
|
|
977
|
-
*/
|
|
978
1178
|
};
|
|
979
1179
|
function isDecoderConfig(frame) {
|
|
980
1180
|
return frame.codec !== void 0;
|
|
@@ -983,6 +1183,19 @@ function isVideoConfig(frame) {
|
|
|
983
1183
|
return frame.codedWidth !== void 0;
|
|
984
1184
|
}
|
|
985
1185
|
var mp4SupportedCodecs = ["avc1", "opus"];
|
|
1186
|
+
function concatBytes(...parts) {
|
|
1187
|
+
const size = parts.reduce((sum, part) => sum + part.byteLength, 0);
|
|
1188
|
+
const combined = new Uint8Array(size);
|
|
1189
|
+
let offset = 0;
|
|
1190
|
+
for (const part of parts) {
|
|
1191
|
+
combined.set(part, offset);
|
|
1192
|
+
offset += part.byteLength;
|
|
1193
|
+
}
|
|
1194
|
+
return combined;
|
|
1195
|
+
}
|
|
1196
|
+
function copyBytes(data) {
|
|
1197
|
+
return new Uint8Array(data);
|
|
1198
|
+
}
|
|
986
1199
|
|
|
987
1200
|
// ../rtp/src/extra/container/mp4/exp-golomb.ts
|
|
988
1201
|
var ExpGolomb = class {
|
|
@@ -1521,6 +1734,18 @@ function annexb2avcc(data) {
|
|
|
1521
1734
|
);
|
|
1522
1735
|
return avcc.getData();
|
|
1523
1736
|
}
|
|
1737
|
+
function annexb2avcSample(data) {
|
|
1738
|
+
const annexbParser = new H264AnnexBParser(data);
|
|
1739
|
+
const nalUnits = [];
|
|
1740
|
+
let naluPayload = null;
|
|
1741
|
+
while ((naluPayload = annexbParser.readNextNaluPayload()) != null) {
|
|
1742
|
+
nalUnits.push(Buffer.from(new H264NaluAVC1(naluPayload).data));
|
|
1743
|
+
}
|
|
1744
|
+
if (nalUnits.length === 0) {
|
|
1745
|
+
throw new Error("annexb sample does not contain nal units");
|
|
1746
|
+
}
|
|
1747
|
+
return Buffer.concat(nalUnits);
|
|
1748
|
+
}
|
|
1524
1749
|
|
|
1525
1750
|
// ../rtp/src/extra/container/ogg/parser.ts
|
|
1526
1751
|
var OggParser = class {
|
|
@@ -1612,8 +1837,25 @@ var OggParser = class {
|
|
|
1612
1837
|
// ../rtp/src/extra/container/webm/container.ts
|
|
1613
1838
|
import { createCipheriv, randomBytes as randomBytes2, randomFillSync } from "crypto";
|
|
1614
1839
|
|
|
1840
|
+
// ../rtp/src/codec/leb128.ts
|
|
1841
|
+
function leb128encode(value) {
|
|
1842
|
+
if (!Number.isInteger(value) || value < 0 || !Number.isSafeInteger(value)) {
|
|
1843
|
+
throw new Error("LEB128 encode requires a non-negative safe integer");
|
|
1844
|
+
}
|
|
1845
|
+
const bytes2 = [];
|
|
1846
|
+
let remaining = value;
|
|
1847
|
+
do {
|
|
1848
|
+
let byte = remaining & 127;
|
|
1849
|
+
remaining = Math.floor(remaining / 128);
|
|
1850
|
+
if (remaining !== 0) {
|
|
1851
|
+
byte |= 128;
|
|
1852
|
+
}
|
|
1853
|
+
bytes2.push(byte);
|
|
1854
|
+
} while (remaining !== 0);
|
|
1855
|
+
return Buffer.from(bytes2);
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1615
1858
|
// ../rtp/src/codec/av1.ts
|
|
1616
|
-
import { LEB128 } from "@minhducsun2002/leb128";
|
|
1617
1859
|
var log2 = debug("werift-rtp : packages/rtp/src/codec/av1.ts");
|
|
1618
1860
|
var AV1RtpPayload = class _AV1RtpPayload {
|
|
1619
1861
|
/**
|
|
@@ -1743,7 +1985,7 @@ var AV1Obu = class _AV1Obu {
|
|
|
1743
1985
|
const header = new BitWriter2(8).set(this.obu_forbidden_bit).set(OBU_TYPE_IDS[this.obu_type], 4).set(this.obu_extension_flag).set(this.obu_has_size_field).set(this.obu_reserved_1bit).buffer;
|
|
1744
1986
|
let obuSize = Buffer.alloc(0);
|
|
1745
1987
|
if (this.obu_has_size_field) {
|
|
1746
|
-
obuSize =
|
|
1988
|
+
obuSize = leb128encode(this.payload.length);
|
|
1747
1989
|
}
|
|
1748
1990
|
return Buffer.concat([header, obuSize, this.payload]);
|
|
1749
1991
|
}
|
|
@@ -1752,14 +1994,20 @@ function leb128decode(buf) {
|
|
|
1752
1994
|
let value = 0;
|
|
1753
1995
|
let leb128bytes = 0;
|
|
1754
1996
|
for (let i = 0; i < 8; i++) {
|
|
1997
|
+
if (i >= buf.length) {
|
|
1998
|
+
throw new Error("LEB128 decode incomplete");
|
|
1999
|
+
}
|
|
1755
2000
|
const leb128byte = buf.readUInt8(i);
|
|
1756
|
-
value
|
|
2001
|
+
value += (leb128byte & 127) * 128 ** i;
|
|
1757
2002
|
leb128bytes++;
|
|
1758
2003
|
if (!(leb128byte & 128)) {
|
|
1759
|
-
|
|
2004
|
+
if (!Number.isSafeInteger(value)) {
|
|
2005
|
+
throw new Error("LEB128 value exceeds safe integer");
|
|
2006
|
+
}
|
|
2007
|
+
return [value, leb128bytes];
|
|
1760
2008
|
}
|
|
1761
2009
|
}
|
|
1762
|
-
|
|
2010
|
+
throw new Error("LEB128 decode incomplete");
|
|
1763
2011
|
}
|
|
1764
2012
|
var OBU_TYPES = {
|
|
1765
2013
|
0: "Reserved",
|
|
@@ -4439,13 +4687,12 @@ var RtpPacket = class _RtpPacket {
|
|
|
4439
4687
|
};
|
|
4440
4688
|
|
|
4441
4689
|
// ../rtp/src/rtp/rtx.ts
|
|
4442
|
-
import { jspack as jspack2 } from "@shinyoshiaki/jspack";
|
|
4443
4690
|
function unwrapRtx(rtx, payloadType, ssrc) {
|
|
4444
4691
|
const packet = new RtpPacket(
|
|
4445
4692
|
new RtpHeader({
|
|
4446
4693
|
payloadType,
|
|
4447
4694
|
marker: rtx.header.marker,
|
|
4448
|
-
sequenceNumber:
|
|
4695
|
+
sequenceNumber: rtx.payload.readUInt16BE(0),
|
|
4449
4696
|
timestamp: rtx.header.timestamp,
|
|
4450
4697
|
ssrc
|
|
4451
4698
|
}),
|
|
@@ -4454,6 +4701,8 @@ function unwrapRtx(rtx, payloadType, ssrc) {
|
|
|
4454
4701
|
return packet;
|
|
4455
4702
|
}
|
|
4456
4703
|
function wrapRtx(packet, payloadType, sequenceNumber, ssrc) {
|
|
4704
|
+
const originalSequence = Buffer.allocUnsafe(2);
|
|
4705
|
+
originalSequence.writeUInt16BE(packet.header.sequenceNumber, 0);
|
|
4457
4706
|
const rtx = new RtpPacket(
|
|
4458
4707
|
new RtpHeader({
|
|
4459
4708
|
payloadType,
|
|
@@ -4464,17 +4713,13 @@ function wrapRtx(packet, payloadType, sequenceNumber, ssrc) {
|
|
|
4464
4713
|
csrc: packet.header.csrc,
|
|
4465
4714
|
extensions: packet.header.extensions
|
|
4466
4715
|
}),
|
|
4467
|
-
Buffer.concat([
|
|
4468
|
-
Buffer.from(jspack2.Pack("!H", [packet.header.sequenceNumber])),
|
|
4469
|
-
packet.payload
|
|
4470
|
-
])
|
|
4716
|
+
Buffer.concat([originalSequence, packet.payload])
|
|
4471
4717
|
);
|
|
4472
4718
|
return rtx;
|
|
4473
4719
|
}
|
|
4474
4720
|
|
|
4475
4721
|
// ../rtp/src/srtp/context/context.ts
|
|
4476
|
-
import { createHmac as createHmac2 } from "crypto";
|
|
4477
|
-
import AES from "aes-js";
|
|
4722
|
+
import { createCipheriv as createCipheriv4, createHmac as createHmac2 } from "crypto";
|
|
4478
4723
|
|
|
4479
4724
|
// ../rtp/src/srtp/cipher/ctr.ts
|
|
4480
4725
|
import {
|
|
@@ -4862,6 +5107,11 @@ function finalizeAuthenticatedDecryption(decipher, packetType) {
|
|
|
4862
5107
|
}
|
|
4863
5108
|
|
|
4864
5109
|
// ../rtp/src/srtp/context/context.ts
|
|
5110
|
+
function aes128EcbEncrypt(key, plaintext) {
|
|
5111
|
+
const cipher = createCipheriv4("aes-128-ecb", key, null);
|
|
5112
|
+
cipher.setAutoPadding(false);
|
|
5113
|
+
return Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
5114
|
+
}
|
|
4865
5115
|
var Context = class {
|
|
4866
5116
|
constructor(masterKey, masterSalt, profile) {
|
|
4867
5117
|
this.masterKey = masterKey;
|
|
@@ -4928,8 +5178,7 @@ var Context = class {
|
|
|
4928
5178
|
sessionKey[j] = sessionKey[j] ^ labelAndIndexOverKdr[i];
|
|
4929
5179
|
}
|
|
4930
5180
|
sessionKey = Buffer.concat([sessionKey, Buffer.from([0, 0])]);
|
|
4931
|
-
|
|
4932
|
-
return Buffer.from(block.encrypt(sessionKey));
|
|
5181
|
+
return aes128EcbEncrypt(this.masterKey, sessionKey);
|
|
4933
5182
|
}
|
|
4934
5183
|
generateSessionSalt(label) {
|
|
4935
5184
|
let sessionSalt = Buffer.from(this.masterSalt);
|
|
@@ -4946,8 +5195,7 @@ var Context = class {
|
|
|
4946
5195
|
sessionSalt[j] = sessionSalt[j] ^ labelAndIndexOverKdr[i];
|
|
4947
5196
|
}
|
|
4948
5197
|
sessionSalt = Buffer.concat([sessionSalt, Buffer.from([0, 0])]);
|
|
4949
|
-
|
|
4950
|
-
sessionSalt = Buffer.from(block.encrypt(sessionSalt));
|
|
5198
|
+
sessionSalt = aes128EcbEncrypt(this.masterKey, sessionSalt);
|
|
4951
5199
|
return sessionSalt.subarray(0, 14);
|
|
4952
5200
|
}
|
|
4953
5201
|
generateSessionAuthTag(label) {
|
|
@@ -4972,9 +5220,8 @@ var Context = class {
|
|
|
4972
5220
|
sessionAuthTag,
|
|
4973
5221
|
Buffer.from([0, 1])
|
|
4974
5222
|
]);
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
secondRun = Buffer.from(block.encrypt(secondRun));
|
|
5223
|
+
firstRun = aes128EcbEncrypt(this.masterKey, firstRun);
|
|
5224
|
+
secondRun = aes128EcbEncrypt(this.masterKey, secondRun);
|
|
4978
5225
|
return Buffer.concat([firstRun, secondRun.subarray(0, 4)]);
|
|
4979
5226
|
}
|
|
4980
5227
|
getSrtpSsrcState(ssrc) {
|
|
@@ -5202,7 +5449,7 @@ var DepacketizeBase = class {
|
|
|
5202
5449
|
frameBroken = false;
|
|
5203
5450
|
keyframeReceived = false;
|
|
5204
5451
|
count = 0;
|
|
5205
|
-
onNeedKeyFrame = new
|
|
5452
|
+
onNeedKeyFrame = new Event2();
|
|
5206
5453
|
internalStats = {};
|
|
5207
5454
|
toJSON() {
|
|
5208
5455
|
return {
|
|
@@ -5562,17 +5809,17 @@ var JitterBufferBase = class {
|
|
|
5562
5809
|
this.rtpBuffer[rtp.header.sequenceNumber] = rtp;
|
|
5563
5810
|
}
|
|
5564
5811
|
resolveBuffer(seqNumFrom) {
|
|
5565
|
-
const
|
|
5812
|
+
const resolve2 = [];
|
|
5566
5813
|
for (let index = seqNumFrom; ; index = uint16Add(index, 1)) {
|
|
5567
5814
|
const rtp = this.rtpBuffer[index];
|
|
5568
5815
|
if (rtp) {
|
|
5569
|
-
|
|
5816
|
+
resolve2.push(rtp);
|
|
5570
5817
|
delete this.rtpBuffer[index];
|
|
5571
5818
|
} else {
|
|
5572
5819
|
break;
|
|
5573
5820
|
}
|
|
5574
5821
|
}
|
|
5575
|
-
return
|
|
5822
|
+
return resolve2;
|
|
5576
5823
|
}
|
|
5577
5824
|
sortAndClearBuffer(rtpBuffer) {
|
|
5578
5825
|
const buffer2 = [];
|
|
@@ -5871,93 +6118,124 @@ var MP4Base = class {
|
|
|
5871
6118
|
video: !!this.tracks.find((t) => t.kind === "video")
|
|
5872
6119
|
}
|
|
5873
6120
|
});
|
|
5874
|
-
this.container.onData.subscribe((data) => {
|
|
5875
|
-
this.output(data);
|
|
6121
|
+
this.container.onData.subscribe(async (data) => {
|
|
6122
|
+
await this.output(data);
|
|
5876
6123
|
});
|
|
5877
6124
|
}
|
|
6125
|
+
audioStopped = false;
|
|
5878
6126
|
internalStats = {};
|
|
5879
6127
|
container;
|
|
5880
6128
|
stopped = false;
|
|
5881
|
-
onStopped = new
|
|
6129
|
+
onStopped = new Event2();
|
|
6130
|
+
videoStopped = false;
|
|
5882
6131
|
toJSON() {
|
|
5883
6132
|
return {
|
|
5884
6133
|
...this.internalStats
|
|
5885
6134
|
};
|
|
5886
6135
|
}
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
6136
|
+
processVideoInput = ({ eol, frame }) => {
|
|
6137
|
+
if (this.stopped) {
|
|
6138
|
+
return;
|
|
6139
|
+
}
|
|
6140
|
+
if (!frame) {
|
|
6141
|
+
if (eol) {
|
|
6142
|
+
this.videoStopped = true;
|
|
6143
|
+
if (!this.tracks.some((track2) => track2.kind === "audio") || this.audioStopped) {
|
|
6144
|
+
void this.stop();
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
6147
|
+
return;
|
|
6148
|
+
}
|
|
6149
|
+
const track = this.tracks.find((t) => t.kind === "video");
|
|
6150
|
+
this.videoStopped = false;
|
|
6151
|
+
if (!this.container.videoTrack) {
|
|
6152
|
+
if (frame.isKeyframe) {
|
|
6153
|
+
const avcc = annexb2avcc(frame.data);
|
|
6154
|
+
const sample = annexb2avcSample(frame.data);
|
|
6155
|
+
const [displayAspectWidth, displayAspectHeight] = computeRatio(
|
|
6156
|
+
track.width,
|
|
6157
|
+
track.height
|
|
6158
|
+
);
|
|
5891
6159
|
this.container.write({
|
|
5892
|
-
codec:
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
),
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
track: "
|
|
6160
|
+
codec: avccToCodecString(avcc),
|
|
6161
|
+
codedWidth: track.width,
|
|
6162
|
+
codedHeight: track.height,
|
|
6163
|
+
description: toArrayBuffer(Buffer.from(avcc)),
|
|
6164
|
+
displayAspectWidth,
|
|
6165
|
+
displayAspectHeight,
|
|
6166
|
+
track: "video"
|
|
5899
6167
|
});
|
|
5900
|
-
} else {
|
|
5901
6168
|
this.container.write({
|
|
5902
|
-
byteLength:
|
|
6169
|
+
byteLength: sample.length,
|
|
5903
6170
|
duration: null,
|
|
5904
6171
|
timestamp: frame.time * 1e3,
|
|
5905
6172
|
type: "key",
|
|
5906
6173
|
copyTo: (destination) => {
|
|
5907
|
-
|
|
6174
|
+
new Uint8Array(destination).set(sample);
|
|
5908
6175
|
},
|
|
5909
|
-
track: "
|
|
6176
|
+
track: "video"
|
|
5910
6177
|
});
|
|
5911
6178
|
}
|
|
6179
|
+
} else {
|
|
6180
|
+
const sample = annexb2avcSample(frame.data);
|
|
6181
|
+
this.container.write({
|
|
6182
|
+
byteLength: sample.length,
|
|
6183
|
+
duration: null,
|
|
6184
|
+
timestamp: frame.time * 1e3,
|
|
6185
|
+
type: frame.isKeyframe ? "key" : "delta",
|
|
6186
|
+
copyTo: (destination) => {
|
|
6187
|
+
new Uint8Array(destination).set(sample);
|
|
6188
|
+
},
|
|
6189
|
+
track: "video"
|
|
6190
|
+
});
|
|
5912
6191
|
}
|
|
5913
6192
|
};
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
);
|
|
5924
|
-
this.container.write({
|
|
5925
|
-
codec: track.codec,
|
|
5926
|
-
codedWidth: track.width,
|
|
5927
|
-
codedHeight: track.height,
|
|
5928
|
-
description: avcc.buffer,
|
|
5929
|
-
displayAspectWidth,
|
|
5930
|
-
displayAspectHeight,
|
|
5931
|
-
track: "video"
|
|
5932
|
-
});
|
|
5933
|
-
this.container.write({
|
|
5934
|
-
byteLength: frame.data.length,
|
|
5935
|
-
duration: null,
|
|
5936
|
-
timestamp: frame.time * 1e3,
|
|
5937
|
-
type: "key",
|
|
5938
|
-
copyTo: (destination) => {
|
|
5939
|
-
frame.data.copy(destination);
|
|
5940
|
-
},
|
|
5941
|
-
track: "video"
|
|
5942
|
-
});
|
|
6193
|
+
processAudioInput = ({ eol, frame }) => {
|
|
6194
|
+
if (this.stopped) {
|
|
6195
|
+
return;
|
|
6196
|
+
}
|
|
6197
|
+
if (!frame) {
|
|
6198
|
+
if (eol) {
|
|
6199
|
+
this.audioStopped = true;
|
|
6200
|
+
if (!this.tracks.some((track2) => track2.kind === "video") || this.videoStopped) {
|
|
6201
|
+
void this.stop();
|
|
5943
6202
|
}
|
|
5944
|
-
} else {
|
|
5945
|
-
this.container.write({
|
|
5946
|
-
byteLength: frame.data.length,
|
|
5947
|
-
duration: null,
|
|
5948
|
-
timestamp: frame.time * 1e3,
|
|
5949
|
-
type: frame.isKeyframe ? "key" : "delta",
|
|
5950
|
-
copyTo: (destination) => {
|
|
5951
|
-
frame.data.copy(destination);
|
|
5952
|
-
},
|
|
5953
|
-
track: "video"
|
|
5954
|
-
});
|
|
5955
6203
|
}
|
|
6204
|
+
return;
|
|
6205
|
+
}
|
|
6206
|
+
const track = this.tracks.find((t) => t.kind === "audio");
|
|
6207
|
+
this.audioStopped = false;
|
|
6208
|
+
if (!this.container.audioTrack) {
|
|
6209
|
+
this.container.write({
|
|
6210
|
+
codec: track.codec,
|
|
6211
|
+
description: toArrayBuffer(OpusRtpPayload.createCodecPrivate()),
|
|
6212
|
+
numberOfChannels: 2,
|
|
6213
|
+
sampleRate: track.clockRate,
|
|
6214
|
+
track: "audio"
|
|
6215
|
+
});
|
|
6216
|
+
} else {
|
|
6217
|
+
this.container.write({
|
|
6218
|
+
byteLength: frame.data.length,
|
|
6219
|
+
duration: null,
|
|
6220
|
+
timestamp: frame.time * 1e3,
|
|
6221
|
+
type: "key",
|
|
6222
|
+
copyTo: (destination) => {
|
|
6223
|
+
new Uint8Array(destination).set(frame.data);
|
|
6224
|
+
},
|
|
6225
|
+
track: "audio"
|
|
6226
|
+
});
|
|
5956
6227
|
}
|
|
5957
6228
|
};
|
|
5958
6229
|
start() {
|
|
5959
6230
|
}
|
|
5960
|
-
stop() {
|
|
6231
|
+
async stop() {
|
|
6232
|
+
if (this.stopped) {
|
|
6233
|
+
return;
|
|
6234
|
+
}
|
|
6235
|
+
this.stopped = true;
|
|
6236
|
+
await this.container.stop();
|
|
6237
|
+
await this.output({ eol: true });
|
|
6238
|
+
await this.onStopped.execute();
|
|
5961
6239
|
}
|
|
5962
6240
|
};
|
|
5963
6241
|
function computeRatio(a, b) {
|
|
@@ -5972,6 +6250,21 @@ function computeRatio(a, b) {
|
|
|
5972
6250
|
const divisor = gcd(a, b);
|
|
5973
6251
|
return [a / divisor, b / divisor];
|
|
5974
6252
|
}
|
|
6253
|
+
function avccToCodecString(avcc) {
|
|
6254
|
+
if (avcc.byteLength < 4) {
|
|
6255
|
+
throw new Error("invalid avcc decoder configuration record");
|
|
6256
|
+
}
|
|
6257
|
+
return `avc1.${toHex(avcc[1])}${toHex(avcc[2])}${toHex(avcc[3])}`;
|
|
6258
|
+
}
|
|
6259
|
+
function toHex(value) {
|
|
6260
|
+
return value.toString(16).padStart(2, "0");
|
|
6261
|
+
}
|
|
6262
|
+
function toArrayBuffer(buffer2) {
|
|
6263
|
+
return buffer2.buffer.slice(
|
|
6264
|
+
buffer2.byteOffset,
|
|
6265
|
+
buffer2.byteOffset + buffer2.byteLength
|
|
6266
|
+
);
|
|
6267
|
+
}
|
|
5975
6268
|
|
|
5976
6269
|
// ../rtp/src/extra/processor/mp4Callback.ts
|
|
5977
6270
|
import { appendFile } from "fs/promises";
|
|
@@ -6001,14 +6294,16 @@ var MP4Callback = class extends MP4Base {
|
|
|
6001
6294
|
this.processVideoInput(input);
|
|
6002
6295
|
};
|
|
6003
6296
|
destroy = () => {
|
|
6004
|
-
this.
|
|
6005
|
-
|
|
6297
|
+
void this.stop().finally(() => {
|
|
6298
|
+
this.cb = void 0;
|
|
6299
|
+
this.queue.cancel();
|
|
6300
|
+
});
|
|
6006
6301
|
};
|
|
6007
6302
|
static saveToFileSystem = (path2) => {
|
|
6008
6303
|
const queue = new PromiseQueue();
|
|
6009
6304
|
return async (value) => {
|
|
6010
6305
|
await queue.push(async () => {
|
|
6011
|
-
if (value
|
|
6306
|
+
if ("data" in value) {
|
|
6012
6307
|
await appendFile(path2, value.data);
|
|
6013
6308
|
} else if (value.eol) {
|
|
6014
6309
|
}
|
|
@@ -6171,8 +6466,8 @@ var NackHandlerBase = class {
|
|
|
6171
6466
|
_lost = {};
|
|
6172
6467
|
clearNackInterval;
|
|
6173
6468
|
internalStats = {};
|
|
6174
|
-
onNackSent = new
|
|
6175
|
-
onPacketLost = new
|
|
6469
|
+
onNackSent = new Event2();
|
|
6470
|
+
onPacketLost = new Event2();
|
|
6176
6471
|
mediaSourceSsrc;
|
|
6177
6472
|
retryCount = 10;
|
|
6178
6473
|
stopped = false;
|
|
@@ -6318,7 +6613,7 @@ var WebmBase = class {
|
|
|
6318
6613
|
stopped = false;
|
|
6319
6614
|
videoKeyframeReceived = false;
|
|
6320
6615
|
internalStats = {};
|
|
6321
|
-
onStopped = new
|
|
6616
|
+
onStopped = new Event2();
|
|
6322
6617
|
toJSON() {
|
|
6323
6618
|
return {
|
|
6324
6619
|
...this.internalStats,
|
|
@@ -6711,7 +7006,7 @@ var NtpTimeCallback = class extends SimpleProcessorCallbackBase(NtpTimeBase) {
|
|
|
6711
7006
|
var RtcpSourceCallback = class {
|
|
6712
7007
|
cb;
|
|
6713
7008
|
destructor;
|
|
6714
|
-
onStopped = new
|
|
7009
|
+
onStopped = new Event2();
|
|
6715
7010
|
toJSON() {
|
|
6716
7011
|
return {};
|
|
6717
7012
|
}
|
|
@@ -6749,7 +7044,7 @@ var RtpSourceCallback = class {
|
|
|
6749
7044
|
}
|
|
6750
7045
|
cb;
|
|
6751
7046
|
destructor;
|
|
6752
|
-
onStopped = new
|
|
7047
|
+
onStopped = new Event2();
|
|
6753
7048
|
stats = {};
|
|
6754
7049
|
buffer = [];
|
|
6755
7050
|
bufferFulfilled = false;
|
|
@@ -6985,9 +7280,7 @@ var WebmStream = class extends WebmBase {
|
|
|
6985
7280
|
};
|
|
6986
7281
|
|
|
6987
7282
|
// src/nonstandard/navigator.ts
|
|
6988
|
-
import { randomBytes as randomBytes5 } from "crypto";
|
|
6989
7283
|
import { createSocket as createSocket3 } from "dgram";
|
|
6990
|
-
import { jspack as jspack8 } from "@shinyoshiaki/jspack";
|
|
6991
7284
|
|
|
6992
7285
|
// ../dtls/src/context/cipher.ts
|
|
6993
7286
|
import { createSign, randomBytes as randomBytes3, webcrypto } from "crypto";
|
|
@@ -7683,7 +7976,7 @@ function createECDHEPSKKeyExchange() {
|
|
|
7683
7976
|
}
|
|
7684
7977
|
|
|
7685
7978
|
// ../dtls/src/cipher/suites/aead.ts
|
|
7686
|
-
import { createCipheriv as
|
|
7979
|
+
import { createCipheriv as createCipheriv5, createDecipheriv as createDecipheriv3 } from "crypto";
|
|
7687
7980
|
var err2 = debug(
|
|
7688
7981
|
"werift-dtls : packages/dtls/src/cipher/suites/aead.ts : err"
|
|
7689
7982
|
);
|
|
@@ -7731,7 +8024,7 @@ var AEADCipher = class extends AbstractCipher {
|
|
|
7731
8024
|
iv.writeUIntBE(header.sequenceNumber, this.nonceImplicitLength + 2, 6);
|
|
7732
8025
|
const explicitNonce = iv.slice(this.nonceImplicitLength);
|
|
7733
8026
|
const additionalBuffer = this.encodeAdditionalBuffer(header, data.length);
|
|
7734
|
-
const cipher =
|
|
8027
|
+
const cipher = createCipheriv5(this.blockAlgorithm, writeKey, iv, {
|
|
7735
8028
|
authTagLength: this.authTagLength
|
|
7736
8029
|
});
|
|
7737
8030
|
cipher.setAAD(additionalBuffer, {
|
|
@@ -8679,45 +8972,115 @@ handlers2[20 /* finished_20 */] = ({ dtls }) => (message) => {
|
|
|
8679
8972
|
// ../dtls/src/server.ts
|
|
8680
8973
|
var log19 = debug("werift-dtls : packages/dtls/src/server.ts : log");
|
|
8681
8974
|
|
|
8682
|
-
// ../ice/src/stun/const.ts
|
|
8975
|
+
// ../ice-server/src/stun/const.ts
|
|
8683
8976
|
var COOKIE = 554869826;
|
|
8684
8977
|
var IPV4_PROTOCOL = 1;
|
|
8685
8978
|
var IPV6_PROTOCOL = 2;
|
|
8686
8979
|
|
|
8687
|
-
// ../ice/src/stun/
|
|
8688
|
-
import
|
|
8689
|
-
|
|
8980
|
+
// ../ice-server/src/stun/ip.ts
|
|
8981
|
+
import { isIPv4, isIPv6 } from "node:net";
|
|
8982
|
+
function ipAddressToBuffer(address) {
|
|
8983
|
+
if (isIPv4(address)) {
|
|
8984
|
+
const buf = Buffer.allocUnsafe(4);
|
|
8985
|
+
const parts = address.split(".");
|
|
8986
|
+
for (let i = 0; i < 4; i++) {
|
|
8987
|
+
buf[i] = Number(parts[i]) & 255;
|
|
8988
|
+
}
|
|
8989
|
+
return buf;
|
|
8990
|
+
}
|
|
8991
|
+
if (!isIPv6(address)) {
|
|
8992
|
+
throw new Error(`Invalid ip address: ${address}`);
|
|
8993
|
+
}
|
|
8994
|
+
const sections = address.split(":", 8);
|
|
8995
|
+
for (let i = 0; i < sections.length; i++) {
|
|
8996
|
+
let v4Buffer;
|
|
8997
|
+
if (isIPv4(sections[i])) {
|
|
8998
|
+
v4Buffer = ipAddressToBuffer(sections[i]);
|
|
8999
|
+
sections[i] = v4Buffer.subarray(0, 2).toString("hex");
|
|
9000
|
+
}
|
|
9001
|
+
if (v4Buffer && ++i < 8) {
|
|
9002
|
+
sections.splice(i, 0, v4Buffer.subarray(2, 4).toString("hex"));
|
|
9003
|
+
}
|
|
9004
|
+
}
|
|
9005
|
+
if (sections[0] === "") {
|
|
9006
|
+
while (sections.length < 8) sections.unshift("0");
|
|
9007
|
+
} else if (sections[sections.length - 1] === "") {
|
|
9008
|
+
while (sections.length < 8) sections.push("0");
|
|
9009
|
+
} else if (sections.length < 8) {
|
|
9010
|
+
let emptyIndex = 0;
|
|
9011
|
+
for (; emptyIndex < sections.length && sections[emptyIndex] !== ""; emptyIndex++) ;
|
|
9012
|
+
const zeros = [];
|
|
9013
|
+
for (let n = 9 - sections.length; n > 0; n--) {
|
|
9014
|
+
zeros.push("0");
|
|
9015
|
+
}
|
|
9016
|
+
sections.splice(emptyIndex, 1, ...zeros);
|
|
9017
|
+
}
|
|
9018
|
+
const result = Buffer.allocUnsafe(16);
|
|
9019
|
+
let offset = 0;
|
|
9020
|
+
for (const section of sections) {
|
|
9021
|
+
const word = Number.parseInt(section, 16) || 0;
|
|
9022
|
+
result[offset++] = word >> 8 & 255;
|
|
9023
|
+
result[offset++] = word & 255;
|
|
9024
|
+
}
|
|
9025
|
+
return result;
|
|
9026
|
+
}
|
|
9027
|
+
function bufferToIpAddress(buf) {
|
|
9028
|
+
if (buf.length === 4) {
|
|
9029
|
+
return `${buf[0]}.${buf[1]}.${buf[2]}.${buf[3]}`;
|
|
9030
|
+
}
|
|
9031
|
+
if (buf.length !== 16) {
|
|
9032
|
+
throw new Error(`Invalid ip address buffer length: ${buf.length}`);
|
|
9033
|
+
}
|
|
9034
|
+
const hextets = [];
|
|
9035
|
+
for (let i = 0; i < 16; i += 2) {
|
|
9036
|
+
hextets.push(buf.readUInt16BE(i).toString(16));
|
|
9037
|
+
}
|
|
9038
|
+
let result = hextets.join(":");
|
|
9039
|
+
result = result.replace(/(^|:)0(:0)*:0(:|$)/, "$1::$3");
|
|
9040
|
+
result = result.replace(/:{3,4}/, "::");
|
|
9041
|
+
return result;
|
|
9042
|
+
}
|
|
9043
|
+
function isIpV4Address(address) {
|
|
9044
|
+
return isIPv4(address);
|
|
9045
|
+
}
|
|
9046
|
+
|
|
9047
|
+
// ../ice-server/src/stun/attributes.ts
|
|
8690
9048
|
function packAddress(value) {
|
|
8691
9049
|
const [address] = value;
|
|
8692
|
-
const protocol =
|
|
9050
|
+
const protocol = isIpV4Address(address) ? IPV4_PROTOCOL : IPV6_PROTOCOL;
|
|
8693
9051
|
const buffer2 = Buffer.alloc(4);
|
|
8694
9052
|
buffer2.writeUInt8(0, 0);
|
|
8695
9053
|
buffer2.writeUInt8(protocol, 1);
|
|
8696
9054
|
buffer2.writeUInt16BE(value[1], 2);
|
|
8697
|
-
return Buffer.concat([buffer2,
|
|
9055
|
+
return Buffer.concat([buffer2, ipAddressToBuffer(address)]);
|
|
8698
9056
|
}
|
|
8699
9057
|
function unpackErrorCode(data) {
|
|
8700
|
-
if (data.length < 4)
|
|
9058
|
+
if (data.length < 4) {
|
|
9059
|
+
throw new Error("STUN error code is less than 4 bytes");
|
|
9060
|
+
}
|
|
8701
9061
|
const codeHigh = data.readUInt8(2);
|
|
8702
9062
|
const codeLow = data.readUInt8(3);
|
|
8703
9063
|
const reason = data.slice(4).toString("utf8");
|
|
8704
9064
|
return [codeHigh * 100 + codeLow, reason];
|
|
8705
9065
|
}
|
|
8706
9066
|
function unpackAddress(data) {
|
|
8707
|
-
if (data.length < 4)
|
|
9067
|
+
if (data.length < 4) {
|
|
8708
9068
|
throw new Error("STUN address length is less than 4 bytes");
|
|
9069
|
+
}
|
|
8709
9070
|
const protocol = data.readUInt8(1);
|
|
8710
9071
|
const port = data.readUInt16BE(2);
|
|
8711
9072
|
const address = data.slice(4);
|
|
8712
9073
|
switch (protocol) {
|
|
8713
9074
|
case IPV4_PROTOCOL:
|
|
8714
|
-
if (address.length
|
|
8715
|
-
throw new Error(
|
|
8716
|
-
|
|
9075
|
+
if (address.length !== 4) {
|
|
9076
|
+
throw new Error("STUN address has invalid length for IPv4");
|
|
9077
|
+
}
|
|
9078
|
+
return [bufferToIpAddress(address), port];
|
|
8717
9079
|
case IPV6_PROTOCOL:
|
|
8718
|
-
if (address.length
|
|
9080
|
+
if (address.length !== 16) {
|
|
8719
9081
|
throw new Error("STUN address has invalid length for IPv6");
|
|
8720
|
-
|
|
9082
|
+
}
|
|
9083
|
+
return [bufferToIpAddress(address), port];
|
|
8721
9084
|
default:
|
|
8722
9085
|
throw new Error("STUN address has unknown protocol");
|
|
8723
9086
|
}
|
|
@@ -8744,11 +9107,28 @@ function packErrorCode(value) {
|
|
|
8744
9107
|
buffer2.writeUInt16BE(0, 0);
|
|
8745
9108
|
buffer2.writeUInt8(Math.floor(value[0] / 100), 2);
|
|
8746
9109
|
buffer2.writeUInt8(value[0] % 100, 3);
|
|
8747
|
-
const
|
|
8748
|
-
return Buffer.concat([buffer2,
|
|
9110
|
+
const encoded = Buffer.from(value[1], "utf8");
|
|
9111
|
+
return Buffer.concat([buffer2, encoded]);
|
|
8749
9112
|
}
|
|
8750
|
-
function
|
|
8751
|
-
|
|
9113
|
+
function packUnknownAttributes(value) {
|
|
9114
|
+
const buffer2 = Buffer.alloc(value.length * 2);
|
|
9115
|
+
value.forEach((attributeType, index) => {
|
|
9116
|
+
buffer2.writeUInt16BE(attributeType, index * 2);
|
|
9117
|
+
});
|
|
9118
|
+
return buffer2;
|
|
9119
|
+
}
|
|
9120
|
+
function unpackUnknownAttributes(data) {
|
|
9121
|
+
if (data.length % 2 !== 0) {
|
|
9122
|
+
throw new Error("UNKNOWN-ATTRIBUTES must have an even length");
|
|
9123
|
+
}
|
|
9124
|
+
const attributes = [];
|
|
9125
|
+
for (let offset = 0; offset < data.length; offset += 2) {
|
|
9126
|
+
attributes.push(data.readUInt16BE(offset));
|
|
9127
|
+
}
|
|
9128
|
+
return attributes;
|
|
9129
|
+
}
|
|
9130
|
+
function packXorAddress(value, transactionId) {
|
|
9131
|
+
return xorAddress(packAddress(value), transactionId);
|
|
8752
9132
|
}
|
|
8753
9133
|
var packUnsigned = (value) => {
|
|
8754
9134
|
const buffer2 = Buffer.alloc(4);
|
|
@@ -8763,18 +9143,23 @@ var packUnsignedShort = (value) => {
|
|
|
8763
9143
|
};
|
|
8764
9144
|
var unpackUnsignedShort = (data) => data.readUInt16BE(0);
|
|
8765
9145
|
var packUnsigned64 = (value) => {
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
const int2 = new Int64.Int64BE(data);
|
|
8770
|
-
return BigInt(int2.toString());
|
|
9146
|
+
const buffer2 = Buffer.allocUnsafe(8);
|
|
9147
|
+
buffer2.writeBigUInt64BE(value, 0);
|
|
9148
|
+
return buffer2;
|
|
8771
9149
|
};
|
|
9150
|
+
var unpackUnsigned64 = (data) => data.readBigUInt64BE(0);
|
|
8772
9151
|
var packString = (value) => Buffer.from(value, "utf8");
|
|
8773
9152
|
var unpackString = (data) => data.toString("utf8");
|
|
9153
|
+
var packSoftware = (value) => {
|
|
9154
|
+
if ([...value].length >= 128) {
|
|
9155
|
+
throw new Error("SOFTWARE must be shorter than 128 characters");
|
|
9156
|
+
}
|
|
9157
|
+
return packString(value);
|
|
9158
|
+
};
|
|
8774
9159
|
var packBytes = (value) => value;
|
|
8775
9160
|
var unpackBytes = (data) => data;
|
|
8776
|
-
var packNone = (
|
|
8777
|
-
var unpackNone = (
|
|
9161
|
+
var packNone = () => Buffer.alloc(0);
|
|
9162
|
+
var unpackNone = () => null;
|
|
8778
9163
|
var ATTRIBUTES = [
|
|
8779
9164
|
[1, "MAPPED-ADDRESS", packAddress, unpackAddress],
|
|
8780
9165
|
[3, "CHANGE-REQUEST", packUnsigned, unpackUnsigned],
|
|
@@ -8783,6 +9168,12 @@ var ATTRIBUTES = [
|
|
|
8783
9168
|
[6, "USERNAME", packString, unpackString],
|
|
8784
9169
|
[8, "MESSAGE-INTEGRITY", packBytes, unpackBytes],
|
|
8785
9170
|
[9, "ERROR-CODE", packErrorCode, unpackErrorCode],
|
|
9171
|
+
[
|
|
9172
|
+
10,
|
|
9173
|
+
"UNKNOWN-ATTRIBUTES",
|
|
9174
|
+
packUnknownAttributes,
|
|
9175
|
+
unpackUnknownAttributes
|
|
9176
|
+
],
|
|
8786
9177
|
[12, "CHANNEL-NUMBER", packUnsignedShort, unpackUnsignedShort],
|
|
8787
9178
|
[13, "LIFETIME", packUnsigned, unpackUnsigned],
|
|
8788
9179
|
[18, "XOR-PEER-ADDRESS", packXorAddress, unpackXorAddress],
|
|
@@ -8790,11 +9181,20 @@ var ATTRIBUTES = [
|
|
|
8790
9181
|
[20, "REALM", packString, unpackString],
|
|
8791
9182
|
[21, "NONCE", packBytes, unpackBytes],
|
|
8792
9183
|
[22, "XOR-RELAYED-ADDRESS", packXorAddress, unpackXorAddress],
|
|
9184
|
+
[23, "REQUESTED-ADDRESS-FAMILY", packUnsigned, unpackUnsigned],
|
|
9185
|
+
[24, "EVEN-PORT", packBytes, unpackBytes],
|
|
8793
9186
|
[25, "REQUESTED-TRANSPORT", packUnsigned, unpackUnsigned],
|
|
9187
|
+
[28, "MESSAGE-INTEGRITY-SHA256", packBytes, unpackBytes],
|
|
9188
|
+
[29, "PASSWORD-ALGORITHM", packBytes, unpackBytes],
|
|
9189
|
+
[30, "USERHASH", packBytes, unpackBytes],
|
|
8794
9190
|
[32, "XOR-MAPPED-ADDRESS", packXorAddress, unpackXorAddress],
|
|
9191
|
+
[34, "RESERVATION-TOKEN", packBytes, unpackBytes],
|
|
8795
9192
|
[36, "PRIORITY", packUnsigned, unpackUnsigned],
|
|
8796
9193
|
[37, "USE-CANDIDATE", packNone, unpackNone],
|
|
8797
|
-
[
|
|
9194
|
+
[32770, "PASSWORD-ALGORITHMS", packBytes, unpackBytes],
|
|
9195
|
+
[32771, "ALTERNATE-DOMAIN", packString, unpackString],
|
|
9196
|
+
[32802, "SOFTWARE", packSoftware, unpackString],
|
|
9197
|
+
[32803, "ALTERNATE-SERVER", packAddress, unpackAddress],
|
|
8798
9198
|
[32808, "FINGERPRINT", packUnsigned, unpackUnsigned],
|
|
8799
9199
|
[32809, "ICE-CONTROLLED", packUnsigned64, unpackUnsigned64],
|
|
8800
9200
|
[32810, "ICE-CONTROLLING", packUnsigned64, unpackUnsigned64],
|
|
@@ -8819,43 +9219,70 @@ var ATTRIBUTES_BY_NAME = ATTRIBUTES.reduce(
|
|
|
8819
9219
|
// ../ice/src/stun/transaction.ts
|
|
8820
9220
|
var log20 = debug("werift-ice:packages/ice/src/stun/transaction.ts");
|
|
8821
9221
|
|
|
9222
|
+
// ../ice/src/stun/tcpProtocol.ts
|
|
9223
|
+
var log21 = debug("werift-ice:packages/ice/src/stun/tcpProtocol.ts");
|
|
9224
|
+
|
|
8822
9225
|
// ../ice/src/stun/protocol.ts
|
|
8823
|
-
var
|
|
9226
|
+
var log22 = debug("werift-ice : packages/ice/src/stun/protocol.ts");
|
|
8824
9227
|
|
|
8825
9228
|
// ../ice/src/turn/protocol.ts
|
|
8826
|
-
|
|
8827
|
-
var log22 = debug("werift-ice:packages/ice/src/turn/protocol.ts");
|
|
8828
|
-
|
|
8829
|
-
// ../ice/src/ice.ts
|
|
8830
|
-
import * as Int642 from "int64-buffer";
|
|
8831
|
-
import isEqual from "fast-deep-equal";
|
|
9229
|
+
var log23 = debug("werift-ice:packages/ice/src/turn/protocol.ts");
|
|
8832
9230
|
|
|
8833
9231
|
// ../ice/src/dns/lookup.ts
|
|
8834
9232
|
import mdns from "multicast-dns";
|
|
8835
9233
|
|
|
8836
9234
|
// ../ice/src/iceBase.ts
|
|
8837
|
-
var
|
|
9235
|
+
var log24 = debug("werift-ice : packages/ice/src/ice.ts : log");
|
|
8838
9236
|
|
|
8839
9237
|
// ../ice/src/utils.ts
|
|
8840
|
-
import nodeIp2 from "ip";
|
|
8841
9238
|
var logger = debug("werift-ice : packages/ice/src/utils.ts");
|
|
8842
9239
|
|
|
8843
9240
|
// ../ice/src/ice.ts
|
|
8844
|
-
var
|
|
9241
|
+
var log25 = debug("werift-ice : packages/ice/src/ice.ts : log");
|
|
8845
9242
|
|
|
8846
9243
|
// src/helper.ts
|
|
8847
9244
|
import { EventEmitter } from "node:events";
|
|
9245
|
+
function divide(from, split) {
|
|
9246
|
+
const arr = from.split(split);
|
|
9247
|
+
return [arr[0], arr.slice(1).join(split)];
|
|
9248
|
+
}
|
|
8848
9249
|
var EventTarget2 = class extends EventEmitter {
|
|
8849
|
-
|
|
9250
|
+
emit(type, ...args) {
|
|
9251
|
+
if (typeof type !== "string") {
|
|
9252
|
+
return super.emit(type, ...args);
|
|
9253
|
+
}
|
|
9254
|
+
if (args.length === 0) {
|
|
9255
|
+
return super.emit(type, new Event(type));
|
|
9256
|
+
}
|
|
9257
|
+
const [event, ...rest] = args;
|
|
9258
|
+
if (event && typeof event === "object" && !("type" in event)) {
|
|
9259
|
+
try {
|
|
9260
|
+
Object.defineProperty(event, "type", {
|
|
9261
|
+
configurable: true,
|
|
9262
|
+
enumerable: true,
|
|
9263
|
+
value: type
|
|
9264
|
+
});
|
|
9265
|
+
} catch {
|
|
9266
|
+
return super.emit(type, { type, ...event }, ...rest);
|
|
9267
|
+
}
|
|
9268
|
+
}
|
|
9269
|
+
return super.emit(type, event, ...rest);
|
|
9270
|
+
}
|
|
9271
|
+
addEventListener = (type, listener, options) => {
|
|
9272
|
+
if (typeof options === "object" && options?.once) {
|
|
9273
|
+
this.once(type, listener);
|
|
9274
|
+
return;
|
|
9275
|
+
}
|
|
8850
9276
|
this.addListener(type, listener);
|
|
8851
9277
|
};
|
|
8852
9278
|
removeEventListener = (type, listener) => {
|
|
8853
9279
|
this.removeListener(type, listener);
|
|
8854
9280
|
};
|
|
9281
|
+
dispatchEvent = (event) => this.emit(event.type, event);
|
|
8855
9282
|
};
|
|
8856
9283
|
|
|
8857
9284
|
// src/dataChannel.ts
|
|
8858
|
-
var
|
|
9285
|
+
var log26 = debug("werift:packages/webrtc/src/dataChannel.ts");
|
|
8859
9286
|
|
|
8860
9287
|
// src/media/extension/rtcpFeedback.ts
|
|
8861
9288
|
var useNACK = () => ({ type: "nack" });
|
|
@@ -8958,6 +9385,15 @@ var DTLS_SETUP_ROLE = Object.keys(
|
|
|
8958
9385
|
acc[key] = cur;
|
|
8959
9386
|
return acc;
|
|
8960
9387
|
}, {});
|
|
9388
|
+
var FMTP_INT_PARAMETERS = [
|
|
9389
|
+
"apt",
|
|
9390
|
+
"max-fr",
|
|
9391
|
+
"max-fs",
|
|
9392
|
+
"maxplaybackrate",
|
|
9393
|
+
"minptime",
|
|
9394
|
+
"stereo",
|
|
9395
|
+
"useinbandfec"
|
|
9396
|
+
];
|
|
8961
9397
|
var SRTP_PROFILE = {
|
|
8962
9398
|
SRTP_AES128_CM_HMAC_SHA1_80: ProtectionProfileAes128CmHmacSha1_80,
|
|
8963
9399
|
SRTP_AEAD_AES_128_GCM: ProtectionProfileAeadAes128Gcm
|
|
@@ -8980,9 +9416,9 @@ var MediaStreamTrack = class extends EventTarget2 {
|
|
|
8980
9416
|
codec;
|
|
8981
9417
|
/**todo impl */
|
|
8982
9418
|
enabled = true;
|
|
8983
|
-
onReceiveRtp = new
|
|
8984
|
-
onReceiveRtcp = new
|
|
8985
|
-
onSourceChanged = new
|
|
9419
|
+
onReceiveRtp = new Event2();
|
|
9420
|
+
onReceiveRtcp = new Event2();
|
|
9421
|
+
onSourceChanged = new Event2();
|
|
8986
9422
|
stopped = false;
|
|
8987
9423
|
muted = true;
|
|
8988
9424
|
constructor(props) {
|
|
@@ -9015,7 +9451,7 @@ var MediaStreamTrack = class extends EventTarget2 {
|
|
|
9015
9451
|
var MediaStream = class {
|
|
9016
9452
|
id;
|
|
9017
9453
|
tracks = [];
|
|
9018
|
-
constructor(props) {
|
|
9454
|
+
constructor(props = {}) {
|
|
9019
9455
|
if (Array.isArray(props)) {
|
|
9020
9456
|
this.tracks = props;
|
|
9021
9457
|
} else {
|
|
@@ -9027,6 +9463,12 @@ var MediaStream = class {
|
|
|
9027
9463
|
track.streamId = this.id;
|
|
9028
9464
|
this.tracks.push(track);
|
|
9029
9465
|
}
|
|
9466
|
+
removeTrack(track) {
|
|
9467
|
+
this.tracks = this.tracks.filter((currentTrack) => currentTrack !== track);
|
|
9468
|
+
if (track.streamId === this.id) {
|
|
9469
|
+
track.streamId = void 0;
|
|
9470
|
+
}
|
|
9471
|
+
}
|
|
9030
9472
|
getTracks() {
|
|
9031
9473
|
return this.tracks;
|
|
9032
9474
|
}
|
|
@@ -9039,33 +9481,24 @@ var MediaStream = class {
|
|
|
9039
9481
|
};
|
|
9040
9482
|
|
|
9041
9483
|
// src/utils.ts
|
|
9042
|
-
var
|
|
9484
|
+
var log27 = debug("werift:packages/webrtc/src/utils.ts");
|
|
9043
9485
|
var milliTime = Date.now;
|
|
9044
9486
|
var startupTimestampInMicroseconds = BigInt(Date.now()) * 1000n - process.hrtime.bigint() / 1000n;
|
|
9045
9487
|
var createSelfSignedCertificate = CipherContext.createSelfSignedCertificateWithKey;
|
|
9046
9488
|
|
|
9047
9489
|
// src/media/receiver/nack.ts
|
|
9048
|
-
var
|
|
9490
|
+
var log28 = debug("werift:packages/webrtc/src/media/receiver/nack.ts");
|
|
9049
9491
|
var LOST_SIZE2 = 30 * 5;
|
|
9050
9492
|
|
|
9051
9493
|
// src/media/receiver/receiverTwcc.ts
|
|
9052
|
-
var
|
|
9053
|
-
|
|
9054
|
-
// src/sdp.ts
|
|
9055
|
-
import * as Int643 from "int64-buffer";
|
|
9494
|
+
var log29 = debug("werift:packages/webrtc/media/receiver/receiverTwcc");
|
|
9056
9495
|
|
|
9057
9496
|
// src/transport/dtls.ts
|
|
9058
9497
|
import { Certificate as Certificate3, PrivateKey as PrivateKey2 } from "@fidm/x509";
|
|
9059
|
-
var
|
|
9498
|
+
var log30 = debug("werift:packages/webrtc/src/transport/dtls.ts");
|
|
9060
9499
|
|
|
9061
9500
|
// src/transport/ice.ts
|
|
9062
|
-
var
|
|
9063
|
-
|
|
9064
|
-
// src/transport/sctp.ts
|
|
9065
|
-
import { jspack as jspack6 } from "@shinyoshiaki/jspack";
|
|
9066
|
-
|
|
9067
|
-
// ../sctp/src/sctp.ts
|
|
9068
|
-
import { jspack as jspack5 } from "@shinyoshiaki/jspack";
|
|
9501
|
+
var log31 = debug("werift:packages/webrtc/src/transport/ice.ts");
|
|
9069
9502
|
|
|
9070
9503
|
// ../sctp/src/chunk.ts
|
|
9071
9504
|
var Chunk = class _Chunk {
|
|
@@ -9454,34 +9887,51 @@ function decodeParams(body) {
|
|
|
9454
9887
|
return params;
|
|
9455
9888
|
}
|
|
9456
9889
|
|
|
9457
|
-
// ../sctp/src/param.ts
|
|
9458
|
-
import { jspack as jspack4 } from "@shinyoshiaki/jspack";
|
|
9459
|
-
|
|
9460
9890
|
// ../sctp/src/sctp.ts
|
|
9461
|
-
var
|
|
9891
|
+
var log32 = debug("werift/sctp/sctp");
|
|
9462
9892
|
var SCTP_RTO_ALPHA = 1 / 8;
|
|
9463
9893
|
var SCTP_RTO_BETA = 1 / 4;
|
|
9464
9894
|
var SCTP_TSN_MODULO = 2 ** 32;
|
|
9465
9895
|
|
|
9466
9896
|
// src/transport/sctp.ts
|
|
9467
|
-
var
|
|
9897
|
+
var log33 = debug("werift:packages/webrtc/src/transport/sctp.ts");
|
|
9468
9898
|
var DEFAULT_MAX_MESSAGE_SIZE = 65536;
|
|
9469
9899
|
|
|
9900
|
+
// src/sdp.ts
|
|
9901
|
+
function codecParametersFromString(str) {
|
|
9902
|
+
const parameters = {};
|
|
9903
|
+
str.split(";").forEach((param) => {
|
|
9904
|
+
if (param.includes("=")) {
|
|
9905
|
+
const [k, v] = divide(param, "=");
|
|
9906
|
+
if (FMTP_INT_PARAMETERS.includes(k)) {
|
|
9907
|
+
parameters[k] = Number(v);
|
|
9908
|
+
} else {
|
|
9909
|
+
parameters[k] = v;
|
|
9910
|
+
}
|
|
9911
|
+
} else if (param.includes(":")) {
|
|
9912
|
+
const [k, v] = param.split(":");
|
|
9913
|
+
parameters[k] = Number.isNaN(Number(v)) ? v : Number(v);
|
|
9914
|
+
} else {
|
|
9915
|
+
parameters[param] = void 0;
|
|
9916
|
+
}
|
|
9917
|
+
});
|
|
9918
|
+
return parameters;
|
|
9919
|
+
}
|
|
9920
|
+
|
|
9470
9921
|
// src/media/rtpReceiver.ts
|
|
9471
|
-
var
|
|
9922
|
+
var log34 = debug("werift:packages/webrtc/src/media/rtpReceiver.ts");
|
|
9472
9923
|
|
|
9473
9924
|
// src/media/router.ts
|
|
9474
|
-
var
|
|
9925
|
+
var log35 = debug("werift:packages/webrtc/src/media/router.ts");
|
|
9475
9926
|
|
|
9476
9927
|
// src/media/rtpSender.ts
|
|
9477
|
-
|
|
9478
|
-
var log35 = debug("werift:packages/webrtc/src/media/rtpSender.ts");
|
|
9928
|
+
var log36 = debug("werift:packages/webrtc/src/media/rtpSender.ts");
|
|
9479
9929
|
|
|
9480
9930
|
// src/sctpManager.ts
|
|
9481
|
-
var
|
|
9931
|
+
var log37 = debug("werift:packages/webrtc/src/transport/sctpManager.ts");
|
|
9482
9932
|
|
|
9483
9933
|
// src/secureTransportManager.ts
|
|
9484
|
-
var
|
|
9934
|
+
var log38 = debug(
|
|
9485
9935
|
"werift:packages/webrtc/src/transport/secureTransportManager.ts"
|
|
9486
9936
|
);
|
|
9487
9937
|
var srtpProfiles = [
|
|
@@ -9491,7 +9941,7 @@ var srtpProfiles = [
|
|
|
9491
9941
|
];
|
|
9492
9942
|
|
|
9493
9943
|
// src/peerConnection.ts
|
|
9494
|
-
var
|
|
9944
|
+
var log39 = debug("werift:packages/webrtc/src/peerConnection.ts");
|
|
9495
9945
|
function generateDefaultPeerConfig() {
|
|
9496
9946
|
return {
|
|
9497
9947
|
codecs: {
|
|
@@ -9503,18 +9953,25 @@ function generateDefaultPeerConfig() {
|
|
|
9503
9953
|
video: []
|
|
9504
9954
|
},
|
|
9505
9955
|
iceTransportPolicy: "all",
|
|
9956
|
+
iceLite: false,
|
|
9506
9957
|
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
|
9507
9958
|
icePortRange: void 0,
|
|
9508
9959
|
iceInterfaceAddresses: void 0,
|
|
9509
9960
|
iceAdditionalHostAddresses: void 0,
|
|
9510
9961
|
iceUseIpv4: true,
|
|
9511
9962
|
iceUseIpv6: true,
|
|
9963
|
+
iceUseTcp: false,
|
|
9964
|
+
turnTransport: void 0,
|
|
9965
|
+
turnTlsOptions: void 0,
|
|
9512
9966
|
iceFilterStunResponse: void 0,
|
|
9513
9967
|
iceFilterCandidatePair: void 0,
|
|
9514
9968
|
icePasswordPrefix: void 0,
|
|
9515
9969
|
iceUseLinkLocalAddress: void 0,
|
|
9516
9970
|
dtls: {},
|
|
9517
9971
|
bundlePolicy: "max-compat",
|
|
9972
|
+
rtcpMuxPolicy: "require",
|
|
9973
|
+
iceCandidatePoolSize: 0,
|
|
9974
|
+
certificates: [],
|
|
9518
9975
|
debug: {},
|
|
9519
9976
|
midSuffix: false,
|
|
9520
9977
|
forceTurnTCP: false,
|
|
@@ -9524,7 +9981,129 @@ function generateDefaultPeerConfig() {
|
|
|
9524
9981
|
var defaultPeerConfig = generateDefaultPeerConfig();
|
|
9525
9982
|
|
|
9526
9983
|
// src/transceiverManager.ts
|
|
9527
|
-
var
|
|
9984
|
+
var log40 = debug("werift:packages/webrtc/src/media/rtpTransceiverManager.ts");
|
|
9985
|
+
|
|
9986
|
+
// src/nonstandard/dummyMedia.ts
|
|
9987
|
+
var AUDIO_PACKET_MS = 20;
|
|
9988
|
+
var AUDIO_TIMESTAMP_STEP = 960;
|
|
9989
|
+
var AUDIO_PAYLOAD = Buffer.from([248, 255, 254]);
|
|
9990
|
+
var DEFAULT_VIDEO_FPS = 30;
|
|
9991
|
+
var VIDEO_TIMESTAMP_STEP = 3e3;
|
|
9992
|
+
var DUMMY_SSRC_BASE = 439041024;
|
|
9993
|
+
var DUMMY_SEQUENCE_BASE = 16384;
|
|
9994
|
+
var DUMMY_SEQUENCE_STEP = 256;
|
|
9995
|
+
var nextDummySourceId = 0;
|
|
9996
|
+
var VP8_KEYFRAME = Buffer.from([
|
|
9997
|
+
16,
|
|
9998
|
+
16,
|
|
9999
|
+
0,
|
|
10000
|
+
0,
|
|
10001
|
+
157,
|
|
10002
|
+
1,
|
|
10003
|
+
42,
|
|
10004
|
+
2,
|
|
10005
|
+
0,
|
|
10006
|
+
2,
|
|
10007
|
+
0
|
|
10008
|
+
]);
|
|
10009
|
+
var VP8_DELTA_FRAME = Buffer.from([16, 17, 0, 0]);
|
|
10010
|
+
function createDummyAudioTrack(options = {}) {
|
|
10011
|
+
const track = new MediaStreamTrack({ kind: "audio" });
|
|
10012
|
+
const source = new ScheduledRtpSource({
|
|
10013
|
+
track,
|
|
10014
|
+
intervalMs: options.packetIntervalMs ?? AUDIO_PACKET_MS,
|
|
10015
|
+
buildPacket: ({ sequenceNumber, timestamp, ssrc }) => new RtpPacket(
|
|
10016
|
+
new RtpHeader({
|
|
10017
|
+
version: 2,
|
|
10018
|
+
payloadType: 111,
|
|
10019
|
+
sequenceNumber,
|
|
10020
|
+
timestamp,
|
|
10021
|
+
ssrc,
|
|
10022
|
+
marker: true
|
|
10023
|
+
}),
|
|
10024
|
+
Buffer.from(AUDIO_PAYLOAD)
|
|
10025
|
+
),
|
|
10026
|
+
nextTimestamp: (timestamp) => timestamp + AUDIO_TIMESTAMP_STEP
|
|
10027
|
+
});
|
|
10028
|
+
source.start();
|
|
10029
|
+
return { track, source };
|
|
10030
|
+
}
|
|
10031
|
+
function createDummyVideoTrack(options = {}) {
|
|
10032
|
+
const fps = options.fps ?? DEFAULT_VIDEO_FPS;
|
|
10033
|
+
const keyframeIntervalFrames = options.keyframeIntervalFrames ?? fps;
|
|
10034
|
+
const intervalMs = 1e3 / fps;
|
|
10035
|
+
const track = new MediaStreamTrack({ kind: "video" });
|
|
10036
|
+
const source = new ScheduledRtpSource({
|
|
10037
|
+
track,
|
|
10038
|
+
intervalMs,
|
|
10039
|
+
buildPacket: ({ sequenceNumber, timestamp, ssrc, iteration }) => {
|
|
10040
|
+
const isKeyframe = iteration % keyframeIntervalFrames === 0;
|
|
10041
|
+
return new RtpPacket(
|
|
10042
|
+
new RtpHeader({
|
|
10043
|
+
version: 2,
|
|
10044
|
+
payloadType: 96,
|
|
10045
|
+
sequenceNumber,
|
|
10046
|
+
timestamp,
|
|
10047
|
+
ssrc,
|
|
10048
|
+
marker: true
|
|
10049
|
+
}),
|
|
10050
|
+
Buffer.from(isKeyframe ? VP8_KEYFRAME : VP8_DELTA_FRAME)
|
|
10051
|
+
);
|
|
10052
|
+
},
|
|
10053
|
+
nextTimestamp: (timestamp) => timestamp + VIDEO_TIMESTAMP_STEP
|
|
10054
|
+
});
|
|
10055
|
+
source.start();
|
|
10056
|
+
return { track, source };
|
|
10057
|
+
}
|
|
10058
|
+
var ScheduledRtpSource = class {
|
|
10059
|
+
constructor(props) {
|
|
10060
|
+
this.props = props;
|
|
10061
|
+
}
|
|
10062
|
+
sourceId = nextDummySourceId++;
|
|
10063
|
+
ssrc = DUMMY_SSRC_BASE + this.sourceId >>> 0;
|
|
10064
|
+
sequenceNumber = DUMMY_SEQUENCE_BASE + this.sourceId * DUMMY_SEQUENCE_STEP & 65535;
|
|
10065
|
+
timestamp = 0;
|
|
10066
|
+
iteration = 0;
|
|
10067
|
+
nextTickAt;
|
|
10068
|
+
timer;
|
|
10069
|
+
stopped = false;
|
|
10070
|
+
start() {
|
|
10071
|
+
this.nextTickAt = performance.now();
|
|
10072
|
+
this.schedule();
|
|
10073
|
+
}
|
|
10074
|
+
stop() {
|
|
10075
|
+
this.stopped = true;
|
|
10076
|
+
if (this.timer) {
|
|
10077
|
+
clearTimeout(this.timer);
|
|
10078
|
+
this.timer = void 0;
|
|
10079
|
+
}
|
|
10080
|
+
}
|
|
10081
|
+
schedule() {
|
|
10082
|
+
if (this.stopped || this.props.track.stopped) {
|
|
10083
|
+
this.stop();
|
|
10084
|
+
return;
|
|
10085
|
+
}
|
|
10086
|
+
this.nextTickAt = (this.nextTickAt ?? performance.now()) + this.props.intervalMs;
|
|
10087
|
+
const delay = Math.max(0, this.nextTickAt - performance.now());
|
|
10088
|
+
this.timer = setTimeout(() => {
|
|
10089
|
+
if (this.stopped || this.props.track.stopped) {
|
|
10090
|
+
this.stop();
|
|
10091
|
+
return;
|
|
10092
|
+
}
|
|
10093
|
+
const packet = this.props.buildPacket({
|
|
10094
|
+
sequenceNumber: this.sequenceNumber,
|
|
10095
|
+
timestamp: this.timestamp,
|
|
10096
|
+
ssrc: this.ssrc,
|
|
10097
|
+
iteration: this.iteration
|
|
10098
|
+
});
|
|
10099
|
+
this.props.track.writeRtp(packet);
|
|
10100
|
+
this.sequenceNumber = this.sequenceNumber + 1 & 65535;
|
|
10101
|
+
this.timestamp = this.props.nextTimestamp(this.timestamp) >>> 0;
|
|
10102
|
+
this.iteration += 1;
|
|
10103
|
+
this.schedule();
|
|
10104
|
+
}, delay);
|
|
10105
|
+
}
|
|
10106
|
+
};
|
|
9528
10107
|
|
|
9529
10108
|
// src/nonstandard/navigator.ts
|
|
9530
10109
|
var Navigator = class {
|
|
@@ -9534,7 +10113,7 @@ var Navigator = class {
|
|
|
9534
10113
|
}
|
|
9535
10114
|
};
|
|
9536
10115
|
var MediaDevices = class extends EventTarget {
|
|
9537
|
-
constructor(props) {
|
|
10116
|
+
constructor(props = {}) {
|
|
9538
10117
|
super();
|
|
9539
10118
|
this.props = props;
|
|
9540
10119
|
this.video = props.video;
|
|
@@ -9542,31 +10121,16 @@ var MediaDevices = class extends EventTarget {
|
|
|
9542
10121
|
}
|
|
9543
10122
|
video;
|
|
9544
10123
|
audio;
|
|
10124
|
+
activeSourceStops = /* @__PURE__ */ new Map();
|
|
9545
10125
|
getUserMedia = async (constraints) => {
|
|
9546
|
-
const
|
|
9547
|
-
|
|
9548
|
-
this.video
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
|
|
9552
|
-
});
|
|
9553
|
-
}
|
|
9554
|
-
const audio = constraints.audio ? new MediaStreamTrack({ kind: "audio" }) : void 0;
|
|
9555
|
-
if (audio) {
|
|
9556
|
-
this.audio?.onReceiveRtp.subscribe((rtp) => {
|
|
9557
|
-
const cloned = rtp.clone();
|
|
9558
|
-
cloned.header.ssrc = jspack8.Unpack("!L", randomBytes5(4))[0];
|
|
9559
|
-
audio.onReceiveRtp.execute(cloned);
|
|
9560
|
-
});
|
|
9561
|
-
}
|
|
9562
|
-
if (constraints.video && constraints.audio) {
|
|
9563
|
-
return new MediaStream([video, audio]);
|
|
9564
|
-
} else if (constraints.audio) {
|
|
9565
|
-
return new MediaStream([audio]);
|
|
9566
|
-
} else if (constraints.video) {
|
|
9567
|
-
return new MediaStream([video]);
|
|
10126
|
+
const tracks = [
|
|
10127
|
+
...constraints.audio ? [this.createTrack("audio", constraints.audio)] : [],
|
|
10128
|
+
...constraints.video ? [this.createTrack("video", constraints.video)] : []
|
|
10129
|
+
];
|
|
10130
|
+
if (tracks.length === 0) {
|
|
10131
|
+
throw new Error("At least one audio or video track is required");
|
|
9568
10132
|
}
|
|
9569
|
-
|
|
10133
|
+
return new MediaStream(tracks);
|
|
9570
10134
|
};
|
|
9571
10135
|
getDisplayMedia = this.getUserMedia;
|
|
9572
10136
|
getUdpMedia = ({
|
|
@@ -9588,8 +10152,83 @@ var MediaDevices = class extends EventTarget {
|
|
|
9588
10152
|
};
|
|
9589
10153
|
return { track, disposer };
|
|
9590
10154
|
};
|
|
10155
|
+
cleanup() {
|
|
10156
|
+
for (const stop of this.activeSourceStops.values()) {
|
|
10157
|
+
stop();
|
|
10158
|
+
}
|
|
10159
|
+
this.activeSourceStops.clear();
|
|
10160
|
+
}
|
|
10161
|
+
createTrack(kind, constraints) {
|
|
10162
|
+
const existingTrack = kind === "audio" ? this.audio : this.video;
|
|
10163
|
+
if (existingTrack) {
|
|
10164
|
+
return this.createClonedTrack(kind, existingTrack);
|
|
10165
|
+
}
|
|
10166
|
+
if (this.props.dummyMedia?.enabled) {
|
|
10167
|
+
return this.createDummyTrack(kind, constraints);
|
|
10168
|
+
}
|
|
10169
|
+
throw new Error(`No ${kind} source configured for getUserMedia`);
|
|
10170
|
+
}
|
|
10171
|
+
createClonedTrack(kind, sourceTrack) {
|
|
10172
|
+
const track = new MediaStreamTrack({ kind });
|
|
10173
|
+
const { unSubscribe } = sourceTrack.onReceiveRtp.subscribe((rtp) => {
|
|
10174
|
+
track.onReceiveRtp.execute(rtp.clone());
|
|
10175
|
+
});
|
|
10176
|
+
this.attachTrackStop(track, () => {
|
|
10177
|
+
unSubscribe();
|
|
10178
|
+
});
|
|
10179
|
+
return track;
|
|
10180
|
+
}
|
|
10181
|
+
createDummyTrack(kind, constraints) {
|
|
10182
|
+
const dummy = kind === "audio" ? createDummyAudioTrack(this.props.dummyMedia?.audio) : createDummyVideoTrack({
|
|
10183
|
+
...this.props.dummyMedia?.video,
|
|
10184
|
+
...resolveVideoOptions(constraints)
|
|
10185
|
+
});
|
|
10186
|
+
this.attachTrackStop(dummy.track, () => {
|
|
10187
|
+
dummy.source.stop();
|
|
10188
|
+
});
|
|
10189
|
+
return dummy.track;
|
|
10190
|
+
}
|
|
10191
|
+
attachTrackStop(track, stop) {
|
|
10192
|
+
let disposed = false;
|
|
10193
|
+
const originalStop = track.stop;
|
|
10194
|
+
const dispose = () => {
|
|
10195
|
+
if (disposed) {
|
|
10196
|
+
return;
|
|
10197
|
+
}
|
|
10198
|
+
disposed = true;
|
|
10199
|
+
stop();
|
|
10200
|
+
this.activeSourceStops.delete(track);
|
|
10201
|
+
};
|
|
10202
|
+
track.stop = () => {
|
|
10203
|
+
dispose();
|
|
10204
|
+
originalStop();
|
|
10205
|
+
};
|
|
10206
|
+
this.activeSourceStops.set(track, dispose);
|
|
10207
|
+
}
|
|
9591
10208
|
};
|
|
9592
10209
|
var navigator = new Navigator();
|
|
10210
|
+
function resolveVideoOptions(constraints) {
|
|
10211
|
+
if (typeof constraints === "boolean") {
|
|
10212
|
+
return {};
|
|
10213
|
+
}
|
|
10214
|
+
const fps = resolveConstrainNumber(constraints.frameRate);
|
|
10215
|
+
if (!fps) {
|
|
10216
|
+
return {};
|
|
10217
|
+
}
|
|
10218
|
+
return {
|
|
10219
|
+
fps,
|
|
10220
|
+
keyframeIntervalFrames: Math.max(1, Math.round(fps))
|
|
10221
|
+
};
|
|
10222
|
+
}
|
|
10223
|
+
function resolveConstrainNumber(value) {
|
|
10224
|
+
if (typeof value === "number") {
|
|
10225
|
+
return value;
|
|
10226
|
+
}
|
|
10227
|
+
if (!value || typeof value !== "object") {
|
|
10228
|
+
return;
|
|
10229
|
+
}
|
|
10230
|
+
return value.exact ?? value.ideal ?? value.max ?? value.min;
|
|
10231
|
+
}
|
|
9593
10232
|
|
|
9594
10233
|
// src/nonstandard/recorder/writer/webm.ts
|
|
9595
10234
|
import { unlink } from "fs/promises";
|
|
@@ -9609,7 +10248,7 @@ var MediaWriter = class {
|
|
|
9609
10248
|
var sourcePath = "packages/webrtc/src/nonstandard/recorder/writer/webm.ts";
|
|
9610
10249
|
var WebmFactory = class extends MediaWriter {
|
|
9611
10250
|
rtpSources = [];
|
|
9612
|
-
onEol = new
|
|
10251
|
+
onEol = new Event2();
|
|
9613
10252
|
ended = false;
|
|
9614
10253
|
unSubscribers = new EventDisposer();
|
|
9615
10254
|
async start(tracks) {
|
|
@@ -9771,7 +10410,7 @@ var MediaRecorder = class {
|
|
|
9771
10410
|
ext;
|
|
9772
10411
|
tracks = [];
|
|
9773
10412
|
started = false;
|
|
9774
|
-
onError = new
|
|
10413
|
+
onError = new Event2();
|
|
9775
10414
|
async addTrack(track) {
|
|
9776
10415
|
this.tracks.push(track);
|
|
9777
10416
|
await this.start();
|
|
@@ -9788,113 +10427,891 @@ var MediaRecorder = class {
|
|
|
9788
10427
|
};
|
|
9789
10428
|
|
|
9790
10429
|
// src/nonstandard/userMedia.ts
|
|
9791
|
-
import { exec } from "child_process";
|
|
9792
10430
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
9793
|
-
import {
|
|
9794
|
-
import {
|
|
9795
|
-
|
|
9796
|
-
|
|
9797
|
-
|
|
9798
|
-
|
|
9799
|
-
|
|
9800
|
-
}
|
|
9801
|
-
|
|
9802
|
-
|
|
9803
|
-
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
|
|
10431
|
+
import { homedir } from "os";
|
|
10432
|
+
import { isAbsolute, resolve } from "path";
|
|
10433
|
+
import { Readable } from "stream";
|
|
10434
|
+
import { readFile } from "fs/promises";
|
|
10435
|
+
import {
|
|
10436
|
+
setImmediate as onNextTurn,
|
|
10437
|
+
setTimeout as wait
|
|
10438
|
+
} from "timers/promises";
|
|
10439
|
+
import {
|
|
10440
|
+
BufferSource,
|
|
10441
|
+
EncodedPacketSink,
|
|
10442
|
+
Input,
|
|
10443
|
+
MP4,
|
|
10444
|
+
WEBM
|
|
10445
|
+
} from "mediabunny";
|
|
10446
|
+
|
|
10447
|
+
// src/nonstandard/userMedia/packetizer.ts
|
|
10448
|
+
var DEFAULT_MAX_RTP_PAYLOAD_SIZE = 1200;
|
|
10449
|
+
var supportedSourceCodecs = [
|
|
10450
|
+
"avc",
|
|
10451
|
+
"vp8",
|
|
10452
|
+
"vp9",
|
|
10453
|
+
"av1",
|
|
10454
|
+
"opus"
|
|
10455
|
+
];
|
|
10456
|
+
function toSupportedMimeType(codec) {
|
|
10457
|
+
switch (codec) {
|
|
10458
|
+
case "avc":
|
|
10459
|
+
return "video/h264";
|
|
10460
|
+
case "vp8":
|
|
10461
|
+
return "video/vp8";
|
|
10462
|
+
case "vp9":
|
|
10463
|
+
return "video/vp9";
|
|
10464
|
+
case "av1":
|
|
10465
|
+
return "video/av1x";
|
|
10466
|
+
case "opus":
|
|
10467
|
+
return "audio/opus";
|
|
10468
|
+
}
|
|
10469
|
+
}
|
|
10470
|
+
function createPacketizer({
|
|
10471
|
+
codec,
|
|
10472
|
+
sourceCodec,
|
|
10473
|
+
decoderDescription
|
|
10474
|
+
}) {
|
|
10475
|
+
switch (sourceCodec) {
|
|
10476
|
+
case "avc":
|
|
10477
|
+
return new H264Packetizer(codec, decoderDescription ?? null);
|
|
10478
|
+
case "vp8":
|
|
10479
|
+
return new Vp8Packetizer(codec);
|
|
10480
|
+
case "vp9":
|
|
10481
|
+
return new Vp9Packetizer(codec);
|
|
10482
|
+
case "av1":
|
|
10483
|
+
return new Av1Packetizer(codec);
|
|
10484
|
+
case "opus":
|
|
10485
|
+
return new OpusPacketizer(codec);
|
|
10486
|
+
}
|
|
10487
|
+
}
|
|
10488
|
+
var BasePacketizer = class {
|
|
10489
|
+
constructor(codec, maxPayloadSize = DEFAULT_MAX_RTP_PAYLOAD_SIZE) {
|
|
10490
|
+
this.codec = codec;
|
|
10491
|
+
this.maxPayloadSize = maxPayloadSize;
|
|
10492
|
+
}
|
|
10493
|
+
sequenceNumber = random16();
|
|
10494
|
+
buildPacket(payload, timestamp, marker) {
|
|
10495
|
+
const packet = new RtpPacket(
|
|
10496
|
+
new RtpHeader({
|
|
10497
|
+
payloadType: this.codec.payloadType ?? 96,
|
|
10498
|
+
sequenceNumber: this.sequenceNumber,
|
|
10499
|
+
timestamp,
|
|
10500
|
+
marker
|
|
10501
|
+
}),
|
|
10502
|
+
payload
|
|
10503
|
+
);
|
|
10504
|
+
this.sequenceNumber = this.sequenceNumber + 1 & 65535;
|
|
10505
|
+
return packet;
|
|
10506
|
+
}
|
|
10507
|
+
};
|
|
10508
|
+
var OpusPacketizer = class extends BasePacketizer {
|
|
10509
|
+
packetize(packet, rtpTimestamp) {
|
|
10510
|
+
return [this.buildPacket(toBuffer(packet.data), rtpTimestamp, true)];
|
|
10511
|
+
}
|
|
10512
|
+
};
|
|
10513
|
+
var Vp8Packetizer = class extends BasePacketizer {
|
|
10514
|
+
packetize(packet, rtpTimestamp) {
|
|
10515
|
+
const frame = toBuffer(packet.data);
|
|
10516
|
+
const chunkSize = this.maxPayloadSize - 1;
|
|
10517
|
+
if (chunkSize <= 0) {
|
|
10518
|
+
throw new Error("invalid VP8 RTP payload size");
|
|
10519
|
+
}
|
|
10520
|
+
const packets = [];
|
|
10521
|
+
for (let offset = 0; offset < frame.length; offset += chunkSize) {
|
|
10522
|
+
const chunk = frame.subarray(
|
|
10523
|
+
offset,
|
|
10524
|
+
Math.min(frame.length, offset + chunkSize)
|
|
10525
|
+
);
|
|
10526
|
+
const descriptor = Buffer.from([offset === 0 ? 16 : 0]);
|
|
10527
|
+
packets.push(
|
|
10528
|
+
this.buildPacket(
|
|
10529
|
+
Buffer.concat([descriptor, chunk]),
|
|
10530
|
+
rtpTimestamp,
|
|
10531
|
+
offset + chunk.length >= frame.length
|
|
10532
|
+
)
|
|
10533
|
+
);
|
|
10534
|
+
}
|
|
10535
|
+
return packets;
|
|
10536
|
+
}
|
|
10537
|
+
};
|
|
10538
|
+
var Vp9Packetizer = class extends BasePacketizer {
|
|
10539
|
+
packetize(packet, rtpTimestamp) {
|
|
10540
|
+
const frame = toBuffer(packet.data);
|
|
10541
|
+
const chunkSize = this.maxPayloadSize - 1;
|
|
10542
|
+
if (chunkSize <= 0) {
|
|
10543
|
+
throw new Error("invalid VP9 RTP payload size");
|
|
10544
|
+
}
|
|
10545
|
+
const packets = [];
|
|
10546
|
+
for (let offset = 0; offset < frame.length; offset += chunkSize) {
|
|
10547
|
+
const chunk = frame.subarray(
|
|
10548
|
+
offset,
|
|
10549
|
+
Math.min(frame.length, offset + chunkSize)
|
|
10550
|
+
);
|
|
10551
|
+
const descriptor = new BitWriter(8).set(1, 0, 0).set(1, 1, packet.type === "delta" ? 1 : 0).set(1, 2, 0).set(1, 3, 0).set(1, 4, offset === 0 ? 1 : 0).set(1, 5, offset + chunk.length >= frame.length ? 1 : 0).set(1, 6, 0).set(1, 7, 0).buffer;
|
|
10552
|
+
packets.push(
|
|
10553
|
+
this.buildPacket(
|
|
10554
|
+
Buffer.concat([descriptor, chunk]),
|
|
10555
|
+
rtpTimestamp,
|
|
10556
|
+
offset + chunk.length >= frame.length
|
|
10557
|
+
)
|
|
10558
|
+
);
|
|
10559
|
+
}
|
|
10560
|
+
return packets;
|
|
10561
|
+
}
|
|
10562
|
+
};
|
|
10563
|
+
var Av1Packetizer = class extends BasePacketizer {
|
|
10564
|
+
packetize(packet, rtpTimestamp) {
|
|
10565
|
+
const obus = splitAv1Obus(toBuffer(packet.data));
|
|
10566
|
+
const packets = [];
|
|
10567
|
+
const fragmentSize = this.maxPayloadSize - 1;
|
|
10568
|
+
if (fragmentSize <= 0) {
|
|
10569
|
+
throw new Error("invalid AV1 RTP payload size");
|
|
10570
|
+
}
|
|
10571
|
+
let firstPacketInFrame = true;
|
|
10572
|
+
const startsNewCodedVideoSequence = packet.type === "key";
|
|
10573
|
+
for (const obu of obus) {
|
|
10574
|
+
if (obu.length <= fragmentSize) {
|
|
10575
|
+
const aggregationHeader = createAv1AggregationHeader({
|
|
10576
|
+
startsWithFragment: false,
|
|
10577
|
+
endsWithFragment: false,
|
|
10578
|
+
startsNewCodedVideoSequence: firstPacketInFrame && startsNewCodedVideoSequence
|
|
10579
|
+
});
|
|
10580
|
+
packets.push(
|
|
10581
|
+
this.buildPacket(
|
|
10582
|
+
Buffer.concat([aggregationHeader, obu]),
|
|
10583
|
+
rtpTimestamp,
|
|
10584
|
+
false
|
|
10585
|
+
)
|
|
10586
|
+
);
|
|
10587
|
+
firstPacketInFrame = false;
|
|
10588
|
+
continue;
|
|
10589
|
+
}
|
|
10590
|
+
for (let offset = 0; offset < obu.length; offset += fragmentSize) {
|
|
10591
|
+
const chunk = obu.subarray(
|
|
10592
|
+
offset,
|
|
10593
|
+
Math.min(obu.length, offset + fragmentSize)
|
|
10594
|
+
);
|
|
10595
|
+
const aggregationHeader = createAv1AggregationHeader({
|
|
10596
|
+
startsWithFragment: offset > 0,
|
|
10597
|
+
endsWithFragment: offset + chunk.length < obu.length,
|
|
10598
|
+
startsNewCodedVideoSequence: firstPacketInFrame && startsNewCodedVideoSequence
|
|
10599
|
+
});
|
|
10600
|
+
packets.push(
|
|
10601
|
+
this.buildPacket(
|
|
10602
|
+
Buffer.concat([aggregationHeader, chunk]),
|
|
10603
|
+
rtpTimestamp,
|
|
10604
|
+
false
|
|
10605
|
+
)
|
|
10606
|
+
);
|
|
10607
|
+
firstPacketInFrame = false;
|
|
10608
|
+
}
|
|
10609
|
+
}
|
|
10610
|
+
if (packets.length === 0) {
|
|
10611
|
+
throw new Error("AV1 sample did not contain any OBU data");
|
|
10612
|
+
}
|
|
10613
|
+
packets.at(-1).header.marker = true;
|
|
10614
|
+
return packets;
|
|
10615
|
+
}
|
|
10616
|
+
};
|
|
10617
|
+
var H264Packetizer = class extends BasePacketizer {
|
|
10618
|
+
naluLengthSize;
|
|
10619
|
+
parameterSets;
|
|
10620
|
+
constructor(codec, decoderDescription) {
|
|
10621
|
+
super(codec);
|
|
10622
|
+
const packetizationMode = getH264PacketizationMode(codec.parameters);
|
|
10623
|
+
if (packetizationMode !== 1) {
|
|
10624
|
+
throw new Error(
|
|
10625
|
+
`Unsupported H264 packetization-mode=${packetizationMode}. File playback requires packetization-mode=1.`
|
|
10626
|
+
);
|
|
10627
|
+
}
|
|
10628
|
+
const avcConfig = parseAvcDecoderConfiguration(decoderDescription);
|
|
10629
|
+
this.naluLengthSize = avcConfig?.naluLengthSize ?? 4;
|
|
10630
|
+
this.parameterSets = avcConfig ? [...avcConfig.sps.map(Buffer.from), ...avcConfig.pps.map(Buffer.from)] : [];
|
|
10631
|
+
}
|
|
10632
|
+
packetize(packet, rtpTimestamp) {
|
|
10633
|
+
let nalUnits = splitH264Sample(toBuffer(packet.data), this.naluLengthSize);
|
|
10634
|
+
if (packet.type === "key" && this.parameterSets.length > 0 && !containsH264ParameterSets(nalUnits)) {
|
|
10635
|
+
nalUnits = [...this.parameterSets, ...nalUnits];
|
|
10636
|
+
}
|
|
10637
|
+
const packets = [];
|
|
10638
|
+
nalUnits.forEach((nalUnit, index) => {
|
|
10639
|
+
const marker = index === nalUnits.length - 1;
|
|
10640
|
+
if (nalUnit.length <= this.maxPayloadSize) {
|
|
10641
|
+
packets.push(this.buildPacket(nalUnit, rtpTimestamp, marker));
|
|
10642
|
+
return;
|
|
10643
|
+
}
|
|
10644
|
+
const nalHeader = nalUnit[0];
|
|
10645
|
+
const fragmentPayload = nalUnit.subarray(1);
|
|
10646
|
+
const fragmentSize = this.maxPayloadSize - 2;
|
|
10647
|
+
if (fragmentSize <= 0) {
|
|
10648
|
+
throw new Error("invalid H264 RTP payload size");
|
|
10649
|
+
}
|
|
10650
|
+
const fuIndicator = nalHeader & 224 | 28;
|
|
10651
|
+
const nalType = nalHeader & 31;
|
|
10652
|
+
for (let offset = 0; offset < fragmentPayload.length; offset += fragmentSize) {
|
|
10653
|
+
const chunk = fragmentPayload.subarray(
|
|
10654
|
+
offset,
|
|
10655
|
+
Math.min(fragmentPayload.length, offset + fragmentSize)
|
|
10656
|
+
);
|
|
10657
|
+
const fuHeader = Buffer.from([
|
|
10658
|
+
(offset === 0 ? 128 : 0) | (offset + chunk.length >= fragmentPayload.length ? 64 : 0) | nalType
|
|
10659
|
+
]);
|
|
10660
|
+
packets.push(
|
|
10661
|
+
this.buildPacket(
|
|
10662
|
+
Buffer.concat([Buffer.from([fuIndicator]), fuHeader, chunk]),
|
|
10663
|
+
rtpTimestamp,
|
|
10664
|
+
marker && offset + chunk.length >= fragmentPayload.length
|
|
10665
|
+
)
|
|
10666
|
+
);
|
|
10667
|
+
}
|
|
9811
10668
|
});
|
|
9812
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
10669
|
+
return packets;
|
|
10670
|
+
}
|
|
10671
|
+
};
|
|
10672
|
+
function createAv1AggregationHeader({
|
|
10673
|
+
startsWithFragment,
|
|
10674
|
+
endsWithFragment,
|
|
10675
|
+
startsNewCodedVideoSequence
|
|
10676
|
+
}) {
|
|
10677
|
+
return new BitWriter(8).set(1, 0, startsWithFragment ? 1 : 0).set(1, 1, endsWithFragment ? 1 : 0).set(2, 2, 1).set(1, 4, startsNewCodedVideoSequence ? 1 : 0).set(3, 5, 0).buffer;
|
|
10678
|
+
}
|
|
10679
|
+
function parseAvcDecoderConfiguration(decoderDescription) {
|
|
10680
|
+
if (!decoderDescription) {
|
|
10681
|
+
return null;
|
|
10682
|
+
}
|
|
10683
|
+
const description = toDescriptionBuffer(decoderDescription);
|
|
10684
|
+
if (description.length < 7) {
|
|
10685
|
+
throw new Error("invalid H264 decoder configuration");
|
|
10686
|
+
}
|
|
10687
|
+
const naluLengthSize = (description[4] & 3) + 1;
|
|
10688
|
+
const spsCount = description[5] & 31;
|
|
10689
|
+
let offset = 6;
|
|
10690
|
+
const sps = [];
|
|
10691
|
+
for (let index = 0; index < spsCount; index++) {
|
|
10692
|
+
const length = description.readUInt16BE(offset);
|
|
10693
|
+
offset += 2;
|
|
10694
|
+
sps.push(description.subarray(offset, offset + length));
|
|
10695
|
+
offset += length;
|
|
10696
|
+
}
|
|
10697
|
+
const ppsCount = description[offset] ?? 0;
|
|
10698
|
+
offset++;
|
|
10699
|
+
const pps = [];
|
|
10700
|
+
for (let index = 0; index < ppsCount; index++) {
|
|
10701
|
+
const length = description.readUInt16BE(offset);
|
|
10702
|
+
offset += 2;
|
|
10703
|
+
pps.push(description.subarray(offset, offset + length));
|
|
10704
|
+
offset += length;
|
|
10705
|
+
}
|
|
10706
|
+
return { naluLengthSize, sps, pps };
|
|
10707
|
+
}
|
|
10708
|
+
function splitH264Sample(sample, naluLengthSize) {
|
|
10709
|
+
if (looksLikeAnnexB(sample)) {
|
|
10710
|
+
const nalUnits2 = [];
|
|
10711
|
+
const parser = new H264AnnexBParser(sample);
|
|
10712
|
+
let payload = parser.readNextNaluPayload();
|
|
10713
|
+
while (payload) {
|
|
10714
|
+
nalUnits2.push(Buffer.from(payload.data));
|
|
10715
|
+
payload = parser.readNextNaluPayload();
|
|
10716
|
+
}
|
|
10717
|
+
return nalUnits2;
|
|
10718
|
+
}
|
|
10719
|
+
const nalUnits = [];
|
|
10720
|
+
let offset = 0;
|
|
10721
|
+
while (offset < sample.length) {
|
|
10722
|
+
const length = readUintLength(sample, offset, naluLengthSize);
|
|
10723
|
+
offset += naluLengthSize;
|
|
10724
|
+
const end = offset + length;
|
|
10725
|
+
if (end > sample.length) {
|
|
10726
|
+
throw new Error("invalid H264 sample length");
|
|
10727
|
+
}
|
|
10728
|
+
nalUnits.push(sample.subarray(offset, end));
|
|
10729
|
+
offset = end;
|
|
10730
|
+
}
|
|
10731
|
+
return nalUnits;
|
|
10732
|
+
}
|
|
10733
|
+
function splitAv1Obus(sample) {
|
|
10734
|
+
const obus = [];
|
|
10735
|
+
let offset = 0;
|
|
10736
|
+
while (offset < sample.length) {
|
|
10737
|
+
const start = offset;
|
|
10738
|
+
const header = sample[offset];
|
|
10739
|
+
offset += 1;
|
|
10740
|
+
if (header == void 0) {
|
|
10741
|
+
break;
|
|
10742
|
+
}
|
|
10743
|
+
const extensionFlag = getBit(header, 5);
|
|
10744
|
+
const hasSizeField = getBit(header, 6);
|
|
10745
|
+
if (extensionFlag) {
|
|
10746
|
+
offset += 1;
|
|
10747
|
+
}
|
|
10748
|
+
if (!hasSizeField) {
|
|
10749
|
+
obus.push(sample.subarray(start));
|
|
10750
|
+
break;
|
|
10751
|
+
}
|
|
10752
|
+
const [obuSize, leb128Length] = leb128decode(sample.subarray(offset));
|
|
10753
|
+
offset += leb128Length;
|
|
10754
|
+
const end = offset + obuSize;
|
|
10755
|
+
if (end > sample.length) {
|
|
10756
|
+
throw new Error("invalid AV1 OBU size");
|
|
10757
|
+
}
|
|
10758
|
+
obus.push(sample.subarray(start, end));
|
|
10759
|
+
offset = end;
|
|
10760
|
+
}
|
|
10761
|
+
return obus;
|
|
10762
|
+
}
|
|
10763
|
+
function containsH264ParameterSets(nalUnits) {
|
|
10764
|
+
return nalUnits.some((nalUnit) => {
|
|
10765
|
+
const nalType = nalUnit[0] & 31;
|
|
10766
|
+
return nalType === 7 || nalType === 8;
|
|
10767
|
+
});
|
|
10768
|
+
}
|
|
10769
|
+
function looksLikeAnnexB(sample) {
|
|
10770
|
+
return sample.length >= 4 && sample[0] === 0 && sample[1] === 0 && (sample[2] === 0 && sample[3] === 1 || sample[2] === 1);
|
|
10771
|
+
}
|
|
10772
|
+
function readUintLength(buffer2, offset, length) {
|
|
10773
|
+
switch (length) {
|
|
10774
|
+
case 1:
|
|
10775
|
+
return buffer2.readUInt8(offset);
|
|
10776
|
+
case 2:
|
|
10777
|
+
return buffer2.readUInt16BE(offset);
|
|
10778
|
+
case 3:
|
|
10779
|
+
return buffer2.readUIntBE(offset, 3);
|
|
10780
|
+
case 4:
|
|
10781
|
+
return buffer2.readUInt32BE(offset);
|
|
10782
|
+
default:
|
|
10783
|
+
throw new Error(`unsupported H264 NALU length size: ${length}`);
|
|
10784
|
+
}
|
|
10785
|
+
}
|
|
10786
|
+
function getH264PacketizationMode(parameters) {
|
|
10787
|
+
if (!parameters) {
|
|
10788
|
+
return 1;
|
|
10789
|
+
}
|
|
10790
|
+
const packetizationMode = parameters.split(";").map((part) => part.trim()).find((part) => part.startsWith("packetization-mode="));
|
|
10791
|
+
if (!packetizationMode) {
|
|
10792
|
+
return 1;
|
|
10793
|
+
}
|
|
10794
|
+
return Number(packetizationMode.split("=")[1] ?? 1);
|
|
10795
|
+
}
|
|
10796
|
+
function toDescriptionBuffer(decoderDescription) {
|
|
10797
|
+
if (ArrayBuffer.isView(decoderDescription)) {
|
|
10798
|
+
return Buffer.from(
|
|
10799
|
+
decoderDescription.buffer,
|
|
10800
|
+
decoderDescription.byteOffset,
|
|
10801
|
+
decoderDescription.byteLength
|
|
10802
|
+
);
|
|
10803
|
+
}
|
|
10804
|
+
return Buffer.from(decoderDescription);
|
|
10805
|
+
}
|
|
10806
|
+
function toBuffer(data) {
|
|
10807
|
+
return Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
|
10808
|
+
}
|
|
10809
|
+
|
|
10810
|
+
// src/nonstandard/userMedia.ts
|
|
10811
|
+
var getUserMedia = async (options) => {
|
|
10812
|
+
assertSupportedOptions(options);
|
|
10813
|
+
const replayableSource = await createReplayableSource(options);
|
|
10814
|
+
const input = await createInput(replayableSource);
|
|
10815
|
+
try {
|
|
10816
|
+
if (!await input.canRead()) {
|
|
10817
|
+
throw new Error("Only MP4 and WebM file playback is supported");
|
|
10818
|
+
}
|
|
10819
|
+
const streamId = randomUUID6().toString();
|
|
10820
|
+
const playbackTracks = await selectPlaybackTracks(input, streamId);
|
|
10821
|
+
if (playbackTracks.length === 0) {
|
|
10822
|
+
throw new Error(
|
|
10823
|
+
"The media source does not contain playable audio or video tracks"
|
|
10824
|
+
);
|
|
10825
|
+
}
|
|
10826
|
+
const mediaStartTimestamp = await input.getFirstTimestamp(
|
|
10827
|
+
playbackTracks.map(({ inputTrack }) => inputTrack)
|
|
10828
|
+
);
|
|
10829
|
+
return new MediaPlayer({
|
|
10830
|
+
source: replayableSource,
|
|
10831
|
+
loop: options.loop ?? false,
|
|
10832
|
+
streamId,
|
|
10833
|
+
session: createPlaybackSession({
|
|
10834
|
+
input,
|
|
10835
|
+
mediaStartTimestamp,
|
|
10836
|
+
playbackTracks
|
|
10837
|
+
})
|
|
9820
10838
|
});
|
|
10839
|
+
} catch (error) {
|
|
10840
|
+
input.dispose();
|
|
10841
|
+
throw error;
|
|
9821
10842
|
}
|
|
9822
10843
|
};
|
|
9823
10844
|
var MediaPlayer = class {
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9829
|
-
streamId
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
|
|
10845
|
+
audio;
|
|
10846
|
+
video;
|
|
10847
|
+
onError = new Event2();
|
|
10848
|
+
source;
|
|
10849
|
+
loop;
|
|
10850
|
+
streamId;
|
|
10851
|
+
session;
|
|
10852
|
+
running;
|
|
10853
|
+
abortController;
|
|
10854
|
+
hasStartedPlayback = false;
|
|
9833
10855
|
stopped = false;
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
9837
|
-
|
|
9838
|
-
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
10856
|
+
constructor({
|
|
10857
|
+
source,
|
|
10858
|
+
loop,
|
|
10859
|
+
streamId,
|
|
10860
|
+
session
|
|
10861
|
+
}) {
|
|
10862
|
+
this.source = source;
|
|
10863
|
+
this.loop = loop;
|
|
10864
|
+
this.streamId = streamId;
|
|
10865
|
+
this.session = session;
|
|
10866
|
+
this.audio = session.playbackTracks.find(
|
|
10867
|
+
({ track }) => track.kind === "audio"
|
|
10868
|
+
)?.track;
|
|
10869
|
+
this.video = session.playbackTracks.find(
|
|
10870
|
+
({ track }) => track.kind === "video"
|
|
10871
|
+
)?.track;
|
|
10872
|
+
}
|
|
10873
|
+
async start() {
|
|
10874
|
+
if (this.stopped) {
|
|
10875
|
+
throw new Error("The media source has already been stopped");
|
|
10876
|
+
}
|
|
10877
|
+
if (this.running) {
|
|
10878
|
+
return;
|
|
10879
|
+
}
|
|
10880
|
+
const abortController = new AbortController();
|
|
10881
|
+
this.abortController = abortController;
|
|
10882
|
+
let startupPending = true;
|
|
10883
|
+
let session;
|
|
10884
|
+
const playback = (async () => {
|
|
10885
|
+
session = await this.getOrCreateSession();
|
|
10886
|
+
if (abortController.signal.aborted || this.stopped) {
|
|
10887
|
+
return;
|
|
10888
|
+
}
|
|
10889
|
+
session.runners.forEach((runner) => runner.assertReady());
|
|
10890
|
+
const initialSourceChanged = this.hasStartedPlayback;
|
|
10891
|
+
this.hasStartedPlayback = true;
|
|
10892
|
+
await this.run(session, abortController, initialSourceChanged);
|
|
10893
|
+
})();
|
|
10894
|
+
const observedPlayback = playback.catch((error) => {
|
|
10895
|
+
if (abortController.signal.aborted) {
|
|
10896
|
+
return;
|
|
10897
|
+
}
|
|
10898
|
+
this.onError.execute(toError(error));
|
|
10899
|
+
if (startupPending) {
|
|
10900
|
+
throw error;
|
|
10901
|
+
}
|
|
9848
10902
|
});
|
|
9849
|
-
|
|
10903
|
+
this.running = observedPlayback.finally(() => {
|
|
10904
|
+
if (this.abortController === abortController) {
|
|
10905
|
+
this.abortController = void 0;
|
|
10906
|
+
}
|
|
10907
|
+
this.running = void 0;
|
|
10908
|
+
this.disposeSession(session);
|
|
10909
|
+
});
|
|
10910
|
+
try {
|
|
10911
|
+
await Promise.race([this.running, onNextTurn()]);
|
|
10912
|
+
} finally {
|
|
10913
|
+
startupPending = false;
|
|
10914
|
+
}
|
|
10915
|
+
}
|
|
9850
10916
|
stop() {
|
|
10917
|
+
if (this.stopped) {
|
|
10918
|
+
return;
|
|
10919
|
+
}
|
|
9851
10920
|
this.stopped = true;
|
|
9852
|
-
this.
|
|
10921
|
+
this.abortController?.abort();
|
|
10922
|
+
if (!this.running) {
|
|
10923
|
+
this.disposeSession();
|
|
10924
|
+
}
|
|
10925
|
+
}
|
|
10926
|
+
async run(session, abortController, initialSourceChanged) {
|
|
10927
|
+
let sourceChanged = initialSourceChanged;
|
|
10928
|
+
do {
|
|
10929
|
+
const startedAt = performance.now();
|
|
10930
|
+
await Promise.all(
|
|
10931
|
+
session.runners.map(
|
|
10932
|
+
(runner) => runner.play({
|
|
10933
|
+
startedAt,
|
|
10934
|
+
signal: abortController.signal,
|
|
10935
|
+
sourceChanged
|
|
10936
|
+
})
|
|
10937
|
+
)
|
|
10938
|
+
);
|
|
10939
|
+
if (!this.loop || abortController.signal.aborted) {
|
|
10940
|
+
break;
|
|
10941
|
+
}
|
|
10942
|
+
sourceChanged = true;
|
|
10943
|
+
} while (!abortController.signal.aborted);
|
|
9853
10944
|
}
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
const
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
cmd = `gst-launch-1.0 filesrc location= ${this.props.path} ! decodebin ! videoscale ! video/x-raw,width=${this.props.width},height=${this.props.height} ! x264enc ! h264parse ! rtph264pay config-interval=10 pt=${payloadType++} ! udpsink host=127.0.0.1 port=${this.props.videoPort}`;
|
|
9863
|
-
} else {
|
|
9864
|
-
cmd = `gst-launch-1.0 filesrc location= ${this.props.path} ! qtdemux name=d ! queue ! h264parse ! rtph264pay config-interval=10 pt=${payloadType++} ! udpsink host=127.0.0.1 port=${this.props.videoPort} d. ! queue ! aacparse ! avdec_aac ! audioresample ! audioconvert ! opusenc ! rtpopuspay pt=${payloadType++} ! udpsink host=127.0.0.1 port=${this.props.audioPort}`;
|
|
9865
|
-
}
|
|
9866
|
-
console.log(cmd);
|
|
9867
|
-
this.process = exec(cmd);
|
|
9868
|
-
this.process.on("error", (e) => console.error("gst error", e));
|
|
9869
|
-
if (this.props.loop) {
|
|
9870
|
-
await new Promise((r) => this.process.on("close", r));
|
|
9871
|
-
if (!this.stopped) {
|
|
9872
|
-
run();
|
|
9873
|
-
}
|
|
10945
|
+
async getOrCreateSession() {
|
|
10946
|
+
if (this.session) {
|
|
10947
|
+
return this.session;
|
|
10948
|
+
}
|
|
10949
|
+
const input = await createInput(this.source);
|
|
10950
|
+
try {
|
|
10951
|
+
if (!await input.canRead()) {
|
|
10952
|
+
throw new Error("Only MP4 and WebM file playback is supported");
|
|
9874
10953
|
}
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
10954
|
+
const playbackTracks = await selectPlaybackTracks(
|
|
10955
|
+
input,
|
|
10956
|
+
this.streamId,
|
|
10957
|
+
buildExistingTrackMap(this.audio, this.video)
|
|
10958
|
+
);
|
|
10959
|
+
const mediaStartTimestamp = await input.getFirstTimestamp(
|
|
10960
|
+
playbackTracks.map(({ inputTrack }) => inputTrack)
|
|
10961
|
+
);
|
|
10962
|
+
const session = createPlaybackSession({
|
|
10963
|
+
input,
|
|
10964
|
+
mediaStartTimestamp,
|
|
10965
|
+
playbackTracks
|
|
10966
|
+
});
|
|
10967
|
+
this.session = session;
|
|
10968
|
+
return session;
|
|
10969
|
+
} catch (error) {
|
|
10970
|
+
input.dispose();
|
|
10971
|
+
throw error;
|
|
10972
|
+
}
|
|
10973
|
+
}
|
|
10974
|
+
disposeSession(session = this.session) {
|
|
10975
|
+
if (!session) {
|
|
10976
|
+
return;
|
|
10977
|
+
}
|
|
10978
|
+
session.input.dispose();
|
|
10979
|
+
if (this.session === session) {
|
|
10980
|
+
this.session = void 0;
|
|
10981
|
+
}
|
|
9878
10982
|
}
|
|
9879
10983
|
};
|
|
9880
|
-
var
|
|
9881
|
-
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
9886
|
-
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
|
|
9890
|
-
|
|
10984
|
+
var TrackPlaybackRunner = class {
|
|
10985
|
+
constructor(props) {
|
|
10986
|
+
this.props = props;
|
|
10987
|
+
}
|
|
10988
|
+
async play({
|
|
10989
|
+
startedAt,
|
|
10990
|
+
signal,
|
|
10991
|
+
sourceChanged
|
|
10992
|
+
}) {
|
|
10993
|
+
const codec = this.requireNegotiatedCodec();
|
|
10994
|
+
const packetizer = createPacketizer({
|
|
10995
|
+
codec,
|
|
10996
|
+
sourceCodec: this.props.sourceCodec,
|
|
10997
|
+
decoderDescription: this.props.decoderDescription
|
|
10998
|
+
});
|
|
10999
|
+
const packetSink = new EncodedPacketSink(this.props.inputTrack);
|
|
11000
|
+
let sourceChangeNotified = false;
|
|
11001
|
+
try {
|
|
11002
|
+
for await (const packet of packetSink.packets(void 0, void 0, {
|
|
11003
|
+
verifyKeyPackets: this.props.track.kind === "video"
|
|
11004
|
+
})) {
|
|
11005
|
+
signal.throwIfAborted?.();
|
|
11006
|
+
const dueAtMs = (packet.timestamp - this.props.mediaStartTimestamp) * 1e3;
|
|
11007
|
+
const remainingMs = dueAtMs - (performance.now() - startedAt);
|
|
11008
|
+
if (remainingMs > 0) {
|
|
11009
|
+
await wait(remainingMs, void 0, { signal });
|
|
11010
|
+
}
|
|
11011
|
+
const rtpTimestamp = toRtpTimestamp(
|
|
11012
|
+
packet.timestamp,
|
|
11013
|
+
this.props.mediaStartTimestamp,
|
|
11014
|
+
codec.clockRate
|
|
11015
|
+
);
|
|
11016
|
+
const packets = packetizer.packetize(packet, rtpTimestamp);
|
|
11017
|
+
if (packets.length === 0) {
|
|
11018
|
+
continue;
|
|
11019
|
+
}
|
|
11020
|
+
if (sourceChanged && !sourceChangeNotified) {
|
|
11021
|
+
this.props.track.onSourceChanged.execute(packets[0].header);
|
|
11022
|
+
sourceChangeNotified = true;
|
|
11023
|
+
}
|
|
11024
|
+
for (const rtpPacket of packets) {
|
|
11025
|
+
this.props.track.writeRtp(rtpPacket);
|
|
9891
11026
|
}
|
|
9892
11027
|
}
|
|
9893
|
-
}
|
|
9894
|
-
|
|
9895
|
-
|
|
11028
|
+
} catch (error) {
|
|
11029
|
+
if (signal.aborted || isAbortError(error)) {
|
|
11030
|
+
return;
|
|
11031
|
+
}
|
|
11032
|
+
throw error;
|
|
11033
|
+
}
|
|
9896
11034
|
}
|
|
9897
|
-
|
|
11035
|
+
assertReady() {
|
|
11036
|
+
const codec = this.requireNegotiatedCodec();
|
|
11037
|
+
createPacketizer({
|
|
11038
|
+
codec,
|
|
11039
|
+
sourceCodec: this.props.sourceCodec,
|
|
11040
|
+
decoderDescription: this.props.decoderDescription
|
|
11041
|
+
});
|
|
11042
|
+
}
|
|
11043
|
+
requireNegotiatedCodec() {
|
|
11044
|
+
const codec = this.props.track.codec;
|
|
11045
|
+
if (!codec) {
|
|
11046
|
+
throw new Error(
|
|
11047
|
+
`Cannot start ${this.props.track.kind} playback before the track is attached to an RTCRtpSender`
|
|
11048
|
+
);
|
|
11049
|
+
}
|
|
11050
|
+
const expectedMimeType = toSupportedMimeType(this.props.sourceCodec);
|
|
11051
|
+
const negotiatedMimeType = normalizeMimeType(codec.mimeType);
|
|
11052
|
+
if (negotiatedMimeType !== expectedMimeType) {
|
|
11053
|
+
throw new Error(
|
|
11054
|
+
`Input ${this.props.track.kind} codec ${expectedMimeType} does not match negotiated codec ${codec.mimeType}`
|
|
11055
|
+
);
|
|
11056
|
+
}
|
|
11057
|
+
const expectedClockRate = this.props.track.kind === "audio" ? 48e3 : 9e4;
|
|
11058
|
+
if (codec.clockRate !== expectedClockRate) {
|
|
11059
|
+
throw new Error(
|
|
11060
|
+
`Input ${this.props.track.kind} clock rate ${expectedClockRate} does not match negotiated clock rate ${codec.clockRate}`
|
|
11061
|
+
);
|
|
11062
|
+
}
|
|
11063
|
+
assertCodecParameters({
|
|
11064
|
+
trackKind: this.props.track.kind === "audio" ? "audio" : "video",
|
|
11065
|
+
codec,
|
|
11066
|
+
sourceCodec: this.props.sourceCodec,
|
|
11067
|
+
decoderDescription: this.props.decoderDescription,
|
|
11068
|
+
sourceChannels: this.props.sourceChannels
|
|
11069
|
+
});
|
|
11070
|
+
return codec;
|
|
11071
|
+
}
|
|
11072
|
+
};
|
|
11073
|
+
async function selectPlaybackTracks(input, streamId, existingTracks = {}) {
|
|
11074
|
+
const primaryVideoTrack = await input.getPrimaryVideoTrack();
|
|
11075
|
+
const primaryAudioTrack = primaryVideoTrack ? await primaryVideoTrack.getPrimaryPairableAudioTrack() ?? await input.getPrimaryAudioTrack() : await input.getPrimaryAudioTrack();
|
|
11076
|
+
const playbackTracks = [];
|
|
11077
|
+
if (primaryAudioTrack) {
|
|
11078
|
+
playbackTracks.push(
|
|
11079
|
+
await createPlaybackTrack(
|
|
11080
|
+
primaryAudioTrack,
|
|
11081
|
+
streamId,
|
|
11082
|
+
existingTracks.audio
|
|
11083
|
+
)
|
|
11084
|
+
);
|
|
11085
|
+
}
|
|
11086
|
+
if (primaryVideoTrack) {
|
|
11087
|
+
playbackTracks.push(
|
|
11088
|
+
await createPlaybackTrack(
|
|
11089
|
+
primaryVideoTrack,
|
|
11090
|
+
streamId,
|
|
11091
|
+
existingTracks.video
|
|
11092
|
+
)
|
|
11093
|
+
);
|
|
11094
|
+
}
|
|
11095
|
+
if (!primaryVideoTrack && !primaryAudioTrack) {
|
|
11096
|
+
const fallbackAudioTrack = await input.getPrimaryAudioTrack();
|
|
11097
|
+
if (fallbackAudioTrack) {
|
|
11098
|
+
playbackTracks.push(
|
|
11099
|
+
await createPlaybackTrack(
|
|
11100
|
+
fallbackAudioTrack,
|
|
11101
|
+
streamId,
|
|
11102
|
+
existingTracks.audio
|
|
11103
|
+
)
|
|
11104
|
+
);
|
|
11105
|
+
}
|
|
11106
|
+
}
|
|
11107
|
+
return playbackTracks;
|
|
11108
|
+
}
|
|
11109
|
+
async function createPlaybackTrack(inputTrack, streamId, track) {
|
|
11110
|
+
const sourceCodec = await requireSupportedCodec(inputTrack);
|
|
11111
|
+
const decoderConfig = await inputTrack.getDecoderConfig();
|
|
11112
|
+
const kind = inputTrack.isAudioTrack() ? "audio" : "video";
|
|
11113
|
+
return {
|
|
11114
|
+
inputTrack,
|
|
11115
|
+
sourceCodec,
|
|
11116
|
+
decoderDescription: decoderConfig && "description" in decoderConfig ? decoderConfig.description ?? null : null,
|
|
11117
|
+
sourceChannels: decoderConfig && "numberOfChannels" in decoderConfig ? decoderConfig.numberOfChannels : void 0,
|
|
11118
|
+
track: track ?? new MediaStreamTrack({
|
|
11119
|
+
kind,
|
|
11120
|
+
streamId
|
|
11121
|
+
})
|
|
11122
|
+
};
|
|
11123
|
+
}
|
|
11124
|
+
async function requireSupportedCodec(inputTrack) {
|
|
11125
|
+
const codec = await inputTrack.getCodec();
|
|
11126
|
+
if (codec && supportedSourceCodecs.includes(codec)) {
|
|
11127
|
+
return codec;
|
|
11128
|
+
}
|
|
11129
|
+
const codecLabel = codec ?? await inputTrack.getCodecParameterString() ?? String(await inputTrack.getInternalCodecId() ?? "unknown");
|
|
11130
|
+
throw new Error(
|
|
11131
|
+
`Unsupported ${inputTrack.type} codec "${codecLabel}". File playback supports H264, VP8, VP9, AV1, and Opus only.`
|
|
11132
|
+
);
|
|
11133
|
+
}
|
|
11134
|
+
async function createReplayableSource(options) {
|
|
11135
|
+
if (typeof options.path === "string") {
|
|
11136
|
+
return { path: options.path };
|
|
11137
|
+
}
|
|
11138
|
+
const buffer2 = options.buffer != void 0 ? toBuffer2(options.buffer) : await readStreamToBuffer(options.stream);
|
|
11139
|
+
return { buffer: buffer2 };
|
|
11140
|
+
}
|
|
11141
|
+
async function createInput(options) {
|
|
11142
|
+
if (typeof options.path === "string") {
|
|
11143
|
+
const buffer2 = await readFile(resolveUserMediaPath(options.path));
|
|
11144
|
+
return new Input({
|
|
11145
|
+
source: new BufferSource(buffer2),
|
|
11146
|
+
formats: [MP4, WEBM]
|
|
11147
|
+
});
|
|
11148
|
+
}
|
|
11149
|
+
return new Input({
|
|
11150
|
+
source: new BufferSource(options.buffer),
|
|
11151
|
+
formats: [MP4, WEBM]
|
|
11152
|
+
});
|
|
11153
|
+
}
|
|
11154
|
+
function resolveUserMediaPath(path2) {
|
|
11155
|
+
if (path2 === "~") {
|
|
11156
|
+
return homedir();
|
|
11157
|
+
}
|
|
11158
|
+
if (path2.startsWith("~/")) {
|
|
11159
|
+
return resolve(homedir(), path2.slice(2));
|
|
11160
|
+
}
|
|
11161
|
+
return isAbsolute(path2) ? path2 : resolve(path2);
|
|
11162
|
+
}
|
|
11163
|
+
async function readStreamToBuffer(stream) {
|
|
11164
|
+
const chunks = [];
|
|
11165
|
+
if (stream instanceof Readable) {
|
|
11166
|
+
for await (const chunk of stream) {
|
|
11167
|
+
chunks.push(toBuffer2(chunk));
|
|
11168
|
+
}
|
|
11169
|
+
return Buffer.concat(chunks);
|
|
11170
|
+
}
|
|
11171
|
+
const reader = stream.getReader();
|
|
11172
|
+
try {
|
|
11173
|
+
for (; ; ) {
|
|
11174
|
+
const { done, value } = await reader.read();
|
|
11175
|
+
if (done) {
|
|
11176
|
+
break;
|
|
11177
|
+
}
|
|
11178
|
+
if (value) {
|
|
11179
|
+
chunks.push(
|
|
11180
|
+
Buffer.from(value.buffer, value.byteOffset, value.byteLength)
|
|
11181
|
+
);
|
|
11182
|
+
}
|
|
11183
|
+
}
|
|
11184
|
+
} finally {
|
|
11185
|
+
reader.releaseLock();
|
|
11186
|
+
}
|
|
11187
|
+
return Buffer.concat(chunks);
|
|
11188
|
+
}
|
|
11189
|
+
function toBuffer2(value) {
|
|
11190
|
+
if (Buffer.isBuffer(value)) {
|
|
11191
|
+
return value;
|
|
11192
|
+
}
|
|
11193
|
+
if (typeof value === "string") {
|
|
11194
|
+
return Buffer.from(value);
|
|
11195
|
+
}
|
|
11196
|
+
if (ArrayBuffer.isView(value)) {
|
|
11197
|
+
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
11198
|
+
}
|
|
11199
|
+
return Buffer.from(new Uint8Array(value));
|
|
11200
|
+
}
|
|
11201
|
+
function normalizeMimeType(mimeType) {
|
|
11202
|
+
return mimeType.toLowerCase();
|
|
11203
|
+
}
|
|
11204
|
+
function assertSupportedOptions(options) {
|
|
11205
|
+
const legacyOptions = options;
|
|
11206
|
+
if (legacyOptions.width != void 0 || legacyOptions.height != void 0) {
|
|
11207
|
+
throw new Error(
|
|
11208
|
+
"getUserMedia({ width, height }) is no longer supported for file playback. Resize or re-encode the source before calling getUserMedia()."
|
|
11209
|
+
);
|
|
11210
|
+
}
|
|
11211
|
+
}
|
|
11212
|
+
function createPlaybackSession({
|
|
11213
|
+
input,
|
|
11214
|
+
mediaStartTimestamp,
|
|
11215
|
+
playbackTracks
|
|
11216
|
+
}) {
|
|
11217
|
+
return {
|
|
11218
|
+
input,
|
|
11219
|
+
mediaStartTimestamp,
|
|
11220
|
+
playbackTracks,
|
|
11221
|
+
runners: playbackTracks.map(
|
|
11222
|
+
(playbackTrack) => new TrackPlaybackRunner({
|
|
11223
|
+
mediaStartTimestamp,
|
|
11224
|
+
...playbackTrack
|
|
11225
|
+
})
|
|
11226
|
+
)
|
|
11227
|
+
};
|
|
11228
|
+
}
|
|
11229
|
+
function buildExistingTrackMap(audio, video) {
|
|
11230
|
+
return {
|
|
11231
|
+
...audio ? { audio } : {},
|
|
11232
|
+
...video ? { video } : {}
|
|
11233
|
+
};
|
|
11234
|
+
}
|
|
11235
|
+
function assertCodecParameters({
|
|
11236
|
+
trackKind,
|
|
11237
|
+
codec,
|
|
11238
|
+
sourceCodec,
|
|
11239
|
+
decoderDescription,
|
|
11240
|
+
sourceChannels
|
|
11241
|
+
}) {
|
|
11242
|
+
if (sourceCodec === "opus" && sourceChannels && codec.channels != void 0) {
|
|
11243
|
+
if (codec.channels !== sourceChannels) {
|
|
11244
|
+
throw new Error(
|
|
11245
|
+
`Input ${trackKind} channels ${sourceChannels} do not match negotiated channels ${codec.channels}`
|
|
11246
|
+
);
|
|
11247
|
+
}
|
|
11248
|
+
return;
|
|
11249
|
+
}
|
|
11250
|
+
if (sourceCodec !== "avc") {
|
|
11251
|
+
return;
|
|
11252
|
+
}
|
|
11253
|
+
const negotiatedParameters = codecParametersFromString(
|
|
11254
|
+
codec.parameters ?? ""
|
|
11255
|
+
);
|
|
11256
|
+
const negotiatedPacketizationMode = Number(
|
|
11257
|
+
negotiatedParameters["packetization-mode"] ?? 1
|
|
11258
|
+
);
|
|
11259
|
+
if (negotiatedPacketizationMode !== 1) {
|
|
11260
|
+
throw new Error(
|
|
11261
|
+
`Input ${trackKind} packetization-mode 1 does not match negotiated packetization-mode ${negotiatedPacketizationMode}`
|
|
11262
|
+
);
|
|
11263
|
+
}
|
|
11264
|
+
const sourceProfileLevelId = getH264ProfileLevelId(decoderDescription);
|
|
11265
|
+
const negotiatedProfileLevelId = normalizeOptionalHexParameter(
|
|
11266
|
+
negotiatedParameters["profile-level-id"]
|
|
11267
|
+
);
|
|
11268
|
+
if (!sourceProfileLevelId) {
|
|
11269
|
+
return;
|
|
11270
|
+
}
|
|
11271
|
+
if (!negotiatedProfileLevelId) {
|
|
11272
|
+
throw new Error(
|
|
11273
|
+
`Input ${trackKind} profile-level-id ${sourceProfileLevelId} does not match negotiated profile-level-id <missing>`
|
|
11274
|
+
);
|
|
11275
|
+
}
|
|
11276
|
+
if (sourceProfileLevelId !== negotiatedProfileLevelId) {
|
|
11277
|
+
throw new Error(
|
|
11278
|
+
`Input ${trackKind} profile-level-id ${sourceProfileLevelId} does not match negotiated profile-level-id ${negotiatedProfileLevelId}`
|
|
11279
|
+
);
|
|
11280
|
+
}
|
|
11281
|
+
}
|
|
11282
|
+
function getH264ProfileLevelId(decoderDescription) {
|
|
11283
|
+
if (!decoderDescription) {
|
|
11284
|
+
return void 0;
|
|
11285
|
+
}
|
|
11286
|
+
const description = toDescriptionBuffer2(decoderDescription);
|
|
11287
|
+
if (description.length < 4) {
|
|
11288
|
+
throw new Error("invalid H264 decoder configuration");
|
|
11289
|
+
}
|
|
11290
|
+
return description.subarray(1, 4).toString("hex");
|
|
11291
|
+
}
|
|
11292
|
+
function normalizeOptionalHexParameter(value) {
|
|
11293
|
+
return typeof value === "string" ? value.toLowerCase() : void 0;
|
|
11294
|
+
}
|
|
11295
|
+
function toRtpTimestamp(packetTimestamp, mediaStartTimestamp, clockRate) {
|
|
11296
|
+
const relativeSeconds = Math.max(0, packetTimestamp - mediaStartTimestamp);
|
|
11297
|
+
return Math.round(relativeSeconds * clockRate) >>> 0;
|
|
11298
|
+
}
|
|
11299
|
+
function isAbortError(error) {
|
|
11300
|
+
return error instanceof Error && (error.name === "AbortError" || error.message === "The operation was aborted");
|
|
11301
|
+
}
|
|
11302
|
+
function toError(error) {
|
|
11303
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
11304
|
+
}
|
|
11305
|
+
function toDescriptionBuffer2(decoderDescription) {
|
|
11306
|
+
if (ArrayBuffer.isView(decoderDescription)) {
|
|
11307
|
+
return Buffer.from(
|
|
11308
|
+
decoderDescription.buffer,
|
|
11309
|
+
decoderDescription.byteOffset,
|
|
11310
|
+
decoderDescription.byteLength
|
|
11311
|
+
);
|
|
11312
|
+
}
|
|
11313
|
+
return Buffer.from(decoderDescription);
|
|
11314
|
+
}
|
|
9898
11315
|
export {
|
|
9899
11316
|
AV1Obu,
|
|
9900
11317
|
AV1RtpPayload,
|
|
@@ -9910,7 +11327,7 @@ export {
|
|
|
9910
11327
|
DtxCallback,
|
|
9911
11328
|
DurationPosition,
|
|
9912
11329
|
Element,
|
|
9913
|
-
Event,
|
|
11330
|
+
Event2 as Event,
|
|
9914
11331
|
EventDisposer,
|
|
9915
11332
|
ExtensionProfiles,
|
|
9916
11333
|
GenericNack,
|
|
@@ -9931,8 +11348,6 @@ export {
|
|
|
9931
11348
|
Max32Uint,
|
|
9932
11349
|
MaxSinged16Int,
|
|
9933
11350
|
MediaDevices,
|
|
9934
|
-
MediaPlayerMp4,
|
|
9935
|
-
MediaPlayerWebm,
|
|
9936
11351
|
MediaRecorder,
|
|
9937
11352
|
Mp4Container,
|
|
9938
11353
|
MuteCallback,
|
|
@@ -9988,6 +11403,7 @@ export {
|
|
|
9988
11403
|
SrtpSession,
|
|
9989
11404
|
StatusVectorChunk,
|
|
9990
11405
|
TcpTransport,
|
|
11406
|
+
TlsTransport,
|
|
9991
11407
|
TransportWideCC,
|
|
9992
11408
|
UNKNOWN_SIZE,
|
|
9993
11409
|
UdpTransport,
|
|
@@ -9999,6 +11415,7 @@ export {
|
|
|
9999
11415
|
WebmCallback,
|
|
10000
11416
|
WebmStream,
|
|
10001
11417
|
WeriftError,
|
|
11418
|
+
annexb2avcSample,
|
|
10002
11419
|
annexb2avcc,
|
|
10003
11420
|
buffer2ArrayBuffer,
|
|
10004
11421
|
bufferArrayXor,
|