Tory Hoke

Essays, art, and comics of the unexpected

FOLLOW:

Exporting a Frame from QuickTime from the Command Line

More

Most Recent Posts

Day 1: An Overview

(Experience this as a TikTok.) “Play is the work of childhood.” – Jean Piaget “or children, play is serious learning.” – Mr. Rogers Adult learning is

Read More »

Oooh ooh ooh!

So I wanted to write something I could run from a command line that would capture the image at a given timecode of a given QuickTime movie, and save it to a given location on the file system. I didn’t care particularly how — I held out hope for a QuickTime python library, but that was silliness.

I Googled and Googled and Googled and finally got the best guidance for what I wanted to do from Cocoa is My Girlfriend. Having no crapping idea how to write in Objective C, nor having any experience with XCode, I am pretty pleased with XCode that it was possible for me to make use of CiMG’s guidance in the afternoon.

I am providing all the objects for clarity’s sake, but all credit is due Cocoa is My Girlfriend for providing most of this code.

1) Set Target -> Info to be like this:

target_info

2) Set up command line arguments like this:

arguments

3) I didn’t need a GUI or threading, so my main.m looks like this. (FrameRipper is my frame-ripping object):


#import
#import

int main(int argc, char *argv[])
{

   NSAutoreleasePool* myPool = [[NSAutoreleasePool alloc] init];

   //Parameters are set up in Executables –> Info
   NSUserDefaults* myArgs = [NSUserDefaults standardUserDefaults];

   NSLog(@”moviePath = %@”, [myArgs stringForKey:@”moviePath”]);
   NSLog(@”timeCode = %@”, [myArgs stringForKey:@”timeCode”]);
   NSLog(@”imageDestinationPath = %@”, [myArgs stringForKey:@”imageDestinationPath”]);

   QTMovie* currentMovie = [QTMovie movieWithFile: [myArgs stringForKey:@”moviePath”]
error: NULL];

   // Create our NSOperation derived object
   FrameRipper* op = [[FrameRipper alloc] init];
   [op setMovie: currentMovie];
   [op setTimeCode: [myArgs stringForKey:@”timeCode”]];
   [op setImageDestinationPath: [myArgs stringForKey:@”imageDestinationPath”]];

   // Run the object’s main method
   [op main];

   [myArgs release];
   [op release];
   [myPool release];

   return 1;
}

You can see this code closely follows the Cocoa is My Girlfriend example. Note I changed an attribute name (outputPath –> imageDestinationPath).

4) FrameRipper.h looks like this:


#import
#import
#import

#import

@interface FrameRipper : NSOperation {

   QTMovie* movie;
   NSString* timeCode;
   NSString* imageDestinationPath;

   NSDictionary* imageAttributes;
   NSLock* lock;
}

@property (assign) QTMovie* movie;
@property (assign) NSString* timeCode;
@property (assign) NSString* imageDestinationPath;

– (void)main;
– (void)saveImage:(NSImage*)image;

@end

5) FrameRipper.m looks like this:


#import “FrameRipper.h”

@implementation FrameRipper

@synthesize movie;
@synthesize timeCode;
@synthesize imageDestinationPath;

//********************************************************
// main()
//********************************************************

– (void) main
{
   if (movie)
   {
      NSImage* image;
      [lock lock];

      QTTime time = QTTimeFromString(timeCode);

      image = [movie frameImageAtTime:time
         withAttributes:imageAttributes
         error:NULL];
      [lock unlock];
      [self saveImage:image];
   }
}

//********************************************************
// init()
//********************************************************

– (id)init
{
   lock = [[NSLock alloc] init];

   // Specify that we want to save out a high-quality image.
   imageAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
      QTMovieFrameImageTypeNSImage, QTMovieFrameImageType,
      [NSNumber numberWithBool:YES], QTMovieFrameImageHighQuality, NULL];
   [imageAttributes retain];
   [super init];
   return self;
}

//********************************************************
// saveImage()
//********************************************************

-(void)saveImage:(NSImage*)image
{
   NSArray* representations;
   NSData* bitmapData;

   representations = [image representations];

   // Specify that we want to save the file as a JPG
   bitmapData = [NSBitmapImageRep representationOfImageRepsInArray:representations
      usingType:NSJPEGFileType
      properties:[NSDictionary dictionaryWithObject:[NSDecimalNumber numberWithFloat:1.0]
      forKey:NSImageCompressionFactor]];

   [bitmapData writeToFile:imageDestinationPath atomically:YES];
}

@end

6) Now build the app in XCode. The resulting app can be run from the command line like this:

build/Release/frameRipper.app/Contents/MacOS/frameRipper -moviePath /path/to/movie.mov -timeCode 0:00:00:04.403/600 -imageDestinationPath /path/to/imageYouWantToMake.jpg

Note the timeCode format. That’s something I’m working on right now — a bum timecode format (like 00:00:00:04) just gets you a capture of the first frame of the movie (since the bum timecode doesn’t exist as far as QuickTime’s concerned.)

Good luck!

Share This:

Comics: Rare Words

Comics: Sneaky VFX

Comics: Pure Silliness