1. Introduction to streaming processing

Let’s start with an example:

ffmpeg -s 0 -i input.mp4 -t 10 output.mp4
Copy the code

This command will intercept the input video input.mp4 from 0 to 10 seconds and save the output.mp4 file. Commands can be divided into three parts:


  1. File location
  2. Network location, used to implement network push flow
  3. -To output the command result as a standard output stream

With the – feature, we can call ffmpeg commands in pipe style, for example:

ffmpeg -s 0 -i input.mp4 -t 10 -f mpegts - | tee output.ts
Copy the code

Tip:

When – is used, FFMPEG will try to follow the pipe communication constraints and output the video stream after command processing to the standard output stream; Output the command execution process information to the standard error stream.

2. Use it in node

The biggest benefit of streaming is that you can retrieve a portion of the data without waiting for the entire video command to be processed. This can improve the response speed in performance-sensitive scenarios such as Web video services, live streaming, etc. This is explained in more detail in my other blog post “IMPLEMENTING dynamic bitstream Video Services with HLS + FFMPEG”. In the node implementation, ffmpeg command is called by the Spawn interface, and the standard output stream of ffMpeg command is output to the target video file by means of the pipeline communication mechanism between the parent and child processes.

const { spawn } = require('child_process');
const fs = require('fs');
const path = require('path');

function transform(sourceFile, outputStream, start, duration) {
	const params = [`-ss`, start, `-i`, sourceFile, `-t`, duration, '-f'.'mpegts'.The '-'];
	return new Promise((r, j) = > {
    const cp = spawn('ffmpeg', params);
    // ffmpeg standard output
		cp.stdout.pipe(outputStream);
    // Outputs procedure logs executed by FFmPEG to the program's standard output stream, usually console
		cp.stderr.pipe(process.stdout);
		cp.on('error', (err) => {
			j(err);
		});
		cp.on('close', (code) => {
			console.log(`ffmpeg process close all stdio with code ${code}`);
			r(code);
		});
		cp.on('exit', (code) => {
			console.log(`ffmpeg process exited with code ${code}`);
		});
	});
}

async function run() {
	const inputFile = path.join(__dirname, 'test.mp4');
	const output = fs.createWriteStream(path.join(__dirname, 'output.mp4'));
	await transform(inputFile, output, 0.100);
}

run();
Copy the code

Operation effect:

3. Pay attention to the point

Although FFMPEG provides streaming output, it is not suitable for all scenarios. I briefly tested it and found:

  1. When the output encapsulation format ismp4When an errormuxer does not support non seekable output, the exception is becausemp4Instead of writing sequentially, you need to do seek multiple times to insert the data into the appropriate place when writing different types of boxes, somp4The format requires that the output stream beseekableStdout will blind you.
  2. When the output encapsulation format ishlsWhen warningCannot use rename on non file protocol, this may lead to races and temporary partial filesThis is because HLS needs to output multiple files, includingm3u8And a pile ofts, by defaulttsShards are named according to the shard sequence, so using a standard output stream that cannot be renamed is a mistake.