Encode PPM Frames to MPEG-1 with ppmtompeg
Turn a numbered stack of PPM frames into a real MPEG-1 file with ppmtompeg, using a parameter file you can adapt for your own sequence. The examples use the installed Netpbm 11.5.2 command on Debian or Ubuntu. Allow about fifteen minutes for a first test, plus whatever your frames need to actually encode.
The route
Jump straight to the step you need, or tick off Done means at the end.
You need ppmtompeg, readable input images in a format Netpbm can convert, and a writable output directory. This guide uses PPM input directly, so it needs no elevated privileges. Do not reach for sudo on an encode just because the output happens to be a video file: fix file permissions or pick a user-owned working directory instead.
1. Check the installed command
Confirm which executable will run and record its version. These are ordinary, read-only checks:
$ command -v ppmtompeg
/usr/bin/ppmtompeg
$ dpkg-query -W -f='${Package} ${Version}\n' netpbm
netpbm 2:11.05.02-1.1build1
$ ppmtompeg --version 2>&1 | head -n 1
ppmtompeg: Using libnetpbm from Netpbm Version: Netpbm 11.5.2
The installed manual is dated 23 July 2006, while the binary reports Netpbm 11.5.2. Keep that gap in mind when comparing another machine. Every parameter name in this guide comes from the installed manual, and the small encode below ran successfully on this binary.
Checkpoint
Stop here if command -v finds nothing, or the package version is not the one you meant to use. Install or select the package through your normal system-management process before touching the example.
2. Put the frames in display order
Make a directory of images in a predictable sequence. The parameter file reads them in the order set by its INPUT block. For four frames named frame0.ppm through frame3.ppm, check names and dimensions before encoding anything:
$ file /path/to/frames/frame*.ppm
$ identify /path/to/frames/frame0.ppm 2>/dev/null || true
$ test -r /path/to/frames/frame0.ppm && echo readable
readable
identify is optional and belongs to ImageMagick, not Netpbm: swap in whatever image inspection tool you have, or skip it. What matters is that every file exists, is readable, and shares the same dimensions. If the sequence has gaps, list explicit file names in the input block or adjust the numeric range. Do not assume shell glob order is the order you actually want in the finished video.
3. Write a parameter file
ppmtompeg takes one parameter-file argument, and the file is case-sensitive: lines starting with # are comments, but the keywords themselves must be upper case. This working example uses a repeated IBP frame pattern, 25 frames per second, and a tiny 16 by 16 test sequence:
PATTERN IBP
OUTPUT /path/to/output/movie.mpg
INPUT_DIR /path/to/frames
INPUT
frame*.ppm [0-3]
END_INPUT
BASE_FILE_FORMAT PPM
INPUT_CONVERT *
SIZE 16x16
GOP_SIZE 3
SLICES_PER_FRAME 1
PIXEL HALF
RANGE 4
PSEARCH_ALG TWOLEVEL
BSEARCH_ALG SIMPLE
IQSCALE 8
PQSCALE 10
BQSCALE 12
REFERENCE_FRAME DECODED
FRAME_RATE 25
BIT_RATE 1150000
BUFFER_SIZE 327680
Change OUTPUT, INPUT_DIR, the filename range and SIZE for your own files. INPUT_CONVERT * means the input is already in the selected base format. For GIF or another source format, the conversion command must write the converted image to standard output, for example INPUT_CONVERT giftopnm *.
The four qscale values deserve care. They run 1 to 31: larger values improve compression but reduce quality, and the example deliberately keeps them moderate rather than promising a particular look. GOP_SIZE is a target frame count, but a GOP can run longer when it needs to start the next GOP with an I frame. The pattern and GOP size do not have to match, though matching them is usually easier to reason about.
Checkpoint
Before running the encoder, check the output path is not a file you value. The program writes wherever OUTPUT names, so pick a new filename for the first run.
4. Run one encode without overwriting an old file
Use a temporary output name while testing. -realquiet suppresses normal screen output and leaves errors visible, which makes a scripted smoke test easier to read:
$ sed 's#movie\.mpg#movie.mpg.new#' /path/to/encode.par > /tmp/ppmtompeg-test.par
$ ppmtompeg -realquiet /tmp/ppmtompeg-test.par
$ test -s /path/to/frames/movie.mpg.new && echo 'non-empty MPEG output'
non-empty MPEG output
The first command makes a temporary copy of the parameter file; it never touches the original, though you should adjust the replacement text if your output name contains something different. A zero exit status means the encode completed. A non-zero one means read the error before trying another option.
If you want progress messages instead, leave out -realquiet. The default -quiet 0 reports an estimate after each frame, and that estimate excludes time spent reading frames, so a quiet spell does not mean the process has stopped.
5. Verify the result and promote it
Check the output is present and non-empty before it replaces anything final:
$ file /path/to/frames/movie.mpg.new
$ stat --format='bytes=%s' /path/to/frames/movie.mpg.new
bytes=...
The exact file description and byte count vary with your frames and settings. A non-empty file only proves the encoder wrote data, not that every player will accept the stream: open it in a player or inspect it with a suitable media tool.
When the result checks out, replace the old destination in one deliberate step. This is the only state-changing command in the whole workflow:
$ mv -- /path/to/frames/movie.mpg.new /path/to/frames/movie.mpg
$ test -s /path/to/frames/movie.mpg && echo 'final output ready'
final output ready
Warning
If the encode failed, remove only the temporary .new file after checking its path. The original output is untouched. Never reach for a broad wildcard like rm *.mpg as a recovery step.
6. Use staged encoding when a long run needs recovery
For a long sequence, -gop encodes one numbered Group of Pictures, starting the count at 0, and writes a file with a .gop.N suffix. The options -gop, -frames, -combine_gops and -combine_frames are mutually exclusive.
$ ppmtompeg -gop 0 /path/to/encode.par
$ ls -l /path/to/frames/movie.mpg.gop.0
$ ppmtompeg -combine_gops /path/to/combine.par
For combining GOPs, the parameter file needs SIZE and OUTPUT. With no explicit GOP_INPUT block, the program looks for the output name with .gop.N suffixes starting at zero. Keep the same parameter file where you can, so generated names and dimensions agree. Mixing files from separate encodes means naming them explicitly and checking they are actually compatible.
-frames first last is a separate diagnostic workflow: it writes individual .frame.N files with no GOP headers, and -combine_frames then needs SIZE, GOP_SIZE and OUTPUT. These intermediate files can eat substantial disk space, so check available space first and remove only the known temporary files once the final stream has been checked.
Common traps
- Missing or unreadable image. Usually means
INPUT_DIR, a filename range, or permissions are wrong. Check the exact path withls -landtest -r. - Malformed parameter file. Often lower-case keywords, a missing
END_INPUT, or a conversion command that does not write an image to standard output. Compare against the working example rather than adding flags at random. - Missing
SIZEfor YUV or parallel mode. YUV input needsSIZEbecause YUV files carry no dimensions of their own, and parallel mode needs it too, matching on every machine. - Encodes fine, plays badly. Check frame dimensions, frame rate, pattern and qscale values before you go changing the output file extension.
Done means
- Checked the installed binary and Netpbm version.
- Confirmed the input files are readable, consistently sized, and listed in display order.
- Built the parameter file with upper-case keywords and a complete
INPUTblock. - Ran a test encode that produced a non-empty MPEG file without overwriting the previous result.
- Checked the final file with a media tool or player before removing temporary files.