Archive for the ‘NihAV’ Category

NihAV: now with VP9 playback support*

Saturday, October 31st, 2026

Recently I’ve discovered some videos on archive.org that I’d like to watch. The problem is that they are VP8/VP9 plus Vorbis/Opus in WebMKV container which NihAV did not support. So I essentially had three options: re-encode it, use a third-party player, or implement support for it. Of course I went the fourth way—implement minimum support for the larger parts and re-encode the audio.

First thing was to determine what has to be done. Since avconv can’t put VP9 into MP4, I had to implement Matroska support. It was moderately annoying but not hard. And it did not lose much respect in my eyes either (mostly because there was not much of it in the first place). Of course I ignored a lot of finer details but it seems to be good enough for my needs, I’ll eventually try it on some 10-bit H.264 plus FLAC files I have somewhere around (another codec not fit for MP4; and don’t point out at the unofficial mapping schemes—my principles say no to it).

Then, audio track. That one was easy, just re-encode it to AC3 from whatever input format. It’s fast and beats writing decoders for Xiph formats.

Finally, video. I have VP8 decoder already (mostly written for the completeness sake) but no VP9 decoder. Re-encoding video is a long process, writing decoder is not particularly fast either. The not so obvious solution was to rely on VA-API for hardware decoder. One thing that’s definitely better in Duck codecs against ITU codecs is much simpler metadata and frame management, so VP9 VA-API decoder is much smaller and simpler than H.264 one. Essentially it’s just read frame header, generate some parameter structures, send it to the decoding and update frame references afterwards—compare that to parsing data in order to find frame header, picture parameter sets it references, sequence parameter set referenced by PPS and preparing various structures for that along with complex frame management rules. VP9 is not that simple either, with its potentially present segmentation and whatnot, but it’s much more manageable for the common case. As a side note, the follow-up codecs (VP 1.0 errata 1 and VP II) are much worse in that aspect, so I don’t know if I’ll ever want to deal with them.

And there it is, my solution for watching some files without wasting too much effort on implementing full support for all involved formats. Of course it would be easier just to re-use some existing code for demuxing support and wrap decoding libraries; some may even point out the futility of it all since there are perfectly working solutions already. But as I have committed to using my own software for my multimedia needs (except for the rare cases which avconv is allowed to handle), I’ll keep improving what I have. But had the things developed better in the past, I’d have no such problem as avp from rust-av project would be usable for my needs. Alas!

na_eofdec 0.1.5 release

Tuesday, October 27th, 2026

Now as the PHP calamity is temporarily over and the sites are back online, it’s as good time as ever to make a release.

I’ve come to realisation that I don’t have enough non-gaming video formats in store to reverse engineer, so instead of waiting indefinitely until I discover enough of them I’m making a release now. Half a dozen new formats are enough for 0.1.5 by my standards, and maybe it will be useful as a reference for somebody.

Since the reason for this point release is the lack of formats, don’t expect new releases in the foreseeable future. From my point of view the tool served both its purposes well: testing decoding of some exotic format and being a proving ground for MOV muxer. It makes sense to port the parts that handle variable framerates and uneven audio blocks to na_game_tool but so far I was not bored enough to do that. Anyway, the new release is at the usual place and I should not bother you with this tool ever again.

nihav-movrender: alpha release

Wednesday, September 30th, 2026

I’ve decided to make a code drop release of nihav-movrender just for the completeness sake. If you look at nihav.org you should be able to find its git bundle among the rest.

One of the hardest parts was to create some documentation for it, so I’ve put something into README.md, hopefully that will be enough for all zero people who want to try it. Essentially if you provide it with the name of input MOV file and output directory it will try to decode the former and create some files in the latter (and if you omit output directory, it will put them into the current directory and you’ll have yourself me to blame for that). For tweaking that behaviour you need to refer either to nihav-movrender --help or to the aforementioned documentation.

