Grabbing a frame image from a Quicktime Movie View

I’m hoping that someone can either point me in the right direction or offer a solution to my problem.

I’m relatively comfortable with AppleScript but have recently found the need to make some calls into Cocoa to solve this problem.

I have an AppleScript application that essentially opens a .m2v file and captures every frame of the m2v file by stepping through each frame one-by-one.

My first pass at doing this worked really well as I simply scripted QT to export an image sequence. However, it turns out that I needed to have more granular control over each image as they are being exported. This led me down the path of having to use the QTKit in Cocoa as well as AppleScript. Well needless to say there’s a little bit of a headache there when you’re not an Objective-C coder!

Right now I can open the m2v just fine. I can play the m2v just fine and I can step the m2v frame by frame jsut fine. All of these are using the ‘call method’ approach to a Cocoa file.

What I just simply can’t seem to figure out is how to capture an image. There’s plenty of reference’s in the Cocoa world to grabbing the ‘currentFrameImage’ - but is there an equivalent in AS?

One word of note, I am not opening Quicktime and scripting it as I want the application to appear as a dedicated application, so I’ve created it as an Xcode project instead.

The movie load is:


set theMoviePath to POSIX path of (choose file with prompt "Choose a movie:" without invisibles)
set movieLoaded to (call method "loadMovie:withPath:" of class "methods" with parameters {movieView, theMoviePath})

Then in Cocoa:


+ (id)loadMovie:(QTMovieView *)movieView withPath:(NSString *)moviePath {
    
	if ([QTMovie canInitWithFile:moviePath]) {
		[[movieView movie] stop];
		[movieView setMovie:[[QTMovie alloc] initWithFile:moviePath error:nil]];
		return [NSNumber numberWithBool:TRUE];
    } else {
		return [NSNumber numberWithBool:FALSE];
    }
}

Stepping through is a simple call:


call method "myStepForward:" of class "methods" with parameters {movieView}

With the associated Cocoa mehtod:


+ (void)myStepForward:(QTMovieView *)movieView {
	[[movieView movie] stepForward];
}

And here’s the image grab call:


set myNewVar to (call method "saveCurrentImage:withPath:" of class "methods" with parameters {movieView, saveFileName})

And the method (which doesn’t actually save anything and is the problem I’m having):


+ (NSString *)saveCurrentImage:(QTMovieView *)movieView withPath:(NSString *)saveFilePath {

	NSString *returnData = saveFilePath;
	NSImage *frameImage = [[movieView movie] currentFrameImage];
	
	NSData *imageData = [frameImage  TIFFRepresentation];
	
	NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
	NSDictionary *imageProps = [NSDictionary dictionaryWithObject: [NSNumber
											numberWithFloat:0.9] forKey:NSImageCompressionFactor];
											imageData = [imageRep representationUsingType:NSJPEGFileType
											properties:imageProps];

	[imageData writeToFile: saveFilePath atomically:YES];
	
	NSLog(saveFilePath);
	
	return returnData;
}


The code above should grab the current frame image as an NSImage and then save it to a file passed in from the AS call. Everything appears to work except no file ever gets created! The ‘return returnData’ is simply sending the filename passed in back to AS so I could display it whilst debugging to ensure the method actually got called.

What I’m hoping is that there is a way to do this in AS, that I’ve completely missed. I’ve literally spent 2 days searching for a solution here as well as on Google but just can’t nail it.

The reason for not using a native image sequence is two fold - first of all I need to manipulate the way each image is saved (file location, naming etc) without having to roll back through a list of images after they are created. The second is because I wanted a complete single application hat doesn’t launch QT Player as a separate application.

Hope someone out there can help! Much appreciated.

Oh - one more thing - this is all in Xcode 3 running on Leopard.

Just in case anyone is following this enthralling thread :wink: I’ve partially solved the problem. The path I am passing in to save the file has leading and trailing quotation marks which means that Cocoa sees an invalid path. Ironically the debugger doesn’t ever complain about the invalid path so a quick check to see if it was a success or not soon identified the issue:


if ([imageData writeToFile:saveFilePath atomically:YES]) {
		NSLog(@"Written Image");
	} else {
		NSLog(@"Image Write Failed");
	}

However, the issue I have is now firmly back in the paws of AS. In AS, doing a POSIX path adds a single quotation both in front of and trailing the path - which Cocoa sees and I need to remove before it gets passed in the method call.

So I did a quick test script:


set myInputPath to (choose folder with prompt "Select the destination folder:" without invisibles) as alias
set myfile to "filename.txt"
set myOutput to quoted form of POSIX path of ((myInputPath as string) & myfile)
display dialog (myInputPath as string)
display dialog myOutput

The output of myOutput when choosing the desktop is:


'/Users/myusername/Desktop/filename.txt'

What I need to pass to my Cocoa script is the following (notice NO single quotation marks):


/Users/myusername/Desktop/filename.txt

*** EDIT ***

Incidentally, I am using the quoted form (which is what is putting in the single quotes) because I am taking the input folder location from a user chosen folder - so I will have no control over the naming convention of the folder so there may be illegal characters in the path name if it weren’t the quoted form.

*** END EDIT ***

Any ideas?

Thanks!

First, quoted form of is not limited to just leading and trailing single quote marks. If there are any single quote marks in the string to be quoted, they will also have special transformations applied.

Second, POSIX paths and quoted strings are independent concepts. They will often be used together because file pathnames are common arguments for command line programs. One can reasonably quote non-pathname string data for use in building POSIX shell commands (do shell script ("echo " & quoted form of someString)). On the other side, if one needs to pass a POSIX pathname to an application command (some commands take POSIX pathnames in addition to or instead of HFS pathnames), quoted form of will cause problems (similar to the problems it looks like you are facing).

quoted form of is only really applicable for use with the “POSIX shell” (usually through do shell script). Other string contexts (SQL, PHP, JavaScript, generated AppleScript code etc.) have other quoting rules and quoted form of will not be sufficient to handle them (though it may work in limited situations like embedded spaces). C-“level” APIs (probably including whatever Cocoa APIs you may be using) use plain string objects (maybe char * in C, maybe string in C++, maybe NSString in Cocoa). These strings almost never need to be quoted in any way.

The whole purpose of quoting is to faithfully represent one string (say a filename with spaces) inside another string (a shell command line) where the former might have character components that would induce special handling because of the the context created by the latter (e.g. the shell might break up a filename with spaces into multiple parameters when one really needs them to be all in a single parameter (with the spaces included)). With C/C++/Cocoa there is no need for quoting because there is no enveloping string/interpreter context. One just passes the string objects to the functions/methods/messages. Any special characters in the pathname (spaces, punctuation, etc.) will be OK. The string object (or the protocols applied to it) encapsulate all the characters, even the special characters.

Just use POSIX path of and not quoted form of POSIX path of for the strings that you will be sending to Cocoa APIs (or just about any other interface that is not based on a string and an interpreter).

Chris

Thanks for the concise explanation! Makes a lot of sense (and explains why it works when using POSIX path of and not quoted form of POSIX path of).

Suffice to say problem solved!

Thanks for the feedback and hopefully this may help anyone else out who runs into similar issues passing paths (or any string for that matter) to Cocoa.

  • Andy