I've been working on a small mod for the old Jurassic Park game, Trespasser, and I would like to implement a new cheat that would record all displayed frames to dump to a video file (any format will do, no audio necessary). I have access to every backbuffer frame (DirectDrawSurface or via DC) right before it is flipped and could probably already dump screens as bitmaps if necessary.

I've been looking for months and have found no information whatsoever about video capture from sequential screen displays. I have the DirectDraw and DirectMedia documentation, but they are very vague about it. All I've gotten from searching for 'video capture' are camera video capture drivers.

I would greatly appreciate any help. Does anyone know of a free library that can take care of this? Of any DirectX functions I may have to use? Tutorials? Anything at all?
Posted on 2005-05-31 09:25:05 by Big Red
If you have every frame, then just store them as AVI. write your own AVI-format-writer, or use VFW. I think it's the easiest approach.
Posted on 2005-05-31 10:17:56 by ti_mo_n
Thank you. However, I have looked far and wide for documentation about the AVI format and have found very little; would you know where these specifications may exist?

As for VFW, how do you convert screen dumps to "streams" that may then be written to files using the AVIFile stream functions (i.e., can I simply append raw bitmap data from successive screens and use the result as a source for stream data in AVIStreamWrite)?
Posted on 2005-05-31 13:51:07 by Big Red
Well, never mind about that last question, as I've figured some of it out. I simply appended 16-bit RGB bmp data to some stream and it basically functions. The only problem being... the capture is extremely slow, I cannot get the file to compress, and the timing is off.

If anybody still has any tips, I'd still be thankful, mainly due to the fact that the VFW documentation I'm using is very unclear and doesn't have any examples.

Posted on 2005-06-02 07:46:26 by Big Red
To improve capture rate, dump the raw frames to a datafile.
Have your "stop capture" function then process the datafile into avi (or whatever you like).
This will put less load on the cpu while trying to play the game (trespasser is quite cpu intensive, due to its physics model being very nice) and will move that load onto the diskdrive... you will create enormous "raw datafiles" but they are temporary as we will process and then delete them as soon as we stop capture.
Posted on 2005-06-02 23:49:33 by Homer
It might actually be less cpu-intensive to dump to some mildly compressed format first - like intel indeo or whatever. Not to mention that, even with a huge drive, you will run out of disk space VERY fast if you don't compress at alll :)
Posted on 2005-06-03 08:40:44 by f0dder
Thanks, I'll definitely try out both suggestions, though I already did try on-the-fly compression which bumped the framerate down to about 2 fps (not sure if I implemented it properly).
Posted on 2005-06-03 12:23:39 by Big Red
zlib seems to have good compression and decompression ratio. Or if you look on board someone give example of using windows nt compression, which i've read in a tutorial is the "fastest compression there is" for windows. Don't know how true that is. Other good compressions are aplib and jcalg, both compress pretty good, but they're also pretty slow. Decompression for them both is very fast though.
Posted on 2005-06-03 23:37:22 by Webring
In such cases I always do some simple theoretic calculations, and I refrain from saying "will run out of disk space VERY fast" without specifying exactly how fast that is. So, here's the simple math, assuming we're in 640x480x16. We'll convert the framebuffer to 16-bit if necessary. Let's assume 30fps (29fps is standard for movies) framerate:

640x480x2 = 614400 bytes = 600kB/frame
For 1 second we'll dump 600kB * 30 = 18000kB ( = 17.58MB/s)
Using UDMA33, we get 33MB/s theoretic writing speed of the HDD, so practically we're safe even if the actual speed is 53% of the theoretic. If UDMA66 is available, then you should setup your Windows to use it(win2k needs registry entries for this).
1GB of HDD will be taken for 1024/17.58 = 58 seconds of video :D
So, approx, 1 minute of video capture will take 1GB. Sparing 40GB temporarily nowadays is not a problem - for whole 40 minutes of capture :D .

You can also go for recording in 320x240 , where those 40GB will go for 160 minutes of capture. This will be a must-do if your HDD cannot write fast enough.

Anyway, you probably should use a double-buffer technique for feeding the HDD, each buffer being 17.58MB - to hold 1 second of data. Both buffers must be allocated with VirtualAlloc. When one buffer is filled with 30 video frames' data, you write it asynchroniously (overlapped WriteFile). The data of the next 30 frames will be filled in the second buffer, then WriteFile,.... and so on.

Maybe allocating the large output file at initialization will speedup the process a bit - so that Windows won't have to resize the file.

just corrected two typos
Posted on 2005-06-04 05:52:38 by Ultrano
Ultrano, if he's simply dumping every frame, he'll have at least 60fps - makes you run dry faster :). Probably a good idea to limit to ~30fps.

