Skip to content

Commit 7140d37

Browse files
[IMP] WIP: /
1 parent 98bad37 commit 7140d37

File tree

4 files changed

+31
-12
lines changed

4 files changed

+31
-12
lines changed

src/config.js

+2
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ export const recording = Object.freeze({
216216
maxDuration: 1000 * 60 * 60, // 1 hour
217217
fileTTL: 1000 * 60 * 60 * 24, // 24 hours
218218
fileType: "mp4",
219+
videoCodec: "libx264",
220+
audioCodec: "aac",
219221
audioLimit: 20,
220222
cameraLimit: 4, // how many camera can be merged into one recording
221223
screenLimit: 1,

src/models/recorder.js

+17-6
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,11 @@ export class Recorder extends EventEmitter {
113113
async _start_fragment(ids) {
114114
const oldProcess = this.ffmpeg;
115115
this.state = RECORDER_STATE.UPDATING;
116+
/** @type {RtpData[]} */
116117
const audioRtps = [];
118+
/** @type {RtpData[]} */
117119
const cameraRtps = [];
120+
/** @type {RtpData[]} */
118121
const screenRtps = [];
119122
for (const id of ids) {
120123
const session = this.channel.sessions.get(id);
@@ -123,22 +126,25 @@ export class Recorder extends EventEmitter {
123126
continue;
124127
}
125128
if (audioRtps.length < recording.audioLimit) {
126-
const audioRtpData = session.getRtp(STREAM_TYPE.AUDIO);
129+
const audioRtpData = await session.getRtp(STREAM_TYPE.AUDIO);
127130
audioRtpData && audioRtps.push(audioRtpData);
128131
}
129132
if (cameraRtps.length < recording.cameraLimit) {
130-
const cameraRtpData = session.getRtp(STREAM_TYPE.CAMERA);
133+
const cameraRtpData = await session.getRtp(STREAM_TYPE.CAMERA);
131134
cameraRtpData && cameraRtps.push(cameraRtpData);
132135
}
133136
if (screenRtps.length < recording.screenLimit) {
134-
const screenRtpData = session.getRtp(STREAM_TYPE.SCREEN);
137+
const screenRtpData = await session.getRtp(STREAM_TYPE.SCREEN);
135138
screenRtpData && screenRtps.push(screenRtpData);
136139
}
137140
}
138-
const tempPath = path.join(recording.directory, `call_${Date.now()}.${recording.fileType}`);
141+
const tempPath = path.join(
142+
recording.directory,
143+
`__FRAGMENT__-${this.uuid}-${Date.now()}.${recording.fileType}`
144+
);
139145
this.ffmpeg = new FFMPEG(tempPath);
140146
try {
141-
await this.ffmpeg.start(audioRtps, screenRtps, cameraRtps); // args should be base on the rtp transports
147+
await this.ffmpeg.merge({ audioRtps, screenRtps, cameraRtps }); // args should be base on the rtp transports
142148
this.state = RECORDER_STATE.RECORDING;
143149
} catch (error) {
144150
logger.error(`Failed to start recording: ${error.message}`);
@@ -206,6 +212,11 @@ export class Recorder extends EventEmitter {
206212
if (filePaths.length === 1) {
207213
return filePaths[0];
208214
}
209-
return filePaths[1]; // TODO merge logic with FFMPEG
215+
const ffmpeg = new FFMPEG(
216+
path.join(recording.directory, `__MERGED__-${this.uuid}.${recording.fileType}`)
217+
);
218+
// should await for ffmpeg complete event.
219+
await ffmpeg.concat(filePaths);
220+
return ""; // TODO merge logic with FFMPEG
210221
}
211222
}

src/models/session.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ export class Session extends EventEmitter {
498498

499499
/**
500500
* @param {STREAM_TYPE[keyof STREAM_TYPE]} type
501-
* @return {Promise<RtpData | undefined>}
501+
* @return {RtpData}
502502
*/
503503
async getRtp(type) {
504504
if (this._rtp[type]) {

src/utils/ffmpeg.js

+11-5
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,17 @@ const SCREEN_LAYOUT = {
4848
* @param {RtpData[]} screenRtps
4949
* @return {string}
5050
*/
51-
function formatFfmpegSdp({ audioRtps, screenRtps, cameraRtps }) {
51+
function formatSdp({ audioRtps, screenRtps, cameraRtps }) {
5252
logger.info(`TODO: ${screenRtps}`);
5353
const sdp = ["v=0", "o=- 0 0 IN IP4 127.0.0.1", "s=FFmpeg", "c=IN IP4 127.0.0.1", "t=0 0"];
5454
for (const audioRtp of audioRtps) {
5555
sdp.push(`m=audio ${audioRtp.port} RTP/AVP ${audioRtp.payloadType}`);
5656
sdp.push(`a=rtpmap:${audioRtp.payloadType} ${audioRtp.codec}/${audioRtp.clockRate}`);
5757
sdp.push(`a=sendonly`);
5858
}
59-
sdp.push(`-c:a aac -b:a 128k -ac 2 -filter_complex amerge=inputs=${audioRtps.length}`);
59+
sdp.push(
60+
`-c:a ${recording.audioCodec} -b:a 128k -ac 2 -filter_complex amerge=inputs=${audioRtps.length}`
61+
);
6062
if (cameraRtps.length > 0) {
6163
const layout = SCREEN_LAYOUT[cameraRtps.length];
6264
if (!layout) {
@@ -68,7 +70,7 @@ function formatFfmpegSdp({ audioRtps, screenRtps, cameraRtps }) {
6870
sdp.push(`a=sendonly`);
6971
}
7072
sdp.push(`-filter_complex`, layout(cameraRtps.map((rtp) => rtp.label)));
71-
sdp.push("-c:v libx264"); // TODO move outside of the condition, should also account for screenRtps
73+
sdp.push(`-c:v ${recording.videoCodec}`); // TODO move outside of the condition, should also account for screenRtps
7274
}
7375
return sdp.join("\n");
7476
}
@@ -122,8 +124,8 @@ export class FFMPEG extends EventEmitter {
122124
* @param {RtpData[]} rtpInputs.screenRtps
123125
* @param {RtpData[]} rtpInputs.cameraRtps
124126
*/
125-
async start(rtpInputs) {
126-
const sdp = formatFfmpegSdp(rtpInputs);
127+
async merge(rtpInputs) {
128+
const sdp = formatSdp(rtpInputs);
127129
this._process = child_process.spawn("ffmpeg", this._processArgs, {
128130
stdio: ["pipe", "pipe", process.stderr],
129131
});
@@ -144,6 +146,10 @@ export class FFMPEG extends EventEmitter {
144146
);
145147
}
146148

149+
concat(filePaths) {
150+
return filePaths[0];
151+
}
152+
147153
kill() {
148154
this._process?.kill("SIGINT");
149155
}

0 commit comments

Comments
 (0)