Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
QTInfo.c
////////// |
// |
// File: QTInfo.c |
// |
// Contains: Sample code for working with QuickTime movie information. |
// |
// Written by: Tim Monroe |
// |
// Copyright: © 2000 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// |
// <1> 04/23/00 rtm first file |
// |
////////// |
////////// |
// |
// header files |
// |
////////// |
#include "QTInfo.h" |
////////// |
// |
// global variables |
// |
////////// |
extern short gAppResFile; // file reference number for this application's resource file |
extern ModalFilterUPP gModalFilterUPP; // UPP to our custom dialog event filter |
////////// |
// |
// QTInfo_MovieHasPreview |
// Does the specified movie have a movie preview? |
// |
////////// |
Boolean QTInfo_MovieHasPreview (Movie theMovie) |
{ |
TimeValue myStart; |
TimeValue myDuration; |
long myCount = 0L; |
long myIndex = 0L; |
Track myTrack = NULL; |
long myUsage = 0L; |
Boolean myHasPreview = false; |
// see if the movie has a positive preview duration |
GetMoviePreviewTime(theMovie, &myStart, &myDuration); |
if (myDuration > 0) |
myHasPreview = true; |
// make sure that some track is used in the movie preview |
myCount = GetMovieTrackCount(theMovie); |
for (myIndex = 1; myIndex <= myCount; myIndex++) { |
myTrack = GetMovieIndTrack(theMovie, myIndex); |
if (myTrack == NULL) |
continue; |
myUsage = GetTrackUsage(myTrack); |
if (myUsage & trackUsageInPreview) |
break; // we found a track with the trackUsageInPreview flag set; break out of the loop |
} |
if (myIndex > myCount) |
myHasPreview = false; // we went thru all tracks without finding one with a preview usage |
return(myHasPreview); |
} |
////////// |
// |
// QTInfo_MovieHasPoster |
// Does the specified movie have a movie poster? |
// |
////////// |
Boolean QTInfo_MovieHasPoster (Movie theMovie) |
{ |
long myCount = 0L; |
long myIndex = 0L; |
Track myTrack = NULL; |
long myUsage = 0L; |
Boolean myHasPoster = true; |
// make sure that some track is used in the movie poster |
myCount = GetMovieTrackCount(theMovie); |
for (myIndex = 1; myIndex <= myCount; myIndex++) { |
myTrack = GetMovieIndTrack(theMovie, myIndex); |
if (myTrack == NULL) |
continue; |
myUsage = GetTrackUsage(myTrack); |
if (myUsage & trackUsageInPoster) |
break; // we found a track with the trackUsageInPoster flag set; break out of the loop |
} |
if (myIndex > myCount) |
myHasPoster = false; // we went thru all tracks without finding one with a poster usage |
return(myHasPoster); |
} |
////////// |
// |
// QTInfo_SetPreviewToSelection |
// Set the movie preview to be the current movie selection. |
// |
////////// |
OSErr QTInfo_SetPreviewToSelection (Movie theMovie, MovieController theMC) |
{ |
TimeValue myStart; |
TimeValue myDuration; |
ComponentResult myErr = noErr; |
GetMovieSelection(theMovie, &myStart, &myDuration); |
SetMoviePreviewTime(theMovie, myStart, myDuration); |
myErr = MCMovieChanged(theMC, theMovie); |
return((OSErr)myErr); |
} |
////////// |
// |
// QTInfo_SetSelectionToPreview |
// Set the movie selection to the current movie preview. |
// |
////////// |
OSErr QTInfo_SetSelectionToPreview (Movie theMovie, MovieController theMC) |
{ |
TimeValue myStart; |
TimeValue myDuration; |
ComponentResult myErr = noErr; |
GetMoviePreviewTime(theMovie, &myStart, &myDuration); |
SetMovieSelection(theMovie, myStart, myDuration); |
myErr = MCMovieChanged(theMC, theMovie); |
return((OSErr)myErr); |
} |
////////// |
// |
// QTInfo_ClearPreview |
// Clear the movie preview. |
// |
////////// |
OSErr QTInfo_ClearPreview (Movie theMovie, MovieController theMC) |
{ |
long myCount = 0L; |
long myIndex = 0L; |
Track myTrack = NULL; |
long myUsage = 0L; |
ComponentResult myErr = noErr; |
// set the movie preview start time and duration to 0 |
SetMoviePreviewTime(theMovie, 0, 0); |
// remove all tracks that are used *only* in the movie preview |
myCount = GetMovieTrackCount(theMovie); |
for (myIndex = myCount; myIndex >= 1; myIndex--) { |
myTrack = GetMovieIndTrack(theMovie, myIndex); |
if (myTrack == NULL) |
continue; |
myUsage = GetTrackUsage(myTrack); |
myUsage &= trackUsageInMovie | trackUsageInPreview | trackUsageInPoster; |
if (myUsage == trackUsageInPreview) |
DisposeMovieTrack(myTrack); |
} |
// add trackUsageInPreview to any remaining tracks that are in the movie |
// (so that subsequently setting the preview to a selection will include |
// these tracks) |
myCount = GetMovieTrackCount(theMovie); |
for (myIndex = 1; myIndex <= myCount; myIndex++) { |
myTrack = GetMovieIndTrack(theMovie, myIndex); |
if (myTrack == NULL) |
continue; |
myUsage = GetTrackUsage(myTrack); |
if (myUsage & trackUsageInMovie) |
SetTrackUsage(myTrack, myUsage | trackUsageInPreview); |
} |
myErr = MCMovieChanged(theMC, theMovie); |
return((OSErr)myErr); |
} |
////////// |
// |
// QTInfo_GoToPosterFrame |
// Set the current movie time to the movie poster time. |
// |
////////// |
OSErr QTInfo_GoToPosterFrame (Movie theMovie, MovieController theMC) |
{ |
TimeRecord myTimeRecord; |
ComponentResult myErr = noErr; |
// stop the movie from playing |
myErr = MCDoAction(theMC, mcActionPlay, (void *)0L); |
if (myErr != noErr) |
goto bail; |
// set up a time record with the desired movie time, scale, and base |
myTimeRecord.value.hi = 0; |
myTimeRecord.value.lo = GetMoviePosterTime(theMovie); |
myTimeRecord.base = GetMovieTimeBase(theMovie); |
myTimeRecord.scale = GetMovieTimeScale(theMovie); |
myErr = MCDoAction(theMC, mcActionGoToTime, &myTimeRecord); |
bail: |
return((OSErr)myErr); |
} |
////////// |
// |
// QTInfo_SetPosterToFrame |
// Set the movie poster time to the current movie time. |
// |
////////// |
OSErr QTInfo_SetPosterToFrame (Movie theMovie, MovieController theMC) |
{ |
TimeValue myTime; |
ComponentResult myErr = noErr; |
// stop the movie from playing |
myErr = MCDoAction(theMC, mcActionPlay, (void *)0L); |
if (myErr != noErr) |
goto bail; |
myTime = GetMovieTime(theMovie, NULL); |
SetMoviePosterTime(theMovie, myTime); |
myErr = MCMovieChanged(theMC, theMovie); |
bail: |
return((OSErr)myErr); |
} |
////////// |
// |
// QTInfo_MakeFilePreview |
// Add a file preview to the specified file. |
// |
// If theRefNum picks out a resource fork, we simply call the ICM function MakeFilePreview. |
// If theRefNum picks out a data fork, we need to add a 'pnot' atom to that file; we do this |
// by appending the appropriate atom data to the end of the file. This strategy will fail if |
// the atom that was previously the last one in the file does not have a correct length value |
// in its atom header. |
// |
// For poster previews, we also need to add an atom of type 'PICT' to the end of the file. |
// |
// NOTE: The current version of this function does not replace any existing 'pnot' atom in the |
// data fork. A better algorithm would be: determine which type of file preview is being created, |
// and count any existing preview data atoms of that type; then look for an existing 'pnot' atom; |
// if one is found, update it to use the (possibly new) file preview type and set the index to |
// one more than the number of existing preview data atoms of that type; than add the new preview |
// data atom. These and other enhancements are left as an exercise for the reader. |
// |
////////// |
OSErr QTInfo_MakeFilePreview (Movie theMovie, short theRefNum, ICMProgressProcRecordPtr theProgressProc) |
{ |
unsigned long myModDate; |
PreviewResourceRecord myPNOTRecord; |
long myEOF; |
long mySize; |
unsigned long myAtomHeader[2]; // an atom header: size and type |
OSType myPreviewType; |
OSErr myErr = noErr; |
////////// |
// |
// determine whether theRefNum is a file reference number of a data fork or a resource fork; |
// if it's a resource fork, then we'll just call the existing ICM function MakeFilePreview |
// |
////////// |
if (QTInfo_IsRefNumOfResourceFork(theRefNum)) { |
myErr = MakeFilePreview(theRefNum, theProgressProc); |
goto bail; |
} |
////////// |
// |
// determine the preview type |
// |
////////// |
// if the movie has a movie preview, use that as the file preview; otherwise use a thumbnail |
// of the movie poster frame as the file preview |
if (QTInfo_MovieHasPreview(theMovie)) |
myPreviewType = MovieAID; |
else |
myPreviewType = kQTFileTypePicture; |
////////// |
// |
// construct the 'pnot' atom |
// |
////////// |
// fill in the 'pnot' atom header |
myAtomHeader[0] = EndianU32_NtoB(sizeof(myAtomHeader) + sizeof(myPNOTRecord)); |
myAtomHeader[1] = EndianU32_NtoB(ShowFilePreviewComponentType); |
// fill in the 'pnot' atom data |
GetDateTime(&myModDate); |
myPNOTRecord.modDate = EndianU32_NtoB(myModDate); // the modification time of the preview |
myPNOTRecord.version = EndianS16_NtoB(0); // version number; must be 0 |
myPNOTRecord.resType = EndianU32_NtoB(myPreviewType); // atom type containing preview |
myPNOTRecord.resID = EndianS16_NtoB(1); // the 1-based index of the atom of the specified type to use |
////////// |
// |
// write the 'pnot' atom at the end of the data fork |
// |
////////// |
// get the current logical end-of-file and extend it by the desired amount |
myErr = GetEOF(theRefNum, &myEOF); |
if (myErr != noErr) |
goto bail; |
myErr = SetEOF(theRefNum, myEOF + sizeof(myAtomHeader) + sizeof(myPNOTRecord)); |
if (myErr != noErr) |
goto bail; |
// set the file mark |
myErr = SetFPos(theRefNum, fsFromStart, myEOF); |
if (myErr != noErr) |
goto bail; |
// write the atom header into the file |
mySize = sizeof(myAtomHeader); |
myErr = FSWrite(theRefNum, &mySize, myAtomHeader); |
if (myErr != noErr) |
goto bail; |
// write the atom data into the file |
mySize = sizeof(myPNOTRecord); |
myErr = FSWrite(theRefNum, &mySize, &myPNOTRecord); |
if (myErr != noErr) |
goto bail; |
////////// |
// |
// write the preview data atom at the end of the data fork |
// |
////////// |
if (myPreviewType == MovieAID) { |
// in theory, we don't need to do anything here, since our 'pnot' atom points |
// to the 'moov' atom; in practice, this doesn't work correctly (it seems like |
// a bug in StandardGetFilePreview) |
} |
if (myPreviewType == kQTFileTypePicture) { |
PicHandle myPicture = NULL; |
PicHandle myThumbnail = NULL; |
// get the poster frame picture |
myPicture = GetMoviePosterPict(theMovie); |
if (myPicture != NULL) { |
// create a thumbnail |
myThumbnail = (PicHandle)NewHandleClear(4); |
if (myThumbnail != NULL) { |
myErr = MakeThumbnailFromPicture(myPicture, 0, myThumbnail, theProgressProc); |
if (myErr == noErr) { |
myAtomHeader[0] = EndianU32_NtoB(sizeof(myAtomHeader) + GetHandleSize((Handle)myThumbnail)); |
myAtomHeader[1] = EndianU32_NtoB(myPreviewType); |
// write the atom header into the file |
mySize = sizeof(myAtomHeader); |
myErr = FSWrite(theRefNum, &mySize, myAtomHeader); |
if (myErr == noErr) { |
// write the atom data into the file |
mySize = GetHandleSize((Handle)myThumbnail); |
myErr = FSWrite(theRefNum, &mySize, *myThumbnail); |
} |
} |
KillPicture(myThumbnail); |
} |
KillPicture(myPicture); |
} |
} |
#if 0 |
// here's how you'd add a text preview; note that the text is hard-coded here.... |
if (myPreviewType == kQTFileTypeText) { |
char myText[] = "The penguin gradually appears from the mist of the ice floe."; |
myAtomHeader[0] = EndianU32_NtoB(sizeof(myAtomHeader) + strlen(myText)); |
myAtomHeader[1] = EndianU32_NtoB(myPreviewType); |
// write the atom header into the file |
mySize = sizeof(myAtomHeader); |
myErr = FSWrite(theRefNum, &mySize, myAtomHeader); |
if (myErr != noErr) |
goto bail; |
// write the atom data into the file |
mySize = strlen(myText); |
myErr = FSWrite(theRefNum, &mySize, myText); |
if (myErr != noErr) |
goto bail; |
} |
#endif |
#if TARGET_OS_MAC |
if (myErr == noErr) { |
short myVolNum; |
// flush the volume |
myErr = GetVRefNum(theRefNum, &myVolNum); |
if (myErr != noErr) |
goto bail; |
myErr = FlushVol(NULL, myVolNum); |
} |
#endif |
bail: |
return(myErr); |
} |
////////// |
// |
// QTInfo_IsRefNumOfResourceFork |
// Does the specified file reference number refer to an open resource fork? |
// |
////////// |
Boolean QTInfo_IsRefNumOfResourceFork (short theRefNum) |
{ |
FCBPBRec myFCBPBRec; |
Boolean myIsResFork = false; |
OSErr myErr = noErr; |
myFCBPBRec.ioCompletion = NULL; |
myFCBPBRec.ioNamePtr = NULL; |
myFCBPBRec.ioVRefNum = 0; |
myFCBPBRec.ioRefNum = theRefNum; |
myFCBPBRec.ioFCBIndx = 0; |
myErr = PBGetFCBInfoSync(&myFCBPBRec); |
if (myErr == noErr) |
if (myFCBPBRec.ioFCBFlags & kioFCBResourceMask) |
myIsResFork = true; |
return(myIsResFork); |
} |
////////// |
// |
// QTInfo_EditAnnotation |
// Allow the user to enter or edit the annotation of the specified sort. Return a Boolean value |
// that indicates whether the user clicked the OK button in the displayed dialog box and the text was |
// successfully added to the movie (it may of course have been the same text already in the movie). |
// |
////////// |
Boolean QTInfo_EditAnnotation (Movie theMovie, OSType theType) |
{ |
DialogPtr myDialog = NULL; |
short myItem; |
short mySavedResFile; |
GrafPtr mySavedPort; |
Handle myHandle = NULL; |
short myItemKind; |
Handle myItemHandle; |
UserData myUserData = NULL; |
Rect myItemRect; |
Str255 myString; |
Boolean myIsChanged = false; |
OSErr myErr = noErr; |
////////// |
// |
// save the current resource file and graphics port |
// |
////////// |
mySavedResFile = CurResFile(); |
GetPort(&mySavedPort); |
// set the application's resource file |
UseResFile(gAppResFile); |
// get the movie user data |
myUserData = GetMovieUserData(theMovie); |
if (myUserData == NULL) |
goto bail; |
////////// |
// |
// create the dialog box in which the user will add or edit the annotation |
// |
////////// |
myDialog = GetNewDialog(kEditTextResourceID, NULL, (WindowPtr)-1L); |
if (myDialog == NULL) |
goto bail; |
MacSetPort(GetDialogPort(myDialog)); |
SetDialogDefaultItem(myDialog, kEditTextItemOK); |
SetDialogCancelItem(myDialog, kEditTextItemCancel); |
// get a string for the specified annotation type |
switch (theType) { |
case kUserDataTextFullName: |
GetIndString(myString, kTextKindsResourceID, kTextKindsFullName); |
break; |
case kUserDataTextCopyright: |
GetIndString(myString, kTextKindsResourceID, kTextKindsCopyright); |
break; |
case kUserDataTextInformation: |
GetIndString(myString, kTextKindsResourceID, kTextKindsInformation); |
break; |
} |
GetDialogItem(myDialog, kEditTextItemEditLabel, &myItemKind, &myItemHandle, &myItemRect); |
SetDialogItemText(myItemHandle, myString); |
////////// |
// |
// set the current annotation of the specified type, if it exists |
// |
////////// |
myHandle = NewHandleClear(4); |
if (myHandle != NULL) { |
myErr = GetUserDataText(myUserData, myHandle, theType, 1, GetScriptManagerVariable(smRegionCode)); |
if (myErr == noErr) { |
QTInfo_TextHandleToPString(myHandle, myString); |
GetDialogItem(myDialog, kEditTextItemEditBox, &myItemKind, &myItemHandle, &myItemRect); |
SetDialogItemText(myItemHandle, myString); |
SelectDialogItemText(myDialog, kEditTextItemEditBox, 0, myString[0]); |
} |
DisposeHandle(myHandle); |
} |
MacShowWindow(GetDialogWindow(myDialog)); |
////////// |
// |
// display and handle events in the dialog box until the user clicks OK or Cancel |
// |
////////// |
do { |
ModalDialog(gModalFilterUPP, &myItem); |
} while ((myItem != kEditTextItemOK) && (myItem != kEditTextItemCancel)); |
////////// |
// |
// handle the selected button |
// |
////////// |
if (myItem != kEditTextItemOK) |
goto bail; |
// retrieve the edited text |
myHandle = NewHandleClear(4); |
if (myHandle != NULL) { |
GetDialogItem(myDialog, kEditTextItemEditBox, &myItemKind, &myItemHandle, &myItemRect); |
GetDialogItemText(myItemHandle, myString); |
QTInfo_PStringToTextHandle(myString, myHandle); |
myErr = AddUserDataText(myUserData, myHandle, theType, 1, GetScriptManagerVariable(smRegionCode)); |
myIsChanged = (myErr == noErr); |
DisposeHandle(myHandle); |
} |
bail: |
// restore the previous resource file and graphics port |
MacSetPort(mySavedPort); |
UseResFile(mySavedResFile); |
if (myDialog != NULL) |
DisposeDialog(myDialog); |
return(myIsChanged); |
} |
////////// |
// |
// QTInfo_TextHandleToPString |
// Copy the characters in the specified handle into the specified Pascal string. |
// |
////////// |
void QTInfo_TextHandleToPString (Handle theHandle, Str255 theString) |
{ |
short myCount; |
myCount = GetHandleSize(theHandle); |
if (myCount > 255) |
myCount = 255; |
theString[0] = myCount; |
BlockMoveData(*theHandle, &(theString[1]), myCount); |
} |
////////// |
// |
// QTInfo_PStringToTextHandle |
// Copy the characters in the specified Pascal string into the specified handle. |
// |
////////// |
void QTInfo_PStringToTextHandle (Str255 theString, Handle theHandle) |
{ |
SetHandleSize(theHandle, theString[0]); |
if (GetHandleSize(theHandle) != theString[0]) |
return; |
BlockMoveData(&(theString[1]), *theHandle, theString[0]); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14