Pulling Frame Images from Video to file using Objective-C

DartStu

Registered
I'm writing a simple program using Objective C to run a QTMovie file, extract each video frame, and save it to file.
I have debugged and debugged and debugged and have one problem I can't seem to get around.

I currently have an NSImage that is returned from currentFrameImage and then tried to convert to .TIFF using TIFFRepresentation.

NSImage * fram = [film currentFrameImage];
NSData * fram1 = [fram TIFFRepresentation];
[fram1 writeToFile:name atomically:YES];

When this failed to work I looked around of many forums, google, etc. And changed my code to:

NSImage * fram = [film currentFrameImage];
//NSData * fram1 = [fram TIFFRepresentation];
NSArray * testArray = [fram representations];
NSImageRep * testType = [testArray lastObject];
NSData * fram1 = [NSBitmapImageRep representationUsingType:NSBMPFileType properties:nil];
[fram1 writeToFile:name atomically:YES];


But this still fails to work. Any thoughts are appreciated. Also if anyone knows of source code that does this frame extraction, so I can give up on my code I'd be happy with that.

Thanks!
 
The first code snippet works for me. Have you verified that all of those objects (film, fram, and fram1) were created successfully before the writeToFile message?
 
I have verified that all the objets were successfully created, but its possible that I have errors elsewhere that I just haven't found. I'm attaching the whole little program (I know that I don't actually have the file names updating with each frame yet). Thanks!


#import <Cocoa/Cocoa.h>
#import <QTMovie.h>
#import <QTTime.h>
#import <QTKit.h>

int main(int argc, char *argv[])
{
QTMovie * film = [[QTMovie alloc] init];
film = [film initWithFile:mad:"/Users/engs190/Desktop/pqs_fish_12sComp.mov" error:nil];
//go to beginning here maybe?

[film stepForward];
QTTime timeNow = [film currentTime];
QTTime timeTotal = [film duration];
int i = 0;

while (QTTimeCompare(timeNow, timeTotal) != NSOrderedDescending) {
i++;
printf("Test\n");
NSString * name = [[NSString alloc] init];
name = @"/Users/engs190/Desktop/fishframes/frame";
//name += i;
name = [name stringByAppendingString:mad:".tiff"];

NSImage * fram = [film currentFrameImage];
//NSData * fram1 = [fram TIFFRepresentation];
NSArray * testArray = [fram representations];
NSImageRep * testType = [testArray lastObject];
NSData * fram1 = [NSBitmapImageRep representationUsingType:NSBMPFileType properties:nil];
[fram1 writeToFile:name atomically:YES];
[film stepForward];
timeNow = [film currentTime];
[name release];
[fram release];
[fram1 release];
}

[film release];
 
Okay, now I'm getting some errors, too. It seems to choke as it tries to create the TIFFRepresentation. I made a bunch of changes, but in the end GDB says there's an error of type 1002 trying to create a CGSWindow. I don't really know what that means. I guess the problem stems from the fact that you're using Cocoa code outside of the Cocoa event loop (i.e., directly in the main() function). I thought you COULD do that, but I guess there are exceptions. Or maybe you need to use CoreFoundation instead of Cocoa. In XCode's new project template, there is no "Cocoa tool" under the "command-line tools" section, but there is a "CoreFoundation tool".

While I haven't managed to get it working right in main(), I have noticed a few problems with your code that you should be aware of anyway.

First of all, you need to create an NSAutoreleasePool. The lack of that caused some errors before I ever got to the CGSWindow error. So you should change it to look like this:
Code:
int main(int argc, char *argv[])
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//.....your code goes here......\\
	[pool release];
}

Normally Cocoa manages an autoreleasePool automatically, but again, since you're operating outside of Cocoa's NSApplicationMain(), things are different. You'd also need to create your own pools if you were using a new thread.

You explicitly release name, fram and fram1, but all of those are autoreleased. Releasing them explicitly will cause errors. The rule of thumb for knowing what's autoreleased and what it is your responsibility to explicitly release is simple: if you created it with -alloc, -new or -copy (or some variant thereof), you need to release it. -stringByAppendingString, -TIFFRepresentation and -currentFrameImage all return autoreleased objects. One more reason you need to set up an autoreleasePool; Cocoa uses them internally quite a bit.

See http://www.cocoadev.com/index.pl?RetainReleaseTips for more on retaining and releasing objects.

I also notice that you init'd film twice:
Code:
QTMovie * film = [[QTMovie alloc] init];
film = [film initWithFile:@"/Users/engs190/Desktop/pqs_fish_12sComp.mov" error:nil];
You shouldn't call two init methods on the same object. It might not fail miserably, but it's never a good idea, anyway. So change that to:
Code:
QTMovie * film = [[QTMovie alloc] initWithFile:@"/Users/engs190/Desktop/pqs_fish_12sComp.mov" error:nil];


There might be something I'm missing with your #imports. When I try to compile it, it gives me errors. Where are you getting QTTime.h, QTMovie.h, and QTKit.h from? The way I do it is to add the QTKit.framework (in /System/Library/Frameworks) to my project, then #import "QTKit/QTKit.h". I guess you did it some other way. Maybe it doesn't matter? I don't know, but I don't think that's the standard way, so you might run into problems with it.


Again, even with all my modifications, it still doesn't finish. However, it DOES finish if I move the code outside of main() and into an Objective-C method. If you don't need this to run as a command-line tool, you shouldn't try to work in main().
 
Back
Top