Skip to content

Deliver a video for the web

A terminal: pconv talk.mp4 site.mp4 –profile best and pconv talk.mp4 hero.webm print their outputs; then pconv probe on each file shows talk.mp4 untagged (color_space and color_range null), and site.mp4 (h264) and hero.webm (vp9) both bt709 and tv range.

Name the input and the output; the output’s extension picks the container and the codec:

  • mp4, mov and mkv get H.264 (libx264, or VAAPI with --profile fast).
  • webm gets VP9 and keeps alpha.

Every output is limited-range BT.709 and tagged so, converted correctly from BT.601, full-range or untagged sources, with rotation and non-square pixels applied, so every player shows the same colours.

$ pconv talk.mp4 site.mp4 --profile best
site.mp4

best is x264 slow at CRF 18. The default, balanced, is x264 medium at CRF 20. For a lighter file, raise the CRF:

pconv master.mov site.mp4 --profile best --crf 23

For screen recordings and terminal captures, --tune animation suits x264 better.

$ pconv talk.mp4 hero.webm --size 720p
hero.webm

balanced is VP9 at CRF 32 and best at CRF 28; a light web WebM is --profile best --crf 40. Alpha in the source stays in the output.

pconv probe prints what pconv reads from a file. The source here was untagged; the delivery is BT.709, limited (tv) range:

$ pconv probe talk.mp4
{"width":1280,"height":720,"fps":30.0,"seconds":6.0,"codec":"h264","pix_fmt":"yuv420p","audio":"aac","color_space":null,"color_range":null,"color_primaries":null,"color_transfer":null,"rotation":0,"sar":null,"video_index":0,"profile":"High","extradata":"CRC32:230e5631","audio_index":1,"audio_rate":48000,"audio_channels":2,"audio_layout":"stereo","audio_extradata":"CRC32:6ddafd20"}
$ pconv probe site.mp4
{"width":1280,"height":720,"fps":30.0,"seconds":6.0,"codec":"h264","pix_fmt":"yuv420p","audio":"aac","color_space":"bt709","color_range":"tv","color_primaries":"bt709","color_transfer":"bt709","rotation":0,"sar":null,"video_index":0,"profile":"High","extradata":"CRC32:e67468b7","audio_index":1,"audio_rate":48000,"audio_channels":2,"audio_layout":"stereo","audio_extradata":"CRC32:6ddafd20"}

The formats reference explains each field.

--size takes a width (1280, the height follows the aspect), WxH within 1 % of the source’s aspect, or 720p, 1080p, 1440p or 2160p. pconv never stretches a picture. --fps sets the frame rate.

pconv master.mov hero.webm --size 1080p
pconv master.mov small.mp4 --size 640 --fps 24
pconv recording.mkv draft.mp4 --profile fast

fast is H.264 through VAAPI at QP 25 (--qp sets it). It needs a VAAPI device at /dev/dri/renderD128 with an H.264 encoder. On Windows and macOS pconv refuses it with exit 1; use balanced or best there.

mp4, mov and mkv get AAC-LC 320k stereo at 48 kHz, and an AAC source is copied when nothing changes its audio. webm gets Opus 128k. --audio-bitrate sets the rate, 32 to 512 kbps. To level the audio, see loudness.

pconv refuses an output that already exists, with exit 1:

$ pconv talk.mp4 site.mp4
pconv: site.mp4 already exists; use --force to replace it

Add --force to replace it. pconv writes to a hidden partial file beside the output and renames it into place when it is done, so a failed or stopped run never leaves half a file.