Microsoft Screen Codec
Also known as Windows Media Screen Codec.
- FourCCs: MSS1, MSS2, MSA1
- Samples:
MSA1 is created by Live Meeting 2007
Some details about format
Both MSS1 and MSS2 are quite close (thus are decoded with single decoder). They employ arithmetic coding - real one, with probability coding. This coding is used with several adaptive models, which look a bit like PPM.
MSS1 details
MSS1 (aka Windows Media Screen codec) compresses only palletised images.
Extradata format
(for some reason, data in .wmv is stored in big-endian order)
4- 7 header length 8-11 major version 12-15 minor version 16-19 display width 20-23 display height 24-27 coded width 28-31 coded height 32-35 frames per second (float) 36-39 bitrate 40-43 max lead time (float) 44-47 max lag time 48-51 max seek time 52-55 nFreeColors 56-... palette (256 RGB triplets)
Frame format
Codec uses arithmetic decoders for all operations and adaptive models. All code for them is suspiciously similar to the one in | 1987 paper by Witten, Neal and Cleary.
Codec uses delta compression and can change top palette entries with every intra frame:
is_inter = coder->decode_bit(); if (!is_inter) { if (nFreeColors) { num_entries = coder->decode_number(nFreeColors + 1); for (i = 0; i < num_entries; i++) { pal[(256 - nFreeColors) + i].R = coder->decode_bits(8); pal[(256 - nFreeColors) + i].G = coder->decode_bits(8); pal[(256 - nFreeColors) + i].B = coder->decode_bits(8); } } recursive_decode_intra(0, 0, width, height); } else { recursive_decode_inter(0, 0, width, height); }
Frame coding is done by recursively partitioning picture horizontally or vertically and coding partitions in some way:
recursive_decode_intra(x, y, width, height) { mode = coder->decode_model(split_mode_model); switch (mode) { case 0: pivot = decode_pivot(height); recursive_decode_intra(x, y, width, pivot); recursive_decode_intra(x, y + pivot, width, height - pivot); break; case 1: pivot = decode_pivot(width); recursive_decode_intra(x, y, pivot, height); recursive_decode_intra(x + pivot, y, width - pivot, height break; case 2: mode = coder->decode_model(intra_decode_model); if (!mode) { pix = decode_pixel(); fill_rect(x, y, width, height, pixel); } else { decode_area(x, y, width, height); } break; } } recursive_decode_inter(x, y, width, height) { mode = coder->decode_model(split_mode_model); switch (mode) { case 0: pivot = decode_pivot(height); recursive_decode_inter(x, y, width, pivot); recursive_decode_inter(x, y + pivot, width, height - pivot); break; case 1: pivot = decode_pivot(width); recursive_decode_inter(x, y, pivot, height); recursive_decode_inter(x + pivot, y, width - pivot, height break; case 2: mode = coder->decode_model(inter_decode_model); if (!mode) { pix = decode_pixel(); if (pix != 0xFF) { copy_rect(x, y, width, height, pixel); } else { mode = coder->decode_model(intra_decode_model); if (!mode) { pix = decode_pixel(); fill_rect(x, y, width, height, pixel); } else { decode_area(x, y, width, height); } } } else { // this decoded change mask first and then // checks - if mask value is 0xFF then decode pixel // otherwise copy if from the previous frame mask = decode_area(x, y, width, height); decode_area_masked(x, y, width, height); } break; } }