werift 0.19.6 → 0.19.8
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/webrtc/src/nonstandard/recorder/index.d.ts +18 -10
- package/lib/webrtc/src/nonstandard/recorder/index.js +2 -1
- package/lib/webrtc/src/nonstandard/recorder/index.js.map +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/index.d.ts +7 -5
- package/lib/webrtc/src/nonstandard/recorder/writer/index.js.map +1 -1
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js +19 -6
- package/lib/webrtc/src/nonstandard/recorder/writer/webm.js.map +1 -1
- package/package.json +1 -1
- package/src/nonstandard/recorder/index.ts +18 -9
- package/src/nonstandard/recorder/writer/index.ts +6 -3
- package/src/nonstandard/recorder/writer/webm.ts +17 -6
|
@@ -1,30 +1,37 @@
|
|
|
1
1
|
import Event from "rx.mini";
|
|
2
|
-
import type { PassThrough } from "stream";
|
|
3
2
|
import type { MediaStreamTrack } from "../../media/track";
|
|
4
|
-
import type { MediaWriter } from "./writer";
|
|
3
|
+
import type { MediaWriter, StreamEvent } from "./writer";
|
|
5
4
|
export declare class MediaRecorder {
|
|
6
|
-
props: Partial<MediaRecorderOptions> & {
|
|
5
|
+
props: Partial<MediaRecorderOptions> & ({
|
|
7
6
|
numOfTracks: number;
|
|
8
|
-
|
|
7
|
+
tracks?: MediaStreamTrack[];
|
|
8
|
+
} | {
|
|
9
|
+
numOfTracks?: number;
|
|
10
|
+
tracks: MediaStreamTrack[];
|
|
11
|
+
}) & ({
|
|
9
12
|
path: string;
|
|
10
|
-
stream?:
|
|
13
|
+
stream?: StreamEvent;
|
|
11
14
|
} | {
|
|
12
15
|
path?: string;
|
|
13
|
-
stream:
|
|
16
|
+
stream: StreamEvent;
|
|
14
17
|
});
|
|
15
18
|
writer: MediaWriter;
|
|
16
19
|
ext?: string;
|
|
17
20
|
tracks: MediaStreamTrack[];
|
|
18
21
|
started: boolean;
|
|
19
22
|
onError: Event<[Error]>;
|
|
20
|
-
constructor(props: Partial<MediaRecorderOptions> & {
|
|
23
|
+
constructor(props: Partial<MediaRecorderOptions> & ({
|
|
21
24
|
numOfTracks: number;
|
|
22
|
-
|
|
25
|
+
tracks?: MediaStreamTrack[];
|
|
26
|
+
} | {
|
|
27
|
+
numOfTracks?: number;
|
|
28
|
+
tracks: MediaStreamTrack[];
|
|
29
|
+
}) & ({
|
|
23
30
|
path: string;
|
|
24
|
-
stream?:
|
|
31
|
+
stream?: StreamEvent;
|
|
25
32
|
} | {
|
|
26
33
|
path?: string;
|
|
27
|
-
stream:
|
|
34
|
+
stream: StreamEvent;
|
|
28
35
|
}));
|
|
29
36
|
addTrack(track: MediaStreamTrack): Promise<void>;
|
|
30
37
|
private start;
|
|
@@ -35,6 +42,7 @@ export interface MediaRecorderOptions {
|
|
|
35
42
|
height: number;
|
|
36
43
|
jitterBufferLatency: number;
|
|
37
44
|
jitterBufferSize: number;
|
|
45
|
+
disableLipSync: boolean;
|
|
38
46
|
waitForKeyframe: boolean;
|
|
39
47
|
defaultDuration: number;
|
|
40
48
|
tracks: MediaStreamTrack[];
|
|
@@ -68,7 +68,8 @@ class MediaRecorder {
|
|
|
68
68
|
stream: stream,
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
|
-
if (this.tracks.length
|
|
71
|
+
if (this.tracks.length > 0) {
|
|
72
|
+
this.props.numOfTracks = this.tracks.length;
|
|
72
73
|
this.start().catch((error) => {
|
|
73
74
|
this.onError.execute(error);
|
|
74
75
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/nonstandard/recorder/index.ts"],"names":[],"mappings":";;;;;;AAAA,sDAA4B;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/nonstandard/recorder/index.ts"],"names":[],"mappings":";;;;;;AAAA,sDAA4B;AAG5B,wCAA4C;AAE5C,MAAa,aAAa;IAOxB,YACS,KAoBJ;QApBH;;;;mBAAO,KAAK;WAoBT;QA3BL;;;;;WAAoB;QACpB;;;;;WAAa;QACb;;;;mBAA6B,EAAE;WAAC;QAChC;;;;mBAAU,KAAK;WAAC;QAChB;;;;mBAAU,IAAI,iBAAK,EAAW;WAAC;QAyB7B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;QAE1C,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;QAE/B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACxC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,EAAE;gBAClB,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC;oBACjB,KAAK,MAAM;wBACT,OAAO,IAAI,kBAAW,CAAC;4BACrB,GAAG,KAAK;4BACR,IAAI,EAAE,IAAK;4BACX,MAAM,EAAE,MAAO;yBAChB,CAAC,CAAC;oBACL;wBACE,MAAM,IAAI,KAAK,EAAE,CAAC;gBACtB,CAAC;YACH,CAAC,CAAC,EAAE,CAAC;QACP,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,GAAG,IAAI,kBAAW,CAAC;gBAC5B,GAAG,KAAK;gBACR,IAAI,EAAE,IAAK;gBACX,MAAM,EAAE,MAAO;aAChB,CAAC,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;YAC5C,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBAC3B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAuB;QACpC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAEO,KAAK,CAAC,KAAK;QACjB,IACE,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,WAAW;YAC7C,IAAI,CAAC,OAAO,KAAK,KAAK,EACtB,CAAC;YACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;CACF;AAlFD,sCAkFC","sourcesContent":["import Event from \"rx.mini\";\nimport type { MediaStreamTrack } from \"../../media/track\";\nimport type { MediaWriter, StreamEvent } from \"./writer\";\nimport { WebmFactory } from \"./writer/webm\";\n\nexport class MediaRecorder {\n writer: MediaWriter;\n ext?: string;\n tracks: MediaStreamTrack[] = [];\n started = false;\n onError = new Event<[Error]>();\n\n constructor(\n public props: Partial<MediaRecorderOptions> &\n (\n | {\n numOfTracks: number;\n tracks?: MediaStreamTrack[];\n }\n | {\n numOfTracks?: number;\n tracks: MediaStreamTrack[];\n }\n ) &\n (\n | {\n path: string;\n stream?: StreamEvent;\n }\n | {\n path?: string;\n stream: StreamEvent;\n }\n ),\n ) {\n this.tracks = props.tracks ?? this.tracks;\n\n const { path, stream } = props;\n\n if (path) {\n this.ext = path.split(\".\").slice(-1)[0];\n this.writer = (() => {\n switch (this.ext) {\n case \"webm\":\n return new WebmFactory({\n ...props,\n path: path!,\n stream: stream!,\n });\n default:\n throw new Error();\n }\n })();\n } else {\n this.writer = new WebmFactory({\n ...props,\n path: path!,\n stream: stream!,\n });\n }\n\n if (this.tracks.length > 0) {\n this.props.numOfTracks = this.tracks.length;\n this.start().catch((error) => {\n this.onError.execute(error);\n });\n }\n }\n\n async addTrack(track: MediaStreamTrack) {\n this.tracks.push(track);\n await this.start();\n }\n\n private async start() {\n if (\n this.tracks.length === this.props.numOfTracks &&\n this.started === false\n ) {\n this.started = true;\n await this.writer.start(this.tracks);\n }\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 disableLipSync: boolean;\n waitForKeyframe: boolean;\n defaultDuration: number;\n tracks: MediaStreamTrack[];\n disableNtp: boolean;\n}\n"]}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Event } from "rx.mini";
|
|
2
2
|
import type { MediaRecorderOptions } from "..";
|
|
3
3
|
import type { MediaStreamTrack } from "../../..";
|
|
4
|
+
import type { WebmOutput } from "../../../../../rtp/src/extra";
|
|
4
5
|
export declare abstract class MediaWriter {
|
|
5
6
|
protected props: Partial<MediaRecorderOptions> & {
|
|
6
7
|
path: string;
|
|
7
|
-
stream?:
|
|
8
|
+
stream?: StreamEvent;
|
|
8
9
|
} & {
|
|
9
10
|
path?: string;
|
|
10
|
-
stream:
|
|
11
|
+
stream: StreamEvent;
|
|
11
12
|
};
|
|
12
13
|
constructor(props: Partial<MediaRecorderOptions> & {
|
|
13
14
|
path: string;
|
|
14
|
-
stream?:
|
|
15
|
+
stream?: StreamEvent;
|
|
15
16
|
} & {
|
|
16
17
|
path?: string;
|
|
17
|
-
stream:
|
|
18
|
+
stream: StreamEvent;
|
|
18
19
|
});
|
|
19
20
|
start(tracks: MediaStreamTrack[]): Promise<void>;
|
|
20
21
|
stop(): Promise<void>;
|
|
21
22
|
}
|
|
23
|
+
export type StreamEvent = Event<[WebmOutput]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../src/nonstandard/recorder/writer/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../src/nonstandard/recorder/writer/index.ts"],"names":[],"mappings":";;;AAKA,MAAsB,WAAW;IAC/B,YACY,KAGgC;QAH1C;;;;mBAAU,KAAK;WAG2B;IACzC,CAAC;IAEJ,KAAK,CAAC,KAAK,CAAC,MAA0B,IAAG,CAAC;IAE1C,KAAK,CAAC,IAAI,KAAI,CAAC;CAChB;AAXD,kCAWC","sourcesContent":["import type { Event } from \"rx.mini\";\nimport type { MediaRecorderOptions } from \"..\";\nimport type { MediaStreamTrack } from \"../../..\";\nimport type { WebmOutput } from \"../../../../../rtp/src/extra\";\n\nexport abstract class MediaWriter {\n constructor(\n protected props: Partial<MediaRecorderOptions> & {\n path: string;\n stream?: StreamEvent;\n } & { path?: string; stream: StreamEvent },\n ) {}\n\n async start(tracks: MediaStreamTrack[]) {}\n\n async stop() {}\n}\n\nexport type StreamEvent = Event<[WebmOutput]>;\n"]}
|
|
@@ -94,21 +94,34 @@ class WebmFactory extends _1.MediaWriter {
|
|
|
94
94
|
const depacketizer = new extra_1.DepacketizeCallback(codec, {
|
|
95
95
|
isFinalPacketInSequence: (h) => h.marker,
|
|
96
96
|
});
|
|
97
|
-
const jitterBuffer = new extra_1.JitterBufferCallback(clockRate
|
|
97
|
+
const jitterBuffer = new extra_1.JitterBufferCallback(clockRate, {
|
|
98
|
+
bufferSize: this.props.jitterBufferSize,
|
|
99
|
+
latency: this.props.jitterBufferLatency,
|
|
100
|
+
});
|
|
98
101
|
rtpSource.pipe(jitterBuffer.input);
|
|
99
102
|
rtcpSource.pipe(time.input);
|
|
100
103
|
jitterBuffer.pipe(time.input);
|
|
101
104
|
time.pipe(depacketizer.input);
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
if (this.props.disableLipSync) {
|
|
106
|
+
depacketizer.pipe(webm.inputVideo);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
depacketizer.pipe(lipsync.inputVideo);
|
|
110
|
+
lipsync.pipeVideo(webm.inputVideo);
|
|
111
|
+
}
|
|
104
112
|
}
|
|
105
113
|
else {
|
|
106
114
|
const depacketizer = new extra_1.DepacketizeCallback(codec);
|
|
107
115
|
rtpSource.pipe(time.input);
|
|
108
116
|
rtcpSource.pipe(time.input);
|
|
109
117
|
time.pipe(depacketizer.input);
|
|
110
|
-
|
|
111
|
-
|
|
118
|
+
if (this.props.disableLipSync) {
|
|
119
|
+
depacketizer.pipe(webm.inputAudio);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
depacketizer.pipe(lipsync.inputAudio);
|
|
123
|
+
lipsync.pipeAudio(webm.inputAudio);
|
|
124
|
+
}
|
|
112
125
|
}
|
|
113
126
|
return rtpSource;
|
|
114
127
|
});
|
|
@@ -117,7 +130,7 @@ class WebmFactory extends _1.MediaWriter {
|
|
|
117
130
|
}
|
|
118
131
|
else if (this.props.stream) {
|
|
119
132
|
webm.pipe(async (o) => {
|
|
120
|
-
this.props.stream.
|
|
133
|
+
this.props.stream.execute(o);
|
|
121
134
|
});
|
|
122
135
|
}
|
|
123
136
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webm.js","sourceRoot":"","sources":["../../../../../../src/nonstandard/recorder/writer/webm.ts"],"names":[],"mappings":";;;AAAA,0CAAqC;AACrC,qCAAwC;AAExC,wBAAgC;AAChC,gCAA8D;AAC9D,wDAWsC;AAEtC,MAAM,UAAU,GAAG,yDAAyD,CAAC;AAE7E,MAAa,WAAY,SAAQ,cAAW;IAA5C;;QACE;;;;mBAAkC,EAAE;WAAC;QAErC;;;;mBAAgB,IAAI,uBAAa,EAAE;WAAC;
|
|
1
|
+
{"version":3,"file":"webm.js","sourceRoot":"","sources":["../../../../../../src/nonstandard/recorder/writer/webm.ts"],"names":[],"mappings":"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","sourcesContent":["import { unlink } from \"fs/promises\";\nimport { EventDisposer } from \"rx.mini\";\n\nimport { MediaWriter } from \".\";\nimport { type MediaStreamTrack, WeriftError } from \"../../..\";\nimport {\n DepacketizeCallback,\n JitterBufferCallback,\n LipsyncCallback,\n NtpTimeCallback,\n RtcpSourceCallback,\n RtpSourceCallback,\n RtpTimeCallback,\n type SupportedCodec,\n WebmCallback,\n saveToFileSystem,\n} from \"../../../../../rtp/src/extra\";\n\nconst sourcePath = \"packages/webrtc/src/nonstandard/recorder/writer/webm.ts\";\n\nexport class WebmFactory extends MediaWriter {\n rtpSources: RtpSourceCallback[] = [];\n\n unSubscribers = new EventDisposer();\n\n async start(tracks: MediaStreamTrack[]) {\n if (this.props.path) {\n await unlink(this.props.path).catch((e) => e);\n }\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.props.width ?? 640,\n height: this.props.height ?? 360,\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 WebmCallback(inputTracks, {\n duration: this.props.defaultDuration ?? 1000 * 60 * 60 * 24,\n });\n const lipsync = new LipsyncCallback();\n\n this.rtpSources = inputTracks.map(({ track, clockRate, codec }) => {\n const rtpSource = new RtpSourceCallback();\n const rtcpSource = new RtcpSourceCallback();\n track.onReceiveRtp\n .subscribe((rtp) => {\n rtpSource.input(rtp.clone());\n })\n .disposer(this.unSubscribers);\n track.onReceiveRtcp\n .subscribe((rtcp) => {\n rtcpSource.input(rtcp);\n })\n .disposer(this.unSubscribers);\n const time = this.props.disableNtp\n ? new RtpTimeCallback(clockRate)\n : new NtpTimeCallback(clockRate);\n\n if (track.kind === \"video\") {\n const depacketizer = new DepacketizeCallback(codec, {\n isFinalPacketInSequence: (h) => h.marker,\n });\n const jitterBuffer = new JitterBufferCallback(clockRate, {\n bufferSize: this.props.jitterBufferSize,\n latency: this.props.jitterBufferLatency,\n });\n\n rtpSource.pipe(jitterBuffer.input);\n rtcpSource.pipe(time.input);\n\n jitterBuffer.pipe(time.input);\n time.pipe(depacketizer.input);\n if (this.props.disableLipSync) {\n depacketizer.pipe(webm.inputVideo);\n } else {\n depacketizer.pipe(lipsync.inputVideo);\n lipsync.pipeVideo(webm.inputVideo);\n }\n } else {\n const depacketizer = new DepacketizeCallback(codec);\n\n rtpSource.pipe(time.input);\n rtcpSource.pipe(time.input);\n\n time.pipe(depacketizer.input);\n if (this.props.disableLipSync) {\n depacketizer.pipe(webm.inputAudio);\n } else {\n depacketizer.pipe(lipsync.inputAudio);\n lipsync.pipeAudio(webm.inputAudio);\n }\n }\n\n return rtpSource;\n });\n if (this.props.path) {\n webm.pipe(saveToFileSystem(this.props.path));\n } else if (this.props.stream) {\n webm.pipe(async (o) => {\n this.props.stream.execute(o);\n });\n }\n }\n\n async stop() {\n await Promise.all(this.rtpSources.map((r) => r.stop()));\n this.unSubscribers.dispose();\n }\n}\n\nconst supportedVideoCodecs = [\"h264\", \"vp8\", \"vp9\", \"av1x\"] as const;\ntype SupportedVideoCodec = (typeof supportedVideoCodecs)[number];\n"]}
|
package/package.json
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import Event from "rx.mini";
|
|
2
|
-
|
|
3
|
-
import type { PassThrough } from "stream";
|
|
4
2
|
import type { MediaStreamTrack } from "../../media/track";
|
|
5
|
-
import type { MediaWriter } from "./writer";
|
|
3
|
+
import type { MediaWriter, StreamEvent } from "./writer";
|
|
6
4
|
import { WebmFactory } from "./writer/webm";
|
|
7
5
|
|
|
8
6
|
export class MediaRecorder {
|
|
@@ -13,16 +11,25 @@ export class MediaRecorder {
|
|
|
13
11
|
onError = new Event<[Error]>();
|
|
14
12
|
|
|
15
13
|
constructor(
|
|
16
|
-
public props: Partial<MediaRecorderOptions> &
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
public props: Partial<MediaRecorderOptions> &
|
|
15
|
+
(
|
|
16
|
+
| {
|
|
17
|
+
numOfTracks: number;
|
|
18
|
+
tracks?: MediaStreamTrack[];
|
|
19
|
+
}
|
|
20
|
+
| {
|
|
21
|
+
numOfTracks?: number;
|
|
22
|
+
tracks: MediaStreamTrack[];
|
|
23
|
+
}
|
|
24
|
+
) &
|
|
25
|
+
(
|
|
19
26
|
| {
|
|
20
27
|
path: string;
|
|
21
|
-
stream?:
|
|
28
|
+
stream?: StreamEvent;
|
|
22
29
|
}
|
|
23
30
|
| {
|
|
24
31
|
path?: string;
|
|
25
|
-
stream:
|
|
32
|
+
stream: StreamEvent;
|
|
26
33
|
}
|
|
27
34
|
),
|
|
28
35
|
) {
|
|
@@ -52,7 +59,8 @@ export class MediaRecorder {
|
|
|
52
59
|
});
|
|
53
60
|
}
|
|
54
61
|
|
|
55
|
-
if (this.tracks.length
|
|
62
|
+
if (this.tracks.length > 0) {
|
|
63
|
+
this.props.numOfTracks = this.tracks.length;
|
|
56
64
|
this.start().catch((error) => {
|
|
57
65
|
this.onError.execute(error);
|
|
58
66
|
});
|
|
@@ -84,6 +92,7 @@ export interface MediaRecorderOptions {
|
|
|
84
92
|
height: number;
|
|
85
93
|
jitterBufferLatency: number;
|
|
86
94
|
jitterBufferSize: number;
|
|
95
|
+
disableLipSync: boolean;
|
|
87
96
|
waitForKeyframe: boolean;
|
|
88
97
|
defaultDuration: number;
|
|
89
98
|
tracks: MediaStreamTrack[];
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Event } from "rx.mini";
|
|
2
2
|
import type { MediaRecorderOptions } from "..";
|
|
3
3
|
import type { MediaStreamTrack } from "../../..";
|
|
4
|
+
import type { WebmOutput } from "../../../../../rtp/src/extra";
|
|
4
5
|
|
|
5
6
|
export abstract class MediaWriter {
|
|
6
7
|
constructor(
|
|
7
8
|
protected props: Partial<MediaRecorderOptions> & {
|
|
8
9
|
path: string;
|
|
9
|
-
stream?:
|
|
10
|
-
} & { path?: string; stream:
|
|
10
|
+
stream?: StreamEvent;
|
|
11
|
+
} & { path?: string; stream: StreamEvent },
|
|
11
12
|
) {}
|
|
12
13
|
|
|
13
14
|
async start(tracks: MediaStreamTrack[]) {}
|
|
14
15
|
|
|
15
16
|
async stop() {}
|
|
16
17
|
}
|
|
18
|
+
|
|
19
|
+
export type StreamEvent = Event<[WebmOutput]>;
|
|
@@ -98,15 +98,22 @@ export class WebmFactory extends MediaWriter {
|
|
|
98
98
|
const depacketizer = new DepacketizeCallback(codec, {
|
|
99
99
|
isFinalPacketInSequence: (h) => h.marker,
|
|
100
100
|
});
|
|
101
|
-
const jitterBuffer = new JitterBufferCallback(clockRate
|
|
101
|
+
const jitterBuffer = new JitterBufferCallback(clockRate, {
|
|
102
|
+
bufferSize: this.props.jitterBufferSize,
|
|
103
|
+
latency: this.props.jitterBufferLatency,
|
|
104
|
+
});
|
|
102
105
|
|
|
103
106
|
rtpSource.pipe(jitterBuffer.input);
|
|
104
107
|
rtcpSource.pipe(time.input);
|
|
105
108
|
|
|
106
109
|
jitterBuffer.pipe(time.input);
|
|
107
110
|
time.pipe(depacketizer.input);
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
if (this.props.disableLipSync) {
|
|
112
|
+
depacketizer.pipe(webm.inputVideo);
|
|
113
|
+
} else {
|
|
114
|
+
depacketizer.pipe(lipsync.inputVideo);
|
|
115
|
+
lipsync.pipeVideo(webm.inputVideo);
|
|
116
|
+
}
|
|
110
117
|
} else {
|
|
111
118
|
const depacketizer = new DepacketizeCallback(codec);
|
|
112
119
|
|
|
@@ -114,8 +121,12 @@ export class WebmFactory extends MediaWriter {
|
|
|
114
121
|
rtcpSource.pipe(time.input);
|
|
115
122
|
|
|
116
123
|
time.pipe(depacketizer.input);
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
if (this.props.disableLipSync) {
|
|
125
|
+
depacketizer.pipe(webm.inputAudio);
|
|
126
|
+
} else {
|
|
127
|
+
depacketizer.pipe(lipsync.inputAudio);
|
|
128
|
+
lipsync.pipeAudio(webm.inputAudio);
|
|
129
|
+
}
|
|
119
130
|
}
|
|
120
131
|
|
|
121
132
|
return rtpSource;
|
|
@@ -124,7 +135,7 @@ export class WebmFactory extends MediaWriter {
|
|
|
124
135
|
webm.pipe(saveToFileSystem(this.props.path));
|
|
125
136
|
} else if (this.props.stream) {
|
|
126
137
|
webm.pipe(async (o) => {
|
|
127
|
-
this.props.stream.
|
|
138
|
+
this.props.stream.execute(o);
|
|
128
139
|
});
|
|
129
140
|
}
|
|
130
141
|
}
|