Even in the current imperfect form it solves the tasks I wanted it to solve: extract (and convert to more convenient format) more exotic QuickTime tracks, and merge otherwise less accessible tracks (panoramas or multiple description tracks). The only thing it can’t do is to merge video from King’s Quest VI intro but it is a special situation (multiple independent tracks with edit lists to boot) so it will require a special handling. Beside that I have no further development plans for nihav-movrender and I’ll probably return to REing obscure formats.

Announcing nihav-movrender

Friday, September 25th, 2026

I’m not yet ready to release it but it’s a good time to tell what I’ve been working on (and intend to keep working for a while).

There are many valid QT MOV files on discmaster that are not decoded (or recognised for that matter, probably exactly for the reason they cannot be decoded). Looking manually into them often shows some exotic content not understood by any program (not even by QuickTime itself these days): Flash, MIDI, PDF, PPT, QT version of Flash aka sprites and so on. And in order to get some useful content out of such files I’m developing nihav-movrender. In theory it should be able to do at least this:

  • extract various kinds of binary data;
  • decode some exotic formats;
  • assemble QTVR or navigable MOV panoramas from individual frames;
  • merge tracks with multiple descriptors into one.

Currently I have first two items done and the other two should be easier to deal with (unless I hit some unforeseen complication of course). Below I want to talk more about different content that MOV files in the wild were discovered to have and how my tool deals (or hopefully will deal) with them.

Binary files

MOV files can embed various binary files, in different forms too. So far I’ve encountered three formats—namely Flash, PDF and PPT—and they all been stored in different ways. Flash files have their own stream type handler and it’s single file in single frame (but there may be two or more tracks with Flash files). The only sample with PDF came as many video streams, each of type “pdf” and containing a single-page PDF file. PPT, on the other hand, came in a generic data stream of sub-type “@ppt” with document split into several frames. In either case extracting them is trivial.

Media files

Occasionally a whole multimedia file of other format gets wrapped into MOV, the main examples being DV (I’ve never seen such a file and I’m fine this way), MPEG-1 (rather common) and ASF/WMF (generated by Flip4Mac). I’m mentioning those mostly for the completeness sake as I’m not sure if I want to do anything with them.

Auxiliary streams

There are several of those, for instance there’s timecode stream (considering that usually it contains just one frame I doubt it’s of any real value). There’s Tween stream (short for “between”) which is used to provide smoother transitions for other tracks (usually sprites).

I also encountered skin streams (which seem to contain single PICT file) and Quartz Composer media, where data is represented as binary .plist (with embedded PNG images if that helps).

Sprite system

Initially I assumed it’s a simple object-based animation system (like Flash Animation) but in reality it’s an interactive system involving multiple tracks i.e. usually one stream contains sprite data for content animation and another one handles controls. And the text streams (which are also complex constructions of QT atoms) seem to contain action handlers and relate to the sprites. So I ended up simply dumping decoded or extracted sprite images plus text-based description of what goes on there.

The process of decoding the samples I collected was rather fun. Sprite images may come in various formats, including GIF, JPEG, PNG, TIFF, Macintosh PICT as well as QT-compressed (usually RLE) frames. What’s more, those PICT files may contain RLE data of various kinds (sometimes even with larger dimensions than the image itself) as well as QT-compressed data. So apparently some presentation frames are coded as a command set like “set foreground colour to blue; paint a rectangle with it; and now call QT RLE decoder to paint the text”. Currently I don’t handle this situation well, so either the rectangle or overlay text is returned but it’s still better than nothing. And there are funny situations like 1100×800 JPEG image embedded inside 640×480 PICT.

MIDI

This data is stored in QT Music Architecture format (with each event data aligned to 32-bit word for convenience), sample descriptor having instrument descriptions and packets simply containing 1KB of data, whatever actual time period it may cover. I wrote a simple converter to SMF format and the result sounds OK to me.

QTVR

This format is actually stored in two or three tracks: image data split into strips and coded as individual frames, QTVR data and (optionally) panorama data. Since looking at a sequence of scenery pieces is rather boring, I want to make my tool assemble one large panorama from them.

Oh, and before QTVR there were Navigable movies (the same thing but simpler and with panorama data stored in track user data). I’m aware of four such files, one of them being the only known file employing CD-Graphics codec.

