werift 0.23.0 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/lib/common/src/binary.js +2 -3
- package/lib/common/src/binary.js.map +1 -1
- package/lib/common/src/transport.d.ts +22 -9
- package/lib/common/src/transport.js +152 -25
- package/lib/common/src/transport.js.map +1 -1
- package/lib/ice/src/candidate.d.ts +16 -1
- package/lib/ice/src/candidate.js +99 -15
- package/lib/ice/src/candidate.js.map +1 -1
- package/lib/ice/src/ice.d.ts +2 -0
- package/lib/ice/src/ice.js +175 -50
- package/lib/ice/src/ice.js.map +1 -1
- package/lib/ice/src/iceBase.d.ts +23 -2
- package/lib/ice/src/iceBase.js +63 -1
- package/lib/ice/src/iceBase.js.map +1 -1
- package/lib/ice/src/index.d.ts +2 -0
- package/lib/ice/src/index.js +2 -0
- package/lib/ice/src/index.js.map +1 -1
- package/lib/ice/src/internal/selectAddresses.d.ts +9 -0
- package/lib/ice/src/internal/selectAddresses.js +40 -0
- package/lib/ice/src/internal/selectAddresses.js.map +1 -0
- package/lib/ice/src/stun/const.d.ts +1 -26
- package/lib/ice/src/stun/const.js +15 -54
- package/lib/ice/src/stun/const.js.map +1 -1
- package/lib/ice/src/stun/message.d.ts +1 -25
- package/lib/ice/src/stun/message.js +15 -149
- package/lib/ice/src/stun/message.js.map +1 -1
- package/lib/ice/src/stun/protocol.d.ts +1 -1
- package/lib/ice/src/stun/protocol.js +2 -2
- package/lib/ice/src/stun/protocol.js.map +1 -1
- package/lib/ice/src/stun/tcpFrame.d.ts +5 -0
- package/lib/ice/src/stun/tcpFrame.js +26 -0
- package/lib/ice/src/stun/tcpFrame.js.map +1 -0
- package/lib/ice/src/stun/tcpProtocol.d.ts +51 -0
- package/lib/ice/src/stun/tcpProtocol.js +326 -0
- package/lib/ice/src/stun/tcpProtocol.js.map +1 -0
- package/lib/ice/src/stun/transaction.d.ts +2 -1
- package/lib/ice/src/stun/transaction.js +9 -3
- package/lib/ice/src/stun/transaction.js.map +1 -1
- package/lib/ice/src/turn/frame.d.ts +1 -0
- package/lib/ice/src/turn/frame.js +18 -0
- package/lib/ice/src/turn/frame.js.map +1 -0
- package/lib/ice/src/turn/protocol.d.ts +9 -7
- package/lib/ice/src/turn/protocol.js +32 -44
- package/lib/ice/src/turn/protocol.js.map +1 -1
- package/lib/ice/src/types/model.d.ts +1 -1
- package/lib/ice/src/types/model.js.map +1 -1
- package/lib/ice/src/utils.js +2 -31
- package/lib/ice/src/utils.js.map +1 -1
- package/lib/{ice → ice-server}/src/stun/attributes.d.ts +12 -5
- package/lib/{ice → ice-server}/src/stun/attributes.js +69 -66
- package/lib/ice-server/src/stun/attributes.js.map +1 -0
- package/lib/ice-server/src/stun/const.d.ts +28 -0
- package/lib/ice-server/src/stun/const.js +88 -0
- package/lib/ice-server/src/stun/const.js.map +1 -0
- package/lib/ice-server/src/stun/ip.d.ts +12 -0
- package/lib/ice-server/src/stun/ip.js +88 -0
- package/lib/ice-server/src/stun/ip.js.map +1 -0
- package/lib/ice-server/src/stun/message.d.ts +37 -0
- package/lib/ice-server/src/stun/message.js +190 -0
- package/lib/ice-server/src/stun/message.js.map +1 -0
- package/lib/ice-server/src/turn/auth.d.ts +1 -0
- package/lib/ice-server/src/turn/auth.js +10 -0
- package/lib/ice-server/src/turn/auth.js.map +1 -0
- package/lib/ice-server/src/turn/frame.d.ts +13 -0
- package/lib/ice-server/src/turn/frame.js +69 -0
- package/lib/ice-server/src/turn/frame.js.map +1 -0
- package/lib/index.mjs +2866 -946
- package/lib/nonstandard/index.mjs +1872 -455
- package/lib/rtp/src/codec/av1.js +11 -5
- package/lib/rtp/src/codec/av1.js.map +1 -1
- package/lib/rtp/src/codec/leb128.d.ts +5 -0
- package/lib/rtp/src/codec/leb128.js +24 -0
- package/lib/rtp/src/codec/leb128.js.map +1 -0
- package/lib/rtp/src/extra/container/mp4/container.d.ts +9 -6
- package/lib/rtp/src/extra/container/mp4/container.js +258 -173
- package/lib/rtp/src/extra/container/mp4/container.js.map +1 -1
- package/lib/rtp/src/extra/container/mp4/h264.d.ts +1 -0
- package/lib/rtp/src/extra/container/mp4/h264.js +13 -0
- package/lib/rtp/src/extra/container/mp4/h264.js.map +1 -1
- package/lib/rtp/src/extra/processor/mp4.d.ts +11 -7
- package/lib/rtp/src/extra/processor/mp4.js +115 -60
- package/lib/rtp/src/extra/processor/mp4.js.map +1 -1
- package/lib/rtp/src/extra/processor/mp4Callback.d.ts +2 -3
- package/lib/rtp/src/extra/processor/mp4Callback.js +5 -3
- package/lib/rtp/src/extra/processor/mp4Callback.js.map +1 -1
- package/lib/rtp/src/rtp/rtx.js +4 -6
- package/lib/rtp/src/rtp/rtx.js.map +1 -1
- package/lib/rtp/src/srtp/context/context.d.ts +2 -0
- package/lib/rtp/src/srtp/context/context.js +12 -12
- package/lib/rtp/src/srtp/context/context.js.map +1 -1
- package/lib/sctp/src/param.js +31 -15
- package/lib/sctp/src/param.js.map +1 -1
- package/lib/sctp/src/sctp.js +10 -9
- package/lib/sctp/src/sctp.js.map +1 -1
- package/lib/webrtc/src/dataChannel.d.ts +3 -2
- package/lib/webrtc/src/dataChannel.js +9 -2
- package/lib/webrtc/src/dataChannel.js.map +1 -1
- package/lib/webrtc/src/errors.d.ts +3 -0
- package/lib/webrtc/src/errors.js +11 -0
- package/lib/webrtc/src/errors.js.map +1 -0
- package/lib/webrtc/src/helper.d.ts +5 -1
- package/lib/webrtc/src/helper.js +33 -1
- package/lib/webrtc/src/helper.js.map +1 -1
- package/lib/webrtc/src/media/receiver/statistics.d.ts +4 -1
- package/lib/webrtc/src/media/receiver/statistics.js +22 -0
- package/lib/webrtc/src/media/receiver/statistics.js.map +1 -1
- package/lib/webrtc/src/media/rtpReceiver.d.ts +15 -2
- package/lib/webrtc/src/media/rtpReceiver.js +135 -20
- package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
- package/lib/webrtc/src/media/rtpSender.d.ts +32 -7
- package/lib/webrtc/src/media/rtpSender.js +179 -31
- package/lib/webrtc/src/media/rtpSender.js.map +1 -1
- package/lib/webrtc/src/media/rtpTransceiver.d.ts +13 -4
- package/lib/webrtc/src/media/rtpTransceiver.js +26 -5
- package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
- package/lib/webrtc/src/media/stats.d.ts +15 -1
- package/lib/webrtc/src/media/stats.js +55 -1
- package/lib/webrtc/src/media/stats.js.map +1 -1
- package/lib/webrtc/src/media/track.d.ts +2 -1
- package/lib/webrtc/src/media/track.js +7 -1
- package/lib/webrtc/src/media/track.js.map +1 -1
- package/lib/webrtc/src/nonstandard/dummyMedia.d.ts +19 -0
- package/lib/webrtc/src/nonstandard/dummyMedia.js +157 -0
- package/lib/webrtc/src/nonstandard/dummyMedia.js.map +1 -0
- package/lib/webrtc/src/nonstandard/navigator.d.ts +18 -8
- package/lib/webrtc/src/nonstandard/navigator.js +96 -32
- package/lib/webrtc/src/nonstandard/navigator.js.map +1 -1
- package/lib/webrtc/src/nonstandard/userMedia/packetizer.d.ts +16 -0
- package/lib/webrtc/src/nonstandard/userMedia/packetizer.js +360 -0
- package/lib/webrtc/src/nonstandard/userMedia/packetizer.js.map +1 -0
- package/lib/webrtc/src/nonstandard/userMedia.d.ts +71 -31
- package/lib/webrtc/src/nonstandard/userMedia.js +454 -108
- package/lib/webrtc/src/nonstandard/userMedia.js.map +1 -1
- package/lib/webrtc/src/peerConnection.d.ts +183 -50
- package/lib/webrtc/src/peerConnection.js +458 -109
- package/lib/webrtc/src/peerConnection.js.map +1 -1
- package/lib/webrtc/src/sctpManager.d.ts +1 -1
- package/lib/webrtc/src/sctpManager.js +27 -7
- package/lib/webrtc/src/sctpManager.js.map +1 -1
- package/lib/webrtc/src/sdp.d.ts +10 -7
- package/lib/webrtc/src/sdp.js +40 -44
- package/lib/webrtc/src/sdp.js.map +1 -1
- package/lib/webrtc/src/sdpManager.d.ts +4 -3
- package/lib/webrtc/src/sdpManager.js +50 -27
- package/lib/webrtc/src/sdpManager.js.map +1 -1
- package/lib/webrtc/src/secureTransportManager.d.ts +13 -4
- package/lib/webrtc/src/secureTransportManager.js +100 -22
- package/lib/webrtc/src/secureTransportManager.js.map +1 -1
- package/lib/webrtc/src/transceiverManager.d.ts +5 -4
- package/lib/webrtc/src/transceiverManager.js +61 -62
- package/lib/webrtc/src/transceiverManager.js.map +1 -1
- package/lib/webrtc/src/transport/dtls.d.ts +15 -4
- package/lib/webrtc/src/transport/dtls.js +91 -23
- package/lib/webrtc/src/transport/dtls.js.map +1 -1
- package/lib/webrtc/src/transport/ice.d.ts +21 -3
- package/lib/webrtc/src/transport/ice.js +159 -30
- package/lib/webrtc/src/transport/ice.js.map +1 -1
- package/lib/webrtc/src/transport/sctp.d.ts +1 -0
- package/lib/webrtc/src/transport/sctp.js +26 -22
- package/lib/webrtc/src/transport/sctp.js.map +1 -1
- package/lib/webrtc/src/utils.d.ts +12 -4
- package/lib/webrtc/src/utils.js +118 -16
- package/lib/webrtc/src/utils.js.map +1 -1
- package/package.json +7 -10
- package/lib/ice/src/stun/attributes.js.map +0 -1
- package/lib/rtp/src/extra/container/mp4/mp4box.d.ts +0 -5
- package/lib/rtp/src/extra/container/mp4/mp4box.js +0 -32
- package/lib/rtp/src/extra/container/mp4/mp4box.js.map +0 -1
|
@@ -1,53 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
if (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
+
};
|
|
35
7
|
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
36
8
|
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
37
9
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
38
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
39
11
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
40
12
|
};
|
|
41
|
-
var
|
|
42
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
43
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
44
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
45
|
-
};
|
|
46
|
-
var _Mp4Container_instances, _Mp4Container_mp4, _Mp4Container_audioFrame, _Mp4Container_videoFrame, _Mp4Container_audioSegment, _Mp4Container_videoSegment, _Mp4Container_init, _Mp4Container_enqueue;
|
|
13
|
+
var _Mp4Container_instances, _Mp4Container_audioFrame, _Mp4Container_audioMeta, _Mp4Container_audioSource, _Mp4Container_currentFragment, _Mp4Container_ftyp, _Mp4Container_initializationEmitted, _Mp4Container_lastAudioDuration, _Mp4Container_lastMoof, _Mp4Container_lastMoofTimestamp, _Mp4Container_lastVideoDuration, _Mp4Container_moov, _Mp4Container_onDataOperation, _Mp4Container_operation, _Mp4Container_output, _Mp4Container_startPromise, _Mp4Container_stopPromise, _Mp4Container_stopped, _Mp4Container_videoFrame, _Mp4Container_videoMeta, _Mp4Container_videoSource, _Mp4Container_init, _Mp4Container_enqueue, _Mp4Container_drainFrameBuffer, _Mp4Container_ensureOutputStarted, _Mp4Container_emitInitializationSegment, _Mp4Container_emitData, _Mp4Container_flushOnData, _Mp4Container_enqueueOperation, _Mp4Container_defaultKind, _Mp4Container_flushPendingFrame, _Mp4Container_processFrame, _Mp4Container_writePacket, _Mp4Container_rememberFragmentPacket;
|
|
47
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
15
|
exports.mp4SupportedCodecs = exports.Mp4Container = void 0;
|
|
16
|
+
const mediabunny_1 = require("mediabunny");
|
|
49
17
|
const common_1 = require("../../../imports/common");
|
|
50
|
-
const MP4 = __importStar(require("./mp4box"));
|
|
51
18
|
class Mp4Container {
|
|
52
19
|
constructor(props) {
|
|
53
20
|
_Mp4Container_instances.add(this);
|
|
@@ -57,37 +24,50 @@ class Mp4Container {
|
|
|
57
24
|
writable: true,
|
|
58
25
|
value: props
|
|
59
26
|
});
|
|
60
|
-
_Mp4Container_mp4.set(this, void 0);
|
|
61
27
|
_Mp4Container_audioFrame.set(this, void 0);
|
|
62
|
-
|
|
28
|
+
_Mp4Container_audioMeta.set(this, void 0);
|
|
29
|
+
_Mp4Container_audioSource.set(this, void 0);
|
|
30
|
+
_Mp4Container_currentFragment.set(this, void 0);
|
|
31
|
+
_Mp4Container_ftyp.set(this, void 0);
|
|
32
|
+
_Mp4Container_initializationEmitted.set(this, false);
|
|
33
|
+
_Mp4Container_lastAudioDuration.set(this, 0);
|
|
34
|
+
_Mp4Container_lastMoof.set(this, void 0);
|
|
35
|
+
_Mp4Container_lastMoofTimestamp.set(this, 0);
|
|
36
|
+
_Mp4Container_lastVideoDuration.set(this, 0);
|
|
37
|
+
_Mp4Container_moov.set(this, void 0);
|
|
38
|
+
_Mp4Container_onDataOperation.set(this, Promise.resolve());
|
|
39
|
+
_Mp4Container_operation.set(this, Promise.resolve());
|
|
40
|
+
_Mp4Container_output.set(this, void 0);
|
|
41
|
+
_Mp4Container_startPromise.set(this, void 0);
|
|
42
|
+
_Mp4Container_stopPromise.set(this, void 0);
|
|
43
|
+
_Mp4Container_stopped.set(this, false);
|
|
44
|
+
_Mp4Container_videoFrame.set(this, void 0);
|
|
45
|
+
_Mp4Container_videoMeta.set(this, void 0);
|
|
46
|
+
_Mp4Container_videoSource.set(this, void 0);
|
|
63
47
|
Object.defineProperty(this, "audioTrack", {
|
|
64
48
|
enumerable: true,
|
|
65
49
|
configurable: true,
|
|
66
50
|
writable: true,
|
|
67
51
|
value: void 0
|
|
68
52
|
});
|
|
69
|
-
Object.defineProperty(this, "
|
|
53
|
+
Object.defineProperty(this, "frameBuffer", {
|
|
70
54
|
enumerable: true,
|
|
71
55
|
configurable: true,
|
|
72
56
|
writable: true,
|
|
73
|
-
value:
|
|
57
|
+
value: []
|
|
74
58
|
});
|
|
75
|
-
_Mp4Container_audioSegment.set(this, 0);
|
|
76
|
-
_Mp4Container_videoSegment.set(this, 0);
|
|
77
59
|
Object.defineProperty(this, "onData", {
|
|
78
60
|
enumerable: true,
|
|
79
61
|
configurable: true,
|
|
80
62
|
writable: true,
|
|
81
63
|
value: new common_1.Event()
|
|
82
64
|
});
|
|
83
|
-
Object.defineProperty(this, "
|
|
65
|
+
Object.defineProperty(this, "videoTrack", {
|
|
84
66
|
enumerable: true,
|
|
85
67
|
configurable: true,
|
|
86
68
|
writable: true,
|
|
87
|
-
value:
|
|
69
|
+
value: void 0
|
|
88
70
|
});
|
|
89
|
-
__classPrivateFieldSet(this, _Mp4Container_mp4, new MP4.ISOFile(), "f");
|
|
90
|
-
__classPrivateFieldGet(this, _Mp4Container_mp4, "f").init();
|
|
91
71
|
}
|
|
92
72
|
get tracksReady() {
|
|
93
73
|
let ready = true;
|
|
@@ -107,156 +87,248 @@ class Mp4Container {
|
|
|
107
87
|
return __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_enqueue).call(this, frame);
|
|
108
88
|
}
|
|
109
89
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
throw new Error("track missing");
|
|
114
|
-
}
|
|
115
|
-
// Check if we should create a new segment
|
|
116
|
-
if (frame.track === "video") {
|
|
117
|
-
if (frame.type == "key") {
|
|
118
|
-
__classPrivateFieldSet(this, _Mp4Container_videoSegment, __classPrivateFieldGet(this, _Mp4Container_videoSegment, "f") + 1, "f");
|
|
119
|
-
}
|
|
120
|
-
else if (__classPrivateFieldGet(this, _Mp4Container_videoSegment, "f") == 0) {
|
|
121
|
-
throw new Error("must start with keyframe");
|
|
122
|
-
}
|
|
90
|
+
stop() {
|
|
91
|
+
if (__classPrivateFieldGet(this, _Mp4Container_stopPromise, "f")) {
|
|
92
|
+
return __classPrivateFieldGet(this, _Mp4Container_stopPromise, "f");
|
|
123
93
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (frame.track === "video") {
|
|
129
|
-
if (!__classPrivateFieldGet(this, _Mp4Container_videoFrame, "f")) {
|
|
130
|
-
__classPrivateFieldSet(this, _Mp4Container_videoFrame, frame, "f");
|
|
94
|
+
__classPrivateFieldSet(this, _Mp4Container_stopped, true, "f");
|
|
95
|
+
__classPrivateFieldSet(this, _Mp4Container_stopPromise, __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_enqueueOperation).call(this, async () => {
|
|
96
|
+
if (!this.tracksReady) {
|
|
97
|
+
this.frameBuffer = [];
|
|
131
98
|
return;
|
|
132
99
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
100
|
+
await __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_ensureOutputStarted).call(this);
|
|
101
|
+
await __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_drainFrameBuffer).call(this);
|
|
102
|
+
await __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_flushPendingFrame).call(this, "audio");
|
|
103
|
+
await __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_flushPendingFrame).call(this, "video");
|
|
104
|
+
__classPrivateFieldGet(this, _Mp4Container_audioSource, "f")?.close();
|
|
105
|
+
__classPrivateFieldGet(this, _Mp4Container_videoSource, "f")?.close();
|
|
106
|
+
if (__classPrivateFieldGet(this, _Mp4Container_output, "f")) {
|
|
107
|
+
await __classPrivateFieldGet(this, _Mp4Container_output, "f").finalize();
|
|
138
108
|
}
|
|
109
|
+
await __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_flushOnData).call(this);
|
|
110
|
+
}), "f");
|
|
111
|
+
return __classPrivateFieldGet(this, _Mp4Container_stopPromise, "f");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
exports.Mp4Container = Mp4Container;
|
|
115
|
+
_Mp4Container_audioFrame = new WeakMap(), _Mp4Container_audioMeta = new WeakMap(), _Mp4Container_audioSource = new WeakMap(), _Mp4Container_currentFragment = new WeakMap(), _Mp4Container_ftyp = new WeakMap(), _Mp4Container_initializationEmitted = new WeakMap(), _Mp4Container_lastAudioDuration = new WeakMap(), _Mp4Container_lastMoof = new WeakMap(), _Mp4Container_lastMoofTimestamp = new WeakMap(), _Mp4Container_lastVideoDuration = new WeakMap(), _Mp4Container_moov = new WeakMap(), _Mp4Container_onDataOperation = new WeakMap(), _Mp4Container_operation = new WeakMap(), _Mp4Container_output = new WeakMap(), _Mp4Container_startPromise = new WeakMap(), _Mp4Container_stopPromise = new WeakMap(), _Mp4Container_stopped = new WeakMap(), _Mp4Container_videoFrame = new WeakMap(), _Mp4Container_videoMeta = new WeakMap(), _Mp4Container_videoSource = new WeakMap(), _Mp4Container_instances = new WeakSet(), _Mp4Container_init = function _Mp4Container_init(frame) {
|
|
116
|
+
if (frame.track === "audio") {
|
|
117
|
+
if (isVideoConfig(frame)) {
|
|
118
|
+
throw new Error("audio track requires an audio decoder config");
|
|
139
119
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
throw new Error("bufferFrame missing");
|
|
120
|
+
if (frame.codec !== "opus") {
|
|
121
|
+
throw new Error(`unsupported codec: ${frame.codec}`);
|
|
143
122
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
for (;;) {
|
|
160
|
-
const moof = __classPrivateFieldGet(this, _Mp4Container_mp4, "f").moofs.shift();
|
|
161
|
-
const mdat = __classPrivateFieldGet(this, _Mp4Container_mp4, "f").mdats.shift();
|
|
162
|
-
if (!moof && !mdat)
|
|
163
|
-
break;
|
|
164
|
-
if (!moof)
|
|
165
|
-
throw new Error("moof missing");
|
|
166
|
-
if (!mdat)
|
|
167
|
-
throw new Error("mdat missing");
|
|
168
|
-
moof.write(stream);
|
|
169
|
-
mdat.write(stream);
|
|
123
|
+
__classPrivateFieldSet(this, _Mp4Container_audioMeta, {
|
|
124
|
+
decoderConfig: {
|
|
125
|
+
codec: "opus",
|
|
126
|
+
description: frame.description != undefined
|
|
127
|
+
? new Uint8Array(frame.description)
|
|
128
|
+
: undefined,
|
|
129
|
+
numberOfChannels: frame.numberOfChannels,
|
|
130
|
+
sampleRate: frame.sampleRate,
|
|
131
|
+
},
|
|
132
|
+
}, "f");
|
|
133
|
+
this.audioTrack = 1;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
if (!isVideoConfig(frame)) {
|
|
137
|
+
throw new Error("video track requires a video decoder config");
|
|
170
138
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (frame.track === "video") {
|
|
174
|
-
__classPrivateFieldSet(this, _Mp4Container_videoFrame, frame, "f");
|
|
139
|
+
if (!frame.codec.startsWith("avc1")) {
|
|
140
|
+
throw new Error(`unsupported codec: ${frame.codec}`);
|
|
175
141
|
}
|
|
176
|
-
|
|
177
|
-
|
|
142
|
+
if (frame.codedWidth == undefined || frame.codedHeight == undefined) {
|
|
143
|
+
throw new Error("missing coded video dimensions");
|
|
178
144
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
145
|
+
__classPrivateFieldSet(this, _Mp4Container_videoMeta, {
|
|
146
|
+
decoderConfig: {
|
|
147
|
+
codec: frame.codec,
|
|
148
|
+
codedHeight: frame.codedHeight,
|
|
149
|
+
codedWidth: frame.codedWidth,
|
|
150
|
+
description: frame.description != undefined
|
|
151
|
+
? new Uint8Array(frame.description)
|
|
152
|
+
: undefined,
|
|
153
|
+
displayAspectHeight: frame.displayAspectHeight,
|
|
154
|
+
displayAspectWidth: frame.displayAspectWidth,
|
|
155
|
+
},
|
|
156
|
+
}, "f");
|
|
157
|
+
this.videoTrack = this.props.track.audio ? 2 : 1;
|
|
187
158
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
_Mp4Container_mp4 = new WeakMap(), _Mp4Container_audioFrame = new WeakMap(), _Mp4Container_videoFrame = new WeakMap(), _Mp4Container_audioSegment = new WeakMap(), _Mp4Container_videoSegment = new WeakMap(), _Mp4Container_instances = new WeakSet(), _Mp4Container_init = function _Mp4Container_init(frame) {
|
|
191
|
-
let codec = frame.codec.substring(0, 4);
|
|
192
|
-
if (codec == "opus") {
|
|
193
|
-
codec = "Opus";
|
|
159
|
+
if (!this.tracksReady) {
|
|
160
|
+
return;
|
|
194
161
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
162
|
+
void __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_enqueueOperation).call(this, async () => {
|
|
163
|
+
await __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_ensureOutputStarted).call(this);
|
|
164
|
+
await __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_drainFrameBuffer).call(this);
|
|
165
|
+
});
|
|
166
|
+
}, _Mp4Container_enqueue = function _Mp4Container_enqueue(frame) {
|
|
167
|
+
this.frameBuffer.push(frame);
|
|
168
|
+
if (!this.tracksReady) {
|
|
169
|
+
return;
|
|
202
170
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
171
|
+
void __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_enqueueOperation).call(this, async () => {
|
|
172
|
+
await __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_ensureOutputStarted).call(this);
|
|
173
|
+
await __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_drainFrameBuffer).call(this);
|
|
174
|
+
});
|
|
175
|
+
}, _Mp4Container_drainFrameBuffer = async function _Mp4Container_drainFrameBuffer() {
|
|
176
|
+
const frames = this.frameBuffer;
|
|
177
|
+
this.frameBuffer = [];
|
|
178
|
+
for (const frame of frames) {
|
|
179
|
+
await __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_processFrame).call(this, frame);
|
|
207
180
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
options.avcDecoderConfigRecord = desc;
|
|
181
|
+
}, _Mp4Container_ensureOutputStarted = async function _Mp4Container_ensureOutputStarted() {
|
|
182
|
+
if (__classPrivateFieldGet(this, _Mp4Container_startPromise, "f")) {
|
|
183
|
+
await __classPrivateFieldGet(this, _Mp4Container_startPromise, "f");
|
|
184
|
+
return;
|
|
213
185
|
}
|
|
214
|
-
|
|
215
|
-
|
|
186
|
+
if (!this.tracksReady) {
|
|
187
|
+
return;
|
|
216
188
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
189
|
+
const output = new mediabunny_1.Output({
|
|
190
|
+
format: new mediabunny_1.Mp4OutputFormat({
|
|
191
|
+
fastStart: "fragmented",
|
|
192
|
+
minimumFragmentDuration: 0,
|
|
193
|
+
onFtyp: (data) => {
|
|
194
|
+
__classPrivateFieldSet(this, _Mp4Container_ftyp, copyBytes(data), "f");
|
|
195
|
+
},
|
|
196
|
+
onMdat: (data) => {
|
|
197
|
+
if (!__classPrivateFieldGet(this, _Mp4Container_lastMoof, "f")) {
|
|
198
|
+
throw new Error("moof missing before mdat");
|
|
199
|
+
}
|
|
200
|
+
const fragment = __classPrivateFieldGet(this, _Mp4Container_currentFragment, "f");
|
|
201
|
+
const timestamp = fragment
|
|
202
|
+
? fragment.timestamp
|
|
203
|
+
: Math.round(__classPrivateFieldGet(this, _Mp4Container_lastMoofTimestamp, "f") * 1000000);
|
|
204
|
+
const duration = fragment?.duration ?? 0;
|
|
205
|
+
const kind = fragment?.kind ?? __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_defaultKind).call(this);
|
|
206
|
+
const type = fragment?.type ?? "key";
|
|
207
|
+
const segment = concatBytes(__classPrivateFieldGet(this, _Mp4Container_lastMoof, "f"), data);
|
|
208
|
+
__classPrivateFieldSet(this, _Mp4Container_currentFragment, undefined, "f");
|
|
209
|
+
__classPrivateFieldSet(this, _Mp4Container_lastMoof, undefined, "f");
|
|
210
|
+
void __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_emitData).call(this, {
|
|
211
|
+
type,
|
|
212
|
+
timestamp,
|
|
213
|
+
duration,
|
|
214
|
+
data: segment,
|
|
215
|
+
kind,
|
|
216
|
+
});
|
|
217
|
+
},
|
|
218
|
+
onMoof: (data, _position, timestamp) => {
|
|
219
|
+
__classPrivateFieldSet(this, _Mp4Container_lastMoof, copyBytes(data), "f");
|
|
220
|
+
__classPrivateFieldSet(this, _Mp4Container_lastMoofTimestamp, timestamp, "f");
|
|
221
|
+
},
|
|
222
|
+
onMoov: (data) => {
|
|
223
|
+
__classPrivateFieldSet(this, _Mp4Container_moov, copyBytes(data), "f");
|
|
224
|
+
__classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_emitInitializationSegment).call(this);
|
|
225
|
+
},
|
|
226
|
+
}),
|
|
227
|
+
target: new mediabunny_1.NullTarget(),
|
|
228
|
+
});
|
|
229
|
+
if (__classPrivateFieldGet(this, _Mp4Container_audioMeta, "f")) {
|
|
230
|
+
__classPrivateFieldSet(this, _Mp4Container_audioSource, new mediabunny_1.EncodedAudioPacketSource("opus"), "f");
|
|
231
|
+
output.addAudioTrack(__classPrivateFieldGet(this, _Mp4Container_audioSource, "f"));
|
|
232
|
+
}
|
|
233
|
+
if (__classPrivateFieldGet(this, _Mp4Container_videoMeta, "f")) {
|
|
234
|
+
__classPrivateFieldSet(this, _Mp4Container_videoSource, new mediabunny_1.EncodedVideoPacketSource("avc"), "f");
|
|
235
|
+
output.addVideoTrack(__classPrivateFieldGet(this, _Mp4Container_videoSource, "f"));
|
|
236
|
+
}
|
|
237
|
+
__classPrivateFieldSet(this, _Mp4Container_output, output, "f");
|
|
238
|
+
__classPrivateFieldSet(this, _Mp4Container_startPromise, output.start(), "f");
|
|
239
|
+
await __classPrivateFieldGet(this, _Mp4Container_startPromise, "f");
|
|
240
|
+
}, _Mp4Container_emitInitializationSegment = function _Mp4Container_emitInitializationSegment() {
|
|
241
|
+
if (__classPrivateFieldGet(this, _Mp4Container_initializationEmitted, "f") || !__classPrivateFieldGet(this, _Mp4Container_ftyp, "f") || !__classPrivateFieldGet(this, _Mp4Container_moov, "f")) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
__classPrivateFieldSet(this, _Mp4Container_initializationEmitted, true, "f");
|
|
245
|
+
void __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_emitData).call(this, {
|
|
246
|
+
type: "init",
|
|
247
|
+
timestamp: 0,
|
|
248
|
+
duration: 0,
|
|
249
|
+
data: concatBytes(__classPrivateFieldGet(this, _Mp4Container_ftyp, "f"), __classPrivateFieldGet(this, _Mp4Container_moov, "f")),
|
|
250
|
+
kind: __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_defaultKind).call(this),
|
|
251
|
+
});
|
|
252
|
+
}, _Mp4Container_emitData = function _Mp4Container_emitData(data) {
|
|
253
|
+
const next = __classPrivateFieldGet(this, _Mp4Container_onDataOperation, "f").then(() => this.onData.execute(data));
|
|
254
|
+
__classPrivateFieldSet(this, _Mp4Container_onDataOperation, next, "f");
|
|
255
|
+
return next;
|
|
256
|
+
}, _Mp4Container_flushOnData = async function _Mp4Container_flushOnData() {
|
|
257
|
+
await __classPrivateFieldGet(this, _Mp4Container_onDataOperation, "f");
|
|
258
|
+
}, _Mp4Container_enqueueOperation = function _Mp4Container_enqueueOperation(operation) {
|
|
259
|
+
const next = __classPrivateFieldGet(this, _Mp4Container_operation, "f").then(operation);
|
|
260
|
+
__classPrivateFieldSet(this, _Mp4Container_operation, next, "f");
|
|
261
|
+
return next;
|
|
262
|
+
}, _Mp4Container_defaultKind = function _Mp4Container_defaultKind() {
|
|
263
|
+
return this.props.track.video ? "video" : "audio";
|
|
264
|
+
}, _Mp4Container_flushPendingFrame = async function _Mp4Container_flushPendingFrame(track) {
|
|
265
|
+
const buffered = track === "audio" ? __classPrivateFieldGet(this, _Mp4Container_audioFrame, "f") : __classPrivateFieldGet(this, _Mp4Container_videoFrame, "f");
|
|
266
|
+
if (!buffered) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const duration = track === "audio" ? __classPrivateFieldGet(this, _Mp4Container_lastAudioDuration, "f") : __classPrivateFieldGet(this, _Mp4Container_lastVideoDuration, "f");
|
|
270
|
+
await __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_writePacket).call(this, buffered, duration);
|
|
271
|
+
if (track === "audio") {
|
|
272
|
+
__classPrivateFieldSet(this, _Mp4Container_audioFrame, undefined, "f");
|
|
224
273
|
}
|
|
225
274
|
else {
|
|
226
|
-
|
|
275
|
+
__classPrivateFieldSet(this, _Mp4Container_videoFrame, undefined, "f");
|
|
227
276
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
277
|
+
}, _Mp4Container_processFrame = async function _Mp4Container_processFrame(frame) {
|
|
278
|
+
const buffered = frame.track === "audio" ? __classPrivateFieldGet(this, _Mp4Container_audioFrame, "f") : __classPrivateFieldGet(this, _Mp4Container_videoFrame, "f");
|
|
279
|
+
if (!buffered) {
|
|
280
|
+
if (frame.track === "audio") {
|
|
281
|
+
__classPrivateFieldSet(this, _Mp4Container_audioFrame, frame, "f");
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
__classPrivateFieldSet(this, _Mp4Container_videoFrame, frame, "f");
|
|
285
|
+
}
|
|
286
|
+
return;
|
|
231
287
|
}
|
|
288
|
+
const duration = Math.max(frame.timestamp - buffered.timestamp, 0);
|
|
289
|
+
await __classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_writePacket).call(this, buffered, duration);
|
|
232
290
|
if (frame.track === "audio") {
|
|
233
|
-
this
|
|
291
|
+
__classPrivateFieldSet(this, _Mp4Container_audioFrame, frame, "f");
|
|
292
|
+
__classPrivateFieldSet(this, _Mp4Container_lastAudioDuration, duration, "f");
|
|
234
293
|
}
|
|
235
294
|
else {
|
|
236
|
-
this
|
|
295
|
+
__classPrivateFieldSet(this, _Mp4Container_videoFrame, frame, "f");
|
|
296
|
+
__classPrivateFieldSet(this, _Mp4Container_lastVideoDuration, duration, "f");
|
|
237
297
|
}
|
|
238
|
-
|
|
239
|
-
|
|
298
|
+
}, _Mp4Container_writePacket = async function _Mp4Container_writePacket(frame, duration) {
|
|
299
|
+
const data = new Uint8Array(frame.byteLength);
|
|
300
|
+
frame.copyTo(data.buffer);
|
|
301
|
+
const packet = new mediabunny_1.EncodedPacket(data, frame.type, frame.timestamp / 1000000, duration / 1000000);
|
|
302
|
+
if (frame.track === "audio") {
|
|
303
|
+
if (!__classPrivateFieldGet(this, _Mp4Container_audioSource, "f") || !__classPrivateFieldGet(this, _Mp4Container_audioMeta, "f")) {
|
|
304
|
+
throw new Error("audio track missing");
|
|
305
|
+
}
|
|
306
|
+
await __classPrivateFieldGet(this, _Mp4Container_audioSource, "f").add(packet, __classPrivateFieldGet(this, _Mp4Container_audioMeta, "f"));
|
|
240
307
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
this
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
308
|
+
else {
|
|
309
|
+
if (!__classPrivateFieldGet(this, _Mp4Container_videoSource, "f") || !__classPrivateFieldGet(this, _Mp4Container_videoMeta, "f")) {
|
|
310
|
+
throw new Error("video track missing");
|
|
311
|
+
}
|
|
312
|
+
await __classPrivateFieldGet(this, _Mp4Container_videoSource, "f").add(packet, __classPrivateFieldGet(this, _Mp4Container_videoMeta, "f"));
|
|
313
|
+
}
|
|
314
|
+
__classPrivateFieldGet(this, _Mp4Container_instances, "m", _Mp4Container_rememberFragmentPacket).call(this, frame.track, frame.type, frame.timestamp, duration);
|
|
315
|
+
}, _Mp4Container_rememberFragmentPacket = function _Mp4Container_rememberFragmentPacket(kind, type, timestamp, duration) {
|
|
316
|
+
const end = timestamp + duration;
|
|
317
|
+
if (!__classPrivateFieldGet(this, _Mp4Container_currentFragment, "f")) {
|
|
318
|
+
__classPrivateFieldSet(this, _Mp4Container_currentFragment, {
|
|
319
|
+
kind,
|
|
320
|
+
type,
|
|
321
|
+
timestamp,
|
|
322
|
+
duration,
|
|
323
|
+
}, "f");
|
|
254
324
|
return;
|
|
255
325
|
}
|
|
256
|
-
|
|
257
|
-
this.
|
|
326
|
+
if (kind === "video" && type === "key") {
|
|
327
|
+
__classPrivateFieldGet(this, _Mp4Container_currentFragment, "f").type = "key";
|
|
258
328
|
}
|
|
259
|
-
this.
|
|
329
|
+
const fragmentEnd = Math.max(__classPrivateFieldGet(this, _Mp4Container_currentFragment, "f").timestamp + __classPrivateFieldGet(this, _Mp4Container_currentFragment, "f").duration, end);
|
|
330
|
+
__classPrivateFieldGet(this, _Mp4Container_currentFragment, "f").duration =
|
|
331
|
+
fragmentEnd - __classPrivateFieldGet(this, _Mp4Container_currentFragment, "f").timestamp;
|
|
260
332
|
};
|
|
261
333
|
function isDecoderConfig(frame) {
|
|
262
334
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
@@ -266,4 +338,17 @@ function isVideoConfig(frame) {
|
|
|
266
338
|
return frame.codedWidth !== undefined;
|
|
267
339
|
}
|
|
268
340
|
exports.mp4SupportedCodecs = ["avc1", "opus"];
|
|
341
|
+
function concatBytes(...parts) {
|
|
342
|
+
const size = parts.reduce((sum, part) => sum + part.byteLength, 0);
|
|
343
|
+
const combined = new Uint8Array(size);
|
|
344
|
+
let offset = 0;
|
|
345
|
+
for (const part of parts) {
|
|
346
|
+
combined.set(part, offset);
|
|
347
|
+
offset += part.byteLength;
|
|
348
|
+
}
|
|
349
|
+
return combined;
|
|
350
|
+
}
|
|
351
|
+
function copyBytes(data) {
|
|
352
|
+
return new Uint8Array(data);
|
|
353
|
+
}
|
|
269
354
|
//# sourceMappingURL=container.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.js","sourceRoot":"","sources":["../../../../../../../rtp/src/extra/container/mp4/container.ts"],"names":[],"mappings":"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","sourcesContent":["import { Event } from \"../../../imports/common\";\n\nimport * as MP4 from \"./mp4box\";\n\ntype DecoderConfig = AudioDecoderConfig | VideoDecoderConfig;\ntype EncodedChunk = EncodedAudioChunk | EncodedVideoChunk;\n\nexport type DataType = \"init\" | \"delta\" | \"key\";\n\nexport interface Mp4Data {\n type: DataType;\n timestamp: number;\n duration: number;\n data: Uint8Array;\n kind: \"audio\" | \"video\";\n}\n\nexport class Mp4Container {\n #mp4: MP4.ISOFile;\n #audioFrame?: EncodedAudioChunk | EncodedVideoChunk;\n #videoFrame?: EncodedAudioChunk | EncodedVideoChunk; // 1 frame buffer\n audioTrack?: number;\n videoTrack?: number;\n #audioSegment = 0;\n #videoSegment = 0;\n onData = new Event<[Mp4Data]>();\n\n constructor(\n private props: {\n track: { audio: boolean; video: boolean };\n },\n ) {\n this.#mp4 = new MP4.ISOFile();\n this.#mp4.init();\n }\n\n get tracksReady() {\n let ready = true;\n if (this.props.track.audio && !this.audioTrack) {\n ready = false;\n }\n if (this.props.track.video && !this.videoTrack) {\n ready = false;\n }\n return ready;\n }\n\n write(\n frame: (DecoderConfig | EncodedChunk) & {\n track: \"video\" | \"audio\";\n },\n ) {\n if (isDecoderConfig(frame)) {\n return this.#init(frame);\n } else {\n return this.#enqueue(frame);\n }\n }\n\n #init(\n frame: DecoderConfig & {\n track: \"video\" | \"audio\";\n },\n ) {\n let codec = frame.codec.substring(0, 4);\n if (codec == \"opus\") {\n codec = \"Opus\";\n }\n\n const options: MP4.TrackOptions = {\n type: codec,\n timescale: 1_000_000,\n };\n\n if (isVideoConfig(frame)) {\n options.width = frame.codedWidth;\n options.height = frame.codedHeight;\n } else {\n options.channel_count = frame.numberOfChannels;\n options.samplerate = frame.sampleRate;\n options.hdlr = \"soun\";\n }\n\n if (!frame.description) throw new Error(\"missing frame description\");\n const desc = frame.description as ArrayBufferLike;\n\n if (codec === \"avc1\") {\n options.avcDecoderConfigRecord = desc;\n } else if (codec === \"hev1\") {\n options.hevcDecoderConfigRecord = desc;\n } else if (codec === \"Opus\") {\n // description is an identification header: https://datatracker.ietf.org/doc/html/rfc7845#section-5.1\n // The first 8 bytes are the magic string \"OpusHead\", followed by what we actually want.\n const dops = new MP4.BoxParser.dOpsBox();\n\n // Annoyingly, the header is little endian while MP4 is big endian, so we have to parse.\n dops.parse(new MP4.Stream(desc, 8, MP4.Stream.LITTLE_ENDIAN));\n\n options.description = dops;\n } else {\n throw new Error(`unsupported codec: ${codec}`);\n }\n\n const track = this.#mp4.addTrack(options);\n if (track == undefined) {\n throw new Error(\"failed to initialize MP4 track\");\n }\n if (frame.track === \"audio\") {\n this.audioTrack = track;\n } else {\n this.videoTrack = track;\n }\n\n if (!this.tracksReady) {\n return;\n }\n\n const buffer = MP4.ISOFile.writeInitializationSegment(\n this.#mp4.ftyp!,\n this.#mp4.moov!,\n 0,\n 0,\n );\n const data = new Uint8Array(buffer);\n const res = {\n type: \"init\",\n timestamp: 0,\n duration: 0,\n data,\n kind: frame.track,\n } as const;\n this.onData.execute(res);\n }\n\n frameBuffer: (EncodedChunk & {\n track: \"video\" | \"audio\";\n })[] = [];\n\n #enqueue(\n frame: EncodedChunk & {\n track: \"video\" | \"audio\";\n },\n ) {\n this.frameBuffer.push(frame);\n if (!this.tracksReady) {\n return;\n }\n for (const frame of this.frameBuffer) {\n this._enqueue(frame);\n }\n this.frameBuffer = [];\n }\n\n private _enqueue(\n frame: EncodedChunk & {\n track: \"video\" | \"audio\";\n },\n ) {\n const track = frame.track === \"audio\" ? this.audioTrack : this.videoTrack;\n if (!track) {\n throw new Error(\"track missing\");\n }\n\n // Check if we should create a new segment\n if (frame.track === \"video\") {\n if (frame.type == \"key\") {\n this.#videoSegment += 1;\n } else if (this.#videoSegment == 0) {\n throw new Error(\"must start with keyframe\");\n }\n } else {\n this.#audioSegment += 1;\n }\n\n // We need a one frame buffer to compute the duration\n if (frame.track === \"video\") {\n if (!this.#videoFrame) {\n this.#videoFrame = frame;\n return;\n }\n } else {\n if (!this.#audioFrame) {\n this.#audioFrame = frame;\n return;\n }\n }\n\n const bufferFrame =\n frame.track === \"video\" ? this.#videoFrame : this.#audioFrame;\n\n if (!bufferFrame) {\n throw new Error(\"bufferFrame missing\");\n }\n\n const duration = frame.timestamp - bufferFrame.timestamp;\n\n // TODO avoid this extra copy by writing to the mdat directly\n // ...which means changing mp4box.js to take an offset instead of ArrayBuffer\n\n const buffer = new ArrayBuffer(bufferFrame.byteLength);\n bufferFrame.copyTo(buffer);\n\n // Add the sample to the container\n this.#mp4.addSample(track, buffer, {\n duration,\n dts: bufferFrame.timestamp,\n cts: bufferFrame.timestamp,\n is_sync: bufferFrame.type == \"key\",\n });\n\n const stream = new MP4.Stream(undefined, 0, MP4.Stream.BIG_ENDIAN);\n\n // Moof and mdat atoms are written in pairs.\n // TODO remove the moof/mdat from the Box to reclaim memory once everything works\n for (;;) {\n const moof = this.#mp4.moofs.shift();\n const mdat = this.#mp4.mdats.shift();\n\n if (!moof && !mdat) break;\n if (!moof) throw new Error(\"moof missing\");\n if (!mdat) throw new Error(\"mdat missing\");\n\n moof.write(stream);\n mdat.write(stream);\n }\n\n // TODO avoid this extra copy by writing to the buffer provided in copyTo\n const data = new Uint8Array(stream.buffer);\n\n if (frame.track === \"video\") {\n this.#videoFrame = frame;\n } else {\n this.#audioFrame = frame;\n }\n\n const res = {\n type: bufferFrame.type,\n timestamp: bufferFrame.timestamp,\n kind: frame.track,\n duration,\n data,\n };\n this.onData.execute(res);\n }\n\n /* TODO flush the last frame\n\t#flush(controller: TransformStreamDefaultController<Chunk>) {\n\t\tif (this.#frame) {\n\t\t\t// TODO guess the duration\n\t\t\tthis.#enqueue(this.#frame, 0, controller)\n\t\t}\n\t}\n\t*/\n}\n\nfunction isDecoderConfig(\n frame: DecoderConfig | EncodedChunk,\n): frame is DecoderConfig {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (frame as DecoderConfig).codec !== undefined;\n}\n\nfunction isVideoConfig(frame: DecoderConfig): frame is VideoDecoderConfig {\n return (frame as VideoDecoderConfig).codedWidth !== undefined;\n}\n\nexport interface AudioDecoderConfig {\n codec: string;\n description?: ArrayBuffer | undefined;\n numberOfChannels: number;\n sampleRate: number;\n}\n\nexport interface VideoDecoderConfig {\n codec: string;\n codedHeight?: number | undefined;\n codedWidth?: number | undefined;\n description?: ArrayBuffer | undefined;\n displayAspectHeight?: number | undefined;\n displayAspectWidth?: number | undefined;\n optimizeForLatency?: boolean | undefined;\n}\n\ninterface EncodedAudioChunk {\n readonly byteLength: number;\n readonly duration: number | null;\n readonly timestamp: number;\n readonly type: EncodedAudioChunkType;\n copyTo(destination: ArrayBuffer): void;\n}\n\ntype EncodedAudioChunkType = \"delta\" | \"key\";\n\ninterface EncodedVideoChunk {\n readonly byteLength: number;\n readonly duration: number | null;\n readonly timestamp: number;\n readonly type: EncodedVideoChunkType;\n copyTo(destination: ArrayBuffer): void;\n}\n\ntype EncodedVideoChunkType = \"delta\" | \"key\";\n\nexport const mp4SupportedCodecs = [\"avc1\", \"opus\"] as const;\nexport type Mp4SupportedCodec = (typeof mp4SupportedCodecs)[number];\n"]}
|
|
1
|
+
{"version":3,"file":"container.js","sourceRoot":"","sources":["../../../../../../../rtp/src/extra/container/mp4/container.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n EncodedAudioPacketSource,\n EncodedPacket,\n EncodedVideoPacketSource,\n Mp4OutputFormat,\n NullTarget,\n Output,\n} from \"mediabunny\";\nimport { Event } from \"../../../imports/common\";\n\ntype DecoderConfig = AudioDecoderConfig | VideoDecoderConfig;\ntype EncodedChunk = EncodedAudioChunk | EncodedVideoChunk;\ntype TrackKind = \"audio\" | \"video\";\n\ninterface AudioChunkMetadata {\n decoderConfig: {\n codec: \"opus\";\n description?: Uint8Array;\n numberOfChannels: number;\n sampleRate: number;\n };\n}\n\ninterface VideoChunkMetadata {\n decoderConfig: {\n codec: string;\n codedHeight: number;\n codedWidth: number;\n description?: Uint8Array;\n displayAspectHeight?: number;\n displayAspectWidth?: number;\n };\n}\n\ninterface FragmentMeta {\n duration: number;\n kind: TrackKind;\n timestamp: number;\n type: DataType;\n}\n\nexport type DataType = \"init\" | \"delta\" | \"key\";\n\nexport interface Mp4Data {\n type: DataType;\n timestamp: number;\n duration: number;\n data: Uint8Array;\n kind: \"audio\" | \"video\";\n}\n\nexport class Mp4Container {\n #audioFrame?: (EncodedChunk & { track: TrackKind }) | undefined;\n #audioMeta?: AudioChunkMetadata;\n #audioSource?: EncodedAudioPacketSource;\n #currentFragment?: FragmentMeta;\n #ftyp?: Uint8Array;\n #initializationEmitted = false;\n #lastAudioDuration = 0;\n #lastMoof?: Uint8Array;\n #lastMoofTimestamp = 0;\n #lastVideoDuration = 0;\n #moov?: Uint8Array;\n #onDataOperation = Promise.resolve();\n #operation = Promise.resolve();\n #output?: Output<Mp4OutputFormat, NullTarget> | undefined;\n #startPromise?: Promise<void>;\n #stopPromise?: Promise<void>;\n #stopped = false;\n #videoFrame?: (EncodedChunk & { track: TrackKind }) | undefined;\n #videoMeta?: VideoChunkMetadata;\n #videoSource?: EncodedVideoPacketSource;\n audioTrack?: number;\n frameBuffer: (EncodedChunk & {\n track: TrackKind;\n })[] = [];\n onData = new Event<[Mp4Data | { eol: true }]>();\n videoTrack?: number;\n\n constructor(\n private props: {\n track: { audio: boolean; video: boolean };\n },\n ) {}\n\n get tracksReady() {\n let ready = true;\n if (this.props.track.audio && !this.audioTrack) {\n ready = false;\n }\n if (this.props.track.video && !this.videoTrack) {\n ready = false;\n }\n return ready;\n }\n\n write(\n frame: (DecoderConfig | EncodedChunk) & {\n track: TrackKind;\n },\n ) {\n if (isDecoderConfig(frame)) {\n return this.#init(frame);\n } else {\n return this.#enqueue(frame);\n }\n }\n\n #init(\n frame: DecoderConfig & {\n track: TrackKind;\n },\n ) {\n if (frame.track === \"audio\") {\n if (isVideoConfig(frame)) {\n throw new Error(\"audio track requires an audio decoder config\");\n }\n if (frame.codec !== \"opus\") {\n throw new Error(`unsupported codec: ${frame.codec}`);\n }\n this.#audioMeta = {\n decoderConfig: {\n codec: \"opus\",\n description:\n frame.description != undefined\n ? new Uint8Array(frame.description)\n : undefined,\n numberOfChannels: frame.numberOfChannels,\n sampleRate: frame.sampleRate,\n },\n };\n this.audioTrack = 1;\n } else {\n if (!isVideoConfig(frame)) {\n throw new Error(\"video track requires a video decoder config\");\n }\n if (!frame.codec.startsWith(\"avc1\")) {\n throw new Error(`unsupported codec: ${frame.codec}`);\n }\n if (frame.codedWidth == undefined || frame.codedHeight == undefined) {\n throw new Error(\"missing coded video dimensions\");\n }\n this.#videoMeta = {\n decoderConfig: {\n codec: frame.codec,\n codedHeight: frame.codedHeight,\n codedWidth: frame.codedWidth,\n description:\n frame.description != undefined\n ? new Uint8Array(frame.description)\n : undefined,\n displayAspectHeight: frame.displayAspectHeight,\n displayAspectWidth: frame.displayAspectWidth,\n },\n };\n this.videoTrack = this.props.track.audio ? 2 : 1;\n }\n\n if (!this.tracksReady) {\n return;\n }\n\n void this.#enqueueOperation(async () => {\n await this.#ensureOutputStarted();\n await this.#drainFrameBuffer();\n });\n }\n\n #enqueue(\n frame: EncodedChunk & {\n track: TrackKind;\n },\n ) {\n this.frameBuffer.push(frame);\n if (!this.tracksReady) {\n return;\n }\n void this.#enqueueOperation(async () => {\n await this.#ensureOutputStarted();\n await this.#drainFrameBuffer();\n });\n }\n\n stop() {\n if (this.#stopPromise) {\n return this.#stopPromise;\n }\n\n this.#stopped = true;\n this.#stopPromise = this.#enqueueOperation(async () => {\n if (!this.tracksReady) {\n this.frameBuffer = [];\n return;\n }\n\n await this.#ensureOutputStarted();\n await this.#drainFrameBuffer();\n await this.#flushPendingFrame(\"audio\");\n await this.#flushPendingFrame(\"video\");\n\n this.#audioSource?.close();\n this.#videoSource?.close();\n if (this.#output) {\n await this.#output.finalize();\n }\n await this.#flushOnData();\n });\n return this.#stopPromise;\n }\n\n async #drainFrameBuffer() {\n const frames = this.frameBuffer;\n this.frameBuffer = [];\n\n for (const frame of frames) {\n await this.#processFrame(frame);\n }\n }\n\n async #ensureOutputStarted() {\n if (this.#startPromise) {\n await this.#startPromise;\n return;\n }\n if (!this.tracksReady) {\n return;\n }\n\n const output = new Output({\n format: new Mp4OutputFormat({\n fastStart: \"fragmented\",\n minimumFragmentDuration: 0,\n onFtyp: (data) => {\n this.#ftyp = copyBytes(data);\n },\n onMdat: (data) => {\n if (!this.#lastMoof) {\n throw new Error(\"moof missing before mdat\");\n }\n\n const fragment = this.#currentFragment;\n const timestamp = fragment\n ? fragment.timestamp\n : Math.round(this.#lastMoofTimestamp * 1_000_000);\n const duration = fragment?.duration ?? 0;\n const kind = fragment?.kind ?? this.#defaultKind();\n const type = fragment?.type ?? \"key\";\n const segment = concatBytes(this.#lastMoof, data);\n\n this.#currentFragment = undefined;\n this.#lastMoof = undefined;\n\n void this.#emitData({\n type,\n timestamp,\n duration,\n data: segment,\n kind,\n });\n },\n onMoof: (data, _position, timestamp) => {\n this.#lastMoof = copyBytes(data);\n this.#lastMoofTimestamp = timestamp;\n },\n onMoov: (data) => {\n this.#moov = copyBytes(data);\n this.#emitInitializationSegment();\n },\n }),\n target: new NullTarget(),\n });\n\n if (this.#audioMeta) {\n this.#audioSource = new EncodedAudioPacketSource(\"opus\");\n output.addAudioTrack(this.#audioSource);\n }\n if (this.#videoMeta) {\n this.#videoSource = new EncodedVideoPacketSource(\"avc\");\n output.addVideoTrack(this.#videoSource);\n }\n\n this.#output = output;\n this.#startPromise = output.start();\n await this.#startPromise;\n }\n\n #emitInitializationSegment() {\n if (this.#initializationEmitted || !this.#ftyp || !this.#moov) {\n return;\n }\n\n this.#initializationEmitted = true;\n void this.#emitData({\n type: \"init\",\n timestamp: 0,\n duration: 0,\n data: concatBytes(this.#ftyp, this.#moov),\n kind: this.#defaultKind(),\n });\n }\n\n #emitData(data: Mp4Data | { eol: true }) {\n const next = this.#onDataOperation.then(() => this.onData.execute(data));\n this.#onDataOperation = next;\n return next;\n }\n\n async #flushOnData() {\n await this.#onDataOperation;\n }\n\n #enqueueOperation(operation: () => Promise<void>) {\n const next = this.#operation.then(operation);\n this.#operation = next;\n return next;\n }\n\n #defaultKind(): TrackKind {\n return this.props.track.video ? \"video\" : \"audio\";\n }\n\n async #flushPendingFrame(track: TrackKind) {\n const buffered = track === \"audio\" ? this.#audioFrame : this.#videoFrame;\n if (!buffered) {\n return;\n }\n\n const duration =\n track === \"audio\" ? this.#lastAudioDuration : this.#lastVideoDuration;\n await this.#writePacket(buffered, duration);\n\n if (track === \"audio\") {\n this.#audioFrame = undefined;\n } else {\n this.#videoFrame = undefined;\n }\n }\n\n async #processFrame(\n frame: EncodedChunk & {\n track: TrackKind;\n },\n ) {\n const buffered =\n frame.track === \"audio\" ? this.#audioFrame : this.#videoFrame;\n if (!buffered) {\n if (frame.track === \"audio\") {\n this.#audioFrame = frame;\n } else {\n this.#videoFrame = frame;\n }\n return;\n }\n\n const duration = Math.max(frame.timestamp - buffered.timestamp, 0);\n await this.#writePacket(buffered, duration);\n\n if (frame.track === \"audio\") {\n this.#audioFrame = frame;\n this.#lastAudioDuration = duration;\n } else {\n this.#videoFrame = frame;\n this.#lastVideoDuration = duration;\n }\n }\n\n async #writePacket(\n frame: EncodedChunk & {\n track: TrackKind;\n },\n duration: number,\n ) {\n const data = new Uint8Array(frame.byteLength);\n frame.copyTo(data.buffer);\n\n const packet = new EncodedPacket(\n data,\n frame.type,\n frame.timestamp / 1_000_000,\n duration / 1_000_000,\n );\n\n if (frame.track === \"audio\") {\n if (!this.#audioSource || !this.#audioMeta) {\n throw new Error(\"audio track missing\");\n }\n\n await this.#audioSource.add(packet, this.#audioMeta);\n } else {\n if (!this.#videoSource || !this.#videoMeta) {\n throw new Error(\"video track missing\");\n }\n\n await this.#videoSource.add(packet, this.#videoMeta);\n }\n\n this.#rememberFragmentPacket(\n frame.track,\n frame.type,\n frame.timestamp,\n duration,\n );\n }\n\n #rememberFragmentPacket(\n kind: TrackKind,\n type: EncodedChunk[\"type\"],\n timestamp: number,\n duration: number,\n ) {\n const end = timestamp + duration;\n if (!this.#currentFragment) {\n this.#currentFragment = {\n kind,\n type,\n timestamp,\n duration,\n };\n return;\n }\n\n if (kind === \"video\" && type === \"key\") {\n this.#currentFragment.type = \"key\";\n }\n\n const fragmentEnd = Math.max(\n this.#currentFragment.timestamp + this.#currentFragment.duration,\n end,\n );\n this.#currentFragment.duration =\n fragmentEnd - this.#currentFragment.timestamp;\n }\n}\n\nfunction isDecoderConfig(\n frame: DecoderConfig | EncodedChunk,\n): frame is DecoderConfig {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (frame as DecoderConfig).codec !== undefined;\n}\n\nfunction isVideoConfig(frame: DecoderConfig): frame is VideoDecoderConfig {\n return (frame as VideoDecoderConfig).codedWidth !== undefined;\n}\n\nexport interface AudioDecoderConfig {\n codec: string;\n description?: ArrayBuffer | undefined;\n numberOfChannels: number;\n sampleRate: number;\n}\n\nexport interface VideoDecoderConfig {\n codec: string;\n codedHeight?: number | undefined;\n codedWidth?: number | undefined;\n description?: ArrayBuffer | undefined;\n displayAspectHeight?: number | undefined;\n displayAspectWidth?: number | undefined;\n optimizeForLatency?: boolean | undefined;\n}\n\ninterface EncodedAudioChunk {\n readonly byteLength: number;\n readonly duration: number | null;\n readonly timestamp: number;\n readonly type: EncodedAudioChunkType;\n copyTo(destination: ArrayBuffer): void;\n}\n\ntype EncodedAudioChunkType = \"delta\" | \"key\";\n\ninterface EncodedVideoChunk {\n readonly byteLength: number;\n readonly duration: number | null;\n readonly timestamp: number;\n readonly type: EncodedVideoChunkType;\n copyTo(destination: ArrayBuffer): void;\n}\n\ntype EncodedVideoChunkType = \"delta\" | \"key\";\n\nexport const mp4SupportedCodecs = [\"avc1\", \"opus\"] as const;\nexport type Mp4SupportedCodec = (typeof mp4SupportedCodecs)[number];\n\nfunction concatBytes(...parts: Uint8Array[]) {\n const size = parts.reduce((sum, part) => sum + part.byteLength, 0);\n const combined = new Uint8Array(size);\n let offset = 0;\n\n for (const part of parts) {\n combined.set(part, offset);\n offset += part.byteLength;\n }\n\n return combined;\n}\n\nfunction copyBytes(data: Uint8Array) {\n return new Uint8Array(data);\n}\n"]}
|