Batch process an entire directory of videos or mp4s from a folder with FFmpeg and Editframe
If you’ve ever tried to perform any sort of changes to a large video collection en masse, you know it’s no easy task. Batch processing can be an efficient way to automate repetitive changes performed on a group of video files—like merging many individual videos into a single file, for example.
In this tutorial, we’ll walk through the process of programmatically merging an entire directory of MP4 videos into a single file using FFmpeg. Then, we’ll explore how to use the Editframe API to programmatically merge videos from a directory in the cloud. Both methods are incredibly useful in instances where you have hundreds of videos that you need combined into one single, continuous file.
Let’s do this.
Files assets
Here are the sample video files provided by pexels.com that we will use in this tutorial:
file1.mp4
file2.mp4
file3.mp4
Part 1: Using FFmpeg
First, we’ll walk through this workflow using FFmpeg.
Required Tools
- Sample video and audio files (provided above)
- FFmpeg: (You’ll need to install FFmpeg and set up the appropriate environment variables before beginning this tutorial)
Create a video directory in which you would like the final merged video file to live:
mkdir video
Here is the FFmpeg command that merges all the videos in a directory into a single file:
for filename in video/*.mp4; do
ffmpeg -y -i "$filename" -c:a copy -c:v copy -bsf:v h264_mp4toannexb -f mpegts "${filename//.mp4/}.ts"
echo "file './${filename//.mp4/}.ts'" >> concat.txt
done
ffmpeg -f concat -segment_time_metadata 1 -safe 0 -i concat.txt -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:-1:-1,setsar=1,fps=30,format=yuv420p" stitched-video.mp4
rm -rf concat.txt video/*.ts
Let’s break down what this is doing.
- In the loop below, we iterate over each mp4 video file in our folder, and run a command that creates a .ts video file. (TS stands for Video Transport Stream, and these files store video data compressed with standard MPEG-2.). Then, we specify a file path for the .ts files in the concat.txt file.
for filename in video/*.mp4; do
ffmpeg -y -i "$filename" -c:a copy -c:v copy -bsf:v h264_mp4toannexb -f mpegts "${filename//.mp4/}.ts"
echo "file './${filename//.mp4/}.ts'" >> concat.txt
done
- In this line, we concat all of the .ts videos files using the concat.txt file. Additionally, we resize the file to 1920:1080 at a frame rate of 30 fps.
ffmpeg -f concat -segment_time_metadata 1 -safe 0 -i concat.txt -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:-1:-1,setsar=1,fps=30,format=yuv420p" stitched-video.mp4
- Here, after merging the video files together, we remove all of the .ts files we created previously. (This is optional):
rm -rf video/*.ts
- In this line, we remove the concat.txt file that holds the video path for the .ts files (this is also optional):
rm -rf concat.txt
Here’s the final output video from the FFmpeg command
stitched-video.mp4
Part 2: Using Editframe
Now let’s perform the same task using Editframe instead of FFmpeg.
Required tools:
- Node.js installed on your machine (v16+)
- Editframe API Token (you can create an account from this link)
*No need to have FFmpeg installed on your machine
- Create a folder for your project:
mkdir editframe-video-bulk-edit && cd editframe-video-bulk-edit
- Initialize a Node.js project:
yarn init -y
- Install the Editframe Node.js SDK
yarn add @editframe/editframe-js
- Create the
index.js
file to merge your videos into one:
const { Editframe } = require('@editframe/editframe-js')
const path = require('path')
const fs = require('fs')
async function main() {
const editframe = new Editframe({
develop: true,
token: 'YOUR_EDITFRAME_TOKEN',
})
const composition = await editframe.videos.new({
backgroundColor: '#000',
dimensions: {
height: 1080,
width: 1920,
},
})
const videos = []
const files = fs
.readdirSync('video')
.filter((name) => name.endsWith('.mp4'))
.map((name) => path.join(__dirname, 'video', name))
for (const file of files) {
const video = await composition.addVideo(file, {
size: { format: 'fit' },
})
videos.push(video)
}
await composition.addSequence(videos)
const video = await composition.encodeSync()
console.log(video)
}
main()
Let’s break down what the code above is doing:
- In this line, we initialize an Editframe instance with our Editframe Token (obtained by creating an Editframe application):
const editframe = new Editframe({
develop: true,
token: 'YOUR_EDITFRAME_TOKEN',
})
- In this line, we create a new video composition with 1920x1080 dimensions:
const composition = await editframe.videos.new({
dimensions: {
height: 1080,
width: 1920,
},
})
- Here, we read the video folder and get all file names within it. Then we loop through the file names array, and check for those that have the .mp4 file extension. We also use the composition.addVideo method to add the videos to the Editframe video composition, and push the layer to the videos array:
const videos = []
const files = fs
.readdirSync('video')
.filter((name) => name.endsWith('.mp4'))
.map((name) => path.join(__dirname, 'video', name))
for (const file of files) {
const video = await composition.addVideo(file, {
size: { format: 'fit' },
})
videos.push(video)
}
- In these lines, we add the videos array layers to the video composition using the composition.addSequence method. We also encode the video synchronously, which we can also do async using a webhook:
// Adding layers into a sequence will automatically calculate and set the start and end times of the layers as well as the final composition duration
await composition.addSequence(filesLayers)
const video = await composition.encodeSync()
- Run the video script
node index
Here’s the output video from the Editframe API:
editframe-stitched-video.mp4
Note: You can add transitions and filters, resize videos, trim videos, and much more using Editframe. Check out the docs here.
Comparison video between FFmpeg and Editframe API
Here’s a comparison between the videos we created with FFmpeg (left) and Editframe (right):