From f65d1babbaa9c6b5357a69f4c0ba8cf76cf6226c Mon Sep 17 00:00:00 2001 From: Momtchil Momtchev Date: Mon, 25 Dec 2023 15:58:54 +0100 Subject: [PATCH] test handling of errors while streaming --- test/streaming.test.ts | 75 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/test/streaming.test.ts b/test/streaming.test.ts index 754b438..c7dac2e 100644 --- a/test/streaming.test.ts +++ b/test/streaming.test.ts @@ -5,7 +5,8 @@ import { assert } from 'chai'; import ffmpeg from '@mmomtchev/ffmpeg'; import { Muxer, Demuxer, VideoDecoder, VideoEncoder, AudioDecoder, AudioEncoder } from '@mmomtchev/ffmpeg/stream'; -import { Readable } from 'node:stream'; +import { Readable, Transform, TransformCallback } from 'node:stream'; +import { MediaTransform } from '../lib/MediaStream'; ffmpeg.setLogLevel(process.env.DEBUG_FFMPEG ? ffmpeg.AV_LOG_DEBUG : ffmpeg.AV_LOG_ERROR); @@ -71,7 +72,7 @@ describe('streaming', () => { }); }); - it('error handling', (done) => { + it('error handling on creation', (done) => { // MP4 does not support streaming in its default configuration const demuxer = new Demuxer({ inputFile: path.resolve(__dirname, 'data', 'launch.mp4') }); @@ -131,4 +132,74 @@ describe('streaming', () => { }); }); + it('error handling while streaming', (done) => { + const demuxer = new Demuxer({ inputFile: path.resolve(__dirname, 'data', 'launch.mp4') }); + + demuxer.on('error', done); + demuxer.on('ready', () => { + try { + assert.lengthOf(demuxer.streams, 2); + assert.lengthOf(demuxer.audio, 1); + assert.lengthOf(demuxer.video, 1); + + const audioInput = new AudioDecoder(demuxer.audio[0]); + const videoInput = new VideoDecoder(demuxer.video[0]); + + const videoDefinition = videoInput.definition(); + const audioDefinition = audioInput.definition(); + + const videoOutput = new VideoEncoder({ + type: 'Video', + codec: ffmpeg.AV_CODEC_H264, + bitRate: 2.5e6, + width: videoDefinition.width, + height: videoDefinition.height, + frameRate: new ffmpeg.Rational(25, 1), + pixelFormat: videoDefinition.pixelFormat + }); + + const audioOutput = new AudioEncoder({ + type: 'Audio', + codec: ffmpeg.AV_CODEC_AAC, + bitRate: 128e3, + sampleRate: audioDefinition.sampleRate, + sampleFormat: audioDefinition.sampleFormat, + channelLayout: audioDefinition.channelLayout + }); + + let frames = 0; + const injectError = new MediaTransform({ + transform: function (this: Transform, chunk: any, encoding: any, callback: TransformCallback) { + if (frames++ === 100) + this.push('invalid'); + else + this.push(chunk); + callback(); + } + }); + + const muxer = new Muxer({ outputFormat: 'matroska', streams: [videoOutput, audioOutput] }); + + muxer.on('finish', () => done('Expected an error')); + videoOutput.on('error', (e) => { + try { + assert.match(e.message, /Input is not a raw video/); + done(); + } catch (e) { + done(e); + } + }); + + assert.instanceOf(muxer.output, Readable); + const output = fs.createWriteStream(tempFile); + + demuxer.video[0].pipe(videoInput).pipe(injectError).pipe(videoOutput).pipe(muxer.video[0]); + demuxer.audio[0].pipe(audioInput).pipe(audioOutput).pipe(muxer.audio[0]); + muxer.output!.pipe(output); + } catch (err) { + done(err); + } + }); + }); + });