182 lines
8.9 KiB
Plaintext
182 lines
8.9 KiB
Plaintext
AVIFile: it's not just an API set, it's a way of life.
|
|
|
|
So, people are confused about how to approach the AVIFile APIs.
|
|
Let's try to help by making some things clear.
|
|
|
|
|
|
1. AVIFile isn't just for AVI files.
|
|
Perhaps this would be clearer if these functions had some different
|
|
name, but let's live with AVIFile for now. The important thing to
|
|
keep in mind is that we aren't talking about .AVI files, but about
|
|
some ideal time-based file format which supports the operations we'd
|
|
like it to support, like reading and writing from multiple
|
|
time-stamped streams. If the APIs conform closely to what's actually
|
|
in .AVI files, it's just because I made up both the .AVI format and
|
|
the AVIFile APIs.
|
|
|
|
The guiding principle is that any other type of file containing this
|
|
time-stamped sort of data, be it a QuickTime file, an MPEG file, a
|
|
.WAV file, or whatever, should be accessible in one simple way.
|
|
|
|
2. AVIFile isn't just for files.
|
|
Having a uniform API to read these different sorts of files is
|
|
convenient, but we can go beyond that. Once you have the abstract
|
|
concept of a "file" which is a bundle of "streams" (each of which has
|
|
a particular data type and format, along with a start time and
|
|
duration, and can be read from or written to at various points in
|
|
time), it is clear that not every "file" needs to correspond to an
|
|
actual disk file, and not every "stream" has to come from a "file" at
|
|
all.
|
|
|
|
Useful kinds of streams to think about:
|
|
Entirely synthetic streams, like the bouncing ball example; in this
|
|
case, a "stream" reduces to a function GiveMeVideoFrame(x).
|
|
Streams which are essentially filters applied to other streams; for
|
|
example, given a stream of video (that is, some abstract object that
|
|
can hand you frame 4 if you want it), you can build from that a
|
|
"compressed" stream, which is simply a stream that when asked for
|
|
frame 4 goes and asks the original stream for its frame 4, then
|
|
compresses it and hands it back to you, saying "here's frame 4, all
|
|
compressed like you wanted it."
|
|
|
|
3. AVIFile tries to conform to the Component Object model.
|
|
Since AVIFile is intended to be extensible to support additional file
|
|
formats, it has to have some way of transparently linking to DLLs
|
|
containing routines that understand those file formats. In the past,
|
|
we in MMSys have done this sort of thing using installable drivers
|
|
and a message-based scheme. (See MCI, ICM, and more....) With the
|
|
release of OLE 2.0, we have a new standard for this sort of thing.
|
|
It's a little scary at first, requires either C++ or some C++-like
|
|
thinking, and involves header files you haven't seen before. In any
|
|
case, we're all going to have to live with it.
|
|
|
|
4. Don't be intimidated by talk of C++ and "objects".
|
|
|
|
From a C point of view, all an "object pointer" like a PAVISTREAM is
|
|
is a pointer to a structure whose first member happens to be another
|
|
pointer to a table of functions. This means that in addition to
|
|
carrying around data like "how long is this video sequence", the
|
|
structure also contains pointers to code that knows how to actually
|
|
get things done. All C++ does for you is provide a nicer syntax for
|
|
doing this sort of thing.
|
|
|
|
You can make yourself a PAVISTREAM by hand. All you have to do
|
|
is allocate room for a structure big enough to contain the pointer
|
|
to the function table and any other data you need to keep around.
|
|
Then, you make a function table with the Read, Write, and other
|
|
functions to operate on your type of stream, and make sure your
|
|
structure points to your table. Just like that, you have a bona fide
|
|
PAVISTREAM which you can pass to AVISave, AVIStreamRead, and so on.
|
|
|
|
5. Luckily, you can usually ignore all of the "Component Object model"
|
|
stuff.
|
|
Unless you're doing something complicated, you should be able to just
|
|
call the various AVIFileXXXX and AVIStreamXXXX APIs and pretty much
|
|
ignore all of the talk of ISomethingOrOther and CLSID_Confusing.
|
|
|
|
If you are trying to make a DLL that will add support for reading and
|
|
writing a new file format, you will in fact have to learn something
|
|
about what a "class factory" is, but cross that bridge when you come
|
|
to it.
|
|
|
|
6. AVISave() is just a helper function.
|
|
All the AVISave function does is copy some streams into a new file.
|
|
It doesn't do anything that you couldn't do yourself by calling the
|
|
AVIFileXXXX and AVIStreamXXXX APIs. It does, however, make your life
|
|
easier by: calling AVIMakeCompressedStream for you according to the
|
|
options you pass in; calling AVIFileOpen and AVIFileCreateStream
|
|
appropriately to make the new streams in the new file you're making;
|
|
and finally, looping through all of the streams from start to end
|
|
copying the data from the old streams into the new file, and doing it
|
|
in the right order so that things come out nicely interleaved.
|
|
|
|
7. AVIStreamReadData, AVIStreamWriteData, AVIFileReadData, and
|
|
AVIFileWriteData are not actually for reading and writing real data.
|
|
I know, these routines have stupid names and they're just confusing
|
|
everybody.
|
|
|
|
What they're not for: These routines are not what you use to read and
|
|
write audio samples and video frames and that sort of thing. For
|
|
that, you must use AVIStreamRead and AVIStreamWrite. (If you have a
|
|
PAVIFile, call AVIFileGetStream and then call AVIStreamRead.)
|
|
|
|
What they're for: Reading and writing copyright information, the
|
|
author's name, and other stuff like that that's kept in INFO chunks
|
|
in RIFF files.
|
|
|
|
8. AVIStreamGetFrame is just another helper function.
|
|
AVIStreamGetFrame (and AVIStreamGetFrameOpen/Close) are just
|
|
functions which handle the relatively simple task of getting a
|
|
decompressed video frame out of a stream. This involves finding the
|
|
right ICM decompressor for the task at hand, figuring out where the
|
|
last key frame was, and decompressing frames as necessary.
|
|
|
|
Perhaps this should all be done automatically for you; perhaps you
|
|
should do this instead by opening a "decompressed" stream based on
|
|
the compressed stream you want to read. It's not either of those
|
|
ways now, so use AVIStreamGetFrame.
|
|
|
|
8a. What in heck is AVIMakeCompressedStream() for?
|
|
AVIMakeCompressedStream makes a new stream pointer for you that acts
|
|
just like the old stream you already had, but is compressed or
|
|
decompressed. That isn't very clear, so I'll try again: If you have
|
|
a PAVISTREAM which, when you read it, gives you back 8-bit RGB frames
|
|
of "Gone With the Wind", you can use AVIMakeCompressedStream to make
|
|
a stream that will return the same pictures, but compressed in, say,
|
|
CRAM format. The reverse is also true: given a compressed stream,
|
|
AVIMakeCompressedStream can make you an uncompressed version of that
|
|
stream.
|
|
|
|
To use it, just fill out an AVICOMPRESSOPTIONS structure. (Or pass
|
|
NULL, which will decompress.)
|
|
|
|
In theory, you can either read from the compressed stream or write to
|
|
it. (Right now, this still doesn't work for audio compression....)
|
|
However, you can only do one at a time.
|
|
|
|
|
|
|
|
9. What in the world is an HRESULT?
|
|
For some reason, all OLE 2.0 functions return HRESULTs which could
|
|
potentially contain extra information about an error that occurred.
|
|
For now, they are essentially just the same as a plain error code.
|
|
To convert one of the AVIERR_XXXX codes to an HRESULT, call
|
|
ResultFromScode() on it. To convert back, use GetScode()....
|
|
|
|
10. In its current state, AVIFile is not finished.
|
|
Some things which are bad about the current AVIFile APIs:
|
|
AVISaveOptions() puts up a nasty big nested dialog which isn't
|
|
user-friendly.
|
|
AVISave() doesn't handle palette changes correctly.
|
|
Almost nothing handles the concept of, say, a wave format changing
|
|
halfway through a stream.
|
|
T here is no way to find when the next palette change happens, short
|
|
of asking for the video format on every frame until you find a place
|
|
it changes.
|
|
Dealing with compressed and uncompressed streams is kind of screwy.
|
|
If you know exactly what you have and what you want, you can call
|
|
AVIMakeCompressedStream() to do what you need.
|
|
Right now, the handler for compressing streams only supports reading,
|
|
not writing. There is no reason this should be true, and maybe I'll
|
|
fix it.
|
|
There are no status callbacks for anything. If something takes a
|
|
really long time, you wait.
|
|
Error returns are generally stupid, if they're there at all.
|
|
There is no way to find what a specific handler can do short of
|
|
trying to do it and seeing what works.
|
|
The documentation is still primitive.
|
|
|
|
11. Miscellaneous hints:
|
|
Be sure to call AVIStreamInit() and AVIStreamExit(). If you don't,
|
|
the component object DLL won't get initialized and nothing will work.
|
|
Be sure the right information is in your registration database. It
|
|
should all get put there automatically, but isn't too resistant to
|
|
tampering.
|
|
|
|
Some functions take pointers to long variables which need to be initialized
|
|
to the size of your buffer before you call them. You need to use code like:
|
|
cbSize = cbBuffer; AVIStreamReadFormat(pstream, 0, lpBuffer, &cbSize);
|
|
After this, cbSize will contain the actual correct size of the format, which
|
|
you can then compare to the size you passed in to see if your buffer
|
|
was large enough.
|