QT Export Dialog – So What Did They Choose?
In the previous post, “Show user QT’s Export Video Dialog“, I showed how to present the user with the standard QuickTime export settings dialog. The next question should be “How can I figure out what they selected?”. For me, I only want to know certain settings for display purposes. If the user chooses h.264 @ 320×240 I’d like to be able to remind them of that later. The settings useful for display purposes are minimal, and that’s all I cover here. To get dirtier, I’ll leave some links throughout the doc and a summary at the end.
I wrote a QTSettingsParser that stores the following:
@interface QTSettingsParser : NSObject {
CodecType codecType;
Fixed width;
Fixed height;
Fixed frameRate;
long dataRateBytes;
}
To parse the data I have one static initializer function and one, should be private but I’m lazy, helper function:
+ (QTSettingsParser *) initWithContainer:(QTAtomContainer)
aContainer;
- (void) parseAtom:(QTAtomContainer) container
withAtom:(QTAtom) atom
ofType:(QTAtomType) type;
At the end of the previously mentioned post we had a QTAtomContainer. Now we have an QTSettingsParser with the details of what I care about easily accessible. The interesting code is in initWithContainer and parseAtom, let’s take a look.
The static initializer is easy:
+ (QTSettingsParser *) initWithContainer:(QTAtomContainer)
aContainer
{
QTSettingsParser *settings =
[[QTSettingsParser alloc] init];
[settings parseAtom:aContainer withAtom:nil ofType:nil];
return settings;
}
The more interesting code is in the recursive function, parseAtom:
- (void) parseAtom:(QTAtomContainer) container
withAtom:(QTAtom) atom
ofType:(QTAtomType) type
{
QTAtomType cur = nil;
// do we have any data?
Ptr data;
long dataSize;
OSErr result = QTGetAtomDataPtr(container,atom,&dataSize,&data);
// yes, we could conditionally GetAtomDataPtr,
// but this keeps things nice and simple.
if( result == noErr )
{
if( type == movieExportWidth )
{
[self setWidth:*(Fixed*)data];
}
else if( type == movieExportHeight )
{
[self setHeight:*(Fixed*)data];
}
else if( type == scSpatialSettingsType )
{
SCSpatialSettings *settings =
(SCSpatialSettings *)data;
[self setCodecType:settings->codecType];
}
else if( type == scDataRateSettingsType )
{
SCDataRateSettings *settings =
(SCDataRateSettings *)data;
[self setDataRateBytes:settings->dataRate];
}
else if( type == scTemporalSettingsType )
{
SCTemporalSettings *tempSettings =
(SCTemporalSettings *)data;
[self setFrameRate:tempSettings->frameRate];
}
}
// go through all the children hunting for more.
while( cur = QTGetNextChildType ( container,
atom, cur ) )
{
// we have children
int childCount = QTCountChildrenOfType(
container, atom, cur );
int i;
for(i = 1; i < = childCount; i++)
{
QTAtom nested_atom;
nested_atom = QTFindChildByIndex(
container, atom, cur, i, nil );
[self parseAtom:container
withAtom:nested_atom ofType:cur];
}
}
}
What's going on here? The function call to QTGetAtomDataPtr get's us a pointer to the current atom. When the static function called this, it passed in a nil atom which will cause QTGetAtomDataPtr to error out. So we skip the err == noErr block and end up at the while loop. The while loop ensures we see every atom. QTGetNextChildType returns a type. A QTAtomType is an array of four characters defining some type. Next we get the children of that type and call parseAtom again to look at its contents as well to parse its potential children. This just happens over and over until we see every individual atom. Next question is, "OK, we have an atom, it's data ptr and a length, what the heck's inside?"
I spent a lot of time trying to figure out what the data pointed to for each QTAtomType that I found. The doc that finally got me on the right path was the SCGetInfo doc. Unfortunatly, SCGetInfo didn't work for me (otherwise I wouldn't need this recursion).
How to find out more: When I started this I didn't know where the CodecType or the frame rate or anything would be stored. So I had to dump the QTAtomType's I saw as a string (remember, it's just a character code) and try to fumble through docs to figure out what was what. Fortunately most of the character codes make sense and most are defined in QuickTimeComponents.h. At first I saw that the movieExportHeight was a 4 byte value. I assumed it was a unsigned int. That was wrong. Turns out the height and width are a 'Fixed' value (FixedToFloat() is handy). I found this out by finding some sample code in BackgroundExporter or ThreadsExportMovie. I forget which one.
Unfortunately, trial and error and the dumping of hex characters seems to be the only route we have to figure out what's in these pointers. Well, that and looking at sample code. In reality, I hope that statement is wrong. I really hope somewhere there is a nice doc explaining each type and its payload definition and that I just couldn't find it. If that document doesn't exist then I either don't understand something important, or it's just crazy.
Links:
Working with QT Atoms
SCGetInfo - just remember, the actual function didn't work for me, but some payload types are in the doc.
Useful sample code:
BackgroundExporter
ThreadsExportMovie
What's Next?
I can now present the user with the QuickTime export settings and programmatically figure out what items of interest they selected. The next work item for me on this project is to get some settings persisted and touch up the UI a bit. I look forward to spending some quality time back with Cocoa. Seriously, these QuickTime API's make me feel dumb. Cocoa makes me feel smart. Anyway, I don't plan to post about persisting data or fumbling with UI's, that's already all over the place and, you don't have to take my word for it.
