How to Add Chapters to MP4s with FFmpeg
How to add chapters to MP4s, with a helper python script to write the chapter metadata
FFmpeg is one of those pieces of software which can do practically anything, if you can work out how. One of the things it can do it modify the metadata for video files, including adding chapters.
Adding chapters is done via ffmetadata files.
Extract Metadata From Video
ffmpeg -i INPUT.mp4 -f ffmetadata FFMETADATAFILE.txt
Example output from a video file with no chapters.
;FFMETADATA1
title=None
major_brand=isom
minor_version=512
compatible_brands=isomiso2avc1mp41
encoder=Lavf57.56.100
Write Metadata To Video
The metadata file can be edited, including to add chapters, and then ffmpeg can write it to a file.
ffmpeg doesn't mutate the existing video, but instead creates a new video with the new metadata, existing video and existing audio.
ffmpeg -i INPUT.mp4 -i FFMETADATAFILE.txt -map_metadata 1 -codec copy OUTPUT.mp4
Adding Chapters
Adding chapters is done by editing the metadata file and adding sections like this:
[CHAPTER]
TIMEBASE=1/1000
START=1
END=448000
title=The Pledge
[CHAPTER]
TIMEBASE=1/1000
START=448001
END= 3883999
title=The Turn
[CHAPTER]
TIMEBASE=1/1000
START=3884000
END=4418000
title= The Prestige
Helper Script
The requirement to specify times as an integer of some time base, instead of the 1:30:20.5 style format usually used for displaying time is very off-putting to me, so I created a small helper script so I could just note down times while watching a video and have the metadata entries automatically worked out how my automatically.
The script has a few requirements:
- It expects to be in the same directory as a 'chapters.txt' file to read from, and an 'FFMETADATAFILE' file to output to.
- It expects the chapters.txt file to be a txt file with 1 chapter per line starting with 0:00:00 style time stamps and followed by the title of the chapter
- It appends to an existing FFMETADATAFILE
- It expects no existing chapters in the file
- It expects an final END chapter which is ignored, but provides the end point for the last real chapter.
Example Chapters.txt
0:23:20 Start
0:40:30 First Performance
0:40:56 Break
1:04:44 Second Performance
1:24:45 Crowd Shots
1:27:45 Credits
Helper script
import re
chapters = list()
with open('chapters.txt', 'r') as f:
for line in f:
x = re.match(r"(\d):(\d{2}):(\d{2}) (.*)", line)
hrs = int(x.group(1))
mins = int(x.group(2))
secs = int(x.group(3))
title = x.group(4)
minutes = (hrs * 60) + mins
seconds = secs + (minutes * 60)
timestamp = (seconds * 1000)
chap = {
"title": title,
"startTime": timestamp
}
chapters.append(chap)
text = ""
for i in range(len(chapters)-1):
chap = chapters[i]
title = chap['title']
start = chap['startTime']
end = chapters[i+1]['startTime']-1
text += f"""
[CHAPTER]
TIMEBASE=1/1000
START={start}
END={end}
title={title}
"""
with open("FFMETADATAFILE", "a") as myfile:
myfile.write(text)
The script could be modified to remove the need for a chapters.txt
file by swapping line 5's with. ...
statement for this.
chapters_text = """0:40:30 First Performance
0:40:56 Break
... etc ...
"""
f = chapters_text.splitlines()
Example Usage With a Video With No Chapters
Get existing video metadata
ffmpeg -i INPUT.mp4 -f ffmetadata FFMETADATAFILE
Check there are no existing chapters.
Watch the video, noting chapters into a chapters.txt
file as you go.
Place FFMETADATAFILE
, chapters.txt
, and the video file in the same directory.
Run the helper script to append chapters to FFMETADATAFILE.
python3 helper.py
Create a new video, copying the video and audio from the original without re-encoding.
ffmpeg -i INPUT.mp4 -i FFMETADATAFILE -map_metadata 1 -codec copy OUTPUT.mp4