Skip to content

Commit

Permalink
Merge commit 'd635c3a7af366849e7663b2105e7a7831579d3a8' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
shinyoshiaki committed Aug 14, 2024
2 parents a792637 + d635c3a commit 7672dd2
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 41 deletions.
8 changes: 4 additions & 4 deletions examples/google-nest/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ const session = async (
],
},
});
const recorder = new MediaRecorder(
`./${device.name.split("/").at(-1)}.webm`,
2,
);
const recorder = new MediaRecorder({
path: `./${device.name.split("/").at(-1)}.webm`,
numOfTracks: 2,
});

const audioTransceiver = pc.addTransceiver("audio", {
direction: "recvonly",
Expand Down
6 changes: 5 additions & 1 deletion examples/mediachannel/red/record/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ server.on("connection", async (socket) => {
const audio = pc.addTransceiver("audio");
audio.onTrack.subscribe((track) => {
audio.sender.replaceTrack(track);
const recorder = new MediaRecorder("./audio.webm", 1, { tracks: [track] });
const recorder = new MediaRecorder({
path: "./audio.webm",
numOfTracks: 1,
tracks: [track],
});
setTimeout(() => {
recorder.stop();
console.log("stop");
Expand Down
4 changes: 3 additions & 1 deletion examples/save_to_disk/av1x.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ const server = new Server({ port: 8878 });
console.log("start");

server.on("connection", async (socket) => {
const recorder = new MediaRecorder("./test.webm", 2, {
const recorder = new MediaRecorder({
path: "./test.webm",
numOfTracks: 2,
width: 640,
height: 360,
});
Expand Down
4 changes: 3 additions & 1 deletion examples/save_to_disk/dump.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import { MediaRecorder } from "../../packages/webrtc/src/nonstandard";
);

const track = new MediaStreamTrack({ kind: "video" });
const recorder = new MediaRecorder("./test.webm", 1, {
const recorder = new MediaRecorder({
path: "./test.webm",
numOfTracks: 1,
width: 640,
height: 360,
tracks: [track],
Expand Down
42 changes: 42 additions & 0 deletions examples/save_to_disk/opus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Server } from "ws";
import { RTCPeerConnection } from "../../packages/webrtc/src";
import { MediaRecorder } from "../../packages/webrtc/src/nonstandard";

// open ./answer.html

const server = new Server({ port: 8878 });
console.log("start");

server.on("connection", async (socket) => {
const recorder = new MediaRecorder({
path: `./opus-${Date.now()}.webm`,
numOfTracks: 1,
});

const pc = new RTCPeerConnection();

pc.addTransceiver("video").onTrack.subscribe(async (track, transceiver) => {
transceiver.sender.replaceTrack(track);
setInterval(() => {
transceiver.receiver.sendRtcpPLI(track.ssrc);
}, 3_000);
});

pc.addTransceiver("audio").onTrack.subscribe(async (track, transceiver) => {
transceiver.sender.replaceTrack(track);
await recorder.addTrack(track);
});

setTimeout(async () => {
await recorder.stop();
console.log("stop");
}, 15_000);

await pc.setLocalDescription(await pc.createOffer());
const sdp = JSON.stringify(pc.localDescription);
socket.send(sdp);

socket.on("message", (data: any) => {
pc.setRemoteDescription(JSON.parse(data));
});
});
4 changes: 3 additions & 1 deletion examples/save_to_disk/packetloss/vp8.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ const server = new Server({ port: 8888 });
console.log("start");

server.on("connection", async (socket) => {
const recorder = new MediaRecorder("./werift.webm", 1, {
const recorder = new MediaRecorder({
path: "./werift.webm",
numOfTracks: 1,
width: 640,
height: 360,
jitterBufferLatency: 50,
Expand Down
4 changes: 3 additions & 1 deletion examples/save_to_disk/vp8.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ const server = new Server({ port: 8878 });
console.log("start");

server.on("connection", async (socket) => {
const recorder = new MediaRecorder(`./vp8-${Date.now()}.webm`, 2, {
const recorder = new MediaRecorder({
path: `./vp8-${Date.now()}.webm`,
numOfTracks: 2,
width: 640,
height: 360,
});
Expand Down
4 changes: 3 additions & 1 deletion examples/save_to_disk/vp9.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ const server = new Server({ port: 8878 });
console.log("start");

server.on("connection", async (socket) => {
const recorder = new MediaRecorder("./test.webm", 2, {
const recorder = new MediaRecorder({
path: "./test.webm",
numOfTracks: 2,
width: 640,
height: 360,
});
Expand Down
62 changes: 45 additions & 17 deletions packages/webrtc/src/nonstandard/recorder/index.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,61 @@
import Event from "rx.mini";

import type { PassThrough } from "stream";
import type { MediaStreamTrack } from "../../media/track";
import type { MediaWriter } from "./writer";
import { WebmFactory } from "./writer/webm";

export class MediaRecorder {
writer: MediaWriter;
ext: string;
ext?: string;
tracks: MediaStreamTrack[] = [];
started = false;
onError = new Event<[Error]>();

constructor(
public path: string,
public numOfTracks = 1,
public options: Partial<MediaRecorderOptions> = {},
public props: Partial<MediaRecorderOptions> & {
numOfTracks: number;
} & (
| {
path: string;
stream?: PassThrough;
}
| {
path?: string;
stream: PassThrough;
}
),
) {
this.ext = path.split(".").slice(-1)[0];
this.writer = (() => {
switch (this.ext) {
case "webm":
return new WebmFactory(path, options);
default:
throw new Error();
}
})();

this.tracks = options.tracks ?? this.tracks;
if (this.tracks.length === numOfTracks) {
this.tracks = props.tracks ?? this.tracks;
if (this.tracks.length === props.numOfTracks) {
this.start().catch((error) => {
this.onError.execute(error);
});
}

const { path, stream } = props;

if (path) {
this.ext = path.split(".").slice(-1)[0];
this.writer = (() => {
switch (this.ext) {
case "webm":
return new WebmFactory({
...props,
path: path!,
stream: stream!,
});
default:
throw new Error();
}
})();
} else {
this.writer = new WebmFactory({
...props,
path: path!,
stream: stream!,
});
}
}

async addTrack(track: MediaStreamTrack) {
Expand All @@ -40,7 +64,10 @@ export class MediaRecorder {
}

private async start() {
if (this.tracks.length === this.numOfTracks && this.started === false) {
if (
this.tracks.length === this.props.numOfTracks &&
this.started === false
) {
this.started = true;
await this.writer.start(this.tracks);
}
Expand All @@ -59,4 +86,5 @@ export interface MediaRecorderOptions {
waitForKeyframe: boolean;
defaultDuration: number;
tracks: MediaStreamTrack[];
disableNtp: boolean;
}
7 changes: 5 additions & 2 deletions packages/webrtc/src/nonstandard/recorder/writer/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import type { PassThrough } from "stream";
import type { MediaRecorderOptions } from "..";
import type { MediaStreamTrack } from "../../..";

export abstract class MediaWriter {
constructor(
protected path: string,
protected options: Partial<MediaRecorderOptions>,
protected props: Partial<MediaRecorderOptions> & {
path: string;
stream?: PassThrough;
} & { path?: string; stream: PassThrough },
) {}

async start(tracks: MediaStreamTrack[]) {}
Expand Down
35 changes: 23 additions & 12 deletions packages/webrtc/src/nonstandard/recorder/writer/webm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
NtpTimeCallback,
RtcpSourceCallback,
RtpSourceCallback,
RtpTimeCallback,
type SupportedCodec,
WebmCallback,
saveToFileSystem,
Expand All @@ -23,7 +24,9 @@ export class WebmFactory extends MediaWriter {
unSubscribers = new EventDisposer();

async start(tracks: MediaStreamTrack[]) {
await unlink(this.path).catch((e) => e);
if (this.props.path) {
await unlink(this.props.path).catch((e) => e);
}

const inputTracks = tracks.map((track, i) => {
const trackNumber = i + 1;
Expand Down Expand Up @@ -52,8 +55,8 @@ export class WebmFactory extends MediaWriter {
codec,
clockRate: 90000,
trackNumber,
width: this.options.width,
height: this.options.height,
width: this.props.width ?? 640,
height: this.props.height ?? 360,
payloadType,
track,
};
Expand All @@ -70,7 +73,7 @@ export class WebmFactory extends MediaWriter {
});

const webm = new WebmCallback(inputTracks, {
duration: this.options.defaultDuration ?? 1000 * 60 * 60 * 24,
duration: this.props.defaultDuration ?? 1000 * 60 * 60 * 24,
});
const lipsync = new LipsyncCallback();

Expand All @@ -87,7 +90,9 @@ export class WebmFactory extends MediaWriter {
rtcpSource.input(rtcp);
})
.disposer(this.unSubscribers);
const ntpTime = new NtpTimeCallback(clockRate);
const time = this.props.disableNtp
? new RtpTimeCallback(clockRate)
: new NtpTimeCallback(clockRate);

if (track.kind === "video") {
const depacketizer = new DepacketizeCallback(codec, {
Expand All @@ -96,26 +101,32 @@ export class WebmFactory extends MediaWriter {
const jitterBuffer = new JitterBufferCallback(clockRate);

rtpSource.pipe(jitterBuffer.input);
rtcpSource.pipe(ntpTime.input);
rtcpSource.pipe(time.input);

jitterBuffer.pipe(ntpTime.input);
ntpTime.pipe(depacketizer.input);
jitterBuffer.pipe(time.input);
time.pipe(depacketizer.input);
depacketizer.pipe(lipsync.inputVideo);
lipsync.pipeVideo(webm.inputVideo);
} else {
const depacketizer = new DepacketizeCallback(codec);

rtpSource.pipe(ntpTime.input);
rtcpSource.pipe(ntpTime.input);
rtpSource.pipe(time.input);
rtcpSource.pipe(time.input);

ntpTime.pipe(depacketizer.input);
time.pipe(depacketizer.input);
depacketizer.pipe(lipsync.inputAudio);
lipsync.pipeAudio(webm.inputAudio);
}

return rtpSource;
});
webm.pipe(saveToFileSystem(this.path));
if (this.props.path) {
webm.pipe(saveToFileSystem(this.props.path));
} else if (this.props.stream) {
webm.pipe(async (o) => {
this.props.stream.write(o);
});
}
}

async stop() {
Expand Down

0 comments on commit 7672dd2

Please sign in to comment.