Other

If you look into QuickTime File Format Specification (it’s like ISO-IEC 14496-12 but older and available for free), it mentions even more auxiliary stream formats like hot sport image track, hint tracks and such. And of course it mentions 3D images, which must’ve been a killer feature at the time.


That’s why I alluded to this format as both common (since its derivative called MP4 is still the major multimedia container, even MXF can’t rival it; and MOV itself was extremely widespread back in the day) and exotic—you can’t claim that most things I listed here are used in modern multimedia workflows on daily basis (beside embedding font files in Matroska of course).

And who knows, maybe this will help me discover some more formats hidden in those files. The whole trove of QuickDraw flavours alone made it worthy for me.

na_game_tool 0.6.0, AVI edition

Saturday, September 5th, 2026

What should follow a political rant that nobody would read? A release of a tool that nobody would use of course!

I’m indifferent to announce the release of na_game_tool version 0.6.0, now with AVI support. Beside being able to write output to AVI it can now also de-obfuscate some AVI files that employ some simple scheme (like XORing with a 32-bit counter). And in addition to that it can also reconstruct Smacker and audio files from AVIs (or IAVFs) used in Black Dahlia and Ripper games.

The rest of the newly-added formats are mostly those I found at discmaster.textfiles.com by searching for unknown files over several megabytes large (and filtering out corrupted samples, segments from the multi-part archives, etc). Despite discmaster not being updated anymore (you know whom to thank for the storage shortage), I’ve found enough to have another release in the future.

Meanwhile here are some short notes about some formats:

  • Elige Tu Muerte is extremely short but very FMV game: it’s a dozen of .mov files with animations plus 20kB .exe engine. You just click on one of four exits and 3D-rendered figure walks there to its demise (which should be obvious from the game title). The coding format is slightly more complex than normal RLE but still nothing to write a full post about;
  • Lords of the Realm VAS format is the one I halfway reverse-engineered from the files themselves and then found corresponding RLE decompression code in the binary specification to fill in the missing details. The main problem there is that it’s an overlay executable so some parts are not loaded by default and there are no references to the decompression functions;
  • Tie Break Tennis 98 intro file was reverse engineered from its contents because it was less effort than to manually load LE segment of the binary specification into Ghidra. After all it has just four opcodes (fill block, raw block, skip block, and palette) so guessing the whole structure was easy;
  • Gunmetal SBV gets another mention here by being the only wavelet video codec I’ve encountered in a game so far;
  • this time I had three formats that should’ve been video files in theory but turned out to be an archive of video and audio tracks in stand-alone formats, optionally with some commands telling how to play them. Those formats are AVI, SMS and Knowledge Adventure movie v3.

na_game_tool is available at the usual place in the usual format.

Now I can move to work on a different tool which would tackle a format that’s very widespread and extremely exotic at the same time.

na_eofdec initial release

Monday, May 11th, 2026

Since I got lucky during weekend with some formats, I got enough of them to release na_eofdec. This is a tool similar to na_game_tool but oriented at generic exotic and obscure formats (or Amiga ones, put them into whatever category you like). So if you’re familiar with that one (why?!) you should have no troubles with the new one either (or at least it should be the same troubles).

The motivation behind it is about the same as with the other tool: decode whatever formats I find interesting enough to implement decoders for but not interesting enough to have them supported in the main NihAV base. Also it serves as a playground for various other things (like MOV muxer in this case, which served as the base for more versatile muxer in NihAV).

Anyway, it is released in hope (but no expectations and definitely no guarantees) that it will be useful for some purpose for others. The release is available at its own sub-page at nihav.org (and there’s a link to it in the appropriate section of this blog too).

NihAV: QT support enhancements

Friday, May 8th, 2026

When I have enough inspiration, I improve NihAV. When I don’t (which is more common state to me), I RE codecs or write blog posts—so here’s one.

