jPSXdec is a cross-platform PlayStation 1 media decoder/converter.
Get the latest version here.

Wednesday, September 24, 2008

How to encode quality PlayStation 1 video

Update: I have since discovered this technique may work, but videos would need to be encoded as mpeg2 due to PlayStation videos having 2 more bits of quality than mpeg1 can handle. Unfortunately that starts getting tricky as mpeg2 has a different chroma subsampling position. In short, this technique has a lot of subtle caveats that should be considered. A future post may be made to explore the issues.

Recently I've run across a couple of groups trying to replace the video of a PlayStation 1 game. As we have to do the same thing with S.E. Lain, I thought I would share some insight on how it might be done with the highest quality output.

Since there are many things that go into encoding mpeg type images (DCT + quantization being the least of it), that task is best left to programs that know how to do it well, such as ffmpeg. It also allows you to tweak those options, and use its advanced features to get the best results.

So here is how you could do it, broken down into 6 ridiculously complicated steps.

  1. Convert your video into yuv4mpeg2 video (or perhaps an AVI with a YCbCr codec), but using the PSX specific rgb->YCbCr conversion.
  2. Feed the YCbCr video into ffmpeg at a mpeg1 allowed fps and create a mpeg1 movie with only I-frames (-intra). This should be done at a variety of different quality levels (-qscale from 1 to n).
  3. Parse the I-frames out of the movie, and parse each frame's macro-blocks
  4. Convert the mpeg1 VLCs to PSX VLCs
  5. Do this for every mpeg1 quality and pick the one that fits best within the amount of space available to each frame
  6. Multiplex the frames and construct all the sectors, including the correct frame headers and the sectors' ECE/EDC codes

If the new video is mostly just the old video with some changes (e.g. subtitles) then quality can improved tremendously with this variation.

  1. Use some method to determine which macro blocks to replace (manually picking them, or performing a diff on lossless video data, or a fuzzy diff on lossy video data)
  2. Only replace those macro blocks with new ones of the same qscale
  3. If the replaced data makes the frame too big, then either replace the entire frame with an ffmpeg created frame that will fit (qscale will probably be bigger), or remove some quality in the frame to make it smaller
  4. Multiplex the frames and construct all the sectors

Alternatively have ffmpeg write an AVI using MJPG codec, then parse the JPEG frames and convert the JPEG VLCs to PSX VLCs (I'm not sure how to get ffmpeg to produce quality variations with the MJPG codec).

Encoding video by the steps above will likely have better results than if you used Sony's official PS1 SDK video encoder (and will also reduce the likelihood of serious legal trouble and C&D letters).

Here is a possible ffmpeg command-line for step 2 above.

ffmpeg -i source.y4m -vcodec mpeg1video -r 30 -qscale #  \
-trellis 1 -intra -dc 10 -dct faan -debug dct_coeff \
-vstats_file qscale#.log out-qscale#.m1v

-i source.y4m : yuv4mpeg2 input file
-vcodec mpeg1video : write mpeg1
-r 30 : use 30 frames/sec
(just to use something
compatible with mpeg1)
-qscale # : chosen qscale
-trellis 1 : trellis quantization
does this even apply?
-intra : only write I-frames
-dc 10 : use 10 bits of precision
for the DC values?
(not sure if this is helpful)
-dct faan : use a floating-point
for best quality
-debug dct_coeff : forgot what this does
-vstats_file qscale#.log : write a log of useful info
out-qscale#.m1v : output file, video only