Archive for July, 2026

Micro Wavelet: done

Friday, July 24th, 2026

So I’ve finally implemented NeXT Micro Wavelet decoder (and updated git bundles on the site while at it) and the codec turned out to be even more interesting than on the first glance.

The codec itself consists of three distinct phases: using delta prediction along with wavelet transform, packing results into 16-bit opcodes, and further packing the opcodes with Huffman or LZSS compression. There’s also a raw mode which simply transmits 8-bit deltas so it’s not particularly interesting.

First stage is probably where the codec name comes from: it works on 2×2 pixel blocks that get transformed into the difference from the previous block means (for each component) plus vertical and horizontal differences between block pixels (the same pair is used for all block components). In other words, not exactly full wavelet transform but not exactly no wavelet transform either.

Then the deltas get quantised to a small set of pre-defined values (13 values for red and blue, 15 values for green, 5 values for horizontal and vertical differences) and get packed into a single 16-bit value (that’s merely 63375 combinations out of 65536 possible, which leaves room for special opcodes). Normally one opcode represents a set of packed deltas but in theory it can hold “skip next N blocks” opcodes though I’ve not encountered a file with those.

After we got those opcodes it’s time to pack them (or write them as is). It can be done either with static Huffman coding or with LZSS that operates on 16-bit symbols. Huffman tree definitions are stored in the codec extradata after the main decoder configuration (which deserves its own mention and should get it below) and usually define about two thousand most common symbols, the rest are accessible via the special escape symbol.

Since I’ve mentioned decoder configuration, it needs to be described for being rather special too. First of all, it is not stored in the usual place (codec descriptor atom) but rather in the user data atom on the top level of track atoms. I actually had to modify a demuxer to handle it properly (and discoverd this fact by chance while looking at the end of MOV file and seeing it being suspiciously similar to the file with the default Huffman tree definition). This decoder configuration actually consists of two parts: fixed-size decoder state (essentially RAM data dumped into the file) and Huffman tree definitions. And the decoder configuration is flexible. Remember what I said above about the delta packing into 16-bit symbols? Well, it’s extensible—you can have different number of quantisation levels and delta values to be defined for each component, I simply decided not to support such rich features and stick to the hard-coded delta sets and packing scheme.

Overall, this codec reminded me about Duck TrueMotion 1 for some reason, maybe because of its custom delta tables. All things considered, that’s probably the most exotic codec I’ve ever worked on since it combines both unconventional coding methods (micro wavelets packing into mini-words) and the fact it comes from the rather obscure OS (at least it was ordinary M68K code). I probably had encountered and shall encounter more of crazy coding approaches but it’s not likely to be this combination of obscure technical approach and the obscure platform. As always, I’ll be glad to be proven wrong.

Another weird format

Friday, July 17th, 2026

Just couple of years ago I had a small rant about German RLE-based codecs named *PEG. Recently I’ve discovered yet another pointless format called NPEG.

Unlike the rest it’s Polish, targets Amiga instead of DOS and it’s essentially MPEG-1 frames stored in IFF. Considering that it appeared around 1998, NP1 is the original MP4 format (if you ignore MPEG-1 in MOV of course).

Currently I have no desire to implement support of it but maybe one day I’ll really have nothing better to do and add MPEG-1 support (with various exotic containers) to NihAV. Though I hope such day will never come.

OMGperformance

Friday, July 10th, 2026

I use my own player for playing videos. It’s not very optimised (video frame decoded with VA-API gets downloaded to my own frame structure in main RAM, which gets uploaded to SDL texture and only then displayed—the proper way is to allocate EGL texture once and use it both for VA-API decoding and displaying; or maybe the same but Vulkan and semaphores all the way) but apparently playing back 25fps video even in that way takes less CPU than updating one line with play time in gnome-console at about 10fps.

I have a feeling something went wrong with the modern software…

A bit about next micro codec

Tuesday, July 7th, 2026

Some time ago I mentioned that I’d stumbled upon a QuickTime sample encoded on NeXT using MicroWavelet codec which was used only there. I also expressed a regret of it being completely lost to time. And what do you know, it’s yet another situation where I’d be glad to be proven wrong—and I was (and I am).

Apparently had I looked around for a bit longer, I would’ve discovered more samples and a decoder to boot. Apparently that decoder was a part of NeXTTime and not QuickTime, which is a completely different thing. And NeXTTime including that decoder could be found on OpenStep 4.x in particular.

While I have not written a decoder for it yet, I’ve figured out enough details to talk about it.

First of all, it’s a sort of several loosely-tied codecs (and I’m yet to figure out what makes it choose which decoding path to take). One of them is a simple wavelet codec with 8-bit coefficients and no additional compression, others are delta coding plus zero-run compression plus optional compression of that data. Optional compression may be either static Huffman coding (with trees stored in a separate file bundled with the decoder—a bit like ClearVideo did it) and an individual tree selected per frame, or it may be LZSS.

It’s nothing outstanding but still it’s a rather curious codec from a rather curious obscure platform (Display PostScript anyone?). And as a final fun fact, it’s internally called “harsh” with the functions being named e.g. harsh_decode_skip. Hopefully implementing a decoder for it would be mild and agreeable despite the naming.