|
Getting a movie frame imageQ: I'd like to be able to get an image for a movie frame at a specific time. Is this possible? A: Yes. Use the - (NSImage *)frameImageAtTime:(QTTime)time; The - (void *)frameImageAtTime:(QTTime)time withAttributes:(NSDictionary *)attributes error:(NSError **)errorPtr; The dictionary of attributes may contain the following keys: QTMovieFrameImageSize QTMovieFrameImageType QTMovieFrameImageRepresentationsType QTMovieFrameImageOpenGLContext QTMovieFrameImagePixelFormat QTMovieFrameImageDeinterlaceFields QTMovieFrameImageHighQuality QTMovieFrameImageSingleField See For the QTMovieFrameImageTypeNSImage QTMovieFrameImageTypeCGImageRef QTMovieFrameImageTypeCIImage QTMovieFrameImageTypeCVPixelBufferRef QTMovieFrameImageTypeCVOpenGLTextureRef For example, here's how to ask for a particular image type ( Listing 1: How to get a CGImage for a movie frame at a given time. NSDictionary *dict = [NSDictionary dictionaryWithObject:QTMovieFrameImageTypeCGImageRef forKey:QTMovieFrameImageType]; QTTime time = [movie currentTime]; CGImageRef theImage = (CGImageRef)[movie frameImageAtTime:time withAttributes:dict error:NULL]; See the QTKitMovieFrameImage sample code for a complete example. Nothing happens when I try to play a sound fileQ: I'm using QTKit to open and play a sound file, but when I do this nothing happens. I hear no sound at all. But if I set my QTMovie *aMovie; QTMovieView *aMovieView; aMovie = [QTMovie movieWithFile:s error:nil]; [aMovie setVolume: volumeVal]; // [aMovieView setMovie:aMovie]; // doing this makes it work! [aMovie play]; A: The following line of code creates an autoreleased ( aMovie = [QTMovie movieWithFile:s error:nil]; Once you re-enter the run loop the object is disposed of, which would explain why nothing happens when you try to use the object again. The reason your code works when you assign the Instead, create the QTMovie* aMovie; aMovie = [[QTMovie alloc] initWithFile:s error:nil]; [aMovie setVolume: volumeVal]; [aMovie play]; ... /* do stuff */ [aMovie release]; // release object when done Alternately, you could use the QTMovie* aMovie; aMovie = [QTMovie movieWithFile:s error:nil]; [aMovie retain]; // retain the object [aMovie setVolume: volumeVal]; [aMovie play]; ... /* do stuff */ [aMovie release]; // release object when done How do I add a QTTrack to a QTMovie?Q: How do I to add a A: There are currently no QTKit methods for adding tracks to a Here's a quick outline of what you need to do:
Here's a code snippet showing how it's done. Listing 2: Adding a QTTrack to a QTMovie @implementation QTMovie (QTMovieExtensions) -(BOOL)isEditable { NSDictionary *movieDict = [self movieAttributes]; NSNumber *isEditable = [movieDict objectForKey:QTMovieEditableAttribute]; return [isEditable boolValue]; } - (QTTrack *) addVideoTrackWithSize:(NSSize) aSize { QTTrack *newTrack = nil; require( [self isEditable] == YES, NOT_EDITABLE); Track videoTrack = NewMovieTrack ([self quickTimeMovie], FixRatio (aSize.width, 1), FixRatio(aSize.height, 1), kFullVolume); require (GetMoviesError() == noErr, NEWTRACK_ERROR); Handle dataRef = NULL; Handle hMovieData = NewHandle (0); require (hMovieData != nil, NEWHANDLE_ERR); OSErr osErr = PtrToHand (&hMovieData, &dataRef, sizeof(Handle)); require (osErr == noErr, PTRTOHAND_ERROR); Media videoMedia = NewTrackMedia (videoTrack, VideoMediaType, [[self timeScale] longValue], dataRef, HandleDataHandlerSubType); require (GetMoviesError() == noErr, TRACKMEDIA_ERROR); QTTime movieDuration = [self duration]; InsertMediaIntoTrack (videoTrack, 0, 0, movieDuration.timeValue, fixed1); require (GetMoviesError() == noErr, INSERTMEDIA_ERROR); newTrack = [QTTrack trackWithQuickTimeTrack: videoTrack error:nil]; return newTrack; INSERTMEDIA_ERROR: DisposeTrackMedia(videoMedia); TRACKMEDIA_ERROR: PTRTOHAND_ERROR: DisposeHandle(hMovieData); NEWHANDLE_ERR: DisposeMovieTrack (videoTrack); NEWTRACK_ERROR: NOT_EDITABLE: return nil; } @end When does the QTMovieTimeDidChangeNotification notification fire?Q: When does the A: The Some examples are: the user clicks in the movie controller bar to change the movie time, or a wired action changes the movie time. Instead, you might consider using an Creating an empty movie and adding images to itQ: I'd like to create a 10-second long A: QuickTime 7.2.1 provides a new method - (id)initToWritableFile:(NSString *)filename error:(NSError **)errorPtr; For versions of QuickTime prior to 7.2.1, you can use the native QuickTime API The Sample Code Project 'QTKitCreateMovie' demonstrates both of these techniques. You can also initialize a Listing 3: Initializing a QTMovie from an NSImage. // instantiate an NSImage object for our image NSImage *image = [NSImage imageNamed:@"some_image"]; if (image) { // Returns a data object containing TIFF for all representations NSData *data = [image TIFFRepresentation]; QTDataReference *dataRef = [QTDataReference dataReferenceWithReferenceToData:data name:@"some_image.tiff" MIMEType:nil]; // make a QTMovie from the NSImage QTMovie *movie = [QTMovie movieWithDataReference:dataRef error:nil]; // make the movie editable [movie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute]; // set the duration to 10 seconds QTTimeRange range = QTMakeTimeRange(QTZeroTime, [movie duration]); [movie scaleSegment:range newDuration:QTMakeTime(10, 1)]; // export as a 3GPP file; or use your existing export code here.... // setup the proper export attributes in a dictionary NSDictionary *dict= [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], QTMovieExport, [NSNumber numberWithLong:kQTFileType3GPP], QTMovieExportType, nil]; // write the QTMovie to a 3GPP movie file on disk [movie writeToFile:@"/tmp/my3GP.mov" withAttributes:dict]; } Exporting a QTMovie to a new file on diskQ: I have a Here is my code : // My movie QTMovie *movie = [qtPlayer movie]; // The codec dictionary NSDictionary *codecDictionary = [NSDictionary dictionaryWithObjectsAndKeys: @"jpeg", QTAddImageCodecType, [NSNumber numberWithInt: codecNormalQuality], QTAddImageCodecQuality, nil]; // The moviePath is also correct [movie writeToFile: moviePath withAttributes: codecDictionary]; A: The keys With the // writeToFile: attributes dictionary keys QTKIT_EXTERN NSString *QTMovieExport AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // NSNumber (BOOL) QTKIT_EXTERN NSString *QTMovieExportType AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // NSNumber (long) QTKIT_EXTERN NSString *QTMovieFlatten AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // NSNumber (BOOL) QTKIT_EXTERN NSString *QTMovieExportSettings AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // NSData (QTAtomContainer) QTKIT_EXTERN NSString *QTMovieExportManufacturer AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; // NSNumber (long) The following samples shows how to perform an export operation: Sample Code Project 'QTKitCreateMovie', Sample Code Project 'QTKitProgressTester' and Sample Code Project 'QTKitCommandLine' . Getting a list of QuickTime supported file types or extensionsQ: How can I get a list of file types or extensions that QuickTime can handle? A: See the This will work fine, and it allows you to selectively include still image types, translatable types, and types that require "aggressive" importers (like text and html files) by adjusting the set of flags passed to the There is however, an even easier technique, and that is to use the - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename { BOOL isDir = NO; [[NSFileManager defaultManager] fileExistsAtPath:filename isDirectory:&isDir]; return isDir ? YES : [QTMovie canInitWithFile:filename]; } This technique is basically equivalent to passing in the array of file types returned by the How does the QTMakeTimeFromString/QTTimeFromString function work?Q: How does the The current documentation states this function is called QTTime oldTime = [qtMovie currentTime]; QTTime incTime = QTTimeFromString( @"00:02:00.00" ); QTTime newTime = QTTimeIncrement( oldTime, incTime ); NSLog( QTStringFromTime( oldTime ) ); NSLog( QTStringFromTime( incTime ) ); NSLog( QTStringFromTime( newtime ) ); I get the following results: 0:00:00:00.00/48000 0:00:00:00.00/1000000 0:00:00:00.00/1000000 I have also tried setting the time string to What am I doing wrong? A: You'll notice the following comment in QTTime.h: // ,,,dd:hh:mm:ss.ff/ts which translates into: days:hours:minutes:seconds:frames/timescale So you should try a string like: QTTime incTime = QTTimeFromString( @"00:00:02:00.00/600" ); NSLog( QTStringFromTime( incTime ) ); which should work fine. The current documentation is incorrect. QTMovie object not fully-formed?Q: I'm able to successfully instantiate a Is there something else I must do to properly instantiate a A: If you query the To prevent this, you can make the initialization call synchronously. To do this, you need to use the NSSize movieSize; NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys: (id)url, QTMovieURLAttribute, [NSNumber numberWithBool:NO], QTMovieOpenAsyncOKAttribute, nil]; QTMovie *movie = [QTMovie movieWithAttributes:attrs error:nil]; movieSize = [[movie attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]; [movie play]; ... The other option is to install a notification handler for the // install a notification handler for QTMovieLoadStateDidChangeNotification QTMovie *movie = [QTMovie movieWithURL:url error:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loadStateChanged:) name:QTMovieLoadStateDidChangeNotification object:movie]; . . . // check for load state changes -(void)loadStateChanged:(QTMovie *)movie { long loadState = [[movie attributeForKey:QTMovieLoadStateAttribute] longValue]; if (loadState >= QTMovieLoadStatePlayable) { // the movie has loaded enough media data to begin playing [movie play]; } else if (loadState >= QTMovieLoadStateLoaded) { // the movie atom has loaded; it's safe to query movie properties NSSize movieSize; movieSize = [[movie attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]; /* ... */ } else if (loadState == -1) { /* error occurred; handle it */ } } Finding the last movie frameQ: How can I determine when I've arrived at the last frame of a movie? I tried using the A: The One alternative would be to define your own custom @implementation QTMovie (MyQTMovieExtension) - (BOOL)rt_stepForward { QTTime curTime, newTime; curTime = [self currentTime]; [self stepForward]; newTime = [self currentTime]; return (QTTimeCompare(curTime, newTime) != NSOrderedSame); } - (BOOL)rt_stepBackward { QTTime curTime, newTime; curTime = [self currentTime]; [self stepBackward]; newTime = [self currentTime]; return (QTTimeCompare(curTime, newTime) != NSOrderedSame); } @end Using QTMovie objects on background threads?Q: I just read the article TN2125: Thread-safe programming in QuickTime , but it doesn't say anything about QTKit. How do I safely use A: Observe the following guidelines when working with (1) allocate all The reason for this is quite simple: a (2) if you want to operate on a (3) call (4) call (5) be prepared to handle errors from any of those calls; some movies cannot be moved to secondary threads (this is codec specific). QTKit does not do any of this automatically for you, but we are working on some new APIs that will perhaps make this process easier (or at least more Cocoa-like). Here's a short code snippet showing how to perform a movie export on a background thread: - (void)doExportOnThread:(QTMovie *)movie { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; EnterMoviesOnThread(0); AttachMovieToCurrentThread([movie quickTimeMovie]); // To workaround a bug in QTKit we need to call the private // method setIdling: to make sure the movie is not tasked on // a background thread if ([movie respondsToSelector:@selector(setIdling:)]) [movie setIdling:NO]; // do export [movie writeToFile:....]; DetachMovieFromCurrentThread([movie quickTimeMovie]); ExitMoviesOnThread(); [pool release]; } Subclassing QTMovieViewQ: I've tried overriding the A: This is a known issue with QTMovie unexpectedly draws to upper left portion of screen?Q: If I create a movie with the Movie Toolbox C API A: You have run into an issue that may arise when mixing the QuickTime Carbon APIs and QTKit. When creating a movie using the native Carbon QuickTime APIs you must always have a valid port set beforehand so the movie knows where to draw. Here's a Q&A which describes this: QA1345: The QuickTime Movie Toolbox requires a valid graphics port for all movies. What's happening in your case is at the time you call To avoid this you can: 1) Use any of the available QTKit methods to open the movie file, such as 2) Create a "dummy" GWorld just before creating the new movie with -(void)QTKitMovieExample:(NSString *)inMoviePath { GWorldPtr gworld = NULL; Rect rect = {0, 0, 1, 1}; // create a "dummy" gworld for our movie QDErr qtErr = NewGWorld(&gworld, 32, &rect, NULL, NULL, 0); NSAssert( qtErr == 0, @"NewGWorld failed"); OSErr err = noErr; Handle dataRefH = nil; OSType dataRefType; // create a file data reference for our movie err = QTNewDataReferenceFromFullPathCFString((CFStringRef)inMoviePath, kQTNativeDefaultPathStyle, 0, &dataRefH, &dataRefType); NSAssert( err == noErr, @"QTNewDataReferenceFromFullPathCFString failed"); // create a QuickTime movie from our movie file data reference Movie nativeMovie = nil; DataHandler outDataHandlerRef; CreateMovieStorage (dataRefH, dataRefType, 'TVOD', smSystemScript, newMovieActive, &outDataHandlerRef, &nativeMovie); err = GetMoviesError(); NSAssert( err == noErr, @"CreateMovieStorage failed"); // set the gworld for the native QuickTime movie SetMovieGWorld(nativeMovie, gworld, NULL); // instantiate a QTKit QTMovie from our QuickTime movie QTMovie *qtMovie = [QTMovie movieWithQuickTimeMovie:nativeMovie disposeWhenDone:NO error:nil]; NSAssert( qtMovie != nil, @"movieWithQuickTimeMovie failed"); [qtMovie retain]; // ... do stuff with movie here if (dataRefH) { DisposeHandle(dataRefH); } if (qtMovie) { [qtMovie release]; } if (nativeMovie) { DisposeMovie(nativeMovie); } } Thereafter the movie will draw to the "dummy" gworld destination. Document Revision History
Posted: 2008-04-24 |
|