First of all, I’ve started adding non-raw encoders for some common QT formats. It’s not that there are no open-source encoders for them, but I do them mostly to find out how it is done and maybe learn something new in the process. For instance, RLE encoding combines skips, runs and pixel copies; this rises the question of optimal encoding as sometimes it may be cheaper to encode a whole area as new pixels instead of a mix of copy+skip+copy. So I’ve implemented a greedy approach (i.e. code longest skip or run and fall back to encoding raw if those two fail) as well as slow but optimal one. It’s a variation of trellis coding: just calculate encoding cost with each mode (skip/run/raw) to all next possible positions and if it’s lower than the existing one, use that mode; at the end simply trace back the decisions that gave least cost at the end and encode them in right order.

Then I also added RPZA encoder. This is essentially the first texture codec before GPUs with the need for texture compression, its main compression mode is encoding 4×4 block with four colours where two colours are linearly interpolated from two explicitly transmitted colours. There is no apparent way on how to do it fast, so I ended up with an extremely simplified scheme: first I calculate the maximum difference between components and pick the one with the largest difference (or code block as single-colour if it’s small enough) to decide what values to pick, then I calculate explicit colours from an average of input pixels close to minimum and maximum ends of that range. I also have a refinement step by running vector quantisation loop to adjust the ends but it’s rarely needed in practice.

There are still more encoders to implement (SMC, SVQ1, IMA ADPCM and MACE) but none of them is interesting beside SVQ1, so probably I’ll write about it when/if I ever get to implementing an encoder for it (it does not matter if The Multimedia Mike has done that over fifteen years ago—NIH is there in the project name for a reason).

Now, surprisingly enough I’ve improved decoding support as well. The original QuickTime had SIVQ codec which is a straightforward 256-entry codebook for 2×2 RGB24 tiles followed by codebook indices. I had read its binary specification some time ago and recently I was able to locate (probably the only existing) sample for it, which is a good reason to write a decoder for it. It was well-spent five minutes of my time. Maybe in the future I’ll also do something about Pixar codecs (Ghidra works better with raw m68k version of the decoder than with 16-bit Windows 3.x version of the same).

And finally I’ve improved the support for multi-descriptor MOV files. I mentioned it some time ago and I got bitten by it again recently. For example, alice_lo_m.mov from samples.mplayerhq.hu got just first frame decoded for me and many QuickTime 1.5 sample videos (with its developers) gave an error on the last frame. For the former it’s because first frame is JPEG and the rest of them are SVQ1, while the latter samples are coded with Cinepak but the last frame may be a special one encoded with RPZA. And there was another file fully encoded with RPZA—but with the majority of it being 160×120 while last dozen of frames or so were 320×240. So I finally got annoyed enough to implement multiple streams per track so at least the frames get marshalled to the correct decoder, even if it leads to the partial streams being rather unusable. Maybe one day I’ll write a tool which will walk through MOV and render all tracks in correct sequence (taking edit lists into account), scaling and adjusting playback rate as needed, producing a raw MOV file that can be played without special hacks; or maybe I don’t hate myself that much.

That’s it for now, don’t expect anything soon (MVS description may appear but who’s waiting for that?).

NihAV: palettisation

Thursday, April 30th, 2026

While I’ve added palettisation support for NihAV about six years ago, it was limited to per-frame palette generation back then. Since I had only two encoders supporting paletted input and both were accepting palette changes, it worked fine. It’s only when I decided to implement MOV muxer I really got a need for palettisation using global palette, so I’ve started experimenting with that.

First and foremost, design. While frame palettisation is a part of NAScale that handles video frame conversion from and to various formats, this palettisation mode is bolted to nihav-encoder. It is actually implemented in two parts: initial pass that decodes input and generates palette at the end, and actual frame palettisation (which can actually work just fine without that pass if you tell it to use some pre-defined palette). I actually started with the second part and added palette generation later (for tests using default QuickTime palette was enough). Then I went even further and extended palette generation to support segmented mode (i.e. palette may change but not for every frame, I made the limit configurable but it should be at least 10 frames—storing palette for each frame before processing is too much).

