Quacking Like Duck

I don’t learn very quickly. A good illustration of this personal shortcoming is my ongoing battle with the Duck TrueMotion 1 video codec, a conflict that is well into its 8th year at this point. This was one of my first exposures to the field of multimedia hacking as I discovered some Duck TM1-encoded AVI files on select Sega Saturn console games all the way back in 1998. I found a few Japanese Windows programs that could play the files. Early in my multimedia hacking career, I worked hard to reverse engineer the TM1 algorithm.


Duck TrueMotion logo

In 2002, On2 (formerly Duck) open sourced their VP3 codec (which would later become Theora). I kept stubbornly trying to RE TM1 from the binary when Alex notified me that On2’s open source VpVision package contains the decoders for TrueMotion 1 and 2. It also included the decoders for Duck DK3 and DK4 ADPCM, both of which I successfully had RE’d from binary code, and at around the time that VpVision had been released as open source.

The VpVision source has been quite valuable in RE’ing both the TM1 and TM2 algorithms. It’s extremely difficult to understand, however, as is often the case with code produced on a deadline. Some TM1 files still fail to work correctly with the native FFmpeg decoder. I always thought it would be useful if I could compile the code and run it in a step debugger, or perhaps profile it with other tools, in order to sort out the correct operation when decoding certain files. But the code seemed like such a mess that I didn’t think it would compile on Linux. It looked like it would only compile on Windows or maybe Mac, and only with some effort.

I feel a little silly since I discovered today how simple it is to compile the TM1 code on Linux. This is how I got it to compile:

  • download and unpack vpvision0.5_source.zip
  • ‘cd vpvision/ducksoft/’
  • edit private.h/duck_dxl.h and comment out the definition of DXL_GetXImageFrameBuffer() at lines 190-192
  • ‘cd tm1.0/linux’
  • ‘make’

This produces a nice little file called libtm1.a in the obj/ directory. Similar work could probably get the TrueMotion 2 library into shape, but Kostya already has that algorithm locked down.

In retrospect, I think I had previously opted not to try compiling the smaller modules in the package based on my foul experience attempting to compile the VP3 decoder. That module has a lot of dependencies on Win32 or Mac platform facilities. Eventually, I did separate out and modify just enough VP3 code to make a standalone decoder that compiles with gcc on Linux. I have since forgotten where I put that code but at the time, it helped me validate my new FFmpeg VP3 decoder.

So, how to make this useful? I suppose we could concatenate all of the files together as a new TM1 decoder and submit a mega-patch to the ffmpeg-devel list, if only to get a rise out of the Guru. However, our new decoder seems to be a bit more efficient, size-wise. Under gcc 4.1.1 on an i386, libavcodec/truemotion1.o compiles to 13304 bytes while obj/libtm1.a weighs in at 59432 bytes (both counts represent strip’d binaries). The TM1 algorithm is fairly simple but does rely on a mess of data tables.

I still want to incorporate this library into FFmpeg, at least on an experimental basis so that I learn the code paths being executed for certain problem files. Now the issue is understanding the API by which one is expected to interact with this beast. And a beast it is. Forget about developer documentation. It’s all up to the developer. I started with a function that I remember as being core to the decoding process and traced up the call tree. Using this strategy, the functions in tm1.c appear to be top level functions. However, the corresponding tm1.h file does not have any useful information. The developers didn’t even want to create that header file based on the comment “To appease the strict Mac”.

However, the real break in the case comes when I recall that decoding the TM1 frame header involves a XOR operation. There is only one ‘^’ operator in the code and that leads me to where encoded data is unequivocally accessed in the decoder. It is beginning to look as though the API works something like:

  • init TM1 system with tm1_Init()
  • allocate xImage with tm1_xImageCreate()
  • call methods assigned within xImage: seedData() specifies a new data buffer to be decoded and dx() asks for a decode operation

So I decide to give it a whirl with just a simple C program calling tm1_Init(). No go, because there are a bunch of support functions required that live in other directories in the VpVision tree. It should be possible to pull these things in. However, I don’t have time to pursue this idea further at this time. Until such time that I do, this blog entry will just serve as a notepad for where I am along this path.

In case someone else wants to take up this task, the immediate goal is to figure out how the API operates and plug it into the FFmpeg decoder for testing purposes. Use the existing truemotion1.c file and reroute the decoding logic to this decoder library. The API is quite mysterious, though. The tm1_Init() function wants a parameter that specifies “maxframes”. I have no idea what kind of number is expected. Further, the seedData() function that apparently receives an encoded data buffer to be decoded only takes a char* parameter. Such functions generally need a buffer length parameter to go along with the raw buffer. I don’t remember anything special about the TM1 coding method where a particular byte value indicated that the end of the stream had been reached.

Related posts:

One thought on “Quacking Like Duck

  1. Kostya

    Well, I know only one person who use reference VC-1 decoder library with FFmpeg and that’s me. If you will add the library it will be replaced with native decoder some time later.
    From my experience with TM2 the hardest part is to find those few files which contain actual decoding code and a lot of energy goes to swearing at how they wrote it.

Comments are closed.