Psygnosis SMV
- Extension: smv
- Company: Psygnosis
- Samples: http://cd.textfiles.com/cdaction/cdaction04/FILM/
SMV is a short-lived FMV format used in the PC game Wipeout by Psygnosis. It employs LZ-like compression to encode 8-bit palettized video and audio.
File Format
An SMV file consist of a number of chunks with unique TwoCC. All numbers are little-endian.
u16 chunkid u16 chunklen u8 payload[chunklen]
ST chunk
File header. Encodes basic movie properties.
u16 width -- movie width u16 height -- and height u16 mbwidth -- macroblock width u16 mbheight -- and height u16 frames -- number of video frames u16 colors -- number of used colors u16 nibbles -- offset of frame's nibble data -- if chunk size is >= 15 u8 codec -- video codec format
- Codec field may be absent, in such case assume codec 0.
- mbwidth is always 16, mbheight may be either 16 or 8.
- Movie playback rate is 12 FPS.
GP chunk
New palette. Contains a number of 6-bit RGB entries, as specified in ST chunk.
MU chunk
Sound chunk. Contains audio fragment in 15862Hz, 8-bit, unsigned, mono format. If the payload size is equal to the amount of audio data for a frame (i.e. the size is freq / fps, which is equal to 1321) then the audio data is stored in raw PCM format. Otherwise, it's additionally LZ-word-packed.
FR chunk
Frame chunk. Contains encoded video frame.
FE chunk
End of the movie. No payload.
Video Compression
Video Codec 0
LZUnpack(payload, temp, words) DrawBlocks(temp)
Video Codec 1
LZUnpack(payload, temp, bytes) DrawBlocks(temp)
Video Codec 2
LZUnpack(payload, temp, bytes) UnpackPartitions(temp, temp2) DrawBlocks(temp2)
UnpackPartitions decodes Macroblock's nibbles.
Source frame is broken on to several equal partitions, 2 across by 4 down. Encoded partitions format:
u8 pixels[16 * num_mblocks] -- passed as is to DrawBlocks() for each partition u8 codebook[256][2] -- nibbles codebook for each partition for each partition for each macroblock u8 index[mb_width*mb_height/4] -- index of codebook entry
Each entry of codebook encodes 4 nibbles. First byte encodes two nibbles of upper macroblock's line, second - next line, and so on.
Video Codec 3
This codec is never used, but the code is still in place.
if GetByte() LZUnpack(payload, temp, bytes) UnpackSpecial(temp, temp2) DrawBlocks(temp2) else same as codec 1
UnpackSpecial performs advanced decompression. TODO this may be some common algorithm
Macroblocks drawing
Macroblocks are stored in the following format:
u8 pixels[16 * num_mblocks] u4 nibbles[num_mblocks]
Size of pixels[] is equal to ST chunk's nibbles. For each macroblock draw it by indexing its pixels by nibbles. First nibble stored in top 4 bits of byte.
Common Algorithms
LZ decompression
This is a more-or-less common LZ77-like algorithm. It can operate in two modes: byte mode and words mode. Mode affects backchain offset encoding.
- "next bit" means next bit from the internal 8-bit bitreader queue. Queue refilled with the input stream bytes, bits are consumed starting from the highest.
- "next byte" means raw octet from the input stream
repeat if next bit == 0 output next byte else if next bit == 0 offset = next byte else if byte mode offset = next 3 bits || next byte + 1 if words mode offset = next 2 bits || next byte + 1 *2 if next bit == 0 length = 2 else if next bit == 0 length = 3 + next bit else if next bit == 0 length = 5 + next bit else if next bit == 0 length = 7 else if next byte != 0 length = 7 + this byte else finish unpacking copy length bytes from output - offset to output note that, bytes may overlap to repeat itself