werift 0.22.9 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- 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/dtls/src/flight/server/flight6.js +12 -2
- package/lib/dtls/src/flight/server/flight6.js.map +1 -1
- package/lib/dtls/src/server.js +6 -1
- package/lib/dtls/src/server.js.map +1 -1
- package/lib/dtls/src/socket.d.ts +1 -0
- package/lib/dtls/src/socket.js +3 -0
- package/lib/dtls/src/socket.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 +171 -41
- 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/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 → ice-server}/src/stun/attributes.d.ts +12 -5
- package/lib/{ice → ice-server}/src/stun/attributes.js +60 -19
- 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/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 +3075 -883
- package/lib/nonstandard/index.mjs +1964 -447
- 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/index.d.ts +1 -0
- package/lib/rtp/src/index.js +1 -0
- package/lib/rtp/src/index.js.map +1 -1
- package/lib/rtp/src/srtp/cipher/ctr.d.ts +3 -3
- package/lib/rtp/src/srtp/cipher/ctr.js +25 -14
- package/lib/rtp/src/srtp/cipher/ctr.js.map +1 -1
- package/lib/rtp/src/srtp/cipher/gcm.d.ts +3 -3
- package/lib/rtp/src/srtp/cipher/gcm.js +57 -20
- package/lib/rtp/src/srtp/cipher/gcm.js.map +1 -1
- package/lib/rtp/src/srtp/cipher/index.d.ts +1 -1
- package/lib/rtp/src/srtp/cipher/index.js +1 -1
- package/lib/rtp/src/srtp/cipher/index.js.map +1 -1
- package/lib/rtp/src/srtp/context/srtp.d.ts +2 -1
- package/lib/rtp/src/srtp/context/srtp.js +22 -5
- package/lib/rtp/src/srtp/context/srtp.js.map +1 -1
- package/lib/rtp/src/srtp/error.d.ts +3 -0
- package/lib/rtp/src/srtp/error.js +11 -0
- package/lib/rtp/src/srtp/error.js.map +1 -0
- package/lib/rtp/src/srtp/packet.d.ts +5 -0
- package/lib/rtp/src/srtp/packet.js +48 -0
- package/lib/rtp/src/srtp/packet.js.map +1 -0
- package/lib/webrtc/src/dataChannel.d.ts +4 -2
- package/lib/webrtc/src/dataChannel.js +14 -4
- 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 +177 -28
- 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 +185 -49
- package/lib/webrtc/src/peerConnection.js +477 -107
- package/lib/webrtc/src/peerConnection.js.map +1 -1
- package/lib/webrtc/src/sctpManager.d.ts +2 -2
- package/lib/webrtc/src/sctpManager.js +30 -9
- package/lib/webrtc/src/sctpManager.js.map +1 -1
- package/lib/webrtc/src/sdp.d.ts +10 -7
- package/lib/webrtc/src/sdp.js +39 -9
- package/lib/webrtc/src/sdp.js.map +1 -1
- package/lib/webrtc/src/sdpManager.d.ts +5 -4
- package/lib/webrtc/src/sdpManager.js +53 -31
- package/lib/webrtc/src/sdpManager.js.map +1 -1
- package/lib/webrtc/src/secureTransportManager.d.ts +13 -4
- package/lib/webrtc/src/secureTransportManager.js +101 -23
- package/lib/webrtc/src/secureTransportManager.js.map +1 -1
- package/lib/webrtc/src/transceiverManager.d.ts +5 -4
- package/lib/webrtc/src/transceiverManager.js +64 -64
- package/lib/webrtc/src/transceiverManager.js.map +1 -1
- package/lib/webrtc/src/transport/dtls.d.ts +16 -4
- package/lib/webrtc/src/transport/dtls.js +208 -35
- 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 +10 -3
- package/lib/webrtc/src/transport/sctp.js +44 -13
- package/lib/webrtc/src/transport/sctp.js.map +1 -1
- package/lib/webrtc/src/utils.d.ts +14 -4
- package/lib/webrtc/src/utils.js +138 -16
- package/lib/webrtc/src/utils.js.map +1 -1
- package/package.json +7 -4
- 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,3 +1,13 @@
|
|
|
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
13
|
import { jspack } from "@shinyoshiaki/jspack";
|
|
@@ -464,7 +474,7 @@ var WeriftError = class extends Error {
|
|
|
464
474
|
var debug = Debug.debug;
|
|
465
475
|
|
|
466
476
|
// ../common/src/event.ts
|
|
467
|
-
var
|
|
477
|
+
var Event2 = class {
|
|
468
478
|
event = {
|
|
469
479
|
stack: [],
|
|
470
480
|
promiseStack: [],
|
|
@@ -570,7 +580,7 @@ var Event = class {
|
|
|
570
580
|
error
|
|
571
581
|
);
|
|
572
582
|
};
|
|
573
|
-
watch = (cb, timeLimit) => new Promise((
|
|
583
|
+
watch = (cb, timeLimit) => new Promise((resolve2, reject) => {
|
|
574
584
|
const timeout = timeLimit && setTimeout(() => {
|
|
575
585
|
reject("Event watch timeout");
|
|
576
586
|
}, timeLimit);
|
|
@@ -579,22 +589,22 @@ var Event = class {
|
|
|
579
589
|
if (done) {
|
|
580
590
|
if (timeout) clearTimeout(timeout);
|
|
581
591
|
unSubscribe();
|
|
582
|
-
|
|
592
|
+
resolve2(args);
|
|
583
593
|
}
|
|
584
594
|
});
|
|
585
595
|
});
|
|
586
|
-
asPromise = (timeLimit) => new Promise((
|
|
596
|
+
asPromise = (timeLimit) => new Promise((resolve2, reject) => {
|
|
587
597
|
const timeout = timeLimit && setTimeout(() => {
|
|
588
598
|
reject("Event asPromise timeout");
|
|
589
599
|
}, timeLimit);
|
|
590
600
|
this.once(
|
|
591
601
|
(...args) => {
|
|
592
602
|
if (timeout) clearTimeout(timeout);
|
|
593
|
-
|
|
603
|
+
resolve2(args);
|
|
594
604
|
},
|
|
595
605
|
() => {
|
|
596
606
|
if (timeout) clearTimeout(timeout);
|
|
597
|
-
|
|
607
|
+
resolve2([]);
|
|
598
608
|
},
|
|
599
609
|
(err5) => {
|
|
600
610
|
if (timeout) clearTimeout(timeout);
|
|
@@ -630,7 +640,8 @@ import {
|
|
|
630
640
|
createSocket as createSocket2
|
|
631
641
|
} from "dgram";
|
|
632
642
|
import * as net from "node:net";
|
|
633
|
-
import
|
|
643
|
+
import * as tls from "node:tls";
|
|
644
|
+
import { connect as connect2 } from "node:net";
|
|
634
645
|
var log = debug("werift-ice:packages/ice/src/transport.ts");
|
|
635
646
|
var UdpTransport = class _UdpTransport {
|
|
636
647
|
constructor(socketType, options = {}) {
|
|
@@ -717,11 +728,89 @@ var UdpTransport = class _UdpTransport {
|
|
|
717
728
|
});
|
|
718
729
|
};
|
|
719
730
|
var TcpTransport = class _TcpTransport {
|
|
731
|
+
type = "tcp";
|
|
732
|
+
stream;
|
|
720
733
|
constructor(addr) {
|
|
721
|
-
this.
|
|
734
|
+
this.stream = new StreamTransport(
|
|
735
|
+
"tcp",
|
|
736
|
+
() => connect2({ port: addr[1], host: addr[0] })
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
static async init(addr) {
|
|
740
|
+
const transport = new _TcpTransport(addr);
|
|
741
|
+
await transport.init();
|
|
742
|
+
return transport;
|
|
743
|
+
}
|
|
744
|
+
async init() {
|
|
745
|
+
await this.stream.waitForConnect();
|
|
746
|
+
}
|
|
747
|
+
get address() {
|
|
748
|
+
return this.stream.address;
|
|
749
|
+
}
|
|
750
|
+
get closed() {
|
|
751
|
+
return this.stream.closed;
|
|
752
|
+
}
|
|
753
|
+
get onData() {
|
|
754
|
+
return this.stream.onData;
|
|
755
|
+
}
|
|
756
|
+
set onData(handler) {
|
|
757
|
+
this.stream.onData = handler;
|
|
758
|
+
}
|
|
759
|
+
send = async (data, addr) => {
|
|
760
|
+
await this.stream.send(data, addr);
|
|
761
|
+
};
|
|
762
|
+
close = async () => {
|
|
763
|
+
await this.stream.close();
|
|
764
|
+
};
|
|
765
|
+
};
|
|
766
|
+
var TlsTransport = class _TlsTransport {
|
|
767
|
+
type = "tls";
|
|
768
|
+
stream;
|
|
769
|
+
constructor(addr, options = {}) {
|
|
770
|
+
this.stream = new StreamTransport(
|
|
771
|
+
"tls",
|
|
772
|
+
() => tls.connect({
|
|
773
|
+
...options,
|
|
774
|
+
host: addr[0],
|
|
775
|
+
port: addr[1]
|
|
776
|
+
})
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
static async init(addr, options = {}) {
|
|
780
|
+
const transport = new _TlsTransport(addr, options);
|
|
781
|
+
await transport.init();
|
|
782
|
+
return transport;
|
|
783
|
+
}
|
|
784
|
+
async init() {
|
|
785
|
+
await this.stream.waitForConnect();
|
|
786
|
+
}
|
|
787
|
+
get address() {
|
|
788
|
+
return this.stream.address;
|
|
789
|
+
}
|
|
790
|
+
get closed() {
|
|
791
|
+
return this.stream.closed;
|
|
792
|
+
}
|
|
793
|
+
get onData() {
|
|
794
|
+
return this.stream.onData;
|
|
795
|
+
}
|
|
796
|
+
set onData(handler) {
|
|
797
|
+
this.stream.onData = handler;
|
|
798
|
+
}
|
|
799
|
+
send = async (data, addr) => {
|
|
800
|
+
await this.stream.send(data, addr);
|
|
801
|
+
};
|
|
802
|
+
close = async () => {
|
|
803
|
+
await this.stream.close();
|
|
804
|
+
};
|
|
805
|
+
};
|
|
806
|
+
var StreamTransport = class {
|
|
807
|
+
constructor(type, createClient, connectEvent = type === "tls" ? "secureConnect" : "connect") {
|
|
808
|
+
this.createClient = createClient;
|
|
809
|
+
this.connectEvent = connectEvent;
|
|
810
|
+
this.type = type;
|
|
722
811
|
this.connect();
|
|
723
812
|
}
|
|
724
|
-
type
|
|
813
|
+
type;
|
|
725
814
|
connecting;
|
|
726
815
|
client;
|
|
727
816
|
onData = () => {
|
|
@@ -734,90 +823,88 @@ var TcpTransport = class _TcpTransport {
|
|
|
734
823
|
if (this.client) {
|
|
735
824
|
this.client.destroy();
|
|
736
825
|
}
|
|
826
|
+
const client = this.createClient();
|
|
827
|
+
this.client = client;
|
|
737
828
|
this.connecting = new Promise((r, f) => {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
829
|
+
const onConnect = () => {
|
|
830
|
+
client.off("error", onConnectError);
|
|
831
|
+
r();
|
|
832
|
+
};
|
|
833
|
+
const onConnectError = (error) => {
|
|
834
|
+
client.off(this.connectEvent, onConnect);
|
|
741
835
|
f(error);
|
|
742
|
-
}
|
|
836
|
+
};
|
|
837
|
+
client.once(this.connectEvent, onConnect);
|
|
838
|
+
client.once("error", onConnectError);
|
|
743
839
|
});
|
|
744
|
-
|
|
840
|
+
client.on("data", (data) => {
|
|
745
841
|
const addr = [
|
|
746
842
|
this.client.remoteAddress,
|
|
747
843
|
this.client.remotePort
|
|
748
844
|
];
|
|
749
845
|
this.onData(data, addr);
|
|
750
846
|
});
|
|
751
|
-
|
|
847
|
+
client.on("end", () => {
|
|
752
848
|
this.connect();
|
|
753
849
|
});
|
|
754
|
-
|
|
755
|
-
|
|
850
|
+
client.on("error", (error) => {
|
|
851
|
+
log(`${this.type} transport error`, error);
|
|
756
852
|
});
|
|
757
853
|
}
|
|
758
|
-
async
|
|
854
|
+
async waitForConnect() {
|
|
759
855
|
await this.connecting;
|
|
760
856
|
}
|
|
761
|
-
static async init(addr) {
|
|
762
|
-
const transport = new _TcpTransport(addr);
|
|
763
|
-
await transport.init();
|
|
764
|
-
return transport;
|
|
765
|
-
}
|
|
766
857
|
get address() {
|
|
767
858
|
return {};
|
|
768
859
|
}
|
|
769
860
|
send = async (data, addr) => {
|
|
861
|
+
void addr;
|
|
770
862
|
await this.connecting;
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
863
|
+
await new Promise((resolve2, reject) => {
|
|
864
|
+
this.client.write(data, (err5) => {
|
|
865
|
+
if (err5) {
|
|
866
|
+
reject(err5);
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
resolve2();
|
|
870
|
+
});
|
|
775
871
|
});
|
|
776
872
|
};
|
|
777
873
|
close = async () => {
|
|
778
874
|
this.closed = true;
|
|
779
|
-
this.client
|
|
875
|
+
this.client?.destroy();
|
|
780
876
|
};
|
|
781
877
|
};
|
|
782
878
|
|
|
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
879
|
// ../rtp/src/extra/container/mp4/container.ts
|
|
806
880
|
var Mp4Container = class {
|
|
807
881
|
constructor(props) {
|
|
808
882
|
this.props = props;
|
|
809
|
-
this.#mp4 = new ISOFile();
|
|
810
|
-
this.#mp4.init();
|
|
811
883
|
}
|
|
812
|
-
#mp4;
|
|
813
884
|
#audioFrame;
|
|
885
|
+
#audioMeta;
|
|
886
|
+
#audioSource;
|
|
887
|
+
#currentFragment;
|
|
888
|
+
#ftyp;
|
|
889
|
+
#initializationEmitted = false;
|
|
890
|
+
#lastAudioDuration = 0;
|
|
891
|
+
#lastMoof;
|
|
892
|
+
#lastMoofTimestamp = 0;
|
|
893
|
+
#lastVideoDuration = 0;
|
|
894
|
+
#moov;
|
|
895
|
+
#onDataOperation = Promise.resolve();
|
|
896
|
+
#operation = Promise.resolve();
|
|
897
|
+
#output;
|
|
898
|
+
#startPromise;
|
|
899
|
+
#stopPromise;
|
|
900
|
+
#stopped = false;
|
|
814
901
|
#videoFrame;
|
|
815
|
-
|
|
902
|
+
#videoMeta;
|
|
903
|
+
#videoSource;
|
|
816
904
|
audioTrack;
|
|
905
|
+
frameBuffer = [];
|
|
906
|
+
onData = new Event2();
|
|
817
907
|
videoTrack;
|
|
818
|
-
#audioSegment = 0;
|
|
819
|
-
#videoSegment = 0;
|
|
820
|
-
onData = new Event();
|
|
821
908
|
get tracksReady() {
|
|
822
909
|
let ready = true;
|
|
823
910
|
if (this.props.track.audio && !this.audioTrack) {
|
|
@@ -836,145 +923,259 @@ var Mp4Container = class {
|
|
|
836
923
|
}
|
|
837
924
|
}
|
|
838
925
|
#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
926
|
if (frame.track === "audio") {
|
|
873
|
-
|
|
927
|
+
if (isVideoConfig(frame)) {
|
|
928
|
+
throw new Error("audio track requires an audio decoder config");
|
|
929
|
+
}
|
|
930
|
+
if (frame.codec !== "opus") {
|
|
931
|
+
throw new Error(`unsupported codec: ${frame.codec}`);
|
|
932
|
+
}
|
|
933
|
+
this.#audioMeta = {
|
|
934
|
+
decoderConfig: {
|
|
935
|
+
codec: "opus",
|
|
936
|
+
description: frame.description != void 0 ? new Uint8Array(frame.description) : void 0,
|
|
937
|
+
numberOfChannels: frame.numberOfChannels,
|
|
938
|
+
sampleRate: frame.sampleRate
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
this.audioTrack = 1;
|
|
874
942
|
} else {
|
|
875
|
-
|
|
943
|
+
if (!isVideoConfig(frame)) {
|
|
944
|
+
throw new Error("video track requires a video decoder config");
|
|
945
|
+
}
|
|
946
|
+
if (!frame.codec.startsWith("avc1")) {
|
|
947
|
+
throw new Error(`unsupported codec: ${frame.codec}`);
|
|
948
|
+
}
|
|
949
|
+
if (frame.codedWidth == void 0 || frame.codedHeight == void 0) {
|
|
950
|
+
throw new Error("missing coded video dimensions");
|
|
951
|
+
}
|
|
952
|
+
this.#videoMeta = {
|
|
953
|
+
decoderConfig: {
|
|
954
|
+
codec: frame.codec,
|
|
955
|
+
codedHeight: frame.codedHeight,
|
|
956
|
+
codedWidth: frame.codedWidth,
|
|
957
|
+
description: frame.description != void 0 ? new Uint8Array(frame.description) : void 0,
|
|
958
|
+
displayAspectHeight: frame.displayAspectHeight,
|
|
959
|
+
displayAspectWidth: frame.displayAspectWidth
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
this.videoTrack = this.props.track.audio ? 2 : 1;
|
|
876
963
|
}
|
|
877
964
|
if (!this.tracksReady) {
|
|
878
965
|
return;
|
|
879
966
|
}
|
|
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);
|
|
967
|
+
void this.#enqueueOperation(async () => {
|
|
968
|
+
await this.#ensureOutputStarted();
|
|
969
|
+
await this.#drainFrameBuffer();
|
|
970
|
+
});
|
|
895
971
|
}
|
|
896
|
-
frameBuffer = [];
|
|
897
972
|
#enqueue(frame) {
|
|
898
973
|
this.frameBuffer.push(frame);
|
|
899
974
|
if (!this.tracksReady) {
|
|
900
975
|
return;
|
|
901
976
|
}
|
|
902
|
-
|
|
903
|
-
this
|
|
977
|
+
void this.#enqueueOperation(async () => {
|
|
978
|
+
await this.#ensureOutputStarted();
|
|
979
|
+
await this.#drainFrameBuffer();
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
stop() {
|
|
983
|
+
if (this.#stopPromise) {
|
|
984
|
+
return this.#stopPromise;
|
|
904
985
|
}
|
|
986
|
+
this.#stopped = true;
|
|
987
|
+
this.#stopPromise = this.#enqueueOperation(async () => {
|
|
988
|
+
if (!this.tracksReady) {
|
|
989
|
+
this.frameBuffer = [];
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
await this.#ensureOutputStarted();
|
|
993
|
+
await this.#drainFrameBuffer();
|
|
994
|
+
await this.#flushPendingFrame("audio");
|
|
995
|
+
await this.#flushPendingFrame("video");
|
|
996
|
+
this.#audioSource?.close();
|
|
997
|
+
this.#videoSource?.close();
|
|
998
|
+
if (this.#output) {
|
|
999
|
+
await this.#output.finalize();
|
|
1000
|
+
}
|
|
1001
|
+
await this.#flushOnData();
|
|
1002
|
+
});
|
|
1003
|
+
return this.#stopPromise;
|
|
1004
|
+
}
|
|
1005
|
+
async #drainFrameBuffer() {
|
|
1006
|
+
const frames = this.frameBuffer;
|
|
905
1007
|
this.frameBuffer = [];
|
|
1008
|
+
for (const frame of frames) {
|
|
1009
|
+
await this.#processFrame(frame);
|
|
1010
|
+
}
|
|
906
1011
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1012
|
+
async #ensureOutputStarted() {
|
|
1013
|
+
if (this.#startPromise) {
|
|
1014
|
+
await this.#startPromise;
|
|
1015
|
+
return;
|
|
911
1016
|
}
|
|
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;
|
|
1017
|
+
if (!this.tracksReady) {
|
|
1018
|
+
return;
|
|
920
1019
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1020
|
+
const output = new Output({
|
|
1021
|
+
format: new Mp4OutputFormat({
|
|
1022
|
+
fastStart: "fragmented",
|
|
1023
|
+
minimumFragmentDuration: 0,
|
|
1024
|
+
onFtyp: (data) => {
|
|
1025
|
+
this.#ftyp = copyBytes(data);
|
|
1026
|
+
},
|
|
1027
|
+
onMdat: (data) => {
|
|
1028
|
+
if (!this.#lastMoof) {
|
|
1029
|
+
throw new Error("moof missing before mdat");
|
|
1030
|
+
}
|
|
1031
|
+
const fragment = this.#currentFragment;
|
|
1032
|
+
const timestamp = fragment ? fragment.timestamp : Math.round(this.#lastMoofTimestamp * 1e6);
|
|
1033
|
+
const duration = fragment?.duration ?? 0;
|
|
1034
|
+
const kind = fragment?.kind ?? this.#defaultKind();
|
|
1035
|
+
const type = fragment?.type ?? "key";
|
|
1036
|
+
const segment = concatBytes(this.#lastMoof, data);
|
|
1037
|
+
this.#currentFragment = void 0;
|
|
1038
|
+
this.#lastMoof = void 0;
|
|
1039
|
+
void this.#emitData({
|
|
1040
|
+
type,
|
|
1041
|
+
timestamp,
|
|
1042
|
+
duration,
|
|
1043
|
+
data: segment,
|
|
1044
|
+
kind
|
|
1045
|
+
});
|
|
1046
|
+
},
|
|
1047
|
+
onMoof: (data, _position, timestamp) => {
|
|
1048
|
+
this.#lastMoof = copyBytes(data);
|
|
1049
|
+
this.#lastMoofTimestamp = timestamp;
|
|
1050
|
+
},
|
|
1051
|
+
onMoov: (data) => {
|
|
1052
|
+
this.#moov = copyBytes(data);
|
|
1053
|
+
this.#emitInitializationSegment();
|
|
1054
|
+
}
|
|
1055
|
+
}),
|
|
1056
|
+
target: new NullTarget()
|
|
1057
|
+
});
|
|
1058
|
+
if (this.#audioMeta) {
|
|
1059
|
+
this.#audioSource = new EncodedAudioPacketSource("opus");
|
|
1060
|
+
output.addAudioTrack(this.#audioSource);
|
|
1061
|
+
}
|
|
1062
|
+
if (this.#videoMeta) {
|
|
1063
|
+
this.#videoSource = new EncodedVideoPacketSource("avc");
|
|
1064
|
+
output.addVideoTrack(this.#videoSource);
|
|
1065
|
+
}
|
|
1066
|
+
this.#output = output;
|
|
1067
|
+
this.#startPromise = output.start();
|
|
1068
|
+
await this.#startPromise;
|
|
1069
|
+
}
|
|
1070
|
+
#emitInitializationSegment() {
|
|
1071
|
+
if (this.#initializationEmitted || !this.#ftyp || !this.#moov) {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
this.#initializationEmitted = true;
|
|
1075
|
+
void this.#emitData({
|
|
1076
|
+
type: "init",
|
|
1077
|
+
timestamp: 0,
|
|
1078
|
+
duration: 0,
|
|
1079
|
+
data: concatBytes(this.#ftyp, this.#moov),
|
|
1080
|
+
kind: this.#defaultKind()
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
#emitData(data) {
|
|
1084
|
+
const next = this.#onDataOperation.then(() => this.onData.execute(data));
|
|
1085
|
+
this.#onDataOperation = next;
|
|
1086
|
+
return next;
|
|
1087
|
+
}
|
|
1088
|
+
async #flushOnData() {
|
|
1089
|
+
await this.#onDataOperation;
|
|
1090
|
+
}
|
|
1091
|
+
#enqueueOperation(operation) {
|
|
1092
|
+
const next = this.#operation.then(operation);
|
|
1093
|
+
this.#operation = next;
|
|
1094
|
+
return next;
|
|
1095
|
+
}
|
|
1096
|
+
#defaultKind() {
|
|
1097
|
+
return this.props.track.video ? "video" : "audio";
|
|
1098
|
+
}
|
|
1099
|
+
async #flushPendingFrame(track) {
|
|
1100
|
+
const buffered = track === "audio" ? this.#audioFrame : this.#videoFrame;
|
|
1101
|
+
if (!buffered) {
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
const duration = track === "audio" ? this.#lastAudioDuration : this.#lastVideoDuration;
|
|
1105
|
+
await this.#writePacket(buffered, duration);
|
|
1106
|
+
if (track === "audio") {
|
|
1107
|
+
this.#audioFrame = void 0;
|
|
926
1108
|
} else {
|
|
927
|
-
|
|
1109
|
+
this.#videoFrame = void 0;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
async #processFrame(frame) {
|
|
1113
|
+
const buffered = frame.track === "audio" ? this.#audioFrame : this.#videoFrame;
|
|
1114
|
+
if (!buffered) {
|
|
1115
|
+
if (frame.track === "audio") {
|
|
928
1116
|
this.#audioFrame = frame;
|
|
929
|
-
|
|
1117
|
+
} else {
|
|
1118
|
+
this.#videoFrame = frame;
|
|
930
1119
|
}
|
|
1120
|
+
return;
|
|
931
1121
|
}
|
|
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") {
|
|
1122
|
+
const duration = Math.max(frame.timestamp - buffered.timestamp, 0);
|
|
1123
|
+
await this.#writePacket(buffered, duration);
|
|
1124
|
+
if (frame.track === "audio") {
|
|
1125
|
+
this.#audioFrame = frame;
|
|
1126
|
+
this.#lastAudioDuration = duration;
|
|
1127
|
+
} else {
|
|
957
1128
|
this.#videoFrame = frame;
|
|
1129
|
+
this.#lastVideoDuration = duration;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
async #writePacket(frame, duration) {
|
|
1133
|
+
const data = new Uint8Array(frame.byteLength);
|
|
1134
|
+
frame.copyTo(data.buffer);
|
|
1135
|
+
const packet = new EncodedPacket(
|
|
1136
|
+
data,
|
|
1137
|
+
frame.type,
|
|
1138
|
+
frame.timestamp / 1e6,
|
|
1139
|
+
duration / 1e6
|
|
1140
|
+
);
|
|
1141
|
+
if (frame.track === "audio") {
|
|
1142
|
+
if (!this.#audioSource || !this.#audioMeta) {
|
|
1143
|
+
throw new Error("audio track missing");
|
|
1144
|
+
}
|
|
1145
|
+
await this.#audioSource.add(packet, this.#audioMeta);
|
|
958
1146
|
} else {
|
|
959
|
-
this.#
|
|
1147
|
+
if (!this.#videoSource || !this.#videoMeta) {
|
|
1148
|
+
throw new Error("video track missing");
|
|
1149
|
+
}
|
|
1150
|
+
await this.#videoSource.add(packet, this.#videoMeta);
|
|
960
1151
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
duration
|
|
966
|
-
|
|
967
|
-
};
|
|
968
|
-
this.onData.execute(res);
|
|
1152
|
+
this.#rememberFragmentPacket(
|
|
1153
|
+
frame.track,
|
|
1154
|
+
frame.type,
|
|
1155
|
+
frame.timestamp,
|
|
1156
|
+
duration
|
|
1157
|
+
);
|
|
969
1158
|
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1159
|
+
#rememberFragmentPacket(kind, type, timestamp, duration) {
|
|
1160
|
+
const end = timestamp + duration;
|
|
1161
|
+
if (!this.#currentFragment) {
|
|
1162
|
+
this.#currentFragment = {
|
|
1163
|
+
kind,
|
|
1164
|
+
type,
|
|
1165
|
+
timestamp,
|
|
1166
|
+
duration
|
|
1167
|
+
};
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
if (kind === "video" && type === "key") {
|
|
1171
|
+
this.#currentFragment.type = "key";
|
|
1172
|
+
}
|
|
1173
|
+
const fragmentEnd = Math.max(
|
|
1174
|
+
this.#currentFragment.timestamp + this.#currentFragment.duration,
|
|
1175
|
+
end
|
|
1176
|
+
);
|
|
1177
|
+
this.#currentFragment.duration = fragmentEnd - this.#currentFragment.timestamp;
|
|
976
1178
|
}
|
|
977
|
-
*/
|
|
978
1179
|
};
|
|
979
1180
|
function isDecoderConfig(frame) {
|
|
980
1181
|
return frame.codec !== void 0;
|
|
@@ -983,6 +1184,19 @@ function isVideoConfig(frame) {
|
|
|
983
1184
|
return frame.codedWidth !== void 0;
|
|
984
1185
|
}
|
|
985
1186
|
var mp4SupportedCodecs = ["avc1", "opus"];
|
|
1187
|
+
function concatBytes(...parts) {
|
|
1188
|
+
const size = parts.reduce((sum, part) => sum + part.byteLength, 0);
|
|
1189
|
+
const combined = new Uint8Array(size);
|
|
1190
|
+
let offset = 0;
|
|
1191
|
+
for (const part of parts) {
|
|
1192
|
+
combined.set(part, offset);
|
|
1193
|
+
offset += part.byteLength;
|
|
1194
|
+
}
|
|
1195
|
+
return combined;
|
|
1196
|
+
}
|
|
1197
|
+
function copyBytes(data) {
|
|
1198
|
+
return new Uint8Array(data);
|
|
1199
|
+
}
|
|
986
1200
|
|
|
987
1201
|
// ../rtp/src/extra/container/mp4/exp-golomb.ts
|
|
988
1202
|
var ExpGolomb = class {
|
|
@@ -1521,6 +1735,18 @@ function annexb2avcc(data) {
|
|
|
1521
1735
|
);
|
|
1522
1736
|
return avcc.getData();
|
|
1523
1737
|
}
|
|
1738
|
+
function annexb2avcSample(data) {
|
|
1739
|
+
const annexbParser = new H264AnnexBParser(data);
|
|
1740
|
+
const nalUnits = [];
|
|
1741
|
+
let naluPayload = null;
|
|
1742
|
+
while ((naluPayload = annexbParser.readNextNaluPayload()) != null) {
|
|
1743
|
+
nalUnits.push(Buffer.from(new H264NaluAVC1(naluPayload).data));
|
|
1744
|
+
}
|
|
1745
|
+
if (nalUnits.length === 0) {
|
|
1746
|
+
throw new Error("annexb sample does not contain nal units");
|
|
1747
|
+
}
|
|
1748
|
+
return Buffer.concat(nalUnits);
|
|
1749
|
+
}
|
|
1524
1750
|
|
|
1525
1751
|
// ../rtp/src/extra/container/ogg/parser.ts
|
|
1526
1752
|
var OggParser = class {
|
|
@@ -4477,7 +4703,12 @@ import { createHmac as createHmac2 } from "crypto";
|
|
|
4477
4703
|
import AES from "aes-js";
|
|
4478
4704
|
|
|
4479
4705
|
// ../rtp/src/srtp/cipher/ctr.ts
|
|
4480
|
-
import {
|
|
4706
|
+
import {
|
|
4707
|
+
createCipheriv as createCipheriv2,
|
|
4708
|
+
createDecipheriv,
|
|
4709
|
+
createHmac,
|
|
4710
|
+
timingSafeEqual
|
|
4711
|
+
} from "crypto";
|
|
4481
4712
|
|
|
4482
4713
|
// ../rtp/src/srtp/cipher/index.ts
|
|
4483
4714
|
var CipherAesBase = class {
|
|
@@ -4490,7 +4721,7 @@ var CipherAesBase = class {
|
|
|
4490
4721
|
encryptRtp(header, payload, rolloverCounter) {
|
|
4491
4722
|
return Buffer.from([]);
|
|
4492
4723
|
}
|
|
4493
|
-
decryptRtp(cipherText, rolloverCounter) {
|
|
4724
|
+
decryptRtp(cipherText, rolloverCounter, header) {
|
|
4494
4725
|
return [];
|
|
4495
4726
|
}
|
|
4496
4727
|
encryptRTCP(rawRtcp, srtcpIndex) {
|
|
@@ -4501,6 +4732,74 @@ var CipherAesBase = class {
|
|
|
4501
4732
|
}
|
|
4502
4733
|
};
|
|
4503
4734
|
|
|
4735
|
+
// ../rtp/src/srtp/error.ts
|
|
4736
|
+
var SrtpAuthenticationError = class extends Error {
|
|
4737
|
+
constructor(message) {
|
|
4738
|
+
super(message);
|
|
4739
|
+
this.name = "SrtpAuthenticationError";
|
|
4740
|
+
}
|
|
4741
|
+
};
|
|
4742
|
+
|
|
4743
|
+
// ../rtp/src/srtp/packet.ts
|
|
4744
|
+
var minRtpHeaderSize = 12;
|
|
4745
|
+
var minRtcpPacketSize = 8;
|
|
4746
|
+
function parseSrtpRtpHeader(packet, authTagLength, message = "Failed to authenticate SRTP packet") {
|
|
4747
|
+
const authTagOffset = packet.length - authTagLength;
|
|
4748
|
+
assertAuthenticatedPacketLength(
|
|
4749
|
+
packet.length >= minRtpHeaderSize + authTagLength,
|
|
4750
|
+
message
|
|
4751
|
+
);
|
|
4752
|
+
const header = wrapAuthenticationError(
|
|
4753
|
+
() => RtpHeader.deSerialize(packet.subarray(0, authTagOffset)),
|
|
4754
|
+
message
|
|
4755
|
+
);
|
|
4756
|
+
header.paddingSize = 0;
|
|
4757
|
+
assertAuthenticatedPacketLength(
|
|
4758
|
+
header.payloadOffset >= minRtpHeaderSize && header.payloadOffset <= authTagOffset,
|
|
4759
|
+
message
|
|
4760
|
+
);
|
|
4761
|
+
return header;
|
|
4762
|
+
}
|
|
4763
|
+
function parseSrtcpHeader(packet, authTagLength, srtcpIndexSize3, message = "Failed to authenticate SRTCP packet") {
|
|
4764
|
+
assertAuthenticatedPacketLength(
|
|
4765
|
+
packet.length >= minRtcpPacketSize + authTagLength + srtcpIndexSize3,
|
|
4766
|
+
message
|
|
4767
|
+
);
|
|
4768
|
+
return wrapAuthenticationError(
|
|
4769
|
+
() => RtcpHeader.deSerialize(packet.subarray(0, RTCP_HEADER_SIZE)),
|
|
4770
|
+
message
|
|
4771
|
+
);
|
|
4772
|
+
}
|
|
4773
|
+
function assertAuthenticatedPacketLength(condition, message) {
|
|
4774
|
+
if (!condition) {
|
|
4775
|
+
throw new SrtpAuthenticationError(message);
|
|
4776
|
+
}
|
|
4777
|
+
}
|
|
4778
|
+
function wrapAuthenticationError(parse, message) {
|
|
4779
|
+
try {
|
|
4780
|
+
return parse();
|
|
4781
|
+
} catch {
|
|
4782
|
+
throw new SrtpAuthenticationError(message);
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4785
|
+
function finalizeSrtpRtpHeader(header, packet, message = "Failed to authenticate SRTP packet") {
|
|
4786
|
+
if (!header.padding) {
|
|
4787
|
+
header.paddingSize = 0;
|
|
4788
|
+
return header;
|
|
4789
|
+
}
|
|
4790
|
+
assertAuthenticatedPacketLength(
|
|
4791
|
+
packet.length > header.payloadOffset,
|
|
4792
|
+
message
|
|
4793
|
+
);
|
|
4794
|
+
const paddingSize = packet[packet.length - 1];
|
|
4795
|
+
assertAuthenticatedPacketLength(
|
|
4796
|
+
paddingSize > 0 && paddingSize <= packet.length - header.payloadOffset,
|
|
4797
|
+
message
|
|
4798
|
+
);
|
|
4799
|
+
header.paddingSize = paddingSize;
|
|
4800
|
+
return header;
|
|
4801
|
+
}
|
|
4802
|
+
|
|
4504
4803
|
// ../rtp/src/srtp/cipher/ctr.ts
|
|
4505
4804
|
var CipherAesCtr = class extends CipherAesBase {
|
|
4506
4805
|
constructor(srtpSessionKey, srtpSessionSalt, srtcpSessionKey, srtcpSessionSalt, srtpSessionAuthTag, srtcpSessionAuthTag) {
|
|
@@ -4526,10 +4825,20 @@ var CipherAesCtr = class extends CipherAesBase {
|
|
|
4526
4825
|
);
|
|
4527
4826
|
return Buffer.concat([headerBuffer, enc, authTag]);
|
|
4528
4827
|
}
|
|
4529
|
-
decryptRtp(cipherText, rolloverCounter) {
|
|
4530
|
-
const
|
|
4531
|
-
const
|
|
4532
|
-
|
|
4828
|
+
decryptRtp(cipherText, rolloverCounter, header = parseSrtpRtpHeader(cipherText, this.authTagLength)) {
|
|
4829
|
+
const authTagOffset = cipherText.length - this.authTagLength;
|
|
4830
|
+
const encryptedPacket = cipherText.subarray(0, authTagOffset);
|
|
4831
|
+
const actualAuthTag = cipherText.subarray(authTagOffset);
|
|
4832
|
+
const expectedAuthTag = this.generateSrtpAuthTag(
|
|
4833
|
+
rolloverCounter,
|
|
4834
|
+
encryptedPacket.subarray(0, header.payloadOffset),
|
|
4835
|
+
encryptedPacket.subarray(header.payloadOffset)
|
|
4836
|
+
);
|
|
4837
|
+
assertAuthTag(
|
|
4838
|
+
actualAuthTag,
|
|
4839
|
+
expectedAuthTag,
|
|
4840
|
+
"Failed to authenticate SRTP packet"
|
|
4841
|
+
);
|
|
4533
4842
|
const counter = this.generateCounter(
|
|
4534
4843
|
header.sequenceNumber,
|
|
4535
4844
|
rolloverCounter,
|
|
@@ -4541,14 +4850,16 @@ var CipherAesCtr = class extends CipherAesBase {
|
|
|
4541
4850
|
this.srtpSessionKey,
|
|
4542
4851
|
counter
|
|
4543
4852
|
);
|
|
4544
|
-
const payload =
|
|
4853
|
+
const payload = encryptedPacket.subarray(header.payloadOffset);
|
|
4545
4854
|
const buf = cipher.update(payload);
|
|
4546
4855
|
const dst = Buffer.concat([
|
|
4547
|
-
|
|
4548
|
-
buf
|
|
4549
|
-
Buffer.alloc(size - header.payloadOffset - buf.length)
|
|
4856
|
+
encryptedPacket.subarray(0, header.payloadOffset),
|
|
4857
|
+
buf
|
|
4550
4858
|
]);
|
|
4551
|
-
return [
|
|
4859
|
+
return [
|
|
4860
|
+
dst,
|
|
4861
|
+
finalizeSrtpRtpHeader(header, dst, "Failed to authenticate SRTP packet")
|
|
4862
|
+
];
|
|
4552
4863
|
}
|
|
4553
4864
|
encryptRTCP(rtcpPacket, srtcpIndex) {
|
|
4554
4865
|
let out = Buffer.from(rtcpPacket);
|
|
@@ -4570,15 +4881,29 @@ var CipherAesCtr = class extends CipherAesBase {
|
|
|
4570
4881
|
return out;
|
|
4571
4882
|
}
|
|
4572
4883
|
decryptRTCP(encrypted) {
|
|
4573
|
-
const header =
|
|
4884
|
+
const header = parseSrtcpHeader(
|
|
4885
|
+
encrypted,
|
|
4886
|
+
this.authTagLength,
|
|
4887
|
+
srtcpIndexSize
|
|
4888
|
+
);
|
|
4574
4889
|
const tailOffset = encrypted.length - (this.authTagLength + srtcpIndexSize);
|
|
4890
|
+
const authenticatedPortion = encrypted.subarray(
|
|
4891
|
+
0,
|
|
4892
|
+
encrypted.length - this.authTagLength
|
|
4893
|
+
);
|
|
4894
|
+
const actualTag = encrypted.subarray(encrypted.length - this.authTagLength);
|
|
4895
|
+
const expectedTag = this.generateSrtcpAuthTag(authenticatedPortion);
|
|
4896
|
+
assertAuthTag(
|
|
4897
|
+
actualTag,
|
|
4898
|
+
expectedTag,
|
|
4899
|
+
"Failed to authenticate SRTCP packet"
|
|
4900
|
+
);
|
|
4575
4901
|
const out = Buffer.from(encrypted).slice(0, tailOffset);
|
|
4576
|
-
const isEncrypted = encrypted[tailOffset]
|
|
4902
|
+
const isEncrypted = encrypted[tailOffset] >>> 7;
|
|
4577
4903
|
if (isEncrypted === 0) return [out, header];
|
|
4578
4904
|
let srtcpIndex = encrypted.readUInt32BE(tailOffset);
|
|
4579
4905
|
srtcpIndex &= ~(1 << 31);
|
|
4580
4906
|
const ssrc = encrypted.readUInt32BE(4);
|
|
4581
|
-
const actualTag = encrypted.subarray(encrypted.length - 10);
|
|
4582
4907
|
const counter = this.generateCounter(
|
|
4583
4908
|
srtcpIndex & 65535,
|
|
4584
4909
|
srtcpIndex >> 16,
|
|
@@ -4619,6 +4944,11 @@ var CipherAesCtr = class extends CipherAesBase {
|
|
|
4619
4944
|
}
|
|
4620
4945
|
};
|
|
4621
4946
|
var srtcpIndexSize = 4;
|
|
4947
|
+
function assertAuthTag(actual, expected, message) {
|
|
4948
|
+
if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
|
|
4949
|
+
throw new SrtpAuthenticationError(message);
|
|
4950
|
+
}
|
|
4951
|
+
}
|
|
4622
4952
|
|
|
4623
4953
|
// ../rtp/src/srtp/cipher/gcm.ts
|
|
4624
4954
|
import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv2 } from "crypto";
|
|
@@ -4641,20 +4971,25 @@ var CipherAesGcm = class extends CipherAesBase {
|
|
|
4641
4971
|
const dst = Buffer.concat([hdr, enc, authTag]);
|
|
4642
4972
|
return dst;
|
|
4643
4973
|
}
|
|
4644
|
-
decryptRtp(cipherText, rolloverCounter) {
|
|
4645
|
-
const
|
|
4974
|
+
decryptRtp(cipherText, rolloverCounter, header = parseSrtpRtpHeader(cipherText, this.aeadAuthTagLen)) {
|
|
4975
|
+
const headerBuffer = cipherText.subarray(0, header.payloadOffset);
|
|
4976
|
+
const authTagOffset = cipherText.length - this.aeadAuthTagLen;
|
|
4977
|
+
const authTag = cipherText.subarray(authTagOffset);
|
|
4646
4978
|
let dst = Buffer.from([]);
|
|
4647
4979
|
dst = growBufferSize(dst, cipherText.length - this.aeadAuthTagLen);
|
|
4648
|
-
|
|
4980
|
+
headerBuffer.copy(dst);
|
|
4649
4981
|
const iv = this.rtpInitializationVector(header, rolloverCounter);
|
|
4650
|
-
const enc = cipherText.slice(
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
);
|
|
4654
|
-
const
|
|
4655
|
-
|
|
4982
|
+
const enc = cipherText.slice(header.payloadOffset, authTagOffset);
|
|
4983
|
+
const decipher = createDecipheriv2("aes-128-gcm", this.srtpSessionKey, iv);
|
|
4984
|
+
decipher.setAAD(headerBuffer);
|
|
4985
|
+
decipher.setAuthTag(authTag);
|
|
4986
|
+
const dec = decipher.update(enc);
|
|
4987
|
+
finalizeAuthenticatedDecryption(decipher, "SRTP");
|
|
4656
4988
|
dec.copy(dst, header.payloadOffset);
|
|
4657
|
-
return [
|
|
4989
|
+
return [
|
|
4990
|
+
dst,
|
|
4991
|
+
finalizeSrtpRtpHeader(header, dst, "Failed to authenticate SRTP packet")
|
|
4992
|
+
];
|
|
4658
4993
|
}
|
|
4659
4994
|
encryptRTCP(rtcpPacket, srtcpIndex) {
|
|
4660
4995
|
const ssrc = rtcpPacket.readUInt32BE(4);
|
|
@@ -4675,19 +5010,38 @@ var CipherAesGcm = class extends CipherAesBase {
|
|
|
4675
5010
|
return dst;
|
|
4676
5011
|
}
|
|
4677
5012
|
decryptRTCP(encrypted) {
|
|
4678
|
-
const header =
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
5013
|
+
const header = parseSrtcpHeader(
|
|
5014
|
+
encrypted,
|
|
5015
|
+
this.aeadAuthTagLen,
|
|
5016
|
+
srtcpIndexSize2
|
|
5017
|
+
);
|
|
5018
|
+
const srtcpIndexOffset = encrypted.length - srtcpIndexSize2;
|
|
5019
|
+
const authTagOffset = srtcpIndexOffset - this.aeadAuthTagLen;
|
|
4682
5020
|
const ssrc = encrypted.readUInt32BE(4);
|
|
4683
|
-
|
|
4684
|
-
|
|
5021
|
+
const encodedSrtcpIndex = encrypted.readUInt32BE(srtcpIndexOffset);
|
|
5022
|
+
const isEncrypted = encodedSrtcpIndex >>> 31 === 1;
|
|
5023
|
+
const srtcpIndex = encodedSrtcpIndex & ~(rtcpEncryptionFlag << 24);
|
|
4685
5024
|
const iv = this.rtcpInitializationVector(ssrc, srtcpIndex);
|
|
4686
|
-
const aad =
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
5025
|
+
const aad = isEncrypted ? Buffer.concat([
|
|
5026
|
+
encrypted.subarray(0, 8),
|
|
5027
|
+
encrypted.subarray(srtcpIndexOffset)
|
|
5028
|
+
]) : Buffer.concat([
|
|
5029
|
+
encrypted.subarray(0, authTagOffset),
|
|
5030
|
+
encrypted.subarray(srtcpIndexOffset)
|
|
5031
|
+
]);
|
|
5032
|
+
const cipherText = isEncrypted ? encrypted.slice(8, authTagOffset) : Buffer.alloc(0);
|
|
5033
|
+
const dst = isEncrypted ? Buffer.alloc(authTagOffset) : Buffer.from(encrypted.subarray(0, authTagOffset));
|
|
5034
|
+
if (isEncrypted) {
|
|
5035
|
+
encrypted.slice(0, 8).copy(dst);
|
|
5036
|
+
}
|
|
5037
|
+
const decipher = createDecipheriv2("aes-128-gcm", this.srtcpSessionKey, iv);
|
|
5038
|
+
decipher.setAAD(aad);
|
|
5039
|
+
decipher.setAuthTag(encrypted.subarray(authTagOffset, srtcpIndexOffset));
|
|
5040
|
+
const dec = decipher.update(cipherText);
|
|
5041
|
+
finalizeAuthenticatedDecryption(decipher, "SRTCP");
|
|
5042
|
+
if (isEncrypted) {
|
|
5043
|
+
dec.copy(dst, 8);
|
|
5044
|
+
}
|
|
4691
5045
|
return [dst, header];
|
|
4692
5046
|
}
|
|
4693
5047
|
// https://tools.ietf.org/html/rfc7714#section-8.1
|
|
@@ -4723,6 +5077,15 @@ var CipherAesGcm = class extends CipherAesBase {
|
|
|
4723
5077
|
};
|
|
4724
5078
|
var srtcpIndexSize2 = 4;
|
|
4725
5079
|
var rtcpEncryptionFlag = 128;
|
|
5080
|
+
function finalizeAuthenticatedDecryption(decipher, packetType) {
|
|
5081
|
+
try {
|
|
5082
|
+
decipher.final();
|
|
5083
|
+
} catch {
|
|
5084
|
+
throw new SrtpAuthenticationError(
|
|
5085
|
+
`Failed to authenticate ${packetType} packet`
|
|
5086
|
+
);
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
4726
5089
|
|
|
4727
5090
|
// ../rtp/src/srtp/context/context.ts
|
|
4728
5091
|
var Context = class {
|
|
@@ -4980,12 +5343,29 @@ var SrtpContext = class extends Context {
|
|
|
4980
5343
|
return enc;
|
|
4981
5344
|
}
|
|
4982
5345
|
decryptRtp(cipherText) {
|
|
4983
|
-
const header =
|
|
4984
|
-
const
|
|
4985
|
-
|
|
4986
|
-
|
|
5346
|
+
const header = parseSrtpRtpHeader(cipherText, this.rtpAuthTagLength);
|
|
5347
|
+
const existingState = this.srtpSSRCStates[header.ssrc];
|
|
5348
|
+
const nextState = existingState ? { ...existingState } : {
|
|
5349
|
+
ssrc: header.ssrc,
|
|
5350
|
+
rolloverCounter: 0,
|
|
5351
|
+
lastSequenceNumber: 0
|
|
5352
|
+
};
|
|
5353
|
+
this.updateRolloverCount(header.sequenceNumber, nextState);
|
|
5354
|
+
const dec = this.cipher.decryptRtp(
|
|
5355
|
+
cipherText,
|
|
5356
|
+
nextState.rolloverCounter,
|
|
5357
|
+
header
|
|
5358
|
+
);
|
|
5359
|
+
if (existingState) {
|
|
5360
|
+
Object.assign(existingState, nextState);
|
|
5361
|
+
} else {
|
|
5362
|
+
this.srtpSSRCStates[header.ssrc] = nextState;
|
|
5363
|
+
}
|
|
4987
5364
|
return dec;
|
|
4988
5365
|
}
|
|
5366
|
+
get rtpAuthTagLength() {
|
|
5367
|
+
return this.profile === ProtectionProfileAeadAes128Gcm ? 16 : 10;
|
|
5368
|
+
}
|
|
4989
5369
|
};
|
|
4990
5370
|
|
|
4991
5371
|
// ../rtp/src/srtp/srtp.ts
|
|
@@ -5048,7 +5428,7 @@ var DepacketizeBase = class {
|
|
|
5048
5428
|
frameBroken = false;
|
|
5049
5429
|
keyframeReceived = false;
|
|
5050
5430
|
count = 0;
|
|
5051
|
-
onNeedKeyFrame = new
|
|
5431
|
+
onNeedKeyFrame = new Event2();
|
|
5052
5432
|
internalStats = {};
|
|
5053
5433
|
toJSON() {
|
|
5054
5434
|
return {
|
|
@@ -5408,17 +5788,17 @@ var JitterBufferBase = class {
|
|
|
5408
5788
|
this.rtpBuffer[rtp.header.sequenceNumber] = rtp;
|
|
5409
5789
|
}
|
|
5410
5790
|
resolveBuffer(seqNumFrom) {
|
|
5411
|
-
const
|
|
5791
|
+
const resolve2 = [];
|
|
5412
5792
|
for (let index = seqNumFrom; ; index = uint16Add(index, 1)) {
|
|
5413
5793
|
const rtp = this.rtpBuffer[index];
|
|
5414
5794
|
if (rtp) {
|
|
5415
|
-
|
|
5795
|
+
resolve2.push(rtp);
|
|
5416
5796
|
delete this.rtpBuffer[index];
|
|
5417
5797
|
} else {
|
|
5418
5798
|
break;
|
|
5419
5799
|
}
|
|
5420
5800
|
}
|
|
5421
|
-
return
|
|
5801
|
+
return resolve2;
|
|
5422
5802
|
}
|
|
5423
5803
|
sortAndClearBuffer(rtpBuffer) {
|
|
5424
5804
|
const buffer2 = [];
|
|
@@ -5717,94 +6097,125 @@ var MP4Base = class {
|
|
|
5717
6097
|
video: !!this.tracks.find((t) => t.kind === "video")
|
|
5718
6098
|
}
|
|
5719
6099
|
});
|
|
5720
|
-
this.container.onData.subscribe((data) => {
|
|
5721
|
-
this.output(data);
|
|
6100
|
+
this.container.onData.subscribe(async (data) => {
|
|
6101
|
+
await this.output(data);
|
|
5722
6102
|
});
|
|
5723
6103
|
}
|
|
6104
|
+
audioStopped = false;
|
|
5724
6105
|
internalStats = {};
|
|
5725
6106
|
container;
|
|
5726
6107
|
stopped = false;
|
|
5727
|
-
onStopped = new
|
|
6108
|
+
onStopped = new Event2();
|
|
6109
|
+
videoStopped = false;
|
|
5728
6110
|
toJSON() {
|
|
5729
6111
|
return {
|
|
5730
6112
|
...this.internalStats
|
|
5731
6113
|
};
|
|
5732
6114
|
}
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
6115
|
+
processVideoInput = ({ eol, frame }) => {
|
|
6116
|
+
if (this.stopped) {
|
|
6117
|
+
return;
|
|
6118
|
+
}
|
|
6119
|
+
if (!frame) {
|
|
6120
|
+
if (eol) {
|
|
6121
|
+
this.videoStopped = true;
|
|
6122
|
+
if (!this.tracks.some((track2) => track2.kind === "audio") || this.audioStopped) {
|
|
6123
|
+
void this.stop();
|
|
6124
|
+
}
|
|
6125
|
+
}
|
|
6126
|
+
return;
|
|
6127
|
+
}
|
|
6128
|
+
const track = this.tracks.find((t) => t.kind === "video");
|
|
6129
|
+
this.videoStopped = false;
|
|
6130
|
+
if (!this.container.videoTrack) {
|
|
6131
|
+
if (frame.isKeyframe) {
|
|
6132
|
+
const avcc = annexb2avcc(frame.data);
|
|
6133
|
+
const sample = annexb2avcSample(frame.data);
|
|
6134
|
+
const [displayAspectWidth, displayAspectHeight] = computeRatio(
|
|
6135
|
+
track.width,
|
|
6136
|
+
track.height
|
|
6137
|
+
);
|
|
5737
6138
|
this.container.write({
|
|
5738
|
-
codec:
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
),
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
track: "
|
|
6139
|
+
codec: avccToCodecString(avcc),
|
|
6140
|
+
codedWidth: track.width,
|
|
6141
|
+
codedHeight: track.height,
|
|
6142
|
+
description: toArrayBuffer(Buffer.from(avcc)),
|
|
6143
|
+
displayAspectWidth,
|
|
6144
|
+
displayAspectHeight,
|
|
6145
|
+
track: "video"
|
|
5745
6146
|
});
|
|
5746
|
-
} else {
|
|
5747
6147
|
this.container.write({
|
|
5748
|
-
byteLength:
|
|
6148
|
+
byteLength: sample.length,
|
|
5749
6149
|
duration: null,
|
|
5750
6150
|
timestamp: frame.time * 1e3,
|
|
5751
6151
|
type: "key",
|
|
5752
6152
|
copyTo: (destination) => {
|
|
5753
|
-
|
|
6153
|
+
new Uint8Array(destination).set(sample);
|
|
5754
6154
|
},
|
|
5755
|
-
track: "
|
|
6155
|
+
track: "video"
|
|
5756
6156
|
});
|
|
5757
6157
|
}
|
|
6158
|
+
} else {
|
|
6159
|
+
const sample = annexb2avcSample(frame.data);
|
|
6160
|
+
this.container.write({
|
|
6161
|
+
byteLength: sample.length,
|
|
6162
|
+
duration: null,
|
|
6163
|
+
timestamp: frame.time * 1e3,
|
|
6164
|
+
type: frame.isKeyframe ? "key" : "delta",
|
|
6165
|
+
copyTo: (destination) => {
|
|
6166
|
+
new Uint8Array(destination).set(sample);
|
|
6167
|
+
},
|
|
6168
|
+
track: "video"
|
|
6169
|
+
});
|
|
5758
6170
|
}
|
|
5759
6171
|
};
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
);
|
|
5770
|
-
this.container.write({
|
|
5771
|
-
codec: track.codec,
|
|
5772
|
-
codedWidth: track.width,
|
|
5773
|
-
codedHeight: track.height,
|
|
5774
|
-
description: avcc.buffer,
|
|
5775
|
-
displayAspectWidth,
|
|
5776
|
-
displayAspectHeight,
|
|
5777
|
-
track: "video"
|
|
5778
|
-
});
|
|
5779
|
-
this.container.write({
|
|
5780
|
-
byteLength: frame.data.length,
|
|
5781
|
-
duration: null,
|
|
5782
|
-
timestamp: frame.time * 1e3,
|
|
5783
|
-
type: "key",
|
|
5784
|
-
copyTo: (destination) => {
|
|
5785
|
-
frame.data.copy(destination);
|
|
5786
|
-
},
|
|
5787
|
-
track: "video"
|
|
5788
|
-
});
|
|
6172
|
+
processAudioInput = ({ eol, frame }) => {
|
|
6173
|
+
if (this.stopped) {
|
|
6174
|
+
return;
|
|
6175
|
+
}
|
|
6176
|
+
if (!frame) {
|
|
6177
|
+
if (eol) {
|
|
6178
|
+
this.audioStopped = true;
|
|
6179
|
+
if (!this.tracks.some((track2) => track2.kind === "video") || this.videoStopped) {
|
|
6180
|
+
void this.stop();
|
|
5789
6181
|
}
|
|
5790
|
-
} else {
|
|
5791
|
-
this.container.write({
|
|
5792
|
-
byteLength: frame.data.length,
|
|
5793
|
-
duration: null,
|
|
5794
|
-
timestamp: frame.time * 1e3,
|
|
5795
|
-
type: frame.isKeyframe ? "key" : "delta",
|
|
5796
|
-
copyTo: (destination) => {
|
|
5797
|
-
frame.data.copy(destination);
|
|
5798
|
-
},
|
|
5799
|
-
track: "video"
|
|
5800
|
-
});
|
|
5801
6182
|
}
|
|
6183
|
+
return;
|
|
5802
6184
|
}
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
6185
|
+
const track = this.tracks.find((t) => t.kind === "audio");
|
|
6186
|
+
this.audioStopped = false;
|
|
6187
|
+
if (!this.container.audioTrack) {
|
|
6188
|
+
this.container.write({
|
|
6189
|
+
codec: track.codec,
|
|
6190
|
+
description: toArrayBuffer(OpusRtpPayload.createCodecPrivate()),
|
|
6191
|
+
numberOfChannels: 2,
|
|
6192
|
+
sampleRate: track.clockRate,
|
|
6193
|
+
track: "audio"
|
|
6194
|
+
});
|
|
6195
|
+
} else {
|
|
6196
|
+
this.container.write({
|
|
6197
|
+
byteLength: frame.data.length,
|
|
6198
|
+
duration: null,
|
|
6199
|
+
timestamp: frame.time * 1e3,
|
|
6200
|
+
type: "key",
|
|
6201
|
+
copyTo: (destination) => {
|
|
6202
|
+
new Uint8Array(destination).set(frame.data);
|
|
6203
|
+
},
|
|
6204
|
+
track: "audio"
|
|
6205
|
+
});
|
|
6206
|
+
}
|
|
6207
|
+
};
|
|
6208
|
+
start() {
|
|
6209
|
+
}
|
|
6210
|
+
async stop() {
|
|
6211
|
+
if (this.stopped) {
|
|
6212
|
+
return;
|
|
6213
|
+
}
|
|
6214
|
+
this.stopped = true;
|
|
6215
|
+
await this.container.stop();
|
|
6216
|
+
await this.output({ eol: true });
|
|
6217
|
+
await this.onStopped.execute();
|
|
6218
|
+
}
|
|
5808
6219
|
};
|
|
5809
6220
|
function computeRatio(a, b) {
|
|
5810
6221
|
function gcd(x, y) {
|
|
@@ -5818,6 +6229,21 @@ function computeRatio(a, b) {
|
|
|
5818
6229
|
const divisor = gcd(a, b);
|
|
5819
6230
|
return [a / divisor, b / divisor];
|
|
5820
6231
|
}
|
|
6232
|
+
function avccToCodecString(avcc) {
|
|
6233
|
+
if (avcc.byteLength < 4) {
|
|
6234
|
+
throw new Error("invalid avcc decoder configuration record");
|
|
6235
|
+
}
|
|
6236
|
+
return `avc1.${toHex(avcc[1])}${toHex(avcc[2])}${toHex(avcc[3])}`;
|
|
6237
|
+
}
|
|
6238
|
+
function toHex(value) {
|
|
6239
|
+
return value.toString(16).padStart(2, "0");
|
|
6240
|
+
}
|
|
6241
|
+
function toArrayBuffer(buffer2) {
|
|
6242
|
+
return buffer2.buffer.slice(
|
|
6243
|
+
buffer2.byteOffset,
|
|
6244
|
+
buffer2.byteOffset + buffer2.byteLength
|
|
6245
|
+
);
|
|
6246
|
+
}
|
|
5821
6247
|
|
|
5822
6248
|
// ../rtp/src/extra/processor/mp4Callback.ts
|
|
5823
6249
|
import { appendFile } from "fs/promises";
|
|
@@ -5847,14 +6273,16 @@ var MP4Callback = class extends MP4Base {
|
|
|
5847
6273
|
this.processVideoInput(input);
|
|
5848
6274
|
};
|
|
5849
6275
|
destroy = () => {
|
|
5850
|
-
this.
|
|
5851
|
-
|
|
6276
|
+
void this.stop().finally(() => {
|
|
6277
|
+
this.cb = void 0;
|
|
6278
|
+
this.queue.cancel();
|
|
6279
|
+
});
|
|
5852
6280
|
};
|
|
5853
6281
|
static saveToFileSystem = (path2) => {
|
|
5854
6282
|
const queue = new PromiseQueue();
|
|
5855
6283
|
return async (value) => {
|
|
5856
6284
|
await queue.push(async () => {
|
|
5857
|
-
if (value
|
|
6285
|
+
if ("data" in value) {
|
|
5858
6286
|
await appendFile(path2, value.data);
|
|
5859
6287
|
} else if (value.eol) {
|
|
5860
6288
|
}
|
|
@@ -6017,8 +6445,8 @@ var NackHandlerBase = class {
|
|
|
6017
6445
|
_lost = {};
|
|
6018
6446
|
clearNackInterval;
|
|
6019
6447
|
internalStats = {};
|
|
6020
|
-
onNackSent = new
|
|
6021
|
-
onPacketLost = new
|
|
6448
|
+
onNackSent = new Event2();
|
|
6449
|
+
onPacketLost = new Event2();
|
|
6022
6450
|
mediaSourceSsrc;
|
|
6023
6451
|
retryCount = 10;
|
|
6024
6452
|
stopped = false;
|
|
@@ -6164,7 +6592,7 @@ var WebmBase = class {
|
|
|
6164
6592
|
stopped = false;
|
|
6165
6593
|
videoKeyframeReceived = false;
|
|
6166
6594
|
internalStats = {};
|
|
6167
|
-
onStopped = new
|
|
6595
|
+
onStopped = new Event2();
|
|
6168
6596
|
toJSON() {
|
|
6169
6597
|
return {
|
|
6170
6598
|
...this.internalStats,
|
|
@@ -6557,7 +6985,7 @@ var NtpTimeCallback = class extends SimpleProcessorCallbackBase(NtpTimeBase) {
|
|
|
6557
6985
|
var RtcpSourceCallback = class {
|
|
6558
6986
|
cb;
|
|
6559
6987
|
destructor;
|
|
6560
|
-
onStopped = new
|
|
6988
|
+
onStopped = new Event2();
|
|
6561
6989
|
toJSON() {
|
|
6562
6990
|
return {};
|
|
6563
6991
|
}
|
|
@@ -6595,7 +7023,7 @@ var RtpSourceCallback = class {
|
|
|
6595
7023
|
}
|
|
6596
7024
|
cb;
|
|
6597
7025
|
destructor;
|
|
6598
|
-
onStopped = new
|
|
7026
|
+
onStopped = new Event2();
|
|
6599
7027
|
stats = {};
|
|
6600
7028
|
buffer = [];
|
|
6601
7029
|
bufferFulfilled = false;
|
|
@@ -6831,9 +7259,7 @@ var WebmStream = class extends WebmBase {
|
|
|
6831
7259
|
};
|
|
6832
7260
|
|
|
6833
7261
|
// src/nonstandard/navigator.ts
|
|
6834
|
-
import { randomBytes as randomBytes5 } from "crypto";
|
|
6835
7262
|
import { createSocket as createSocket3 } from "dgram";
|
|
6836
|
-
import { jspack as jspack8 } from "@shinyoshiaki/jspack";
|
|
6837
7263
|
|
|
6838
7264
|
// ../dtls/src/context/cipher.ts
|
|
6839
7265
|
import { createSign, randomBytes as randomBytes3, webcrypto } from "crypto";
|
|
@@ -8508,6 +8934,16 @@ handlers2[16 /* client_key_exchange_16 */] = ({ cipher, dtls }) => (message) =>
|
|
|
8508
8934
|
);
|
|
8509
8935
|
log18(dtls.sessionId, "setup cipher", cipher.cipher.summary);
|
|
8510
8936
|
};
|
|
8937
|
+
handlers2[11 /* certificate_11 */] = ({ cipher, dtls }) => (message) => {
|
|
8938
|
+
log18(dtls.sessionId, "handshake certificate", message);
|
|
8939
|
+
cipher.remoteCertificate = message.certificateList[0];
|
|
8940
|
+
};
|
|
8941
|
+
handlers2[15 /* certificate_verify_15 */] = ({ cipher, dtls }) => (message) => {
|
|
8942
|
+
if (!cipher.remoteCertificate) {
|
|
8943
|
+
throw new Error("client certificate missing before certificate verify");
|
|
8944
|
+
}
|
|
8945
|
+
log18(dtls.sessionId, "certificate_verify", message.algorithm);
|
|
8946
|
+
};
|
|
8511
8947
|
handlers2[20 /* finished_20 */] = ({ dtls }) => (message) => {
|
|
8512
8948
|
log18(dtls.sessionId, "finished", message);
|
|
8513
8949
|
};
|
|
@@ -8515,12 +8951,12 @@ handlers2[20 /* finished_20 */] = ({ dtls }) => (message) => {
|
|
|
8515
8951
|
// ../dtls/src/server.ts
|
|
8516
8952
|
var log19 = debug("werift-dtls : packages/dtls/src/server.ts : log");
|
|
8517
8953
|
|
|
8518
|
-
// ../ice/src/stun/const.ts
|
|
8954
|
+
// ../ice-server/src/stun/const.ts
|
|
8519
8955
|
var COOKIE = 554869826;
|
|
8520
8956
|
var IPV4_PROTOCOL = 1;
|
|
8521
8957
|
var IPV6_PROTOCOL = 2;
|
|
8522
8958
|
|
|
8523
|
-
// ../ice/src/stun/attributes.ts
|
|
8959
|
+
// ../ice-server/src/stun/attributes.ts
|
|
8524
8960
|
import * as Int64 from "int64-buffer";
|
|
8525
8961
|
import nodeIp from "ip";
|
|
8526
8962
|
function packAddress(value) {
|
|
@@ -8533,26 +8969,31 @@ function packAddress(value) {
|
|
|
8533
8969
|
return Buffer.concat([buffer2, nodeIp.toBuffer(address)]);
|
|
8534
8970
|
}
|
|
8535
8971
|
function unpackErrorCode(data) {
|
|
8536
|
-
if (data.length < 4)
|
|
8972
|
+
if (data.length < 4) {
|
|
8973
|
+
throw new Error("STUN error code is less than 4 bytes");
|
|
8974
|
+
}
|
|
8537
8975
|
const codeHigh = data.readUInt8(2);
|
|
8538
8976
|
const codeLow = data.readUInt8(3);
|
|
8539
8977
|
const reason = data.slice(4).toString("utf8");
|
|
8540
8978
|
return [codeHigh * 100 + codeLow, reason];
|
|
8541
8979
|
}
|
|
8542
8980
|
function unpackAddress(data) {
|
|
8543
|
-
if (data.length < 4)
|
|
8981
|
+
if (data.length < 4) {
|
|
8544
8982
|
throw new Error("STUN address length is less than 4 bytes");
|
|
8983
|
+
}
|
|
8545
8984
|
const protocol = data.readUInt8(1);
|
|
8546
8985
|
const port = data.readUInt16BE(2);
|
|
8547
8986
|
const address = data.slice(4);
|
|
8548
8987
|
switch (protocol) {
|
|
8549
8988
|
case IPV4_PROTOCOL:
|
|
8550
|
-
if (address.length
|
|
8551
|
-
throw new Error(
|
|
8989
|
+
if (address.length !== 4) {
|
|
8990
|
+
throw new Error("STUN address has invalid length for IPv4");
|
|
8991
|
+
}
|
|
8552
8992
|
return [nodeIp.toString(address), port];
|
|
8553
8993
|
case IPV6_PROTOCOL:
|
|
8554
|
-
if (address.length
|
|
8994
|
+
if (address.length !== 16) {
|
|
8555
8995
|
throw new Error("STUN address has invalid length for IPv6");
|
|
8996
|
+
}
|
|
8556
8997
|
return [nodeIp.toString(address), port];
|
|
8557
8998
|
default:
|
|
8558
8999
|
throw new Error("STUN address has unknown protocol");
|
|
@@ -8580,8 +9021,25 @@ function packErrorCode(value) {
|
|
|
8580
9021
|
buffer2.writeUInt16BE(0, 0);
|
|
8581
9022
|
buffer2.writeUInt8(Math.floor(value[0] / 100), 2);
|
|
8582
9023
|
buffer2.writeUInt8(value[0] % 100, 3);
|
|
8583
|
-
const
|
|
8584
|
-
return Buffer.concat([buffer2,
|
|
9024
|
+
const encoded = Buffer.from(value[1], "utf8");
|
|
9025
|
+
return Buffer.concat([buffer2, encoded]);
|
|
9026
|
+
}
|
|
9027
|
+
function packUnknownAttributes(value) {
|
|
9028
|
+
const buffer2 = Buffer.alloc(value.length * 2);
|
|
9029
|
+
value.forEach((attributeType, index) => {
|
|
9030
|
+
buffer2.writeUInt16BE(attributeType, index * 2);
|
|
9031
|
+
});
|
|
9032
|
+
return buffer2;
|
|
9033
|
+
}
|
|
9034
|
+
function unpackUnknownAttributes(data) {
|
|
9035
|
+
if (data.length % 2 !== 0) {
|
|
9036
|
+
throw new Error("UNKNOWN-ATTRIBUTES must have an even length");
|
|
9037
|
+
}
|
|
9038
|
+
const attributes = [];
|
|
9039
|
+
for (let offset = 0; offset < data.length; offset += 2) {
|
|
9040
|
+
attributes.push(data.readUInt16BE(offset));
|
|
9041
|
+
}
|
|
9042
|
+
return attributes;
|
|
8585
9043
|
}
|
|
8586
9044
|
function packXorAddress(value, transactionId) {
|
|
8587
9045
|
return xorAddress(packAddress(value), transactionId);
|
|
@@ -8607,10 +9065,16 @@ var unpackUnsigned64 = (data) => {
|
|
|
8607
9065
|
};
|
|
8608
9066
|
var packString = (value) => Buffer.from(value, "utf8");
|
|
8609
9067
|
var unpackString = (data) => data.toString("utf8");
|
|
9068
|
+
var packSoftware = (value) => {
|
|
9069
|
+
if ([...value].length >= 128) {
|
|
9070
|
+
throw new Error("SOFTWARE must be shorter than 128 characters");
|
|
9071
|
+
}
|
|
9072
|
+
return packString(value);
|
|
9073
|
+
};
|
|
8610
9074
|
var packBytes = (value) => value;
|
|
8611
9075
|
var unpackBytes = (data) => data;
|
|
8612
|
-
var packNone = (
|
|
8613
|
-
var unpackNone = (
|
|
9076
|
+
var packNone = () => Buffer.alloc(0);
|
|
9077
|
+
var unpackNone = () => null;
|
|
8614
9078
|
var ATTRIBUTES = [
|
|
8615
9079
|
[1, "MAPPED-ADDRESS", packAddress, unpackAddress],
|
|
8616
9080
|
[3, "CHANGE-REQUEST", packUnsigned, unpackUnsigned],
|
|
@@ -8619,6 +9083,12 @@ var ATTRIBUTES = [
|
|
|
8619
9083
|
[6, "USERNAME", packString, unpackString],
|
|
8620
9084
|
[8, "MESSAGE-INTEGRITY", packBytes, unpackBytes],
|
|
8621
9085
|
[9, "ERROR-CODE", packErrorCode, unpackErrorCode],
|
|
9086
|
+
[
|
|
9087
|
+
10,
|
|
9088
|
+
"UNKNOWN-ATTRIBUTES",
|
|
9089
|
+
packUnknownAttributes,
|
|
9090
|
+
unpackUnknownAttributes
|
|
9091
|
+
],
|
|
8622
9092
|
[12, "CHANNEL-NUMBER", packUnsignedShort, unpackUnsignedShort],
|
|
8623
9093
|
[13, "LIFETIME", packUnsigned, unpackUnsigned],
|
|
8624
9094
|
[18, "XOR-PEER-ADDRESS", packXorAddress, unpackXorAddress],
|
|
@@ -8626,11 +9096,20 @@ var ATTRIBUTES = [
|
|
|
8626
9096
|
[20, "REALM", packString, unpackString],
|
|
8627
9097
|
[21, "NONCE", packBytes, unpackBytes],
|
|
8628
9098
|
[22, "XOR-RELAYED-ADDRESS", packXorAddress, unpackXorAddress],
|
|
9099
|
+
[23, "REQUESTED-ADDRESS-FAMILY", packUnsigned, unpackUnsigned],
|
|
9100
|
+
[24, "EVEN-PORT", packBytes, unpackBytes],
|
|
8629
9101
|
[25, "REQUESTED-TRANSPORT", packUnsigned, unpackUnsigned],
|
|
9102
|
+
[28, "MESSAGE-INTEGRITY-SHA256", packBytes, unpackBytes],
|
|
9103
|
+
[29, "PASSWORD-ALGORITHM", packBytes, unpackBytes],
|
|
9104
|
+
[30, "USERHASH", packBytes, unpackBytes],
|
|
8630
9105
|
[32, "XOR-MAPPED-ADDRESS", packXorAddress, unpackXorAddress],
|
|
9106
|
+
[34, "RESERVATION-TOKEN", packBytes, unpackBytes],
|
|
8631
9107
|
[36, "PRIORITY", packUnsigned, unpackUnsigned],
|
|
8632
9108
|
[37, "USE-CANDIDATE", packNone, unpackNone],
|
|
8633
|
-
[
|
|
9109
|
+
[32770, "PASSWORD-ALGORITHMS", packBytes, unpackBytes],
|
|
9110
|
+
[32771, "ALTERNATE-DOMAIN", packString, unpackString],
|
|
9111
|
+
[32802, "SOFTWARE", packSoftware, unpackString],
|
|
9112
|
+
[32803, "ALTERNATE-SERVER", packAddress, unpackAddress],
|
|
8634
9113
|
[32808, "FINGERPRINT", packUnsigned, unpackUnsigned],
|
|
8635
9114
|
[32809, "ICE-CONTROLLED", packUnsigned64, unpackUnsigned64],
|
|
8636
9115
|
[32810, "ICE-CONTROLLING", packUnsigned64, unpackUnsigned64],
|
|
@@ -8655,12 +9134,14 @@ var ATTRIBUTES_BY_NAME = ATTRIBUTES.reduce(
|
|
|
8655
9134
|
// ../ice/src/stun/transaction.ts
|
|
8656
9135
|
var log20 = debug("werift-ice:packages/ice/src/stun/transaction.ts");
|
|
8657
9136
|
|
|
9137
|
+
// ../ice/src/stun/tcpProtocol.ts
|
|
9138
|
+
var log21 = debug("werift-ice:packages/ice/src/stun/tcpProtocol.ts");
|
|
9139
|
+
|
|
8658
9140
|
// ../ice/src/stun/protocol.ts
|
|
8659
|
-
var
|
|
9141
|
+
var log22 = debug("werift-ice : packages/ice/src/stun/protocol.ts");
|
|
8660
9142
|
|
|
8661
9143
|
// ../ice/src/turn/protocol.ts
|
|
8662
|
-
|
|
8663
|
-
var log22 = debug("werift-ice:packages/ice/src/turn/protocol.ts");
|
|
9144
|
+
var log23 = debug("werift-ice:packages/ice/src/turn/protocol.ts");
|
|
8664
9145
|
|
|
8665
9146
|
// ../ice/src/ice.ts
|
|
8666
9147
|
import * as Int642 from "int64-buffer";
|
|
@@ -8670,28 +9151,58 @@ import isEqual from "fast-deep-equal";
|
|
|
8670
9151
|
import mdns from "multicast-dns";
|
|
8671
9152
|
|
|
8672
9153
|
// ../ice/src/iceBase.ts
|
|
8673
|
-
var
|
|
9154
|
+
var log24 = debug("werift-ice : packages/ice/src/ice.ts : log");
|
|
8674
9155
|
|
|
8675
9156
|
// ../ice/src/utils.ts
|
|
8676
9157
|
import nodeIp2 from "ip";
|
|
8677
9158
|
var logger = debug("werift-ice : packages/ice/src/utils.ts");
|
|
8678
9159
|
|
|
8679
9160
|
// ../ice/src/ice.ts
|
|
8680
|
-
var
|
|
9161
|
+
var log25 = debug("werift-ice : packages/ice/src/ice.ts : log");
|
|
8681
9162
|
|
|
8682
9163
|
// src/helper.ts
|
|
8683
9164
|
import { EventEmitter } from "node:events";
|
|
9165
|
+
function divide(from, split) {
|
|
9166
|
+
const arr = from.split(split);
|
|
9167
|
+
return [arr[0], arr.slice(1).join(split)];
|
|
9168
|
+
}
|
|
8684
9169
|
var EventTarget2 = class extends EventEmitter {
|
|
8685
|
-
|
|
9170
|
+
emit(type, ...args) {
|
|
9171
|
+
if (typeof type !== "string") {
|
|
9172
|
+
return super.emit(type, ...args);
|
|
9173
|
+
}
|
|
9174
|
+
if (args.length === 0) {
|
|
9175
|
+
return super.emit(type, new Event(type));
|
|
9176
|
+
}
|
|
9177
|
+
const [event, ...rest] = args;
|
|
9178
|
+
if (event && typeof event === "object" && !("type" in event)) {
|
|
9179
|
+
try {
|
|
9180
|
+
Object.defineProperty(event, "type", {
|
|
9181
|
+
configurable: true,
|
|
9182
|
+
enumerable: true,
|
|
9183
|
+
value: type
|
|
9184
|
+
});
|
|
9185
|
+
} catch {
|
|
9186
|
+
return super.emit(type, { type, ...event }, ...rest);
|
|
9187
|
+
}
|
|
9188
|
+
}
|
|
9189
|
+
return super.emit(type, event, ...rest);
|
|
9190
|
+
}
|
|
9191
|
+
addEventListener = (type, listener, options) => {
|
|
9192
|
+
if (typeof options === "object" && options?.once) {
|
|
9193
|
+
this.once(type, listener);
|
|
9194
|
+
return;
|
|
9195
|
+
}
|
|
8686
9196
|
this.addListener(type, listener);
|
|
8687
9197
|
};
|
|
8688
9198
|
removeEventListener = (type, listener) => {
|
|
8689
9199
|
this.removeListener(type, listener);
|
|
8690
9200
|
};
|
|
9201
|
+
dispatchEvent = (event) => this.emit(event.type, event);
|
|
8691
9202
|
};
|
|
8692
9203
|
|
|
8693
9204
|
// src/dataChannel.ts
|
|
8694
|
-
var
|
|
9205
|
+
var log26 = debug("werift:packages/webrtc/src/dataChannel.ts");
|
|
8695
9206
|
|
|
8696
9207
|
// src/media/extension/rtcpFeedback.ts
|
|
8697
9208
|
var useNACK = () => ({ type: "nack" });
|
|
@@ -8794,6 +9305,15 @@ var DTLS_SETUP_ROLE = Object.keys(
|
|
|
8794
9305
|
acc[key] = cur;
|
|
8795
9306
|
return acc;
|
|
8796
9307
|
}, {});
|
|
9308
|
+
var FMTP_INT_PARAMETERS = [
|
|
9309
|
+
"apt",
|
|
9310
|
+
"max-fr",
|
|
9311
|
+
"max-fs",
|
|
9312
|
+
"maxplaybackrate",
|
|
9313
|
+
"minptime",
|
|
9314
|
+
"stereo",
|
|
9315
|
+
"useinbandfec"
|
|
9316
|
+
];
|
|
8797
9317
|
var SRTP_PROFILE = {
|
|
8798
9318
|
SRTP_AES128_CM_HMAC_SHA1_80: ProtectionProfileAes128CmHmacSha1_80,
|
|
8799
9319
|
SRTP_AEAD_AES_128_GCM: ProtectionProfileAeadAes128Gcm
|
|
@@ -8816,9 +9336,9 @@ var MediaStreamTrack = class extends EventTarget2 {
|
|
|
8816
9336
|
codec;
|
|
8817
9337
|
/**todo impl */
|
|
8818
9338
|
enabled = true;
|
|
8819
|
-
onReceiveRtp = new
|
|
8820
|
-
onReceiveRtcp = new
|
|
8821
|
-
onSourceChanged = new
|
|
9339
|
+
onReceiveRtp = new Event2();
|
|
9340
|
+
onReceiveRtcp = new Event2();
|
|
9341
|
+
onSourceChanged = new Event2();
|
|
8822
9342
|
stopped = false;
|
|
8823
9343
|
muted = true;
|
|
8824
9344
|
constructor(props) {
|
|
@@ -8851,7 +9371,7 @@ var MediaStreamTrack = class extends EventTarget2 {
|
|
|
8851
9371
|
var MediaStream = class {
|
|
8852
9372
|
id;
|
|
8853
9373
|
tracks = [];
|
|
8854
|
-
constructor(props) {
|
|
9374
|
+
constructor(props = {}) {
|
|
8855
9375
|
if (Array.isArray(props)) {
|
|
8856
9376
|
this.tracks = props;
|
|
8857
9377
|
} else {
|
|
@@ -8863,6 +9383,12 @@ var MediaStream = class {
|
|
|
8863
9383
|
track.streamId = this.id;
|
|
8864
9384
|
this.tracks.push(track);
|
|
8865
9385
|
}
|
|
9386
|
+
removeTrack(track) {
|
|
9387
|
+
this.tracks = this.tracks.filter((currentTrack) => currentTrack !== track);
|
|
9388
|
+
if (track.streamId === this.id) {
|
|
9389
|
+
track.streamId = void 0;
|
|
9390
|
+
}
|
|
9391
|
+
}
|
|
8866
9392
|
getTracks() {
|
|
8867
9393
|
return this.tracks;
|
|
8868
9394
|
}
|
|
@@ -8875,33 +9401,33 @@ var MediaStream = class {
|
|
|
8875
9401
|
};
|
|
8876
9402
|
|
|
8877
9403
|
// src/utils.ts
|
|
8878
|
-
var
|
|
9404
|
+
var log27 = debug("werift:packages/webrtc/src/utils.ts");
|
|
8879
9405
|
var milliTime = Date.now;
|
|
8880
9406
|
var startupTimestampInMicroseconds = BigInt(Date.now()) * 1000n - process.hrtime.bigint() / 1000n;
|
|
8881
9407
|
var createSelfSignedCertificate = CipherContext.createSelfSignedCertificateWithKey;
|
|
8882
9408
|
|
|
8883
9409
|
// src/media/receiver/nack.ts
|
|
8884
|
-
var
|
|
9410
|
+
var log28 = debug("werift:packages/webrtc/src/media/receiver/nack.ts");
|
|
8885
9411
|
var LOST_SIZE2 = 30 * 5;
|
|
8886
9412
|
|
|
8887
9413
|
// src/media/receiver/receiverTwcc.ts
|
|
8888
|
-
var
|
|
9414
|
+
var log29 = debug("werift:packages/webrtc/media/receiver/receiverTwcc");
|
|
8889
9415
|
|
|
8890
9416
|
// src/sdp.ts
|
|
8891
9417
|
import * as Int643 from "int64-buffer";
|
|
8892
9418
|
|
|
8893
9419
|
// src/transport/dtls.ts
|
|
8894
9420
|
import { Certificate as Certificate3, PrivateKey as PrivateKey2 } from "@fidm/x509";
|
|
8895
|
-
var
|
|
9421
|
+
var log30 = debug("werift:packages/webrtc/src/transport/dtls.ts");
|
|
8896
9422
|
|
|
8897
9423
|
// src/transport/ice.ts
|
|
8898
|
-
var
|
|
9424
|
+
var log31 = debug("werift:packages/webrtc/src/transport/ice.ts");
|
|
8899
9425
|
|
|
8900
9426
|
// src/transport/sctp.ts
|
|
8901
|
-
import { jspack as
|
|
9427
|
+
import { jspack as jspack5 } from "@shinyoshiaki/jspack";
|
|
8902
9428
|
|
|
8903
9429
|
// ../sctp/src/sctp.ts
|
|
8904
|
-
import { jspack as
|
|
9430
|
+
import { jspack as jspack4 } from "@shinyoshiaki/jspack";
|
|
8905
9431
|
|
|
8906
9432
|
// ../sctp/src/chunk.ts
|
|
8907
9433
|
var Chunk = class _Chunk {
|
|
@@ -9291,32 +9817,54 @@ function decodeParams(body) {
|
|
|
9291
9817
|
}
|
|
9292
9818
|
|
|
9293
9819
|
// ../sctp/src/param.ts
|
|
9294
|
-
import { jspack as
|
|
9820
|
+
import { jspack as jspack3 } from "@shinyoshiaki/jspack";
|
|
9295
9821
|
|
|
9296
9822
|
// ../sctp/src/sctp.ts
|
|
9297
|
-
var
|
|
9823
|
+
var log32 = debug("werift/sctp/sctp");
|
|
9298
9824
|
var SCTP_RTO_ALPHA = 1 / 8;
|
|
9299
9825
|
var SCTP_RTO_BETA = 1 / 4;
|
|
9300
9826
|
var SCTP_TSN_MODULO = 2 ** 32;
|
|
9301
9827
|
|
|
9302
9828
|
// src/transport/sctp.ts
|
|
9303
|
-
var
|
|
9829
|
+
var log33 = debug("werift:packages/webrtc/src/transport/sctp.ts");
|
|
9830
|
+
var DEFAULT_MAX_MESSAGE_SIZE = 65536;
|
|
9831
|
+
|
|
9832
|
+
// src/sdp.ts
|
|
9833
|
+
function codecParametersFromString(str) {
|
|
9834
|
+
const parameters = {};
|
|
9835
|
+
str.split(";").forEach((param) => {
|
|
9836
|
+
if (param.includes("=")) {
|
|
9837
|
+
const [k, v] = divide(param, "=");
|
|
9838
|
+
if (FMTP_INT_PARAMETERS.includes(k)) {
|
|
9839
|
+
parameters[k] = Number(v);
|
|
9840
|
+
} else {
|
|
9841
|
+
parameters[k] = v;
|
|
9842
|
+
}
|
|
9843
|
+
} else if (param.includes(":")) {
|
|
9844
|
+
const [k, v] = param.split(":");
|
|
9845
|
+
parameters[k] = Number.isNaN(Number(v)) ? v : Number(v);
|
|
9846
|
+
} else {
|
|
9847
|
+
parameters[param] = void 0;
|
|
9848
|
+
}
|
|
9849
|
+
});
|
|
9850
|
+
return parameters;
|
|
9851
|
+
}
|
|
9304
9852
|
|
|
9305
9853
|
// src/media/rtpReceiver.ts
|
|
9306
|
-
var
|
|
9854
|
+
var log34 = debug("werift:packages/webrtc/src/media/rtpReceiver.ts");
|
|
9307
9855
|
|
|
9308
9856
|
// src/media/router.ts
|
|
9309
|
-
var
|
|
9857
|
+
var log35 = debug("werift:packages/webrtc/src/media/router.ts");
|
|
9310
9858
|
|
|
9311
9859
|
// src/media/rtpSender.ts
|
|
9312
|
-
import { jspack as
|
|
9313
|
-
var
|
|
9860
|
+
import { jspack as jspack6 } from "@shinyoshiaki/jspack";
|
|
9861
|
+
var log36 = debug("werift:packages/webrtc/src/media/rtpSender.ts");
|
|
9314
9862
|
|
|
9315
9863
|
// src/sctpManager.ts
|
|
9316
|
-
var
|
|
9864
|
+
var log37 = debug("werift:packages/webrtc/src/transport/sctpManager.ts");
|
|
9317
9865
|
|
|
9318
9866
|
// src/secureTransportManager.ts
|
|
9319
|
-
var
|
|
9867
|
+
var log38 = debug(
|
|
9320
9868
|
"werift:packages/webrtc/src/transport/secureTransportManager.ts"
|
|
9321
9869
|
);
|
|
9322
9870
|
var srtpProfiles = [
|
|
@@ -9326,7 +9874,7 @@ var srtpProfiles = [
|
|
|
9326
9874
|
];
|
|
9327
9875
|
|
|
9328
9876
|
// src/peerConnection.ts
|
|
9329
|
-
var
|
|
9877
|
+
var log39 = debug("werift:packages/webrtc/src/peerConnection.ts");
|
|
9330
9878
|
function generateDefaultPeerConfig() {
|
|
9331
9879
|
return {
|
|
9332
9880
|
codecs: {
|
|
@@ -9338,27 +9886,157 @@ function generateDefaultPeerConfig() {
|
|
|
9338
9886
|
video: []
|
|
9339
9887
|
},
|
|
9340
9888
|
iceTransportPolicy: "all",
|
|
9889
|
+
iceLite: false,
|
|
9341
9890
|
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
|
9342
9891
|
icePortRange: void 0,
|
|
9343
9892
|
iceInterfaceAddresses: void 0,
|
|
9344
9893
|
iceAdditionalHostAddresses: void 0,
|
|
9345
9894
|
iceUseIpv4: true,
|
|
9346
9895
|
iceUseIpv6: true,
|
|
9896
|
+
iceUseTcp: false,
|
|
9897
|
+
turnTransport: void 0,
|
|
9898
|
+
turnTlsOptions: void 0,
|
|
9347
9899
|
iceFilterStunResponse: void 0,
|
|
9348
9900
|
iceFilterCandidatePair: void 0,
|
|
9349
9901
|
icePasswordPrefix: void 0,
|
|
9350
9902
|
iceUseLinkLocalAddress: void 0,
|
|
9351
9903
|
dtls: {},
|
|
9352
9904
|
bundlePolicy: "max-compat",
|
|
9905
|
+
rtcpMuxPolicy: "require",
|
|
9906
|
+
iceCandidatePoolSize: 0,
|
|
9907
|
+
certificates: [],
|
|
9353
9908
|
debug: {},
|
|
9354
9909
|
midSuffix: false,
|
|
9355
|
-
forceTurnTCP: false
|
|
9910
|
+
forceTurnTCP: false,
|
|
9911
|
+
maxMessageSize: DEFAULT_MAX_MESSAGE_SIZE
|
|
9356
9912
|
};
|
|
9357
9913
|
}
|
|
9358
9914
|
var defaultPeerConfig = generateDefaultPeerConfig();
|
|
9359
9915
|
|
|
9360
9916
|
// src/transceiverManager.ts
|
|
9361
|
-
var
|
|
9917
|
+
var log40 = debug("werift:packages/webrtc/src/media/rtpTransceiverManager.ts");
|
|
9918
|
+
|
|
9919
|
+
// src/nonstandard/dummyMedia.ts
|
|
9920
|
+
var AUDIO_PACKET_MS = 20;
|
|
9921
|
+
var AUDIO_TIMESTAMP_STEP = 960;
|
|
9922
|
+
var AUDIO_PAYLOAD = Buffer.from([248, 255, 254]);
|
|
9923
|
+
var DEFAULT_VIDEO_FPS = 30;
|
|
9924
|
+
var VIDEO_TIMESTAMP_STEP = 3e3;
|
|
9925
|
+
var DUMMY_SSRC_BASE = 439041024;
|
|
9926
|
+
var DUMMY_SEQUENCE_BASE = 16384;
|
|
9927
|
+
var DUMMY_SEQUENCE_STEP = 256;
|
|
9928
|
+
var nextDummySourceId = 0;
|
|
9929
|
+
var VP8_KEYFRAME = Buffer.from([
|
|
9930
|
+
16,
|
|
9931
|
+
16,
|
|
9932
|
+
0,
|
|
9933
|
+
0,
|
|
9934
|
+
157,
|
|
9935
|
+
1,
|
|
9936
|
+
42,
|
|
9937
|
+
2,
|
|
9938
|
+
0,
|
|
9939
|
+
2,
|
|
9940
|
+
0
|
|
9941
|
+
]);
|
|
9942
|
+
var VP8_DELTA_FRAME = Buffer.from([16, 17, 0, 0]);
|
|
9943
|
+
function createDummyAudioTrack(options = {}) {
|
|
9944
|
+
const track = new MediaStreamTrack({ kind: "audio" });
|
|
9945
|
+
const source = new ScheduledRtpSource({
|
|
9946
|
+
track,
|
|
9947
|
+
intervalMs: options.packetIntervalMs ?? AUDIO_PACKET_MS,
|
|
9948
|
+
buildPacket: ({ sequenceNumber, timestamp, ssrc }) => new RtpPacket(
|
|
9949
|
+
new RtpHeader({
|
|
9950
|
+
version: 2,
|
|
9951
|
+
payloadType: 111,
|
|
9952
|
+
sequenceNumber,
|
|
9953
|
+
timestamp,
|
|
9954
|
+
ssrc,
|
|
9955
|
+
marker: true
|
|
9956
|
+
}),
|
|
9957
|
+
Buffer.from(AUDIO_PAYLOAD)
|
|
9958
|
+
),
|
|
9959
|
+
nextTimestamp: (timestamp) => timestamp + AUDIO_TIMESTAMP_STEP
|
|
9960
|
+
});
|
|
9961
|
+
source.start();
|
|
9962
|
+
return { track, source };
|
|
9963
|
+
}
|
|
9964
|
+
function createDummyVideoTrack(options = {}) {
|
|
9965
|
+
const fps = options.fps ?? DEFAULT_VIDEO_FPS;
|
|
9966
|
+
const keyframeIntervalFrames = options.keyframeIntervalFrames ?? fps;
|
|
9967
|
+
const intervalMs = 1e3 / fps;
|
|
9968
|
+
const track = new MediaStreamTrack({ kind: "video" });
|
|
9969
|
+
const source = new ScheduledRtpSource({
|
|
9970
|
+
track,
|
|
9971
|
+
intervalMs,
|
|
9972
|
+
buildPacket: ({ sequenceNumber, timestamp, ssrc, iteration }) => {
|
|
9973
|
+
const isKeyframe = iteration % keyframeIntervalFrames === 0;
|
|
9974
|
+
return new RtpPacket(
|
|
9975
|
+
new RtpHeader({
|
|
9976
|
+
version: 2,
|
|
9977
|
+
payloadType: 96,
|
|
9978
|
+
sequenceNumber,
|
|
9979
|
+
timestamp,
|
|
9980
|
+
ssrc,
|
|
9981
|
+
marker: true
|
|
9982
|
+
}),
|
|
9983
|
+
Buffer.from(isKeyframe ? VP8_KEYFRAME : VP8_DELTA_FRAME)
|
|
9984
|
+
);
|
|
9985
|
+
},
|
|
9986
|
+
nextTimestamp: (timestamp) => timestamp + VIDEO_TIMESTAMP_STEP
|
|
9987
|
+
});
|
|
9988
|
+
source.start();
|
|
9989
|
+
return { track, source };
|
|
9990
|
+
}
|
|
9991
|
+
var ScheduledRtpSource = class {
|
|
9992
|
+
constructor(props) {
|
|
9993
|
+
this.props = props;
|
|
9994
|
+
}
|
|
9995
|
+
sourceId = nextDummySourceId++;
|
|
9996
|
+
ssrc = DUMMY_SSRC_BASE + this.sourceId >>> 0;
|
|
9997
|
+
sequenceNumber = DUMMY_SEQUENCE_BASE + this.sourceId * DUMMY_SEQUENCE_STEP & 65535;
|
|
9998
|
+
timestamp = 0;
|
|
9999
|
+
iteration = 0;
|
|
10000
|
+
nextTickAt;
|
|
10001
|
+
timer;
|
|
10002
|
+
stopped = false;
|
|
10003
|
+
start() {
|
|
10004
|
+
this.nextTickAt = performance.now();
|
|
10005
|
+
this.schedule();
|
|
10006
|
+
}
|
|
10007
|
+
stop() {
|
|
10008
|
+
this.stopped = true;
|
|
10009
|
+
if (this.timer) {
|
|
10010
|
+
clearTimeout(this.timer);
|
|
10011
|
+
this.timer = void 0;
|
|
10012
|
+
}
|
|
10013
|
+
}
|
|
10014
|
+
schedule() {
|
|
10015
|
+
if (this.stopped || this.props.track.stopped) {
|
|
10016
|
+
this.stop();
|
|
10017
|
+
return;
|
|
10018
|
+
}
|
|
10019
|
+
this.nextTickAt = (this.nextTickAt ?? performance.now()) + this.props.intervalMs;
|
|
10020
|
+
const delay = Math.max(0, this.nextTickAt - performance.now());
|
|
10021
|
+
this.timer = setTimeout(() => {
|
|
10022
|
+
if (this.stopped || this.props.track.stopped) {
|
|
10023
|
+
this.stop();
|
|
10024
|
+
return;
|
|
10025
|
+
}
|
|
10026
|
+
const packet = this.props.buildPacket({
|
|
10027
|
+
sequenceNumber: this.sequenceNumber,
|
|
10028
|
+
timestamp: this.timestamp,
|
|
10029
|
+
ssrc: this.ssrc,
|
|
10030
|
+
iteration: this.iteration
|
|
10031
|
+
});
|
|
10032
|
+
this.props.track.writeRtp(packet);
|
|
10033
|
+
this.sequenceNumber = this.sequenceNumber + 1 & 65535;
|
|
10034
|
+
this.timestamp = this.props.nextTimestamp(this.timestamp) >>> 0;
|
|
10035
|
+
this.iteration += 1;
|
|
10036
|
+
this.schedule();
|
|
10037
|
+
}, delay);
|
|
10038
|
+
}
|
|
10039
|
+
};
|
|
9362
10040
|
|
|
9363
10041
|
// src/nonstandard/navigator.ts
|
|
9364
10042
|
var Navigator = class {
|
|
@@ -9368,7 +10046,7 @@ var Navigator = class {
|
|
|
9368
10046
|
}
|
|
9369
10047
|
};
|
|
9370
10048
|
var MediaDevices = class extends EventTarget {
|
|
9371
|
-
constructor(props) {
|
|
10049
|
+
constructor(props = {}) {
|
|
9372
10050
|
super();
|
|
9373
10051
|
this.props = props;
|
|
9374
10052
|
this.video = props.video;
|
|
@@ -9376,31 +10054,16 @@ var MediaDevices = class extends EventTarget {
|
|
|
9376
10054
|
}
|
|
9377
10055
|
video;
|
|
9378
10056
|
audio;
|
|
10057
|
+
activeSourceStops = /* @__PURE__ */ new Map();
|
|
9379
10058
|
getUserMedia = async (constraints) => {
|
|
9380
|
-
const
|
|
9381
|
-
|
|
9382
|
-
this.video
|
|
9383
|
-
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
});
|
|
9387
|
-
}
|
|
9388
|
-
const audio = constraints.audio ? new MediaStreamTrack({ kind: "audio" }) : void 0;
|
|
9389
|
-
if (audio) {
|
|
9390
|
-
this.audio?.onReceiveRtp.subscribe((rtp) => {
|
|
9391
|
-
const cloned = rtp.clone();
|
|
9392
|
-
cloned.header.ssrc = jspack8.Unpack("!L", randomBytes5(4))[0];
|
|
9393
|
-
audio.onReceiveRtp.execute(cloned);
|
|
9394
|
-
});
|
|
9395
|
-
}
|
|
9396
|
-
if (constraints.video && constraints.audio) {
|
|
9397
|
-
return new MediaStream([video, audio]);
|
|
9398
|
-
} else if (constraints.audio) {
|
|
9399
|
-
return new MediaStream([audio]);
|
|
9400
|
-
} else if (constraints.video) {
|
|
9401
|
-
return new MediaStream([video]);
|
|
10059
|
+
const tracks = [
|
|
10060
|
+
...constraints.audio ? [this.createTrack("audio", constraints.audio)] : [],
|
|
10061
|
+
...constraints.video ? [this.createTrack("video", constraints.video)] : []
|
|
10062
|
+
];
|
|
10063
|
+
if (tracks.length === 0) {
|
|
10064
|
+
throw new Error("At least one audio or video track is required");
|
|
9402
10065
|
}
|
|
9403
|
-
|
|
10066
|
+
return new MediaStream(tracks);
|
|
9404
10067
|
};
|
|
9405
10068
|
getDisplayMedia = this.getUserMedia;
|
|
9406
10069
|
getUdpMedia = ({
|
|
@@ -9422,8 +10085,83 @@ var MediaDevices = class extends EventTarget {
|
|
|
9422
10085
|
};
|
|
9423
10086
|
return { track, disposer };
|
|
9424
10087
|
};
|
|
10088
|
+
cleanup() {
|
|
10089
|
+
for (const stop of this.activeSourceStops.values()) {
|
|
10090
|
+
stop();
|
|
10091
|
+
}
|
|
10092
|
+
this.activeSourceStops.clear();
|
|
10093
|
+
}
|
|
10094
|
+
createTrack(kind, constraints) {
|
|
10095
|
+
const existingTrack = kind === "audio" ? this.audio : this.video;
|
|
10096
|
+
if (existingTrack) {
|
|
10097
|
+
return this.createClonedTrack(kind, existingTrack);
|
|
10098
|
+
}
|
|
10099
|
+
if (this.props.dummyMedia?.enabled) {
|
|
10100
|
+
return this.createDummyTrack(kind, constraints);
|
|
10101
|
+
}
|
|
10102
|
+
throw new Error(`No ${kind} source configured for getUserMedia`);
|
|
10103
|
+
}
|
|
10104
|
+
createClonedTrack(kind, sourceTrack) {
|
|
10105
|
+
const track = new MediaStreamTrack({ kind });
|
|
10106
|
+
const { unSubscribe } = sourceTrack.onReceiveRtp.subscribe((rtp) => {
|
|
10107
|
+
track.onReceiveRtp.execute(rtp.clone());
|
|
10108
|
+
});
|
|
10109
|
+
this.attachTrackStop(track, () => {
|
|
10110
|
+
unSubscribe();
|
|
10111
|
+
});
|
|
10112
|
+
return track;
|
|
10113
|
+
}
|
|
10114
|
+
createDummyTrack(kind, constraints) {
|
|
10115
|
+
const dummy = kind === "audio" ? createDummyAudioTrack(this.props.dummyMedia?.audio) : createDummyVideoTrack({
|
|
10116
|
+
...this.props.dummyMedia?.video,
|
|
10117
|
+
...resolveVideoOptions(constraints)
|
|
10118
|
+
});
|
|
10119
|
+
this.attachTrackStop(dummy.track, () => {
|
|
10120
|
+
dummy.source.stop();
|
|
10121
|
+
});
|
|
10122
|
+
return dummy.track;
|
|
10123
|
+
}
|
|
10124
|
+
attachTrackStop(track, stop) {
|
|
10125
|
+
let disposed = false;
|
|
10126
|
+
const originalStop = track.stop;
|
|
10127
|
+
const dispose = () => {
|
|
10128
|
+
if (disposed) {
|
|
10129
|
+
return;
|
|
10130
|
+
}
|
|
10131
|
+
disposed = true;
|
|
10132
|
+
stop();
|
|
10133
|
+
this.activeSourceStops.delete(track);
|
|
10134
|
+
};
|
|
10135
|
+
track.stop = () => {
|
|
10136
|
+
dispose();
|
|
10137
|
+
originalStop();
|
|
10138
|
+
};
|
|
10139
|
+
this.activeSourceStops.set(track, dispose);
|
|
10140
|
+
}
|
|
9425
10141
|
};
|
|
9426
10142
|
var navigator = new Navigator();
|
|
10143
|
+
function resolveVideoOptions(constraints) {
|
|
10144
|
+
if (typeof constraints === "boolean") {
|
|
10145
|
+
return {};
|
|
10146
|
+
}
|
|
10147
|
+
const fps = resolveConstrainNumber(constraints.frameRate);
|
|
10148
|
+
if (!fps) {
|
|
10149
|
+
return {};
|
|
10150
|
+
}
|
|
10151
|
+
return {
|
|
10152
|
+
fps,
|
|
10153
|
+
keyframeIntervalFrames: Math.max(1, Math.round(fps))
|
|
10154
|
+
};
|
|
10155
|
+
}
|
|
10156
|
+
function resolveConstrainNumber(value) {
|
|
10157
|
+
if (typeof value === "number") {
|
|
10158
|
+
return value;
|
|
10159
|
+
}
|
|
10160
|
+
if (!value || typeof value !== "object") {
|
|
10161
|
+
return;
|
|
10162
|
+
}
|
|
10163
|
+
return value.exact ?? value.ideal ?? value.max ?? value.min;
|
|
10164
|
+
}
|
|
9427
10165
|
|
|
9428
10166
|
// src/nonstandard/recorder/writer/webm.ts
|
|
9429
10167
|
import { unlink } from "fs/promises";
|
|
@@ -9443,7 +10181,7 @@ var MediaWriter = class {
|
|
|
9443
10181
|
var sourcePath = "packages/webrtc/src/nonstandard/recorder/writer/webm.ts";
|
|
9444
10182
|
var WebmFactory = class extends MediaWriter {
|
|
9445
10183
|
rtpSources = [];
|
|
9446
|
-
onEol = new
|
|
10184
|
+
onEol = new Event2();
|
|
9447
10185
|
ended = false;
|
|
9448
10186
|
unSubscribers = new EventDisposer();
|
|
9449
10187
|
async start(tracks) {
|
|
@@ -9605,7 +10343,7 @@ var MediaRecorder = class {
|
|
|
9605
10343
|
ext;
|
|
9606
10344
|
tracks = [];
|
|
9607
10345
|
started = false;
|
|
9608
|
-
onError = new
|
|
10346
|
+
onError = new Event2();
|
|
9609
10347
|
async addTrack(track) {
|
|
9610
10348
|
this.tracks.push(track);
|
|
9611
10349
|
await this.start();
|
|
@@ -9622,113 +10360,891 @@ var MediaRecorder = class {
|
|
|
9622
10360
|
};
|
|
9623
10361
|
|
|
9624
10362
|
// src/nonstandard/userMedia.ts
|
|
9625
|
-
import { exec } from "child_process";
|
|
9626
10363
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
9627
|
-
import {
|
|
9628
|
-
import {
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
}
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
9638
|
-
|
|
9639
|
-
|
|
9640
|
-
|
|
9641
|
-
|
|
9642
|
-
|
|
9643
|
-
|
|
9644
|
-
|
|
10364
|
+
import { homedir } from "os";
|
|
10365
|
+
import { isAbsolute, resolve } from "path";
|
|
10366
|
+
import { Readable } from "stream";
|
|
10367
|
+
import { readFile } from "fs/promises";
|
|
10368
|
+
import {
|
|
10369
|
+
setImmediate as onNextTurn,
|
|
10370
|
+
setTimeout as wait
|
|
10371
|
+
} from "timers/promises";
|
|
10372
|
+
import {
|
|
10373
|
+
BufferSource,
|
|
10374
|
+
EncodedPacketSink,
|
|
10375
|
+
Input,
|
|
10376
|
+
MP4,
|
|
10377
|
+
WEBM
|
|
10378
|
+
} from "mediabunny";
|
|
10379
|
+
|
|
10380
|
+
// src/nonstandard/userMedia/packetizer.ts
|
|
10381
|
+
var DEFAULT_MAX_RTP_PAYLOAD_SIZE = 1200;
|
|
10382
|
+
var supportedSourceCodecs = [
|
|
10383
|
+
"avc",
|
|
10384
|
+
"vp8",
|
|
10385
|
+
"vp9",
|
|
10386
|
+
"av1",
|
|
10387
|
+
"opus"
|
|
10388
|
+
];
|
|
10389
|
+
function toSupportedMimeType(codec) {
|
|
10390
|
+
switch (codec) {
|
|
10391
|
+
case "avc":
|
|
10392
|
+
return "video/h264";
|
|
10393
|
+
case "vp8":
|
|
10394
|
+
return "video/vp8";
|
|
10395
|
+
case "vp9":
|
|
10396
|
+
return "video/vp9";
|
|
10397
|
+
case "av1":
|
|
10398
|
+
return "video/av1x";
|
|
10399
|
+
case "opus":
|
|
10400
|
+
return "audio/opus";
|
|
10401
|
+
}
|
|
10402
|
+
}
|
|
10403
|
+
function createPacketizer({
|
|
10404
|
+
codec,
|
|
10405
|
+
sourceCodec,
|
|
10406
|
+
decoderDescription
|
|
10407
|
+
}) {
|
|
10408
|
+
switch (sourceCodec) {
|
|
10409
|
+
case "avc":
|
|
10410
|
+
return new H264Packetizer(codec, decoderDescription ?? null);
|
|
10411
|
+
case "vp8":
|
|
10412
|
+
return new Vp8Packetizer(codec);
|
|
10413
|
+
case "vp9":
|
|
10414
|
+
return new Vp9Packetizer(codec);
|
|
10415
|
+
case "av1":
|
|
10416
|
+
return new Av1Packetizer(codec);
|
|
10417
|
+
case "opus":
|
|
10418
|
+
return new OpusPacketizer(codec);
|
|
10419
|
+
}
|
|
10420
|
+
}
|
|
10421
|
+
var BasePacketizer = class {
|
|
10422
|
+
constructor(codec, maxPayloadSize = DEFAULT_MAX_RTP_PAYLOAD_SIZE) {
|
|
10423
|
+
this.codec = codec;
|
|
10424
|
+
this.maxPayloadSize = maxPayloadSize;
|
|
10425
|
+
}
|
|
10426
|
+
sequenceNumber = random16();
|
|
10427
|
+
buildPacket(payload, timestamp, marker) {
|
|
10428
|
+
const packet = new RtpPacket(
|
|
10429
|
+
new RtpHeader({
|
|
10430
|
+
payloadType: this.codec.payloadType ?? 96,
|
|
10431
|
+
sequenceNumber: this.sequenceNumber,
|
|
10432
|
+
timestamp,
|
|
10433
|
+
marker
|
|
10434
|
+
}),
|
|
10435
|
+
payload
|
|
10436
|
+
);
|
|
10437
|
+
this.sequenceNumber = this.sequenceNumber + 1 & 65535;
|
|
10438
|
+
return packet;
|
|
10439
|
+
}
|
|
10440
|
+
};
|
|
10441
|
+
var OpusPacketizer = class extends BasePacketizer {
|
|
10442
|
+
packetize(packet, rtpTimestamp) {
|
|
10443
|
+
return [this.buildPacket(toBuffer(packet.data), rtpTimestamp, true)];
|
|
10444
|
+
}
|
|
10445
|
+
};
|
|
10446
|
+
var Vp8Packetizer = class extends BasePacketizer {
|
|
10447
|
+
packetize(packet, rtpTimestamp) {
|
|
10448
|
+
const frame = toBuffer(packet.data);
|
|
10449
|
+
const chunkSize = this.maxPayloadSize - 1;
|
|
10450
|
+
if (chunkSize <= 0) {
|
|
10451
|
+
throw new Error("invalid VP8 RTP payload size");
|
|
10452
|
+
}
|
|
10453
|
+
const packets = [];
|
|
10454
|
+
for (let offset = 0; offset < frame.length; offset += chunkSize) {
|
|
10455
|
+
const chunk = frame.subarray(
|
|
10456
|
+
offset,
|
|
10457
|
+
Math.min(frame.length, offset + chunkSize)
|
|
10458
|
+
);
|
|
10459
|
+
const descriptor = Buffer.from([offset === 0 ? 16 : 0]);
|
|
10460
|
+
packets.push(
|
|
10461
|
+
this.buildPacket(
|
|
10462
|
+
Buffer.concat([descriptor, chunk]),
|
|
10463
|
+
rtpTimestamp,
|
|
10464
|
+
offset + chunk.length >= frame.length
|
|
10465
|
+
)
|
|
10466
|
+
);
|
|
10467
|
+
}
|
|
10468
|
+
return packets;
|
|
10469
|
+
}
|
|
10470
|
+
};
|
|
10471
|
+
var Vp9Packetizer = class extends BasePacketizer {
|
|
10472
|
+
packetize(packet, rtpTimestamp) {
|
|
10473
|
+
const frame = toBuffer(packet.data);
|
|
10474
|
+
const chunkSize = this.maxPayloadSize - 1;
|
|
10475
|
+
if (chunkSize <= 0) {
|
|
10476
|
+
throw new Error("invalid VP9 RTP payload size");
|
|
10477
|
+
}
|
|
10478
|
+
const packets = [];
|
|
10479
|
+
for (let offset = 0; offset < frame.length; offset += chunkSize) {
|
|
10480
|
+
const chunk = frame.subarray(
|
|
10481
|
+
offset,
|
|
10482
|
+
Math.min(frame.length, offset + chunkSize)
|
|
10483
|
+
);
|
|
10484
|
+
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;
|
|
10485
|
+
packets.push(
|
|
10486
|
+
this.buildPacket(
|
|
10487
|
+
Buffer.concat([descriptor, chunk]),
|
|
10488
|
+
rtpTimestamp,
|
|
10489
|
+
offset + chunk.length >= frame.length
|
|
10490
|
+
)
|
|
10491
|
+
);
|
|
10492
|
+
}
|
|
10493
|
+
return packets;
|
|
10494
|
+
}
|
|
10495
|
+
};
|
|
10496
|
+
var Av1Packetizer = class extends BasePacketizer {
|
|
10497
|
+
packetize(packet, rtpTimestamp) {
|
|
10498
|
+
const obus = splitAv1Obus(toBuffer(packet.data));
|
|
10499
|
+
const packets = [];
|
|
10500
|
+
const fragmentSize = this.maxPayloadSize - 1;
|
|
10501
|
+
if (fragmentSize <= 0) {
|
|
10502
|
+
throw new Error("invalid AV1 RTP payload size");
|
|
10503
|
+
}
|
|
10504
|
+
let firstPacketInFrame = true;
|
|
10505
|
+
const startsNewCodedVideoSequence = packet.type === "key";
|
|
10506
|
+
for (const obu of obus) {
|
|
10507
|
+
if (obu.length <= fragmentSize) {
|
|
10508
|
+
const aggregationHeader = createAv1AggregationHeader({
|
|
10509
|
+
startsWithFragment: false,
|
|
10510
|
+
endsWithFragment: false,
|
|
10511
|
+
startsNewCodedVideoSequence: firstPacketInFrame && startsNewCodedVideoSequence
|
|
10512
|
+
});
|
|
10513
|
+
packets.push(
|
|
10514
|
+
this.buildPacket(
|
|
10515
|
+
Buffer.concat([aggregationHeader, obu]),
|
|
10516
|
+
rtpTimestamp,
|
|
10517
|
+
false
|
|
10518
|
+
)
|
|
10519
|
+
);
|
|
10520
|
+
firstPacketInFrame = false;
|
|
10521
|
+
continue;
|
|
10522
|
+
}
|
|
10523
|
+
for (let offset = 0; offset < obu.length; offset += fragmentSize) {
|
|
10524
|
+
const chunk = obu.subarray(
|
|
10525
|
+
offset,
|
|
10526
|
+
Math.min(obu.length, offset + fragmentSize)
|
|
10527
|
+
);
|
|
10528
|
+
const aggregationHeader = createAv1AggregationHeader({
|
|
10529
|
+
startsWithFragment: offset > 0,
|
|
10530
|
+
endsWithFragment: offset + chunk.length < obu.length,
|
|
10531
|
+
startsNewCodedVideoSequence: firstPacketInFrame && startsNewCodedVideoSequence
|
|
10532
|
+
});
|
|
10533
|
+
packets.push(
|
|
10534
|
+
this.buildPacket(
|
|
10535
|
+
Buffer.concat([aggregationHeader, chunk]),
|
|
10536
|
+
rtpTimestamp,
|
|
10537
|
+
false
|
|
10538
|
+
)
|
|
10539
|
+
);
|
|
10540
|
+
firstPacketInFrame = false;
|
|
10541
|
+
}
|
|
10542
|
+
}
|
|
10543
|
+
if (packets.length === 0) {
|
|
10544
|
+
throw new Error("AV1 sample did not contain any OBU data");
|
|
10545
|
+
}
|
|
10546
|
+
packets.at(-1).header.marker = true;
|
|
10547
|
+
return packets;
|
|
10548
|
+
}
|
|
10549
|
+
};
|
|
10550
|
+
var H264Packetizer = class extends BasePacketizer {
|
|
10551
|
+
naluLengthSize;
|
|
10552
|
+
parameterSets;
|
|
10553
|
+
constructor(codec, decoderDescription) {
|
|
10554
|
+
super(codec);
|
|
10555
|
+
const packetizationMode = getH264PacketizationMode(codec.parameters);
|
|
10556
|
+
if (packetizationMode !== 1) {
|
|
10557
|
+
throw new Error(
|
|
10558
|
+
`Unsupported H264 packetization-mode=${packetizationMode}. File playback requires packetization-mode=1.`
|
|
10559
|
+
);
|
|
10560
|
+
}
|
|
10561
|
+
const avcConfig = parseAvcDecoderConfiguration(decoderDescription);
|
|
10562
|
+
this.naluLengthSize = avcConfig?.naluLengthSize ?? 4;
|
|
10563
|
+
this.parameterSets = avcConfig ? [...avcConfig.sps.map(Buffer.from), ...avcConfig.pps.map(Buffer.from)] : [];
|
|
10564
|
+
}
|
|
10565
|
+
packetize(packet, rtpTimestamp) {
|
|
10566
|
+
let nalUnits = splitH264Sample(toBuffer(packet.data), this.naluLengthSize);
|
|
10567
|
+
if (packet.type === "key" && this.parameterSets.length > 0 && !containsH264ParameterSets(nalUnits)) {
|
|
10568
|
+
nalUnits = [...this.parameterSets, ...nalUnits];
|
|
10569
|
+
}
|
|
10570
|
+
const packets = [];
|
|
10571
|
+
nalUnits.forEach((nalUnit, index) => {
|
|
10572
|
+
const marker = index === nalUnits.length - 1;
|
|
10573
|
+
if (nalUnit.length <= this.maxPayloadSize) {
|
|
10574
|
+
packets.push(this.buildPacket(nalUnit, rtpTimestamp, marker));
|
|
10575
|
+
return;
|
|
10576
|
+
}
|
|
10577
|
+
const nalHeader = nalUnit[0];
|
|
10578
|
+
const fragmentPayload = nalUnit.subarray(1);
|
|
10579
|
+
const fragmentSize = this.maxPayloadSize - 2;
|
|
10580
|
+
if (fragmentSize <= 0) {
|
|
10581
|
+
throw new Error("invalid H264 RTP payload size");
|
|
10582
|
+
}
|
|
10583
|
+
const fuIndicator = nalHeader & 224 | 28;
|
|
10584
|
+
const nalType = nalHeader & 31;
|
|
10585
|
+
for (let offset = 0; offset < fragmentPayload.length; offset += fragmentSize) {
|
|
10586
|
+
const chunk = fragmentPayload.subarray(
|
|
10587
|
+
offset,
|
|
10588
|
+
Math.min(fragmentPayload.length, offset + fragmentSize)
|
|
10589
|
+
);
|
|
10590
|
+
const fuHeader = Buffer.from([
|
|
10591
|
+
(offset === 0 ? 128 : 0) | (offset + chunk.length >= fragmentPayload.length ? 64 : 0) | nalType
|
|
10592
|
+
]);
|
|
10593
|
+
packets.push(
|
|
10594
|
+
this.buildPacket(
|
|
10595
|
+
Buffer.concat([Buffer.from([fuIndicator]), fuHeader, chunk]),
|
|
10596
|
+
rtpTimestamp,
|
|
10597
|
+
marker && offset + chunk.length >= fragmentPayload.length
|
|
10598
|
+
)
|
|
10599
|
+
);
|
|
10600
|
+
}
|
|
9645
10601
|
});
|
|
9646
|
-
|
|
9647
|
-
|
|
9648
|
-
|
|
9649
|
-
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
|
|
9653
|
-
|
|
10602
|
+
return packets;
|
|
10603
|
+
}
|
|
10604
|
+
};
|
|
10605
|
+
function createAv1AggregationHeader({
|
|
10606
|
+
startsWithFragment,
|
|
10607
|
+
endsWithFragment,
|
|
10608
|
+
startsNewCodedVideoSequence
|
|
10609
|
+
}) {
|
|
10610
|
+
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;
|
|
10611
|
+
}
|
|
10612
|
+
function parseAvcDecoderConfiguration(decoderDescription) {
|
|
10613
|
+
if (!decoderDescription) {
|
|
10614
|
+
return null;
|
|
10615
|
+
}
|
|
10616
|
+
const description = toDescriptionBuffer(decoderDescription);
|
|
10617
|
+
if (description.length < 7) {
|
|
10618
|
+
throw new Error("invalid H264 decoder configuration");
|
|
10619
|
+
}
|
|
10620
|
+
const naluLengthSize = (description[4] & 3) + 1;
|
|
10621
|
+
const spsCount = description[5] & 31;
|
|
10622
|
+
let offset = 6;
|
|
10623
|
+
const sps = [];
|
|
10624
|
+
for (let index = 0; index < spsCount; index++) {
|
|
10625
|
+
const length = description.readUInt16BE(offset);
|
|
10626
|
+
offset += 2;
|
|
10627
|
+
sps.push(description.subarray(offset, offset + length));
|
|
10628
|
+
offset += length;
|
|
10629
|
+
}
|
|
10630
|
+
const ppsCount = description[offset] ?? 0;
|
|
10631
|
+
offset++;
|
|
10632
|
+
const pps = [];
|
|
10633
|
+
for (let index = 0; index < ppsCount; index++) {
|
|
10634
|
+
const length = description.readUInt16BE(offset);
|
|
10635
|
+
offset += 2;
|
|
10636
|
+
pps.push(description.subarray(offset, offset + length));
|
|
10637
|
+
offset += length;
|
|
10638
|
+
}
|
|
10639
|
+
return { naluLengthSize, sps, pps };
|
|
10640
|
+
}
|
|
10641
|
+
function splitH264Sample(sample, naluLengthSize) {
|
|
10642
|
+
if (looksLikeAnnexB(sample)) {
|
|
10643
|
+
const nalUnits2 = [];
|
|
10644
|
+
const parser = new H264AnnexBParser(sample);
|
|
10645
|
+
let payload = parser.readNextNaluPayload();
|
|
10646
|
+
while (payload) {
|
|
10647
|
+
nalUnits2.push(Buffer.from(payload.data));
|
|
10648
|
+
payload = parser.readNextNaluPayload();
|
|
10649
|
+
}
|
|
10650
|
+
return nalUnits2;
|
|
10651
|
+
}
|
|
10652
|
+
const nalUnits = [];
|
|
10653
|
+
let offset = 0;
|
|
10654
|
+
while (offset < sample.length) {
|
|
10655
|
+
const length = readUintLength(sample, offset, naluLengthSize);
|
|
10656
|
+
offset += naluLengthSize;
|
|
10657
|
+
const end = offset + length;
|
|
10658
|
+
if (end > sample.length) {
|
|
10659
|
+
throw new Error("invalid H264 sample length");
|
|
10660
|
+
}
|
|
10661
|
+
nalUnits.push(sample.subarray(offset, end));
|
|
10662
|
+
offset = end;
|
|
10663
|
+
}
|
|
10664
|
+
return nalUnits;
|
|
10665
|
+
}
|
|
10666
|
+
function splitAv1Obus(sample) {
|
|
10667
|
+
const obus = [];
|
|
10668
|
+
let offset = 0;
|
|
10669
|
+
while (offset < sample.length) {
|
|
10670
|
+
const start = offset;
|
|
10671
|
+
const header = sample[offset];
|
|
10672
|
+
offset += 1;
|
|
10673
|
+
if (header == void 0) {
|
|
10674
|
+
break;
|
|
10675
|
+
}
|
|
10676
|
+
const extensionFlag = getBit(header, 5);
|
|
10677
|
+
const hasSizeField = getBit(header, 6);
|
|
10678
|
+
if (extensionFlag) {
|
|
10679
|
+
offset += 1;
|
|
10680
|
+
}
|
|
10681
|
+
if (!hasSizeField) {
|
|
10682
|
+
obus.push(sample.subarray(start));
|
|
10683
|
+
break;
|
|
10684
|
+
}
|
|
10685
|
+
const [obuSize, leb128Length] = leb128decode(sample.subarray(offset));
|
|
10686
|
+
offset += leb128Length;
|
|
10687
|
+
const end = offset + obuSize;
|
|
10688
|
+
if (end > sample.length) {
|
|
10689
|
+
throw new Error("invalid AV1 OBU size");
|
|
10690
|
+
}
|
|
10691
|
+
obus.push(sample.subarray(start, end));
|
|
10692
|
+
offset = end;
|
|
10693
|
+
}
|
|
10694
|
+
return obus;
|
|
10695
|
+
}
|
|
10696
|
+
function containsH264ParameterSets(nalUnits) {
|
|
10697
|
+
return nalUnits.some((nalUnit) => {
|
|
10698
|
+
const nalType = nalUnit[0] & 31;
|
|
10699
|
+
return nalType === 7 || nalType === 8;
|
|
10700
|
+
});
|
|
10701
|
+
}
|
|
10702
|
+
function looksLikeAnnexB(sample) {
|
|
10703
|
+
return sample.length >= 4 && sample[0] === 0 && sample[1] === 0 && (sample[2] === 0 && sample[3] === 1 || sample[2] === 1);
|
|
10704
|
+
}
|
|
10705
|
+
function readUintLength(buffer2, offset, length) {
|
|
10706
|
+
switch (length) {
|
|
10707
|
+
case 1:
|
|
10708
|
+
return buffer2.readUInt8(offset);
|
|
10709
|
+
case 2:
|
|
10710
|
+
return buffer2.readUInt16BE(offset);
|
|
10711
|
+
case 3:
|
|
10712
|
+
return buffer2.readUIntBE(offset, 3);
|
|
10713
|
+
case 4:
|
|
10714
|
+
return buffer2.readUInt32BE(offset);
|
|
10715
|
+
default:
|
|
10716
|
+
throw new Error(`unsupported H264 NALU length size: ${length}`);
|
|
10717
|
+
}
|
|
10718
|
+
}
|
|
10719
|
+
function getH264PacketizationMode(parameters) {
|
|
10720
|
+
if (!parameters) {
|
|
10721
|
+
return 1;
|
|
10722
|
+
}
|
|
10723
|
+
const packetizationMode = parameters.split(";").map((part) => part.trim()).find((part) => part.startsWith("packetization-mode="));
|
|
10724
|
+
if (!packetizationMode) {
|
|
10725
|
+
return 1;
|
|
10726
|
+
}
|
|
10727
|
+
return Number(packetizationMode.split("=")[1] ?? 1);
|
|
10728
|
+
}
|
|
10729
|
+
function toDescriptionBuffer(decoderDescription) {
|
|
10730
|
+
if (ArrayBuffer.isView(decoderDescription)) {
|
|
10731
|
+
return Buffer.from(
|
|
10732
|
+
decoderDescription.buffer,
|
|
10733
|
+
decoderDescription.byteOffset,
|
|
10734
|
+
decoderDescription.byteLength
|
|
10735
|
+
);
|
|
10736
|
+
}
|
|
10737
|
+
return Buffer.from(decoderDescription);
|
|
10738
|
+
}
|
|
10739
|
+
function toBuffer(data) {
|
|
10740
|
+
return Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
|
10741
|
+
}
|
|
10742
|
+
|
|
10743
|
+
// src/nonstandard/userMedia.ts
|
|
10744
|
+
var getUserMedia = async (options) => {
|
|
10745
|
+
assertSupportedOptions(options);
|
|
10746
|
+
const replayableSource = await createReplayableSource(options);
|
|
10747
|
+
const input = await createInput(replayableSource);
|
|
10748
|
+
try {
|
|
10749
|
+
if (!await input.canRead()) {
|
|
10750
|
+
throw new Error("Only MP4 and WebM file playback is supported");
|
|
10751
|
+
}
|
|
10752
|
+
const streamId = randomUUID6().toString();
|
|
10753
|
+
const playbackTracks = await selectPlaybackTracks(input, streamId);
|
|
10754
|
+
if (playbackTracks.length === 0) {
|
|
10755
|
+
throw new Error(
|
|
10756
|
+
"The media source does not contain playable audio or video tracks"
|
|
10757
|
+
);
|
|
10758
|
+
}
|
|
10759
|
+
const mediaStartTimestamp = await input.getFirstTimestamp(
|
|
10760
|
+
playbackTracks.map(({ inputTrack }) => inputTrack)
|
|
10761
|
+
);
|
|
10762
|
+
return new MediaPlayer({
|
|
10763
|
+
source: replayableSource,
|
|
10764
|
+
loop: options.loop ?? false,
|
|
10765
|
+
streamId,
|
|
10766
|
+
session: createPlaybackSession({
|
|
10767
|
+
input,
|
|
10768
|
+
mediaStartTimestamp,
|
|
10769
|
+
playbackTracks
|
|
10770
|
+
})
|
|
9654
10771
|
});
|
|
10772
|
+
} catch (error) {
|
|
10773
|
+
input.dispose();
|
|
10774
|
+
throw error;
|
|
9655
10775
|
}
|
|
9656
10776
|
};
|
|
9657
10777
|
var MediaPlayer = class {
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
streamId
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
10778
|
+
audio;
|
|
10779
|
+
video;
|
|
10780
|
+
onError = new Event2();
|
|
10781
|
+
source;
|
|
10782
|
+
loop;
|
|
10783
|
+
streamId;
|
|
10784
|
+
session;
|
|
10785
|
+
running;
|
|
10786
|
+
abortController;
|
|
10787
|
+
hasStartedPlayback = false;
|
|
9667
10788
|
stopped = false;
|
|
9668
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
10789
|
+
constructor({
|
|
10790
|
+
source,
|
|
10791
|
+
loop,
|
|
10792
|
+
streamId,
|
|
10793
|
+
session
|
|
10794
|
+
}) {
|
|
10795
|
+
this.source = source;
|
|
10796
|
+
this.loop = loop;
|
|
10797
|
+
this.streamId = streamId;
|
|
10798
|
+
this.session = session;
|
|
10799
|
+
this.audio = session.playbackTracks.find(
|
|
10800
|
+
({ track }) => track.kind === "audio"
|
|
10801
|
+
)?.track;
|
|
10802
|
+
this.video = session.playbackTracks.find(
|
|
10803
|
+
({ track }) => track.kind === "video"
|
|
10804
|
+
)?.track;
|
|
10805
|
+
}
|
|
10806
|
+
async start() {
|
|
10807
|
+
if (this.stopped) {
|
|
10808
|
+
throw new Error("The media source has already been stopped");
|
|
10809
|
+
}
|
|
10810
|
+
if (this.running) {
|
|
10811
|
+
return;
|
|
10812
|
+
}
|
|
10813
|
+
const abortController = new AbortController();
|
|
10814
|
+
this.abortController = abortController;
|
|
10815
|
+
let startupPending = true;
|
|
10816
|
+
let session;
|
|
10817
|
+
const playback = (async () => {
|
|
10818
|
+
session = await this.getOrCreateSession();
|
|
10819
|
+
if (abortController.signal.aborted || this.stopped) {
|
|
10820
|
+
return;
|
|
10821
|
+
}
|
|
10822
|
+
session.runners.forEach((runner) => runner.assertReady());
|
|
10823
|
+
const initialSourceChanged = this.hasStartedPlayback;
|
|
10824
|
+
this.hasStartedPlayback = true;
|
|
10825
|
+
await this.run(session, abortController, initialSourceChanged);
|
|
10826
|
+
})();
|
|
10827
|
+
const observedPlayback = playback.catch((error) => {
|
|
10828
|
+
if (abortController.signal.aborted) {
|
|
10829
|
+
return;
|
|
10830
|
+
}
|
|
10831
|
+
this.onError.execute(toError(error));
|
|
10832
|
+
if (startupPending) {
|
|
10833
|
+
throw error;
|
|
10834
|
+
}
|
|
9682
10835
|
});
|
|
9683
|
-
|
|
10836
|
+
this.running = observedPlayback.finally(() => {
|
|
10837
|
+
if (this.abortController === abortController) {
|
|
10838
|
+
this.abortController = void 0;
|
|
10839
|
+
}
|
|
10840
|
+
this.running = void 0;
|
|
10841
|
+
this.disposeSession(session);
|
|
10842
|
+
});
|
|
10843
|
+
try {
|
|
10844
|
+
await Promise.race([this.running, onNextTurn()]);
|
|
10845
|
+
} finally {
|
|
10846
|
+
startupPending = false;
|
|
10847
|
+
}
|
|
10848
|
+
}
|
|
9684
10849
|
stop() {
|
|
10850
|
+
if (this.stopped) {
|
|
10851
|
+
return;
|
|
10852
|
+
}
|
|
9685
10853
|
this.stopped = true;
|
|
9686
|
-
this.
|
|
10854
|
+
this.abortController?.abort();
|
|
10855
|
+
if (!this.running) {
|
|
10856
|
+
this.disposeSession();
|
|
10857
|
+
}
|
|
10858
|
+
}
|
|
10859
|
+
async run(session, abortController, initialSourceChanged) {
|
|
10860
|
+
let sourceChanged = initialSourceChanged;
|
|
10861
|
+
do {
|
|
10862
|
+
const startedAt = performance.now();
|
|
10863
|
+
await Promise.all(
|
|
10864
|
+
session.runners.map(
|
|
10865
|
+
(runner) => runner.play({
|
|
10866
|
+
startedAt,
|
|
10867
|
+
signal: abortController.signal,
|
|
10868
|
+
sourceChanged
|
|
10869
|
+
})
|
|
10870
|
+
)
|
|
10871
|
+
);
|
|
10872
|
+
if (!this.loop || abortController.signal.aborted) {
|
|
10873
|
+
break;
|
|
10874
|
+
}
|
|
10875
|
+
sourceChanged = true;
|
|
10876
|
+
} while (!abortController.signal.aborted);
|
|
9687
10877
|
}
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
const
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
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}`;
|
|
9697
|
-
} else {
|
|
9698
|
-
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}`;
|
|
9699
|
-
}
|
|
9700
|
-
console.log(cmd);
|
|
9701
|
-
this.process = exec(cmd);
|
|
9702
|
-
this.process.on("error", (e) => console.error("gst error", e));
|
|
9703
|
-
if (this.props.loop) {
|
|
9704
|
-
await new Promise((r) => this.process.on("close", r));
|
|
9705
|
-
if (!this.stopped) {
|
|
9706
|
-
run();
|
|
9707
|
-
}
|
|
10878
|
+
async getOrCreateSession() {
|
|
10879
|
+
if (this.session) {
|
|
10880
|
+
return this.session;
|
|
10881
|
+
}
|
|
10882
|
+
const input = await createInput(this.source);
|
|
10883
|
+
try {
|
|
10884
|
+
if (!await input.canRead()) {
|
|
10885
|
+
throw new Error("Only MP4 and WebM file playback is supported");
|
|
9708
10886
|
}
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
10887
|
+
const playbackTracks = await selectPlaybackTracks(
|
|
10888
|
+
input,
|
|
10889
|
+
this.streamId,
|
|
10890
|
+
buildExistingTrackMap(this.audio, this.video)
|
|
10891
|
+
);
|
|
10892
|
+
const mediaStartTimestamp = await input.getFirstTimestamp(
|
|
10893
|
+
playbackTracks.map(({ inputTrack }) => inputTrack)
|
|
10894
|
+
);
|
|
10895
|
+
const session = createPlaybackSession({
|
|
10896
|
+
input,
|
|
10897
|
+
mediaStartTimestamp,
|
|
10898
|
+
playbackTracks
|
|
10899
|
+
});
|
|
10900
|
+
this.session = session;
|
|
10901
|
+
return session;
|
|
10902
|
+
} catch (error) {
|
|
10903
|
+
input.dispose();
|
|
10904
|
+
throw error;
|
|
10905
|
+
}
|
|
10906
|
+
}
|
|
10907
|
+
disposeSession(session = this.session) {
|
|
10908
|
+
if (!session) {
|
|
10909
|
+
return;
|
|
10910
|
+
}
|
|
10911
|
+
session.input.dispose();
|
|
10912
|
+
if (this.session === session) {
|
|
10913
|
+
this.session = void 0;
|
|
10914
|
+
}
|
|
9712
10915
|
}
|
|
9713
10916
|
};
|
|
9714
|
-
var
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
10917
|
+
var TrackPlaybackRunner = class {
|
|
10918
|
+
constructor(props) {
|
|
10919
|
+
this.props = props;
|
|
10920
|
+
}
|
|
10921
|
+
async play({
|
|
10922
|
+
startedAt,
|
|
10923
|
+
signal,
|
|
10924
|
+
sourceChanged
|
|
10925
|
+
}) {
|
|
10926
|
+
const codec = this.requireNegotiatedCodec();
|
|
10927
|
+
const packetizer = createPacketizer({
|
|
10928
|
+
codec,
|
|
10929
|
+
sourceCodec: this.props.sourceCodec,
|
|
10930
|
+
decoderDescription: this.props.decoderDescription
|
|
10931
|
+
});
|
|
10932
|
+
const packetSink = new EncodedPacketSink(this.props.inputTrack);
|
|
10933
|
+
let sourceChangeNotified = false;
|
|
10934
|
+
try {
|
|
10935
|
+
for await (const packet of packetSink.packets(void 0, void 0, {
|
|
10936
|
+
verifyKeyPackets: this.props.track.kind === "video"
|
|
10937
|
+
})) {
|
|
10938
|
+
signal.throwIfAborted?.();
|
|
10939
|
+
const dueAtMs = (packet.timestamp - this.props.mediaStartTimestamp) * 1e3;
|
|
10940
|
+
const remainingMs = dueAtMs - (performance.now() - startedAt);
|
|
10941
|
+
if (remainingMs > 0) {
|
|
10942
|
+
await wait(remainingMs, void 0, { signal });
|
|
10943
|
+
}
|
|
10944
|
+
const rtpTimestamp = toRtpTimestamp(
|
|
10945
|
+
packet.timestamp,
|
|
10946
|
+
this.props.mediaStartTimestamp,
|
|
10947
|
+
codec.clockRate
|
|
10948
|
+
);
|
|
10949
|
+
const packets = packetizer.packetize(packet, rtpTimestamp);
|
|
10950
|
+
if (packets.length === 0) {
|
|
10951
|
+
continue;
|
|
10952
|
+
}
|
|
10953
|
+
if (sourceChanged && !sourceChangeNotified) {
|
|
10954
|
+
this.props.track.onSourceChanged.execute(packets[0].header);
|
|
10955
|
+
sourceChangeNotified = true;
|
|
10956
|
+
}
|
|
10957
|
+
for (const rtpPacket of packets) {
|
|
10958
|
+
this.props.track.writeRtp(rtpPacket);
|
|
9725
10959
|
}
|
|
9726
10960
|
}
|
|
9727
|
-
}
|
|
9728
|
-
|
|
9729
|
-
|
|
10961
|
+
} catch (error) {
|
|
10962
|
+
if (signal.aborted || isAbortError(error)) {
|
|
10963
|
+
return;
|
|
10964
|
+
}
|
|
10965
|
+
throw error;
|
|
10966
|
+
}
|
|
9730
10967
|
}
|
|
9731
|
-
|
|
10968
|
+
assertReady() {
|
|
10969
|
+
const codec = this.requireNegotiatedCodec();
|
|
10970
|
+
createPacketizer({
|
|
10971
|
+
codec,
|
|
10972
|
+
sourceCodec: this.props.sourceCodec,
|
|
10973
|
+
decoderDescription: this.props.decoderDescription
|
|
10974
|
+
});
|
|
10975
|
+
}
|
|
10976
|
+
requireNegotiatedCodec() {
|
|
10977
|
+
const codec = this.props.track.codec;
|
|
10978
|
+
if (!codec) {
|
|
10979
|
+
throw new Error(
|
|
10980
|
+
`Cannot start ${this.props.track.kind} playback before the track is attached to an RTCRtpSender`
|
|
10981
|
+
);
|
|
10982
|
+
}
|
|
10983
|
+
const expectedMimeType = toSupportedMimeType(this.props.sourceCodec);
|
|
10984
|
+
const negotiatedMimeType = normalizeMimeType(codec.mimeType);
|
|
10985
|
+
if (negotiatedMimeType !== expectedMimeType) {
|
|
10986
|
+
throw new Error(
|
|
10987
|
+
`Input ${this.props.track.kind} codec ${expectedMimeType} does not match negotiated codec ${codec.mimeType}`
|
|
10988
|
+
);
|
|
10989
|
+
}
|
|
10990
|
+
const expectedClockRate = this.props.track.kind === "audio" ? 48e3 : 9e4;
|
|
10991
|
+
if (codec.clockRate !== expectedClockRate) {
|
|
10992
|
+
throw new Error(
|
|
10993
|
+
`Input ${this.props.track.kind} clock rate ${expectedClockRate} does not match negotiated clock rate ${codec.clockRate}`
|
|
10994
|
+
);
|
|
10995
|
+
}
|
|
10996
|
+
assertCodecParameters({
|
|
10997
|
+
trackKind: this.props.track.kind === "audio" ? "audio" : "video",
|
|
10998
|
+
codec,
|
|
10999
|
+
sourceCodec: this.props.sourceCodec,
|
|
11000
|
+
decoderDescription: this.props.decoderDescription,
|
|
11001
|
+
sourceChannels: this.props.sourceChannels
|
|
11002
|
+
});
|
|
11003
|
+
return codec;
|
|
11004
|
+
}
|
|
11005
|
+
};
|
|
11006
|
+
async function selectPlaybackTracks(input, streamId, existingTracks = {}) {
|
|
11007
|
+
const primaryVideoTrack = await input.getPrimaryVideoTrack();
|
|
11008
|
+
const primaryAudioTrack = primaryVideoTrack ? await primaryVideoTrack.getPrimaryPairableAudioTrack() ?? await input.getPrimaryAudioTrack() : await input.getPrimaryAudioTrack();
|
|
11009
|
+
const playbackTracks = [];
|
|
11010
|
+
if (primaryAudioTrack) {
|
|
11011
|
+
playbackTracks.push(
|
|
11012
|
+
await createPlaybackTrack(
|
|
11013
|
+
primaryAudioTrack,
|
|
11014
|
+
streamId,
|
|
11015
|
+
existingTracks.audio
|
|
11016
|
+
)
|
|
11017
|
+
);
|
|
11018
|
+
}
|
|
11019
|
+
if (primaryVideoTrack) {
|
|
11020
|
+
playbackTracks.push(
|
|
11021
|
+
await createPlaybackTrack(
|
|
11022
|
+
primaryVideoTrack,
|
|
11023
|
+
streamId,
|
|
11024
|
+
existingTracks.video
|
|
11025
|
+
)
|
|
11026
|
+
);
|
|
11027
|
+
}
|
|
11028
|
+
if (!primaryVideoTrack && !primaryAudioTrack) {
|
|
11029
|
+
const fallbackAudioTrack = await input.getPrimaryAudioTrack();
|
|
11030
|
+
if (fallbackAudioTrack) {
|
|
11031
|
+
playbackTracks.push(
|
|
11032
|
+
await createPlaybackTrack(
|
|
11033
|
+
fallbackAudioTrack,
|
|
11034
|
+
streamId,
|
|
11035
|
+
existingTracks.audio
|
|
11036
|
+
)
|
|
11037
|
+
);
|
|
11038
|
+
}
|
|
11039
|
+
}
|
|
11040
|
+
return playbackTracks;
|
|
11041
|
+
}
|
|
11042
|
+
async function createPlaybackTrack(inputTrack, streamId, track) {
|
|
11043
|
+
const sourceCodec = await requireSupportedCodec(inputTrack);
|
|
11044
|
+
const decoderConfig = await inputTrack.getDecoderConfig();
|
|
11045
|
+
const kind = inputTrack.isAudioTrack() ? "audio" : "video";
|
|
11046
|
+
return {
|
|
11047
|
+
inputTrack,
|
|
11048
|
+
sourceCodec,
|
|
11049
|
+
decoderDescription: decoderConfig && "description" in decoderConfig ? decoderConfig.description ?? null : null,
|
|
11050
|
+
sourceChannels: decoderConfig && "numberOfChannels" in decoderConfig ? decoderConfig.numberOfChannels : void 0,
|
|
11051
|
+
track: track ?? new MediaStreamTrack({
|
|
11052
|
+
kind,
|
|
11053
|
+
streamId
|
|
11054
|
+
})
|
|
11055
|
+
};
|
|
11056
|
+
}
|
|
11057
|
+
async function requireSupportedCodec(inputTrack) {
|
|
11058
|
+
const codec = await inputTrack.getCodec();
|
|
11059
|
+
if (codec && supportedSourceCodecs.includes(codec)) {
|
|
11060
|
+
return codec;
|
|
11061
|
+
}
|
|
11062
|
+
const codecLabel = codec ?? await inputTrack.getCodecParameterString() ?? String(await inputTrack.getInternalCodecId() ?? "unknown");
|
|
11063
|
+
throw new Error(
|
|
11064
|
+
`Unsupported ${inputTrack.type} codec "${codecLabel}". File playback supports H264, VP8, VP9, AV1, and Opus only.`
|
|
11065
|
+
);
|
|
11066
|
+
}
|
|
11067
|
+
async function createReplayableSource(options) {
|
|
11068
|
+
if (typeof options.path === "string") {
|
|
11069
|
+
return { path: options.path };
|
|
11070
|
+
}
|
|
11071
|
+
const buffer2 = options.buffer != void 0 ? toBuffer2(options.buffer) : await readStreamToBuffer(options.stream);
|
|
11072
|
+
return { buffer: buffer2 };
|
|
11073
|
+
}
|
|
11074
|
+
async function createInput(options) {
|
|
11075
|
+
if (typeof options.path === "string") {
|
|
11076
|
+
const buffer2 = await readFile(resolveUserMediaPath(options.path));
|
|
11077
|
+
return new Input({
|
|
11078
|
+
source: new BufferSource(buffer2),
|
|
11079
|
+
formats: [MP4, WEBM]
|
|
11080
|
+
});
|
|
11081
|
+
}
|
|
11082
|
+
return new Input({
|
|
11083
|
+
source: new BufferSource(options.buffer),
|
|
11084
|
+
formats: [MP4, WEBM]
|
|
11085
|
+
});
|
|
11086
|
+
}
|
|
11087
|
+
function resolveUserMediaPath(path2) {
|
|
11088
|
+
if (path2 === "~") {
|
|
11089
|
+
return homedir();
|
|
11090
|
+
}
|
|
11091
|
+
if (path2.startsWith("~/")) {
|
|
11092
|
+
return resolve(homedir(), path2.slice(2));
|
|
11093
|
+
}
|
|
11094
|
+
return isAbsolute(path2) ? path2 : resolve(path2);
|
|
11095
|
+
}
|
|
11096
|
+
async function readStreamToBuffer(stream) {
|
|
11097
|
+
const chunks = [];
|
|
11098
|
+
if (stream instanceof Readable) {
|
|
11099
|
+
for await (const chunk of stream) {
|
|
11100
|
+
chunks.push(toBuffer2(chunk));
|
|
11101
|
+
}
|
|
11102
|
+
return Buffer.concat(chunks);
|
|
11103
|
+
}
|
|
11104
|
+
const reader = stream.getReader();
|
|
11105
|
+
try {
|
|
11106
|
+
for (; ; ) {
|
|
11107
|
+
const { done, value } = await reader.read();
|
|
11108
|
+
if (done) {
|
|
11109
|
+
break;
|
|
11110
|
+
}
|
|
11111
|
+
if (value) {
|
|
11112
|
+
chunks.push(
|
|
11113
|
+
Buffer.from(value.buffer, value.byteOffset, value.byteLength)
|
|
11114
|
+
);
|
|
11115
|
+
}
|
|
11116
|
+
}
|
|
11117
|
+
} finally {
|
|
11118
|
+
reader.releaseLock();
|
|
11119
|
+
}
|
|
11120
|
+
return Buffer.concat(chunks);
|
|
11121
|
+
}
|
|
11122
|
+
function toBuffer2(value) {
|
|
11123
|
+
if (Buffer.isBuffer(value)) {
|
|
11124
|
+
return value;
|
|
11125
|
+
}
|
|
11126
|
+
if (typeof value === "string") {
|
|
11127
|
+
return Buffer.from(value);
|
|
11128
|
+
}
|
|
11129
|
+
if (ArrayBuffer.isView(value)) {
|
|
11130
|
+
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
11131
|
+
}
|
|
11132
|
+
return Buffer.from(new Uint8Array(value));
|
|
11133
|
+
}
|
|
11134
|
+
function normalizeMimeType(mimeType) {
|
|
11135
|
+
return mimeType.toLowerCase();
|
|
11136
|
+
}
|
|
11137
|
+
function assertSupportedOptions(options) {
|
|
11138
|
+
const legacyOptions = options;
|
|
11139
|
+
if (legacyOptions.width != void 0 || legacyOptions.height != void 0) {
|
|
11140
|
+
throw new Error(
|
|
11141
|
+
"getUserMedia({ width, height }) is no longer supported for file playback. Resize or re-encode the source before calling getUserMedia()."
|
|
11142
|
+
);
|
|
11143
|
+
}
|
|
11144
|
+
}
|
|
11145
|
+
function createPlaybackSession({
|
|
11146
|
+
input,
|
|
11147
|
+
mediaStartTimestamp,
|
|
11148
|
+
playbackTracks
|
|
11149
|
+
}) {
|
|
11150
|
+
return {
|
|
11151
|
+
input,
|
|
11152
|
+
mediaStartTimestamp,
|
|
11153
|
+
playbackTracks,
|
|
11154
|
+
runners: playbackTracks.map(
|
|
11155
|
+
(playbackTrack) => new TrackPlaybackRunner({
|
|
11156
|
+
mediaStartTimestamp,
|
|
11157
|
+
...playbackTrack
|
|
11158
|
+
})
|
|
11159
|
+
)
|
|
11160
|
+
};
|
|
11161
|
+
}
|
|
11162
|
+
function buildExistingTrackMap(audio, video) {
|
|
11163
|
+
return {
|
|
11164
|
+
...audio ? { audio } : {},
|
|
11165
|
+
...video ? { video } : {}
|
|
11166
|
+
};
|
|
11167
|
+
}
|
|
11168
|
+
function assertCodecParameters({
|
|
11169
|
+
trackKind,
|
|
11170
|
+
codec,
|
|
11171
|
+
sourceCodec,
|
|
11172
|
+
decoderDescription,
|
|
11173
|
+
sourceChannels
|
|
11174
|
+
}) {
|
|
11175
|
+
if (sourceCodec === "opus" && sourceChannels && codec.channels != void 0) {
|
|
11176
|
+
if (codec.channels !== sourceChannels) {
|
|
11177
|
+
throw new Error(
|
|
11178
|
+
`Input ${trackKind} channels ${sourceChannels} do not match negotiated channels ${codec.channels}`
|
|
11179
|
+
);
|
|
11180
|
+
}
|
|
11181
|
+
return;
|
|
11182
|
+
}
|
|
11183
|
+
if (sourceCodec !== "avc") {
|
|
11184
|
+
return;
|
|
11185
|
+
}
|
|
11186
|
+
const negotiatedParameters = codecParametersFromString(
|
|
11187
|
+
codec.parameters ?? ""
|
|
11188
|
+
);
|
|
11189
|
+
const negotiatedPacketizationMode = Number(
|
|
11190
|
+
negotiatedParameters["packetization-mode"] ?? 1
|
|
11191
|
+
);
|
|
11192
|
+
if (negotiatedPacketizationMode !== 1) {
|
|
11193
|
+
throw new Error(
|
|
11194
|
+
`Input ${trackKind} packetization-mode 1 does not match negotiated packetization-mode ${negotiatedPacketizationMode}`
|
|
11195
|
+
);
|
|
11196
|
+
}
|
|
11197
|
+
const sourceProfileLevelId = getH264ProfileLevelId(decoderDescription);
|
|
11198
|
+
const negotiatedProfileLevelId = normalizeOptionalHexParameter(
|
|
11199
|
+
negotiatedParameters["profile-level-id"]
|
|
11200
|
+
);
|
|
11201
|
+
if (!sourceProfileLevelId) {
|
|
11202
|
+
return;
|
|
11203
|
+
}
|
|
11204
|
+
if (!negotiatedProfileLevelId) {
|
|
11205
|
+
throw new Error(
|
|
11206
|
+
`Input ${trackKind} profile-level-id ${sourceProfileLevelId} does not match negotiated profile-level-id <missing>`
|
|
11207
|
+
);
|
|
11208
|
+
}
|
|
11209
|
+
if (sourceProfileLevelId !== negotiatedProfileLevelId) {
|
|
11210
|
+
throw new Error(
|
|
11211
|
+
`Input ${trackKind} profile-level-id ${sourceProfileLevelId} does not match negotiated profile-level-id ${negotiatedProfileLevelId}`
|
|
11212
|
+
);
|
|
11213
|
+
}
|
|
11214
|
+
}
|
|
11215
|
+
function getH264ProfileLevelId(decoderDescription) {
|
|
11216
|
+
if (!decoderDescription) {
|
|
11217
|
+
return void 0;
|
|
11218
|
+
}
|
|
11219
|
+
const description = toDescriptionBuffer2(decoderDescription);
|
|
11220
|
+
if (description.length < 4) {
|
|
11221
|
+
throw new Error("invalid H264 decoder configuration");
|
|
11222
|
+
}
|
|
11223
|
+
return description.subarray(1, 4).toString("hex");
|
|
11224
|
+
}
|
|
11225
|
+
function normalizeOptionalHexParameter(value) {
|
|
11226
|
+
return typeof value === "string" ? value.toLowerCase() : void 0;
|
|
11227
|
+
}
|
|
11228
|
+
function toRtpTimestamp(packetTimestamp, mediaStartTimestamp, clockRate) {
|
|
11229
|
+
const relativeSeconds = Math.max(0, packetTimestamp - mediaStartTimestamp);
|
|
11230
|
+
return Math.round(relativeSeconds * clockRate) >>> 0;
|
|
11231
|
+
}
|
|
11232
|
+
function isAbortError(error) {
|
|
11233
|
+
return error instanceof Error && (error.name === "AbortError" || error.message === "The operation was aborted");
|
|
11234
|
+
}
|
|
11235
|
+
function toError(error) {
|
|
11236
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
11237
|
+
}
|
|
11238
|
+
function toDescriptionBuffer2(decoderDescription) {
|
|
11239
|
+
if (ArrayBuffer.isView(decoderDescription)) {
|
|
11240
|
+
return Buffer.from(
|
|
11241
|
+
decoderDescription.buffer,
|
|
11242
|
+
decoderDescription.byteOffset,
|
|
11243
|
+
decoderDescription.byteLength
|
|
11244
|
+
);
|
|
11245
|
+
}
|
|
11246
|
+
return Buffer.from(decoderDescription);
|
|
11247
|
+
}
|
|
9732
11248
|
export {
|
|
9733
11249
|
AV1Obu,
|
|
9734
11250
|
AV1RtpPayload,
|
|
@@ -9744,7 +11260,7 @@ export {
|
|
|
9744
11260
|
DtxCallback,
|
|
9745
11261
|
DurationPosition,
|
|
9746
11262
|
Element,
|
|
9747
|
-
Event,
|
|
11263
|
+
Event2 as Event,
|
|
9748
11264
|
EventDisposer,
|
|
9749
11265
|
ExtensionProfiles,
|
|
9750
11266
|
GenericNack,
|
|
@@ -9765,8 +11281,6 @@ export {
|
|
|
9765
11281
|
Max32Uint,
|
|
9766
11282
|
MaxSinged16Int,
|
|
9767
11283
|
MediaDevices,
|
|
9768
|
-
MediaPlayerMp4,
|
|
9769
|
-
MediaPlayerWebm,
|
|
9770
11284
|
MediaRecorder,
|
|
9771
11285
|
Mp4Container,
|
|
9772
11286
|
MuteCallback,
|
|
@@ -9818,9 +11332,11 @@ export {
|
|
|
9818
11332
|
SourceDescriptionChunk,
|
|
9819
11333
|
SourceDescriptionItem,
|
|
9820
11334
|
SrtcpSession,
|
|
11335
|
+
SrtpAuthenticationError,
|
|
9821
11336
|
SrtpSession,
|
|
9822
11337
|
StatusVectorChunk,
|
|
9823
11338
|
TcpTransport,
|
|
11339
|
+
TlsTransport,
|
|
9824
11340
|
TransportWideCC,
|
|
9825
11341
|
UNKNOWN_SIZE,
|
|
9826
11342
|
UdpTransport,
|
|
@@ -9832,6 +11348,7 @@ export {
|
|
|
9832
11348
|
WebmCallback,
|
|
9833
11349
|
WebmStream,
|
|
9834
11350
|
WeriftError,
|
|
11351
|
+
annexb2avcSample,
|
|
9835
11352
|
annexb2avcc,
|
|
9836
11353
|
buffer2ArrayBuffer,
|
|
9837
11354
|
bufferArrayXor,
|