werift 0.15.11 → 0.16.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/lib/common/src/index.d.ts +2 -0
- package/lib/common/src/index.js +2 -0
- package/lib/common/src/index.js.map +1 -1
- package/lib/common/src/log.d.ts +11 -0
- package/lib/common/src/log.js +17 -0
- package/lib/common/src/log.js.map +1 -0
- package/lib/common/src/type.d.ts +3 -0
- package/lib/common/src/type.js +3 -0
- package/lib/common/src/type.js.map +1 -0
- package/lib/dtls/src/context/cipher.js.map +1 -1
- package/lib/dtls/src/flight/server/flight2.js +10 -0
- package/lib/dtls/src/flight/server/flight2.js.map +1 -1
- package/lib/ice/src/ice.d.ts +1 -0
- package/lib/ice/src/ice.js +4 -0
- package/lib/ice/src/ice.js.map +1 -1
- package/lib/rtp/src/codec/index.d.ts +18 -0
- package/lib/rtp/src/codec/index.js +81 -0
- package/lib/rtp/src/codec/index.js.map +1 -0
- package/lib/rtp/src/codec/vp8.d.ts +5 -3
- package/lib/rtp/src/codec/vp8.js +19 -5
- package/lib/rtp/src/codec/vp8.js.map +1 -1
- package/lib/rtp/src/container/webm.d.ts +6 -1
- package/lib/rtp/src/container/webm.js +9 -2
- package/lib/rtp/src/container/webm.js.map +1 -1
- package/lib/rtp/src/index.d.ts +2 -6
- package/lib/rtp/src/index.js +2 -6
- package/lib/rtp/src/index.js.map +1 -1
- package/lib/rtp/src/processor/base.d.ts +3 -1
- package/lib/rtp/src/processor/base.js +19 -6
- package/lib/rtp/src/processor/base.js.map +1 -1
- package/lib/rtp/src/processor/jitterBuffer.js +1 -1
- package/lib/rtp/src/processor/jitterBuffer.js.map +1 -1
- package/lib/rtp/src/processor/lipsync.js +22 -2
- package/lib/rtp/src/processor/lipsync.js.map +1 -1
- package/lib/rtp/src/processor/webm.d.ts +11 -7
- package/lib/rtp/src/processor/webm.js +16 -41
- package/lib/rtp/src/processor/webm.js.map +1 -1
- package/lib/rtp/src/processor_v2/depacketizer.d.ts +17 -0
- package/lib/rtp/src/processor_v2/depacketizer.js +84 -0
- package/lib/rtp/src/processor_v2/depacketizer.js.map +1 -0
- package/lib/rtp/src/processor_v2/index.d.ts +4 -0
- package/lib/rtp/src/processor_v2/index.js +21 -0
- package/lib/rtp/src/processor_v2/index.js.map +1 -0
- package/lib/rtp/src/processor_v2/jitterBuffer.d.ts +33 -0
- package/lib/rtp/src/processor_v2/jitterBuffer.js +154 -0
- package/lib/rtp/src/processor_v2/jitterBuffer.js.map +1 -0
- package/lib/rtp/src/processor_v2/source/base.d.ts +8 -0
- package/lib/rtp/src/processor_v2/source/base.js +16 -0
- package/lib/rtp/src/processor_v2/source/base.js.map +1 -0
- package/lib/rtp/src/processor_v2/source/index.d.ts +2 -0
- package/lib/rtp/src/processor_v2/source/index.js +6 -0
- package/lib/rtp/src/processor_v2/source/index.js.map +1 -0
- package/lib/rtp/src/processor_v2/source/rtp.d.ts +14 -0
- package/lib/rtp/src/processor_v2/source/rtp.js +24 -0
- package/lib/rtp/src/processor_v2/source/rtp.js.map +1 -0
- package/lib/rtp/src/processor_v2/webmLive.d.ts +51 -0
- package/lib/rtp/src/processor_v2/webmLive.js +154 -0
- package/lib/rtp/src/processor_v2/webmLive.js.map +1 -0
- package/lib/webrtc/src/media/rtpTransceiver.d.ts +1 -1
- package/lib/webrtc/src/media/rtpTransceiver.js +3 -2
- package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
- package/lib/webrtc/src/nonstandard/recorder/index.d.ts +5 -1
- package/lib/webrtc/src/nonstandard/recorder/index.js +2 -2
- package/lib/webrtc/src/nonstandard/recorder/index.js.map +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/index.d.ts +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/index.js +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/index.js.map +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.d.ts +3 -3
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js +61 -41
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js.map +1 -1
- package/lib/webrtc/src/peerConnection.d.ts +9 -1
- package/lib/webrtc/src/peerConnection.js +9 -1
- package/lib/webrtc/src/peerConnection.js.map +1 -1
- package/package.json +2 -2
- package/src/media/rtpTransceiver.ts +4 -2
- package/src/nonstandard/recorder/index.ts +6 -2
- package/src/nonstandard/recorder/writer/index.ts +1 -1
- package/src/nonstandard/recorder/writer/webm.ts +105 -57
- package/src/peerConnection.ts +26 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rtp.js","sourceRoot":"","sources":["../../../../../../rtp/src/processor_v2/source/rtp.ts"],"names":[],"mappings":";;;AAAA,qCAA+C;AAG/C,iCAAsC;AAOtC,MAAa,eAAgB,SAAQ,mBAAuB;IAG1D,YAAY,EAAsB,EAAE,UAAoC,EAAE;QACxE,KAAK,EAAE,CAAC;QAHO,aAAQ,GAAG,IAAI,uBAAa,EAAE,CAAC;QAK9C,EAAE,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE;YACnB,IACE,OAAO,CAAC,WAAW,IAAI,SAAS;gBAChC,OAAO,CAAC,WAAW,KAAK,GAAG,CAAC,MAAM,CAAC,WAAW,EAC9C;gBACA,OAAO;aACR;YAED,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;CACF;AAtBD,0CAsBC","sourcesContent":["import Event, { EventDisposer } from \"rx.mini\";\n\nimport { RtpPacket } from \"../../rtp/rtp\";\nimport { SourceStream } from \"./base\";\n\nexport interface RtpOutput {\n rtp?: RtpPacket;\n eol?: boolean;\n}\n\nexport class RtpSourceStream extends SourceStream<RtpOutput> {\n private readonly disposer = new EventDisposer();\n\n constructor(ev: Event<[RtpPacket]>, options: { payloadType?: number } = {}) {\n super();\n\n ev.subscribe((rtp) => {\n if (\n options.payloadType != undefined &&\n options.payloadType !== rtp.header.payloadType\n ) {\n return;\n }\n\n this.write({ rtp });\n }).disposer(this.disposer);\n }\n\n async stop() {\n this.controller.enqueue({ eol: true });\n this.disposer.dispose();\n }\n}\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { ReadableStream, WritableStream } from "stream/web";
|
|
4
|
+
import { SupportedCodec } from "../container/webm";
|
|
5
|
+
import { DepacketizerOutput } from "./depacketizer";
|
|
6
|
+
export declare type WebmLiveInput = DepacketizerOutput;
|
|
7
|
+
export declare type WebmLiveOutput = {
|
|
8
|
+
packet?: Buffer;
|
|
9
|
+
eol?: {
|
|
10
|
+
/**ms */
|
|
11
|
+
duration: number;
|
|
12
|
+
durationElement: Uint8Array;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export interface WebmLiveOption {
|
|
16
|
+
/**ms */
|
|
17
|
+
duration?: number;
|
|
18
|
+
}
|
|
19
|
+
export declare class WebmLiveSink {
|
|
20
|
+
tracks: {
|
|
21
|
+
width?: number;
|
|
22
|
+
height?: number;
|
|
23
|
+
kind: "audio" | "video";
|
|
24
|
+
codec: SupportedCodec;
|
|
25
|
+
clockRate: number;
|
|
26
|
+
trackNumber: number;
|
|
27
|
+
}[];
|
|
28
|
+
private options;
|
|
29
|
+
private builder;
|
|
30
|
+
private queue;
|
|
31
|
+
private relativeTimestamp;
|
|
32
|
+
private timestamps;
|
|
33
|
+
private cuePoints;
|
|
34
|
+
private position;
|
|
35
|
+
stopped: boolean;
|
|
36
|
+
audioStream: WritableStream<WebmLiveInput>;
|
|
37
|
+
videoStream: WritableStream<WebmLiveInput>;
|
|
38
|
+
webmStream: ReadableStream<WebmLiveOutput>;
|
|
39
|
+
private controller;
|
|
40
|
+
constructor(tracks: {
|
|
41
|
+
width?: number;
|
|
42
|
+
height?: number;
|
|
43
|
+
kind: "audio" | "video";
|
|
44
|
+
codec: SupportedCodec;
|
|
45
|
+
clockRate: number;
|
|
46
|
+
trackNumber: number;
|
|
47
|
+
}[], options?: WebmLiveOption);
|
|
48
|
+
private init;
|
|
49
|
+
private onFrameReceived;
|
|
50
|
+
private stop;
|
|
51
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.WebmLiveSink = void 0;
|
|
7
|
+
const debug_1 = __importDefault(require("debug"));
|
|
8
|
+
const web_1 = require("stream/web");
|
|
9
|
+
const __1 = require("..");
|
|
10
|
+
const webm_1 = require("../container/webm");
|
|
11
|
+
const sourcePath = `werift-rtp : packages/rtp/src/processor_v2/webmLive.ts`;
|
|
12
|
+
const log = (0, debug_1.default)(sourcePath);
|
|
13
|
+
class WebmLiveSink {
|
|
14
|
+
constructor(tracks, options = {}) {
|
|
15
|
+
this.tracks = tracks;
|
|
16
|
+
this.options = options;
|
|
17
|
+
this.queue = new __1.PromiseQueue();
|
|
18
|
+
this.relativeTimestamp = 0;
|
|
19
|
+
this.timestamps = {};
|
|
20
|
+
this.cuePoints = [];
|
|
21
|
+
this.position = 0;
|
|
22
|
+
this.stopped = false;
|
|
23
|
+
this.builder = new webm_1.WEBMBuilder(tracks);
|
|
24
|
+
tracks.forEach((t) => {
|
|
25
|
+
this.timestamps[t.trackNumber] = new ClusterTimestamp(t.clockRate);
|
|
26
|
+
});
|
|
27
|
+
const createStream = (trackNumber) => new web_1.WritableStream({
|
|
28
|
+
write: ({ frame, eol }) => {
|
|
29
|
+
if (this.stopped)
|
|
30
|
+
return;
|
|
31
|
+
if (!frame) {
|
|
32
|
+
if (eol) {
|
|
33
|
+
this.stop();
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
this.queue.push(() => this.onFrameReceived({ ...frame, trackNumber }));
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
const audioTrack = tracks.find((t) => t.kind === "audio");
|
|
41
|
+
if (audioTrack) {
|
|
42
|
+
this.audioStream = createStream(audioTrack.trackNumber);
|
|
43
|
+
}
|
|
44
|
+
const videoTrack = tracks.find((t) => t.kind === "video");
|
|
45
|
+
if (videoTrack) {
|
|
46
|
+
this.videoStream = createStream(videoTrack.trackNumber);
|
|
47
|
+
}
|
|
48
|
+
this.webmStream = new web_1.ReadableStream({
|
|
49
|
+
start: (controller) => {
|
|
50
|
+
this.controller = controller;
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
this.queue.push(() => this.init());
|
|
54
|
+
}
|
|
55
|
+
async init() {
|
|
56
|
+
const staticPart = Buffer.concat([
|
|
57
|
+
this.builder.ebmlHeader,
|
|
58
|
+
this.builder.createSegment(this.options.duration),
|
|
59
|
+
]);
|
|
60
|
+
this.controller.enqueue({ packet: staticPart });
|
|
61
|
+
this.position += staticPart.length;
|
|
62
|
+
const video = this.tracks.find((t) => t.kind === "video");
|
|
63
|
+
if (video) {
|
|
64
|
+
this.cuePoints.push(new CuePoint(this.builder, video.trackNumber, 0.0, this.position));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async onFrameReceived(frame) {
|
|
68
|
+
const track = this.tracks.find((t) => t.trackNumber === frame.trackNumber);
|
|
69
|
+
if (!track) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const timestampManager = this.timestamps[track.trackNumber];
|
|
73
|
+
let elapsed = timestampManager.update(frame.timestamp);
|
|
74
|
+
if ((track.kind === "video" && frame.isKeyframe) ||
|
|
75
|
+
elapsed > MaxSinged16Int) {
|
|
76
|
+
this.relativeTimestamp += elapsed;
|
|
77
|
+
const cluster = this.builder.createCluster(this.relativeTimestamp);
|
|
78
|
+
this.controller.enqueue({ packet: Buffer.from(cluster) });
|
|
79
|
+
if (elapsed !== 0) {
|
|
80
|
+
this.cuePoints.push(new CuePoint(this.builder, track.trackNumber, this.relativeTimestamp, this.position));
|
|
81
|
+
}
|
|
82
|
+
this.position += cluster.length;
|
|
83
|
+
Object.values(this.timestamps).forEach((t) => t.reset());
|
|
84
|
+
elapsed = timestampManager.update(frame.timestamp);
|
|
85
|
+
}
|
|
86
|
+
const block = this.builder.createSimpleBlock(frame.data, frame.isKeyframe, track.trackNumber, elapsed);
|
|
87
|
+
this.controller.enqueue({ packet: block });
|
|
88
|
+
this.position += block.length;
|
|
89
|
+
const [cuePoint] = this.cuePoints.slice(-1);
|
|
90
|
+
if (cuePoint) {
|
|
91
|
+
cuePoint.blockNumber++;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async stop() {
|
|
95
|
+
if (this.stopped) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
this.stopped = true;
|
|
99
|
+
log("stop");
|
|
100
|
+
const cues = this.builder.createCues(this.cuePoints.map((c) => c.build()));
|
|
101
|
+
this.controller.enqueue({ packet: Buffer.from(cues) });
|
|
102
|
+
const latestTimestamp = Object.values(this.timestamps).sort((a, b) => a.elapsed - b.elapsed)[0].elapsed;
|
|
103
|
+
const duration = this.relativeTimestamp + latestTimestamp;
|
|
104
|
+
const durationElement = this.builder.createDuration(duration);
|
|
105
|
+
this.controller.enqueue({ eol: { duration, durationElement } });
|
|
106
|
+
this.controller.close();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.WebmLiveSink = WebmLiveSink;
|
|
110
|
+
class ClusterTimestamp {
|
|
111
|
+
constructor(clockRate) {
|
|
112
|
+
this.clockRate = clockRate;
|
|
113
|
+
this.elapsed = 0;
|
|
114
|
+
}
|
|
115
|
+
reset() {
|
|
116
|
+
this.baseTimestamp = undefined;
|
|
117
|
+
}
|
|
118
|
+
update(timestamp) {
|
|
119
|
+
if (this.baseTimestamp == undefined) {
|
|
120
|
+
this.baseTimestamp = timestamp;
|
|
121
|
+
}
|
|
122
|
+
const rotate = Math.abs(timestamp - this.baseTimestamp) > (Max32Uint / 4) * 3;
|
|
123
|
+
if (rotate) {
|
|
124
|
+
log("rotate", { baseTimestamp: this.baseTimestamp, timestamp });
|
|
125
|
+
}
|
|
126
|
+
const elapsed = rotate
|
|
127
|
+
? timestamp + Max32Uint - this.baseTimestamp
|
|
128
|
+
: timestamp - this.baseTimestamp;
|
|
129
|
+
this.elapsed = (0, __1.int)((elapsed / this.clockRate) * 1000);
|
|
130
|
+
return this.elapsed;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
class CuePoint {
|
|
134
|
+
constructor(builder, trackNumber, relativeTimestamp, position) {
|
|
135
|
+
this.builder = builder;
|
|
136
|
+
this.trackNumber = trackNumber;
|
|
137
|
+
this.relativeTimestamp = relativeTimestamp;
|
|
138
|
+
this.position = position;
|
|
139
|
+
/**
|
|
140
|
+
* cuesの後のclusterのあるべき位置
|
|
141
|
+
* cuesはclusterの前に挿入される
|
|
142
|
+
*/
|
|
143
|
+
this.cuesLength = 0;
|
|
144
|
+
this.blockNumber = 0;
|
|
145
|
+
}
|
|
146
|
+
build() {
|
|
147
|
+
return this.builder.createCuePoint(this.relativeTimestamp, this.trackNumber, this.position - 48 + this.cuesLength, this.blockNumber);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**4294967295 */
|
|
151
|
+
const Max32Uint = Number(0x01n << 32n) - 1;
|
|
152
|
+
/**32767 */
|
|
153
|
+
const MaxSinged16Int = (0x01 << 16) / 2 - 1;
|
|
154
|
+
//# sourceMappingURL=webmLive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webmLive.js","sourceRoot":"","sources":["../../../../../rtp/src/processor_v2/webmLive.ts"],"names":[],"mappings":";;;;;;AAAA,kDAA0B;AAC1B,oCAIoB;AAEpB,0BAAuC;AACvC,4CAAgE;AAGhE,MAAM,UAAU,GAAG,wDAAwD,CAAC;AAC5E,MAAM,GAAG,GAAG,IAAA,eAAK,EAAC,UAAU,CAAC,CAAC;AAkB9B,MAAa,YAAY;IAavB,YACS,MAOJ,EACK,UAA0B,EAAE;QAR7B,WAAM,GAAN,MAAM,CAOV;QACK,YAAO,GAAP,OAAO,CAAqB;QApB9B,UAAK,GAAG,IAAI,gBAAY,EAAE,CAAC;QAC3B,sBAAiB,GAAG,CAAC,CAAC;QACtB,eAAU,GAAuC,EAAE,CAAC;QACpD,cAAS,GAAe,EAAE,CAAC;QAC3B,aAAQ,GAAG,CAAC,CAAC;QACrB,YAAO,GAAG,KAAK,CAAC;QAiBd,IAAI,CAAC,OAAO,GAAG,IAAI,kBAAW,CAAC,MAAM,CAAC,CAAC;QAEvC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YACnB,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,IAAI,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;QAEH,MAAM,YAAY,GAAG,CAAC,WAAmB,EAAE,EAAE,CAC3C,IAAI,oBAAc,CAAC;YACjB,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAiB,EAAE,EAAE;gBACvC,IAAI,IAAI,CAAC,OAAO;oBAAE,OAAO;gBACzB,IAAI,CAAC,KAAK,EAAE;oBACV,IAAI,GAAG,EAAE;wBACP,IAAI,CAAC,IAAI,EAAE,CAAC;qBACb;oBACD,OAAO;iBACR;gBAED,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CACnB,IAAI,CAAC,eAAe,CAAC,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,CAAC,CAChD,CAAC;YACJ,CAAC;SACF,CAAC,CAAC;QAEL,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QAC1D,IAAI,UAAU,EAAE;YACd,IAAI,CAAC,WAAW,GAAG,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;SACzD;QAED,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QAC1D,IAAI,UAAU,EAAE;YACd,IAAI,CAAC,WAAW,GAAG,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;SACzD;QAED,IAAI,CAAC,UAAU,GAAG,IAAI,oBAAc,CAAiB;YACnD,KAAK,EAAE,CAAC,UAAU,EAAE,EAAE;gBACpB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;YAC/B,CAAC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;YAC/B,IAAI,CAAC,OAAO,CAAC,UAAU;YACvB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;SAClD,CAAC,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,QAAQ,IAAI,UAAU,CAAC,MAAM,CAAC;QAEnC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QAC1D,IAAI,KAAK,EAAE;YACT,IAAI,CAAC,SAAS,CAAC,IAAI,CACjB,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAClE,CAAC;SACH;IACH,CAAC;IAEO,KAAK,CAAC,eAAe,CAC3B,KAAuD;QAEvD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW,CAAC,CAAC;QAC3E,IAAI,CAAC,KAAK,EAAE;YACV,OAAO;SACR;QAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC5D,IAAI,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAEvD,IACE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,UAAU,CAAC;YAC5C,OAAO,GAAG,cAAc,EACxB;YACA,IAAI,CAAC,iBAAiB,IAAI,OAAO,CAAC;YAElC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YACnE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAE1D,IAAI,OAAO,KAAK,CAAC,EAAE;gBACjB,IAAI,CAAC,SAAS,CAAC,IAAI,CACjB,IAAI,QAAQ,CACV,IAAI,CAAC,OAAO,EACZ,KAAK,CAAC,WAAW,EACjB,IAAI,CAAC,iBAAiB,EACtB,IAAI,CAAC,QAAQ,CACd,CACF,CAAC;aACH;YACD,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;YAChC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YACzD,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;SACpD;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAC1C,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,WAAW,EACjB,OAAO,CACR,CAAC;QACF,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;QAC9B,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,IAAI,QAAQ,EAAE;YACZ,QAAQ,CAAC,WAAW,EAAE,CAAC;SACxB;IACH,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,IAAI,IAAI,CAAC,OAAO,EAAE;YAChB,OAAO;SACR;QACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QAEpB,GAAG,CAAC,MAAM,CAAC,CAAC;QAEZ,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC3E,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEvD,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CACzD,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAChC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QACb,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,GAAG,eAAe,CAAC;QAC1D,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC9D,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE,EAAE,CAAC,CAAC;QAEhE,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;CACF;AAvJD,oCAuJC;AAED,MAAM,gBAAgB;IAIpB,YAAmB,SAAiB;QAAjB,cAAS,GAAT,SAAS,CAAQ;QAFpC,YAAO,GAAG,CAAC,CAAC;IAE2B,CAAC;IAExC,KAAK;QACH,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;IACjC,CAAC;IAED,MAAM,CAAC,SAAiB;QACtB,IAAI,IAAI,CAAC,aAAa,IAAI,SAAS,EAAE;YACnC,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;SAChC;QACD,MAAM,MAAM,GACV,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAEjE,IAAI,MAAM,EAAE;YACV,GAAG,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,EAAE,CAAC,CAAC;SACjE;QAED,MAAM,OAAO,GAAG,MAAM;YACpB,CAAC,CAAC,SAAS,GAAG,SAAS,GAAG,IAAI,CAAC,aAAa;YAC5C,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC;QAEnC,IAAI,CAAC,OAAO,GAAG,IAAA,OAAG,EAAC,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF;AAED,MAAM,QAAQ;IAQZ,YACmB,OAAoB,EACpB,WAAmB,EACnB,iBAAyB,EACnC,QAAgB;QAHN,YAAO,GAAP,OAAO,CAAa;QACpB,gBAAW,GAAX,WAAW,CAAQ;QACnB,sBAAiB,GAAjB,iBAAiB,CAAQ;QACnC,aAAQ,GAAR,QAAQ,CAAQ;QAXzB;;;WAGG;QACH,eAAU,GAAG,CAAC,CAAC;QACf,gBAAW,GAAG,CAAC,CAAC;IAOb,CAAC;IAEJ,KAAK;QACH,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAChC,IAAI,CAAC,iBAAiB,EACtB,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,EACpC,IAAI,CAAC,WAAW,CACjB,CAAC;IACJ,CAAC;CACF;AAED,gBAAgB;AAChB,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;AAC3C,WAAW;AACX,MAAM,cAAc,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC","sourcesContent":["import debug from \"debug\";\nimport {\n ReadableStream,\n ReadableStreamController,\n WritableStream,\n} from \"stream/web\";\n\nimport { int, PromiseQueue } from \"..\";\nimport { SupportedCodec, WEBMBuilder } from \"../container/webm\";\nimport { DepacketizerOutput } from \"./depacketizer\";\n\nconst sourcePath = `werift-rtp : packages/rtp/src/processor_v2/webmLive.ts`;\nconst log = debug(sourcePath);\n\nexport type WebmLiveInput = DepacketizerOutput;\n\nexport type WebmLiveOutput = {\n packet?: Buffer;\n eol?: {\n /**ms */\n duration: number;\n durationElement: Uint8Array;\n };\n};\n\nexport interface WebmLiveOption {\n /**ms */\n duration?: number;\n}\n\nexport class WebmLiveSink {\n private builder: WEBMBuilder;\n private queue = new PromiseQueue();\n private relativeTimestamp = 0;\n private timestamps: { [pt: number]: ClusterTimestamp } = {};\n private cuePoints: CuePoint[] = [];\n private position = 0;\n stopped = false;\n audioStream!: WritableStream<WebmLiveInput>;\n videoStream!: WritableStream<WebmLiveInput>;\n webmStream: ReadableStream<WebmLiveOutput>;\n private controller!: ReadableStreamController<WebmLiveOutput>;\n\n constructor(\n public tracks: {\n width?: number;\n height?: number;\n kind: \"audio\" | \"video\";\n codec: SupportedCodec;\n clockRate: number;\n trackNumber: number;\n }[],\n private options: WebmLiveOption = {}\n ) {\n this.builder = new WEBMBuilder(tracks);\n\n tracks.forEach((t) => {\n this.timestamps[t.trackNumber] = new ClusterTimestamp(t.clockRate);\n });\n\n const createStream = (trackNumber: number) =>\n new WritableStream({\n write: ({ frame, eol }: WebmLiveInput) => {\n if (this.stopped) return;\n if (!frame) {\n if (eol) {\n this.stop();\n }\n return;\n }\n\n this.queue.push(() =>\n this.onFrameReceived({ ...frame, trackNumber })\n );\n },\n });\n\n const audioTrack = tracks.find((t) => t.kind === \"audio\");\n if (audioTrack) {\n this.audioStream = createStream(audioTrack.trackNumber);\n }\n\n const videoTrack = tracks.find((t) => t.kind === \"video\");\n if (videoTrack) {\n this.videoStream = createStream(videoTrack.trackNumber);\n }\n\n this.webmStream = new ReadableStream<WebmLiveOutput>({\n start: (controller) => {\n this.controller = controller;\n },\n });\n\n this.queue.push(() => this.init());\n }\n\n private async init() {\n const staticPart = Buffer.concat([\n this.builder.ebmlHeader,\n this.builder.createSegment(this.options.duration),\n ]);\n this.controller.enqueue({ packet: staticPart });\n this.position += staticPart.length;\n\n const video = this.tracks.find((t) => t.kind === \"video\");\n if (video) {\n this.cuePoints.push(\n new CuePoint(this.builder, video.trackNumber, 0.0, this.position)\n );\n }\n }\n\n private async onFrameReceived(\n frame: WebmLiveInput[\"frame\"] & { trackNumber: number }\n ) {\n const track = this.tracks.find((t) => t.trackNumber === frame.trackNumber);\n if (!track) {\n return;\n }\n\n const timestampManager = this.timestamps[track.trackNumber];\n let elapsed = timestampManager.update(frame.timestamp);\n\n if (\n (track.kind === \"video\" && frame.isKeyframe) ||\n elapsed > MaxSinged16Int\n ) {\n this.relativeTimestamp += elapsed;\n\n const cluster = this.builder.createCluster(this.relativeTimestamp);\n this.controller.enqueue({ packet: Buffer.from(cluster) });\n\n if (elapsed !== 0) {\n this.cuePoints.push(\n new CuePoint(\n this.builder,\n track.trackNumber,\n this.relativeTimestamp,\n this.position\n )\n );\n }\n this.position += cluster.length;\n Object.values(this.timestamps).forEach((t) => t.reset());\n elapsed = timestampManager.update(frame.timestamp);\n }\n\n const block = this.builder.createSimpleBlock(\n frame.data,\n frame.isKeyframe,\n track.trackNumber,\n elapsed\n );\n this.controller.enqueue({ packet: block });\n this.position += block.length;\n const [cuePoint] = this.cuePoints.slice(-1);\n if (cuePoint) {\n cuePoint.blockNumber++;\n }\n }\n\n private async stop() {\n if (this.stopped) {\n return;\n }\n this.stopped = true;\n\n log(\"stop\");\n\n const cues = this.builder.createCues(this.cuePoints.map((c) => c.build()));\n this.controller.enqueue({ packet: Buffer.from(cues) });\n\n const latestTimestamp = Object.values(this.timestamps).sort(\n (a, b) => a.elapsed - b.elapsed\n )[0].elapsed;\n const duration = this.relativeTimestamp + latestTimestamp;\n const durationElement = this.builder.createDuration(duration);\n this.controller.enqueue({ eol: { duration, durationElement } });\n\n this.controller.close();\n }\n}\n\nclass ClusterTimestamp {\n baseTimestamp?: number;\n elapsed = 0;\n\n constructor(public clockRate: number) {}\n\n reset() {\n this.baseTimestamp = undefined;\n }\n\n update(timestamp: number) {\n if (this.baseTimestamp == undefined) {\n this.baseTimestamp = timestamp;\n }\n const rotate =\n Math.abs(timestamp - this.baseTimestamp) > (Max32Uint / 4) * 3;\n\n if (rotate) {\n log(\"rotate\", { baseTimestamp: this.baseTimestamp, timestamp });\n }\n\n const elapsed = rotate\n ? timestamp + Max32Uint - this.baseTimestamp\n : timestamp - this.baseTimestamp;\n\n this.elapsed = int((elapsed / this.clockRate) * 1000);\n return this.elapsed;\n }\n}\n\nclass CuePoint {\n /**\n * cuesの後のclusterのあるべき位置\n * cuesはclusterの前に挿入される\n */\n cuesLength = 0;\n blockNumber = 0;\n\n constructor(\n private readonly builder: WEBMBuilder,\n private readonly trackNumber: number,\n private readonly relativeTimestamp: number,\n public position: number\n ) {}\n\n build() {\n return this.builder.createCuePoint(\n this.relativeTimestamp,\n this.trackNumber,\n this.position - 48 + this.cuesLength,\n this.blockNumber\n );\n }\n}\n\n/**4294967295 */\nconst Max32Uint = Number(0x01n << 32n) - 1;\n/**32767 */\nconst MaxSinged16Int = (0x01 << 16) / 2 - 1;\n"]}
|
|
@@ -12,7 +12,7 @@ export declare class RTCRtpTransceiver {
|
|
|
12
12
|
/**RFC 8829 4.2.4. direction the transceiver was initialized with */
|
|
13
13
|
direction: Direction;
|
|
14
14
|
readonly uuid: string;
|
|
15
|
-
readonly onTrack: Event<[MediaStreamTrack]>;
|
|
15
|
+
readonly onTrack: Event<[MediaStreamTrack, RTCRtpTransceiver]>;
|
|
16
16
|
mid?: string;
|
|
17
17
|
mLineIndex?: number;
|
|
18
18
|
usedForSender: boolean;
|
|
@@ -76,8 +76,9 @@ class RTCRtpTransceiver {
|
|
|
76
76
|
}
|
|
77
77
|
addTrack(track) {
|
|
78
78
|
const res = this.receiver.addTrack(track);
|
|
79
|
-
if (res)
|
|
80
|
-
this.onTrack.execute(track);
|
|
79
|
+
if (res) {
|
|
80
|
+
this.onTrack.execute(track, this);
|
|
81
|
+
}
|
|
81
82
|
}
|
|
82
83
|
// todo impl
|
|
83
84
|
// https://www.w3.org/TR/webrtc/#methods-8
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rtpTransceiver.js","sourceRoot":"","sources":["../../../../src/media/rtpTransceiver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,sDAA4B;AAC5B,2CAA6B;AAG7B,oCAA4C;AAU5C,MAAa,iBAAiB;IA+B5B,YACkB,IAAU,EAC1B,aAA+B,EACxB,QAAwB,EACxB,MAAoB;IAC3B,qEAAqE;IAC9D,SAAoB;QALX,SAAI,GAAJ,IAAI,CAAM;QAEnB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,WAAM,GAAN,MAAM,CAAc;QAEpB,cAAS,GAAT,SAAS,CAAW;QApCpB,SAAI,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;QACjB,YAAO,GAAG,IAAI,iBAAK,
|
|
1
|
+
{"version":3,"file":"rtpTransceiver.js","sourceRoot":"","sources":["../../../../src/media/rtpTransceiver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,sDAA4B;AAC5B,2CAA6B;AAG7B,oCAA4C;AAU5C,MAAa,iBAAiB;IA+B5B,YACkB,IAAU,EAC1B,aAA+B,EACxB,QAAwB,EACxB,MAAoB;IAC3B,qEAAqE;IAC9D,SAAoB;QALX,SAAI,GAAJ,IAAI,CAAM;QAEnB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,WAAM,GAAN,MAAM,CAAc;QAEpB,cAAS,GAAT,SAAS,CAAW;QApCpB,SAAI,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;QACjB,YAAO,GAAG,IAAI,iBAAK,EAAyC,CAAC;QAGtE,kBAAa,GAAG,KAAK,CAAC;QActB,YAAO,GAA4B,EAAE,CAAC;QAOtC,qBAAgB,GAAsC,EAAE,CAAC;QACzD,YAAO,GAAgC,EAAE,CAAC;QAC1C,aAAQ,GAAG,KAAK,CAAC;QACjB,YAAO,GAAG,KAAK,CAAC;QAUd,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;IACvC,CAAC;IAjCD,IAAI,gBAAgB,CAAC,SAA4C;QAC/D,IAAI,CAAC,iBAAiB,GAAG,SAAS,CAAC;QACnC,IAAI,wBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,iBAAiB,IAAI,EAAE,CAAC,EAAE;YAC3D,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;SAC3B;IACH,CAAC;IACD,+CAA+C;IAC/C,IAAI,gBAAgB;QAClB,OAAO,IAAI,CAAC,iBAAiB,CAAC;IAChC,CAAC;IAID,IAAI,MAAM,CAAC,MAA+B;QACxC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,CAAC;IACD,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAiBD,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC;IACrC,CAAC;IAED,gBAAgB,CAAC,IAAsB;QACrC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,IAAI;QACN,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IAC1D,CAAC;IAED,QAAQ,CAAC,KAAuB;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,GAAG,EAAE;YACP,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;SACnC;IACH,CAAC;IAED,YAAY;IACZ,0CAA0C;IAC1C,IAAI;QACF,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAE1B,oDAAoD;QAEpD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,cAAc,CAAC,QAAgB;QAC7B,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAChC,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAC9D,EAAE,WAAW,CAAC;IACjB,CAAC;CACF;AA7ED,8CA6EC;AAEY,QAAA,QAAQ,GAAG,UAAU,CAAC;AACtB,QAAA,QAAQ,GAAG,UAAU,CAAC;AACtB,QAAA,QAAQ,GAAG,UAAU,CAAC;AACtB,QAAA,QAAQ,GAAG,UAAU,CAAC;AAEtB,QAAA,UAAU,GAAG,CAAC,gBAAQ,EAAE,gBAAQ,EAAE,gBAAQ,EAAE,gBAAQ,CAAU,CAAC","sourcesContent":["import Event from \"rx.mini\";\nimport * as uuid from \"uuid\";\n\nimport { RTCDtlsTransport } from \"..\";\nimport { SenderDirections } from \"../const\";\nimport { Kind } from \"../types/domain\";\nimport {\n RTCRtpCodecParameters,\n RTCRtpHeaderExtensionParameters,\n} from \"./parameters\";\nimport { RTCRtpReceiver } from \"./rtpReceiver\";\nimport { RTCRtpSender } from \"./rtpSender\";\nimport { MediaStreamTrack } from \"./track\";\n\nexport class RTCRtpTransceiver {\n readonly uuid = uuid.v4();\n readonly onTrack = new Event<[MediaStreamTrack, RTCRtpTransceiver]>();\n mid?: string;\n mLineIndex?: number;\n usedForSender = false;\n private _currentDirection?: Direction | \"stopped\";\n set currentDirection(direction: Direction | \"stopped\" | undefined) {\n this._currentDirection = direction;\n if (SenderDirections.includes(this._currentDirection || \"\")) {\n this.usedForSender = true;\n }\n }\n /**RFC 8829 4.2.5. last negotiated direction */\n get currentDirection(): Direction | \"stopped\" | undefined {\n return this._currentDirection;\n }\n\n offerDirection!: Direction;\n _codecs: RTCRtpCodecParameters[] = [];\n set codecs(codecs: RTCRtpCodecParameters[]) {\n this._codecs = codecs;\n }\n get codecs() {\n return this._codecs;\n }\n headerExtensions: RTCRtpHeaderExtensionParameters[] = [];\n options: Partial<TransceiverOptions> = {};\n stopping = false;\n stopped = false;\n\n constructor(\n public readonly kind: Kind,\n dtlsTransport: RTCDtlsTransport,\n public receiver: RTCRtpReceiver,\n public sender: RTCRtpSender,\n /**RFC 8829 4.2.4. direction the transceiver was initialized with */\n public direction: Direction\n ) {\n this.setDtlsTransport(dtlsTransport);\n }\n\n get dtlsTransport() {\n return this.receiver.dtlsTransport;\n }\n\n setDtlsTransport(dtls: RTCDtlsTransport) {\n this.receiver.setDtlsTransport(dtls);\n this.sender.setDtlsTransport(dtls);\n }\n\n get msid() {\n return `${this.sender.streamId} ${this.sender.trackId}`;\n }\n\n addTrack(track: MediaStreamTrack) {\n const res = this.receiver.addTrack(track);\n if (res) {\n this.onTrack.execute(track, this);\n }\n }\n\n // todo impl\n // https://www.w3.org/TR/webrtc/#methods-8\n stop() {\n if (this.stopping) return;\n\n // todo Stop sending and receiving with transceiver.\n\n this.stopping = true;\n }\n\n getPayloadType(mimeType: string) {\n return this.codecs.find((codec) =>\n codec.mimeType.toLowerCase().includes(mimeType.toLowerCase())\n )?.payloadType;\n }\n}\n\nexport const Inactive = \"inactive\";\nexport const Sendonly = \"sendonly\";\nexport const Recvonly = \"recvonly\";\nexport const Sendrecv = \"sendrecv\";\n\nexport const Directions = [Inactive, Sendonly, Recvonly, Sendrecv] as const;\n\nexport type Direction = typeof Directions[number];\n\ntype SimulcastDirection = \"send\" | \"recv\";\n\nexport interface TransceiverOptions {\n direction: Direction;\n simulcast: { direction: SimulcastDirection; rid: string }[];\n}\n"]}
|
|
@@ -8,10 +8,14 @@ export declare class MediaRecorder {
|
|
|
8
8
|
ext: string;
|
|
9
9
|
constructor(tracks: MediaStreamTrack[], path: string, options?: Partial<MediaRecorderOptions>);
|
|
10
10
|
addTrack(track: MediaStreamTrack): void;
|
|
11
|
-
start(): void
|
|
11
|
+
start(): Promise<void>;
|
|
12
12
|
stop(): Promise<void>;
|
|
13
13
|
}
|
|
14
14
|
export interface MediaRecorderOptions {
|
|
15
15
|
width: number;
|
|
16
16
|
height: number;
|
|
17
|
+
jitterBufferLatency: number;
|
|
18
|
+
jitterBufferSize: number;
|
|
19
|
+
waitForKeyframe: boolean;
|
|
20
|
+
defaultDuration: number;
|
|
17
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/nonstandard/recorder/index.ts"],"names":[],"mappings":";;;AAEA,wCAA4C;AAE5C,MAAa,aAAa;IAIxB,YACS,MAA0B,EAC1B,IAAY,EACZ,UAAyC,EAAE;QAF3C,WAAM,GAAN,MAAM,CAAoB;QAC1B,SAAI,GAAJ,IAAI,CAAQ;QACZ,YAAO,GAAP,OAAO,CAAoC;QAElD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,EAAE;YAClB,QAAQ,IAAI,CAAC,GAAG,EAAE;gBAChB,KAAK,MAAM;oBACT,OAAO,IAAI,kBAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACxC;oBACE,MAAM,IAAI,KAAK,EAAE,CAAC;aACrB;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;IAED,QAAQ,CAAC,KAAuB;QAC9B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/nonstandard/recorder/index.ts"],"names":[],"mappings":";;;AAEA,wCAA4C;AAE5C,MAAa,aAAa;IAIxB,YACS,MAA0B,EAC1B,IAAY,EACZ,UAAyC,EAAE;QAF3C,WAAM,GAAN,MAAM,CAAoB;QAC1B,SAAI,GAAJ,IAAI,CAAQ;QACZ,YAAO,GAAP,OAAO,CAAoC;QAElD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,EAAE;YAClB,QAAQ,IAAI,CAAC,GAAG,EAAE;gBAChB,KAAK,MAAM;oBACT,OAAO,IAAI,kBAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACxC;oBACE,MAAM,IAAI,KAAK,EAAE,CAAC;aACrB;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;IAED,QAAQ,CAAC,KAAuB;QAC9B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;CACF;AA/BD,sCA+BC","sourcesContent":["import { MediaStreamTrack } from \"../../media/track\";\nimport { MediaWriter } from \"./writer\";\nimport { WebmFactory } from \"./writer/webm\";\n\nexport class MediaRecorder {\n writer: MediaWriter;\n ext: string;\n\n constructor(\n public tracks: MediaStreamTrack[],\n public path: string,\n public options: Partial<MediaRecorderOptions> = {}\n ) {\n this.ext = path.split(\".\").slice(-1)[0];\n this.writer = (() => {\n switch (this.ext) {\n case \"webm\":\n return new WebmFactory(path, options);\n default:\n throw new Error();\n }\n })();\n }\n\n addTrack(track: MediaStreamTrack) {\n this.tracks.push(track);\n }\n\n async start() {\n await this.writer.start(this.tracks);\n }\n\n async stop() {\n await this.writer.stop();\n }\n}\n\nexport interface MediaRecorderOptions {\n width: number;\n height: number;\n jitterBufferLatency: number;\n jitterBufferSize: number;\n waitForKeyframe: boolean;\n defaultDuration: number;\n}\n"]}
|
|
@@ -4,6 +4,6 @@ export declare abstract class MediaWriter {
|
|
|
4
4
|
protected path: string;
|
|
5
5
|
protected options: Partial<MediaRecorderOptions>;
|
|
6
6
|
constructor(path: string, options: Partial<MediaRecorderOptions>);
|
|
7
|
-
start(tracks: MediaStreamTrack[]): void
|
|
7
|
+
start(tracks: MediaStreamTrack[]): Promise<void>;
|
|
8
8
|
stop(): Promise<void>;
|
|
9
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../src/nonstandard/recorder/writer/index.ts"],"names":[],"mappings":";;;AAGA,MAAsB,WAAW;IAC/B,YACY,IAAY,EACZ,OAAsC;QADtC,SAAI,GAAJ,IAAI,CAAQ;QACZ,YAAO,GAAP,OAAO,CAA+B;IAC/C,CAAC;IAEJ,KAAK,CAAC,MAA0B,IAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../src/nonstandard/recorder/writer/index.ts"],"names":[],"mappings":";;;AAGA,MAAsB,WAAW;IAC/B,YACY,IAAY,EACZ,OAAsC;QADtC,SAAI,GAAJ,IAAI,CAAQ;QACZ,YAAO,GAAP,OAAO,CAA+B;IAC/C,CAAC;IAEJ,KAAK,CAAC,KAAK,CAAC,MAA0B,IAAG,CAAC;IAE1C,KAAK,CAAC,IAAI,KAAI,CAAC;CAChB;AATD,kCASC","sourcesContent":["import { MediaStreamTrack } from \"../../..\";\nimport { MediaRecorderOptions } from \"..\";\n\nexport abstract class MediaWriter {\n constructor(\n protected path: string,\n protected options: Partial<MediaRecorderOptions>\n ) {}\n\n async start(tracks: MediaStreamTrack[]) {}\n\n async stop() {}\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { MediaStreamTrack,
|
|
1
|
+
import { MediaStreamTrack, RtpSourceStream } from "../../..";
|
|
2
2
|
import { MediaWriter } from ".";
|
|
3
3
|
export declare class WebmFactory extends MediaWriter {
|
|
4
|
-
|
|
5
|
-
start(tracks: MediaStreamTrack[]): void
|
|
4
|
+
rtpSources: RtpSourceStream[];
|
|
5
|
+
start(tracks: MediaStreamTrack[]): Promise<void>;
|
|
6
6
|
stop(): Promise<void>;
|
|
7
7
|
}
|
|
@@ -1,35 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
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 (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
3
|
exports.WebmFactory = void 0;
|
|
27
|
-
const
|
|
4
|
+
const promises_1 = require("fs/promises");
|
|
28
5
|
const __1 = require("../../..");
|
|
29
6
|
const _1 = require(".");
|
|
7
|
+
const sourcePath = "packages/webrtc/src/nonstandard/recorder/writer/webm.ts";
|
|
30
8
|
class WebmFactory extends _1.MediaWriter {
|
|
31
|
-
|
|
32
|
-
|
|
9
|
+
constructor() {
|
|
10
|
+
super(...arguments);
|
|
11
|
+
this.rtpSources = [];
|
|
12
|
+
}
|
|
13
|
+
async start(tracks) {
|
|
14
|
+
await (0, promises_1.unlink)(this.path).catch((e) => e);
|
|
15
|
+
const inputTracks = tracks.map((track, i) => {
|
|
33
16
|
const trackNumber = i + 1;
|
|
34
17
|
const payloadType = track.codec.payloadType;
|
|
35
18
|
if (track.kind === "video") {
|
|
@@ -44,41 +27,78 @@ class WebmFactory extends _1.MediaWriter {
|
|
|
44
27
|
case "av1x":
|
|
45
28
|
return "AV1";
|
|
46
29
|
default:
|
|
47
|
-
throw new
|
|
30
|
+
throw new __1.WeriftError({
|
|
31
|
+
message: "unsupported codec",
|
|
32
|
+
payload: { track, path: sourcePath },
|
|
33
|
+
});
|
|
48
34
|
}
|
|
49
35
|
})();
|
|
50
36
|
return {
|
|
51
37
|
kind: "video",
|
|
38
|
+
codec,
|
|
52
39
|
clockRate: 90000,
|
|
53
|
-
payloadType,
|
|
54
40
|
trackNumber,
|
|
55
|
-
codec,
|
|
56
41
|
width: this.options.width,
|
|
57
42
|
height: this.options.height,
|
|
43
|
+
payloadType,
|
|
44
|
+
track,
|
|
58
45
|
};
|
|
59
46
|
}
|
|
60
47
|
else {
|
|
61
48
|
return {
|
|
62
49
|
kind: "audio",
|
|
50
|
+
codec: "OPUS",
|
|
63
51
|
clockRate: 48000,
|
|
64
|
-
payloadType,
|
|
65
52
|
trackNumber,
|
|
66
|
-
|
|
53
|
+
payloadType,
|
|
54
|
+
track,
|
|
67
55
|
};
|
|
68
56
|
}
|
|
69
|
-
}));
|
|
70
|
-
tracks.forEach((track) => {
|
|
71
|
-
const sampleBuilder = track.kind === "video"
|
|
72
|
-
? new __1.SampleBuilder((h) => !!h.marker).pipe(this.webm)
|
|
73
|
-
: new __1.SampleBuilder(() => true).pipe(this.webm);
|
|
74
|
-
new __1.JitterBuffer({
|
|
75
|
-
rtpStream: track.onReceiveRtp,
|
|
76
|
-
rtcpStream: track.onReceiveRtcp,
|
|
77
|
-
}).pipe(sampleBuilder);
|
|
78
57
|
});
|
|
58
|
+
const webm = new __1.WebmLiveSink(inputTracks, {
|
|
59
|
+
duration: this.options.defaultDuration ?? 1000 * 60 * 60 * 24,
|
|
60
|
+
});
|
|
61
|
+
this.rtpSources = inputTracks.map(({ track, clockRate, codec }) => {
|
|
62
|
+
const rtpSource = new __1.RtpSourceStream(track.onReceiveRtp);
|
|
63
|
+
const jitterBuffer = (0, __1.jitterBufferTransformer)(clockRate, {
|
|
64
|
+
latency: this.options.jitterBufferLatency,
|
|
65
|
+
bufferSize: this.options.jitterBufferSize,
|
|
66
|
+
});
|
|
67
|
+
if (track.kind === "video") {
|
|
68
|
+
rtpSource.readable
|
|
69
|
+
.pipeThrough(jitterBuffer)
|
|
70
|
+
.pipeThrough((0, __1.depacketizeTransformer)((h) => h.marker, codec, {
|
|
71
|
+
waitForKeyframe: this.options.waitForKeyframe,
|
|
72
|
+
}))
|
|
73
|
+
.pipeTo(webm.videoStream);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
rtpSource.readable
|
|
77
|
+
.pipeThrough(jitterBuffer)
|
|
78
|
+
.pipeThrough((0, __1.depacketizeTransformer)(() => true, codec))
|
|
79
|
+
.pipeTo(webm.audioStream);
|
|
80
|
+
}
|
|
81
|
+
return rtpSource;
|
|
82
|
+
});
|
|
83
|
+
const reader = webm.webmStream.getReader();
|
|
84
|
+
const readChunk = async ({ value, done, }) => {
|
|
85
|
+
if (done)
|
|
86
|
+
return;
|
|
87
|
+
if (value.packet) {
|
|
88
|
+
await (0, promises_1.appendFile)(this.path, value.packet);
|
|
89
|
+
}
|
|
90
|
+
else if (value.eol) {
|
|
91
|
+
const { durationElement } = value.eol;
|
|
92
|
+
const handler = await (0, promises_1.open)(this.path, "r+");
|
|
93
|
+
await handler.write(durationElement, 0, durationElement.length, 83);
|
|
94
|
+
await handler.close();
|
|
95
|
+
}
|
|
96
|
+
reader.read().then(readChunk);
|
|
97
|
+
};
|
|
98
|
+
reader.read().then(readChunk);
|
|
79
99
|
}
|
|
80
100
|
async stop() {
|
|
81
|
-
await this.
|
|
101
|
+
await Promise.all(this.rtpSources.map((r) => r.stop()));
|
|
82
102
|
}
|
|
83
103
|
}
|
|
84
104
|
exports.WebmFactory = WebmFactory;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webm.js","sourceRoot":"","sources":["../../../../../../src/nonstandard/recorder/writer/webm.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"webm.js","sourceRoot":"","sources":["../../../../../../src/nonstandard/recorder/writer/webm.ts"],"names":[],"mappings":"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","sourcesContent":["import { appendFile, open, unlink } from \"fs/promises\";\nimport { ReadableStreamDefaultReadResult } from \"stream/web\";\n\nimport { SupportedCodec } from \"../../../../../rtp/src/container/webm\";\nimport {\n depacketizeTransformer,\n jitterBufferTransformer,\n MediaStreamTrack,\n RtpSourceStream,\n WebmLiveOutput,\n WebmLiveSink,\n WeriftError,\n} from \"../../..\";\nimport { MediaWriter } from \".\";\n\nconst sourcePath = \"packages/webrtc/src/nonstandard/recorder/writer/webm.ts\";\n\nexport class WebmFactory extends MediaWriter {\n rtpSources: RtpSourceStream[] = [];\n\n async start(tracks: MediaStreamTrack[]) {\n await unlink(this.path).catch((e) => e);\n\n const inputTracks = tracks.map((track, i) => {\n const trackNumber = i + 1;\n const payloadType = track.codec!.payloadType;\n\n if (track.kind === \"video\") {\n const codec = ((): SupportedCodec => {\n switch (track.codec?.name.toLowerCase() as SupportedVideoCodec) {\n case \"vp8\":\n return \"VP8\";\n case \"vp9\":\n return \"VP9\";\n case \"h264\":\n return \"MPEG4/ISO/AVC\";\n case \"av1x\":\n return \"AV1\";\n default:\n throw new WeriftError({\n message: \"unsupported codec\",\n payload: { track, path: sourcePath },\n });\n }\n })();\n return {\n kind: \"video\" as const,\n codec,\n clockRate: 90000,\n trackNumber,\n width: this.options.width,\n height: this.options.height,\n payloadType,\n track,\n };\n } else {\n return {\n kind: \"audio\" as const,\n codec: \"OPUS\" as const,\n clockRate: 48000,\n trackNumber,\n payloadType,\n track,\n };\n }\n });\n\n const webm = new WebmLiveSink(inputTracks, {\n duration: this.options.defaultDuration ?? 1000 * 60 * 60 * 24,\n });\n\n this.rtpSources = inputTracks.map(({ track, clockRate, codec }) => {\n const rtpSource = new RtpSourceStream(track.onReceiveRtp);\n\n const jitterBuffer = jitterBufferTransformer(clockRate, {\n latency: this.options.jitterBufferLatency,\n bufferSize: this.options.jitterBufferSize,\n });\n\n if (track.kind === \"video\") {\n rtpSource.readable\n .pipeThrough(jitterBuffer)\n .pipeThrough(\n depacketizeTransformer((h) => h.marker, codec, {\n waitForKeyframe: this.options.waitForKeyframe,\n })\n )\n .pipeTo(webm.videoStream);\n } else {\n rtpSource.readable\n .pipeThrough(jitterBuffer)\n .pipeThrough(depacketizeTransformer(() => true, codec))\n .pipeTo(webm.audioStream);\n }\n\n return rtpSource;\n });\n\n const reader = webm.webmStream.getReader();\n const readChunk = async ({\n value,\n done,\n }: ReadableStreamDefaultReadResult<WebmLiveOutput>) => {\n if (done) return;\n\n if (value.packet) {\n await appendFile(this.path, value.packet);\n } else if (value.eol) {\n const { durationElement } = value.eol;\n const handler = await open(this.path, \"r+\");\n await handler.write(durationElement, 0, durationElement.length, 83);\n await handler.close();\n }\n reader.read().then(readChunk);\n };\n reader.read().then(readChunk);\n }\n\n async stop() {\n await Promise.all(this.rtpSources.map((r) => r.stop()));\n }\n}\n\nconst supportedVideoCodecs = [\"h264\", \"vp8\", \"vp9\", \"av1x\"] as const;\ntype SupportedVideoCodec = typeof supportedVideoCodecs[number];\n"]}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import Event from "rx.mini";
|
|
2
|
-
import {
|
|
2
|
+
import { Message } from "../../ice/src/stun/message";
|
|
3
|
+
import { Protocol } from "../../ice/src/types/model";
|
|
4
|
+
import { Address, InterfaceAddresses } from ".";
|
|
3
5
|
import { DtlsKeys } from ".";
|
|
4
6
|
import { RTCDataChannel } from "./dataChannel";
|
|
5
7
|
import { EventTarget } from "./helper";
|
|
@@ -34,6 +36,7 @@ export declare class RTCPeerConnection extends EventTarget {
|
|
|
34
36
|
readonly onTransceiverAdded: Event<[RTCRtpTransceiver]>;
|
|
35
37
|
readonly onIceCandidate: Event<[RTCIceCandidate]>;
|
|
36
38
|
readonly onNegotiationneeded: Event<[]>;
|
|
39
|
+
readonly onTrack: Event<[MediaStreamTrack]>;
|
|
37
40
|
ondatachannel?: CallbackWithValue<RTCDataChannelEvent>;
|
|
38
41
|
onicecandidate?: CallbackWithValue<RTCPeerConnectionIceEvent>;
|
|
39
42
|
onnegotiationneeded?: CallbackWithValue<any>;
|
|
@@ -135,6 +138,11 @@ export interface PeerConfig {
|
|
|
135
138
|
/**Minimum port and Maximum port must not be the same value */
|
|
136
139
|
icePortRange: [number, number] | undefined;
|
|
137
140
|
iceInterfaceAddresses: InterfaceAddresses | undefined;
|
|
141
|
+
iceUseIpv4: boolean;
|
|
142
|
+
iceUseIpv6: boolean;
|
|
143
|
+
/** If provided, is called on each STUN request.
|
|
144
|
+
* Return `true` if a STUN response should be sent, false if it should be skipped. */
|
|
145
|
+
iceFilterStunResponse: ((message: Message, addr: Address, protocol: Protocol) => boolean) | undefined;
|
|
138
146
|
dtls: Partial<{
|
|
139
147
|
keys: DtlsKeys;
|
|
140
148
|
}>;
|
|
@@ -70,6 +70,7 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
70
70
|
this.onTransceiverAdded = new rx_mini_1.default();
|
|
71
71
|
this.onIceCandidate = new rx_mini_1.default();
|
|
72
72
|
this.onNegotiationneeded = new rx_mini_1.default();
|
|
73
|
+
this.onTrack = new rx_mini_1.default();
|
|
73
74
|
this.router = new router_1.RtpRouter();
|
|
74
75
|
this.certificates = [];
|
|
75
76
|
this.seenMid = new Set();
|
|
@@ -219,7 +220,7 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
219
220
|
}
|
|
220
221
|
if (transceiver.headerExtensions.length === 0) {
|
|
221
222
|
transceiver.headerExtensions =
|
|
222
|
-
this.config.headerExtensions[transceiver.kind];
|
|
223
|
+
this.config.headerExtensions[transceiver.kind] ?? [];
|
|
223
224
|
}
|
|
224
225
|
});
|
|
225
226
|
const description = new sdp_1.SessionDescription();
|
|
@@ -339,6 +340,9 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
339
340
|
forceTurn: this.config.iceTransportPolicy === "relay",
|
|
340
341
|
portRange: this.config.icePortRange,
|
|
341
342
|
interfaceAddresses: this.config.iceInterfaceAddresses,
|
|
343
|
+
filterStunResponse: this.config.iceFilterStunResponse,
|
|
344
|
+
useIpv4: this.config.iceUseIpv4,
|
|
345
|
+
useIpv6: this.config.iceUseIpv6,
|
|
342
346
|
});
|
|
343
347
|
if (existing) {
|
|
344
348
|
iceGatherer.connection.localUserName = existing.connection.localUserName;
|
|
@@ -828,6 +832,7 @@ class RTCPeerConnection extends helper_1.EventTarget {
|
|
|
828
832
|
transceiver,
|
|
829
833
|
receiver: transceiver.receiver,
|
|
830
834
|
};
|
|
835
|
+
this.onTrack.execute(track);
|
|
831
836
|
this.emit("track", event);
|
|
832
837
|
if (this.ontrack)
|
|
833
838
|
this.ontrack(event);
|
|
@@ -1206,6 +1211,9 @@ exports.defaultPeerConfig = {
|
|
|
1206
1211
|
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
|
1207
1212
|
icePortRange: undefined,
|
|
1208
1213
|
iceInterfaceAddresses: undefined,
|
|
1214
|
+
iceUseIpv4: true,
|
|
1215
|
+
iceUseIpv6: true,
|
|
1216
|
+
iceFilterStunResponse: undefined,
|
|
1209
1217
|
dtls: {},
|
|
1210
1218
|
bundlePolicy: "max-compat",
|
|
1211
1219
|
debug: {},
|