Probably not too practical compressing on-the-fly with any of the "good" algorithms, but perhaps a simple XOR + RLE encoding could work okay?
Posted on 2005-06-04 08:18:43 by f0dder
Yes, FPS limiting must be applied - a simple algo has to decide whether to dump the current frame or not. If he has a fast PC, doing a simple on-the-fly compression will obviously be a good decision, but there's something that's bugging me: viable arguments to solve this problem are missing:
Big Red, you should give us specs, details - how fast/good your PC is , do you target it for other PCs too, how cpu-intensive is that game, what OS do you use/target, have you configured well the OS (swapfile mainly),... what is your required framerate and resolution for the video (where will you be using it...). And most importantly - how long is the video you want to capture?

A problem cannot be solved practically if you don't have the arguments to it and the desired output/outcome of the solution. And of these, you have not mentioned anything specific. Before we hear any specs, we can only guess - and bring absolutely no real solution.
Posted on 2005-06-04 09:45:47 by Ultrano
Yes, of course. Right now, I'm running a 900Mhz w/256 MB RAM, Windows ME and a decent HD w/a few gigs of space. However, I intend the final function to work for any computer above 1Ghz.

I have worked a bit on this code since this thread was first posted. Right now, the default settings are as follows, though they can be modifed by the userl: 320*240 resolution (no resizing), storage as 24-bit bitmaps (as many of the codecs used by the VFW compression options dialog don't seem to work properly with 16-bit bitmaps, at least on my system), and 15-20 frames per second, outputted as AVI. I'm aiming to keep the default settings conservative though not restrictive, and optimize mainly for these values.

I'm no expert at bitmap manipulation, so for the moment the screen is captured using the standard GDI functions, the real problem being BitBlt (screen to DIB), which seriously slows down framerate (from 60 to 20 fps for me). Again, if there's a faster way to dump the screen, I'd be glad to hear it! With this, I cannot afford losing CPU cycles on compression. And yes, the game is very CPU intensive. The other problem with compressing on-the-fly is that it will be difficult to evaluate performance, because the function lets the user specify the CODEC (via standard VFW "Compression Options" dialog).

At the moment, I'm using a buffer created by CreateFileMapping to store the data, and using CreateDIBSection and BitBlt to store the frames one by one. The ouput is only written to the final file after the entire recording is done (which is long, but that is acceptable, as long as it's not during capture). It seemed convenient becaused it does not require any extra CPU usage from other functions (BitBlt will write to the mapping object passed to CreateDIBSection). Will switching to VirtualAlloc really give a performance increase over this?

I have already implemented framerate-capping techniques, and a partially-functional system to compensate for low framerates.
Posted on 2005-06-06 15:34:32 by Big Red
By the way, http://www.fraps.com
Posted on 2005-06-07 06:56:59 by Ultrano
Yes, I am aware of the existence of Fraps. However, ...

"Fraps 2.5.5
for Windows 2000, XP, 2003 and x64 Editions"

... and ...

"Capture Your Finest Gaming Moments With Fraps For Only $29.95!"

... and ...

No Source

... and I'm sure you get the idea ;) . Thanks anyway, though.

This question still stands:

"Will switching to VirtualAlloc really give a performance increase over ?"

And again, thanks very much for all your help.
Posted on 2005-06-07 10:33:53 by Big Red
VirtualAlloc is necessary for the overlapped WriteFile. I haven't done any benchmarks, but it's logical for overlapped writefile to be much faster than wrinting to a mem-mapped file.
Posted on 2005-06-08 13:16:26 by Ultrano
By using CreateDIBSection w/file mapping object as parameter for DIB raw data, BitBlt stores the screen DIB data directly to the file-mapping object (at a certain offset) without having to copy the data to a second buffer. Then, I guess my real question is, how do you make BitBlt efficiently write DIB data directly to a VirtualAlloc-ated memory space (without having to copy the data twice)? I'd like to avoid moving large buffers as much as possible.

Posted on 2005-06-08 13:55:35 by Big Red
memmapped files are slightly slower than normal memory, if used *just* for allocating memory. You probably wont be able to benchmark it, especially on fast computers, but it's slower (even on access). Allocation speed should be measurable, but since mmap/valloc are usually used for larger blocks, this is often irrelevant. However, unless you need to do shared memory, there's no reason to suffer overhead even if it's only 0.0001% slower :)
Posted on 2005-06-08 18:07:54 by f0dder
In that case, what would be faster:

1. The current method (getting BitBlt to draw directly to the file mapping object)
2. Using BitBlt to copy the DIB data to a CreateCompatibleBitmap-allocated buffer, and then re-copying the data from the bitmap's buffer to the true buffer (created with VirtualAlloc)
Posted on 2005-06-09 12:33:14 by Big Red
Red, what about CreateDIBSection with a VirtualAlloc buffer? If you can BitBlt to the CreateDIBSection bitmap, you have direct access to the pixels afterwards...
Posted on 2005-06-10 09:20:54 by f0dder
According to Win32hlp, the CreateDIBSection parameter for raw DIB data accepts only a file mapping object created using the CreateFileMapping function.
Posted on 2005-06-13 10:38:36 by Big Red