This mode is a bit fragile since palettes are calculated for the decoded frames and not for processed frames. And for palette segments it’s even worse since it tells the number of frames for which the palette is valid, so framerate conversion will make it a mess. But the alternative leads to madness libavfilter and that’s hard pass for me. I see filters more as a drug that makes multimedia projects shift attention to them, making it more and more about e.g. filter negotiation and complex graphs support and less about playing actual media; consequently, making everything a filter is a sure sign the project is on its way to becoming obsolete (yes, I don’t hold neither DirectShow nor gstreamer in high regard).

Anyway, after overall design description it’s time to talk about implementation details. Palette generation for video differs from palette generation for single image by the sheer amount of data it needs to take into account. So while I have “let’s waste memory and have a table of 64-bit counters for each possible colour” my first mode was putting colours into smaller buckets (bucket index is calculated from the top bits of components) and join similar colours (e.g. differing just by two low bits in each component) when the number of entries gets too high. It is slower and not as accurate but it consumes less memory and performing vector quantisation on hundreds of thousands of entries is much faster than on millions. So it may have its uses.

Then palette segments generation. I don’t do anything fancy and simply calculate coarse colour histogram to decide when to start a new segment. For cut-off criterion I selected the ratio between correlated histograms and auto-correlated new frame histogram. If they’re of about the same magnitude then I can add this frame to a group, update group histogram and continue, otherwise I generate palette for the just finished segment and start a new one. It’s naïve but it seems to work reasonably well.

Palettisation itself consists of finding an appropriate palette entry for the input colour (I’m aware of dithering but haven’t bothered with it yet). I use the same three methods as back then: brute force search, local search and k-d tree search. The latter is faster than the rest but gives horrible quality so I don’t know if I should improve it or throw away. Local search (especially with a small cache for last 32 results) is a nice trade-off between the rest. And brute force search is implemented by filling a small 16MB table which maps each input colour to the palette index; it is slow to generate but it works extremely fast with global palette and reasonably large video (i.e. more than those sixteen million pixels). For segmented palettes it’s better to use local search though.

That’s it. I realise that I’m the only user of such feature but it gave me something to play with and brought some joy implementing it. After all, who else can claim he converted movieCD into animated GIF without using any 16- or even 32-bit code?

NihAV: OS/2 multimedia support

Friday, April 17th, 2026

In theory I should be documenting the codes Paul has shared with me or MVS (did you know that it employs a rather interesting chroma subsampling method—coding three 8×8 blocks in a macroblock but chroma samples have less than a half of coefficients in zigzag order coded) but instead I’ll write about something nobody really cares about.

As some of you may know, RedHat had (at least) two multimedia formats developed: RLE-based PhotoMotion for RedHat PC (later licensed to American Laser Games that seems to extend it somewhat) and gradient-based UltiMotion codec for AVI (the format of choice for VfOS/2).

Since the codec is somewhat unique, I decided to write an encoder for it. This way I can re-encode e.g. some movieCD (a format hardly anybody remembers) to another obscure format nobody remembers exactly just because I can. But the main reason is to learn how it’s organised.

There are three distinctive features it has: shared chroma (i.e. 8×8 super-block can have just one pair of chroma samples instead of coding each 4×4 block with its own pair), quantised values (6-bit luma and 4-bit chroma) and of course gradients. Actually there are seven block coding modes and only half of them are gradient-based—the rest are more conventional skip block, scaled-down block (4 luma samples only), BTC (2 samples plus fill pattern) and raw block.

Gradients here are essentially filling the block in one of the direction a lot like intra prediction works in H.264 and later, the main differences being fewer angles and fill values being transmitted explicitly. Fun thing is that unlike other codecs there’s no easy way to transmit a flat block, you need to code it in some extended way as the simplest (“shallow”) mode codes a coarse gradient with two values, the second value being implicit N+1. More complicated (“LTC”) mode codes a fine gradient but allows only a four-colour combination present in 4096-entry codebook. There’s also an extended mode where you can code any values for a gradient.

