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;
}
}