Skip to content

Commit

Permalink
Merge pull request #75 from kieransimkin/main
Browse files Browse the repository at this point in the history
add ffmpegAdditionalOptions config prop. Also, improve handling of er…
  • Loading branch information
prasanaworld authored Feb 18, 2024
2 parents 9c1aa12 + 98feeef commit c5982d7
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 13 deletions.
7 changes: 7 additions & 0 deletions src/lib/pageVideoStreamTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ export type PuppeteerScreenRecorderOptions = {
*/
readonly videoPixelFormat?: string;

/**
* @name ffmpegAdditionalOptions
* @member PuppeteerScreenRecorderOptions
* @description Allows you to pass additional options to the ffmpeg encoder - for example you might want to pass "-movflags +faststart"
*/
readonly ffmpegAdditionalOptions?: string[];

/**
* @name autopad
* @member PuppeteerScreenRecorderOptions
Expand Down
59 changes: 46 additions & 13 deletions src/lib/pageVideoStreamWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ export default class PageVideoStreamWriter extends EventEmitter {
this.handleWriteStreamError(e.message);
resolve(false);
})
.on('stderr', (e) => {
this.handleWriteStreamError(e);
resolve(false);
})
.on('end', () => resolve(true))
.save(destinationPath);

Expand All @@ -154,6 +158,10 @@ export default class PageVideoStreamWriter extends EventEmitter {
writableStream.emit('error', e);
resolve(false);
})
.on('stderr', (e) => {
writableStream.emit('error', { message: e });
resolve(false);
})
.on('end', () => {
writableStream.end();
resolve(true);
Expand All @@ -169,6 +177,25 @@ export default class PageVideoStreamWriter extends EventEmitter {

private getDestinationStream(): ffmpeg {
const cpu = Math.max(1, os.cpus().length - 1);
const outputOptions = [];
outputOptions.push(`-crf ${this.options.videoCrf ?? 23}`);
outputOptions.push(`-preset ${this.options.videoPreset || 'ultrafast'}`);
outputOptions.push(
`-pix_fmt ${this.options.videoPixelFormat || 'yuv420p'}`
);
outputOptions.push(`-minrate ${this.options.videoBitrate || 1000}`);
outputOptions.push(`-maxrate ${this.options.videoBitrate || 1000}`);
outputOptions.push('-framerate 1');
outputOptions.push(`-threads ${cpu}`);
outputOptions.push(`-loglevel error`);
if (
this.options.ffmpegAdditionalOptions &&
this.options.ffmpegAdditionalOptions.length
) {
for (const opt of this.options.ffmpegAdditionalOptions) {
outputOptions.push(opt);
}
}
const outputStream = ffmpeg({
source: this.videoMediatorStream,
priority: 20,
Expand All @@ -179,13 +206,7 @@ export default class PageVideoStreamWriter extends EventEmitter {
.autopad(this.autopad.activation, this.autopad?.color)
.inputFormat('image2pipe')
.inputFPS(this.options.fps)
.outputOptions(`-crf ${this.options.videoCrf ?? 23}`)
.outputOptions(`-preset ${this.options.videoPreset || 'ultrafast'}`)
.outputOptions(`-pix_fmt ${this.options.videoPixelFormat || 'yuv420p'}`)
.outputOptions(`-minrate ${this.options.videoBitrate || 1000}`)
.outputOptions(`-maxrate ${this.options.videoBitrate || 1000}`)
.outputOptions('-framerate 1')
.outputOptions(`-threads ${cpu}`)
.outputOptions(outputOptions)
.on('progress', (progressDetails) => {
this.duration = progressDetails.timemark;
});
Expand Down Expand Up @@ -241,7 +262,10 @@ export default class PageVideoStreamWriter extends EventEmitter {
0,
numberOfFramesToSplice
);
this.processFrameBeforeWrite(framesToProcess, this.screenCastFrames[0].timestamp);
this.processFrameBeforeWrite(
framesToProcess,
this.screenCastFrames[0].timestamp
);
}

const insertionIndex = this.findSlot(frame.timestamp);
Expand All @@ -253,19 +277,28 @@ export default class PageVideoStreamWriter extends EventEmitter {
}
}

private trimFrame(fameList: pageScreenFrame[], chunckEndTime: number): pageScreenFrame[] {
private trimFrame(
fameList: pageScreenFrame[],
chunckEndTime: number
): pageScreenFrame[] {
return fameList.map((currentFrame: pageScreenFrame, index: number) => {
const endTime = (index !== fameList.length-1) ? fameList[index+1].timestamp : chunckEndTime;
const duration = endTime - currentFrame.timestamp;

const endTime =
index !== fameList.length - 1
? fameList[index + 1].timestamp
: chunckEndTime;
const duration = endTime - currentFrame.timestamp;

return {
...currentFrame,
duration,
};
});
}

private processFrameBeforeWrite(frames: pageScreenFrame[], chunckEndTime: number): void {
private processFrameBeforeWrite(
frames: pageScreenFrame[],
chunckEndTime: number
): void {
const processedFrames = this.trimFrame(frames, chunckEndTime);

processedFrames.forEach(({ blob, duration }) => {
Expand Down

0 comments on commit c5982d7

Please sign in to comment.