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