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;
}
}
other decoding routines
Decoding pivot point:
decode_pivot(ref_value) {
edge = coder->decode_model(edge_model);
coord = coder->decode_model(pivot_model) + 1;
if (coord > 2)
coord = coder->decode_number((ref_value + 1) / 2 - 2) + 3;
if (edge)
return ref_value - coord;
else
return coord;
}
Decoding pixels is not that trivial. Codec uses neighbour pixels (left, top-left, top, top-right) to form a cache which is used along with cached move-to-front queue and several models to restore pixel.