Karl Morton's Video Codec

From MultimediaWiki
Jump to navigation Jump to search

KMVC is used in various games by Team17, especially Worms PC games.

Common details

This codec operates on 8x8 or 16x16 blocks in 256 colors mode. This codec is also employs exotic bitstream reading: actually you need to read byte and when it's exhausted, read another byte, so flags and other data are very mixed. Two versions of this codec are known: one stores palette in frame, another one also could do this but stores palette in extradata instead. Compression principle is simple: divide frame into sequence of blocks and then either fill it, copy from other position or divide onto four subblocks and do the same.

Palette change frames

Newer version of KVMC has two palette change methods - via 'xxpc' (palette change) chunk in AVI and via special frames (some files has both!). Old version of KMVC always sent palette updates in intra frames before actual data.

Frame header in case of palette change frames consists of two bytes, first one specifies palette start offset (usually 1 or 128) and the second byte is palette block size (= 127 colors). Legal frames have second byte (block size) equal to 8 (or possible 16) when this frames have 0x7F, so this is how they could be distinguished.

Next two bytes are zeroes, and the rest of frame data is 127 palette entries packed in 4-tuples.

Frame structure

Frame consists of the next parts: header byte, palette chunk (if needed), block size (1 byte), compressed blocks.

Header byte may have this bits set:

KPxx MMMM
K - is keyframe
P - palette is present
xx - unused bits
MMMM - compression method (only methods 3 and 4 are known)

Old KMVC videos (employing compression methods 1 and 2) always transmit palette update for intra frames (without palette flag set). In that case palette part consists of palette start index byte, palette update length byte, and palette entries. Otherwise palette size is defined in byte 10 of extradata (usually 127 or rarely 255) and is standard R,G,B triplets.

Compressed blocks are decoded with given method until frame is fully decoded.

Video compression works by either filling (sub)block with a single colour, copying contents from some other block (e.g. previous frame) or subdividing it into four parts and applying the same compression. Blocks of size 1x1 are always coded as raw.

Method 1

Intra compression (in newer KMVC files it has a different meaning):

 if (size > 1 && getbit()) {
   split block into four subblocks, decode each
 } else {
   val = getbyte();
   fill block with val;
 }

Method 2

Inter compression, essentially method 1 with added skip option:

 if (size > 1 && getbit()) {
   split block into four subblocks, decode each
 } else if (size == 1 || getbit()) {
   val = getbyte();
   fill block with val;
 } else {
   leave block unchanged from the previous frame
 }

Method 3

Intra compression. This is method 1 modified to use intra prediction from the already decoded part of the frame (but it is not allowed for the largest block size).

 if (size > 1 && getbit()) {
   split block into four subblocks, decode each
 } else if (size > 1 && size < initial_block_size && getbit()) {
   val = getbyte();
   mx = val & 0xF;
   my = val >> 4;
   copy block from the current frame with offset (-mx, -my)
 } else {
   val = getbyte();
   fill block with val;
 }

Method 4

This method is used for interframes and has only two differences from method 3:

  • the largest block can be skipped as well as subdivided or filled;
  • motion compensation uses previous frame for reference and has motion vector range -8..7 instead of -15..0.
 if (size > 1 && getbit()) {
   split block into four subblocks, decode each
 } else if (size > 1 && getbit()) {
   if (size != initial_block_size) {
     val = getbyte();
     mx = (val & 0xF) - 8;
     my = (val >> 4) - 8;
     copy block from the previous frame using offset (mx, my)
   } else {
     copy block from the previous frame using offset (0, 0)
   }
 } else {
   val = getbyte();
   fill block with val;
 }