This of course poses a challenge of finding a good gradient in reasonable time (because trying all 4096 combinations with all 16 directions may get a bit slow). For shallow coding it’s easier, you essentially have block split into two parts, so checking averages for those parts and seeing if they fit is enough. For LTC and extended mode I applied a similar trick by finding the averages of four samples used in each gradient angle and saw if they fit well enough (for LTC it was also checking that the samples are monotone increasing and checking only the more or less close codebook entries; probably there’s more of optimisation potential but I’m fine with it as is).

Actually I started it gradually: first by implementing simple “raw or skip blocks only” mode, then “any block type that does not introduce additional loss (beside YUV quantisation)” mode, then lossy mode, and finally fast-and-shitty mode. The idea behind the last one is to calculate blocks variance and to use that information to force block selection process (i.e. not try more complex block types on blocks with low variance). As you can guess from the name it did not work out that nice (but it was about twice as fast). But overall lossy mode works rather good and by introducing distortion thresholds I can vary output file size significantly (3-5 times smaller and still not being a block soup). I’m not going to bother with any rate control and overall I consider this experiment done.

In conclusion I’d like to write something but nothing comes to my mind. Stay tuned for more stuff nobody cares about (like obscure codecs or my experiments with palettisation).

(Ir)regular NihAV news

Monday, March 30th, 2026

Here’s another portion of news about what I’ve been doing in last couple of weeks. Hopefully I’ll be able to write about fruity MVS soon.

While most of what I did is not worth mentioning, there are some bits I find funny or curious enough to share.

First of all, I’ve added VAXL and (Clari)SSA decoding support to na_eofdec (just some more and there will be enough formats for 0.1.0 release). And I actually added them mostly to test an IFF parser (derived from my AVI parser and enhanced). The idea is simple: just point it to the data needed to be parsed and provide chunk handlers and it should do the trick. The main enhancement is also providing a condition when to stop—at the end, on any unknown chunk, before some pre-defined chunk, or right after such chunk has been parsed. This way I can control parsing process without relying too much on some special callbacks or states. For example, in VAXL that means I parse header by expecting VXHD chunk and nothing else. The rest of VAXL consists of time code, palette, image data, and audio samples chunks; there I simply stop after BMAP chunk and output reconstructed frame (and audio if present). In SSA (IFF variant, I added FEE one too for completeness sake) each frame is wrapped into FORM DLTA list so there I just parse sub-chunks ignoring unknown ones.

Then I’ve finally had another look at TCA and improved my decoder, going from less than half of samples I had being supported to virtually all of them. While I presumed that video is always compressed with LZW, in reality it also has RLE and raw modes. RLE mode is rather curious at that as it codes run+mode as 00 00 xx yy zz, 00 xx yy or xx number (i.e. if first one or two bytes are zero, read one or two bytes of the number too) with low bit signalling run or skip and the rest being its length (e.g. 55 means skip 42 bytes while 00 01 00 01 means repeating 01 128 times). But since the format is next to impossible to detect statically (no magic constants in the header, so you’d better check packet structure instead), it’ll remain a useless curiosity—just like I love it.

And most of the time I spent on writing MOV muxer. Since I’ve written one for na_eofdec already (raw output only) I had some base to start off at least. Of course it’s wonky and even copying data may or may not work depending on your luck, at least now I have a format to output variable framerate content losslessly (previously the only alternative for VFR content in NihAV was to encode data with RealVideo 4 and Cook). At least now, after all possible enhancements, I can either re-mux old-style MOV into a new one (and the output may actually be playable) or use nihav-encoder -i input.mov -o output.mov --profile raw to decode some more exotic format not supported by anything else (like MoviePak in MacBinary MOV) to a raw format that (hopefully) more tools can understand. I can also use my Cinepak or Indeo 3 encoders there (and Truemotion 1 too in the future just for lulz) and I should probably write encoders for other common QT codecs (even if there are other implementations, up to SVQ1 by The Multimedia Mike). This should keep me entertained for a while. And who knows, maybe it will serve as a base for MP4 muxer if the need for one arises.

That’s it for now, hopefully I’ll have more to write about later.