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.
QTMakeMovie.c
////////// |
// |
// File: QTMakeMovie.c |
// |
// Contains: QuickTime movie-making sample code. |
// |
// Written by: Tim Monroe |
// Parts based on the movie-making code in Inside Macintosh: QuickTime. |
// |
// Copyright: © 2000 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// |
// <1> 03/09/00 rtm first file |
// |
// This file contains functions that illustrate the standard way of building QuickTime movies. |
// The function QTMM_CreateVideoMovie elicits a destination file from the user and then builds |
// a 10-second movie that contains a single video track. The video track is created from a picture |
// read from the application's resource file; the picture is drawn with varying levels of opacity |
// (using QuickDraw's blend mode), starting with full transparency and progressing up to full |
// opacity. Each image thus generated is compressed using JPEG compression and added to the |
// movie's video track. The resulting movie shows the picture gradually appearing from an empty |
// rectangle. |
// |
////////// |
#include "QTMakeMovie.h" |
////////// |
// |
// QTMM_CreateVideoMovie |
// Create a movie with a video track. |
// |
////////// |
OSErr QTMM_CreateVideoMovie (void) |
{ |
Movie myMovie = NULL; |
Track myTrack = NULL; |
Media myMedia = NULL; |
FSSpec myFile; |
Boolean myIsSelected = false; |
Boolean myIsReplacing = false; |
StringPtr myPrompt = QTUtils_ConvertCToPascalString(kNewMoviePrompt); |
StringPtr myFileName = QTUtils_ConvertCToPascalString(kNewMovieFileName); |
long myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile; |
short myResRefNum = 0; |
short myResID = movieInDataForkResID; |
OSErr myErr = noErr; |
// prompt the user for new file name |
QTFrame_PutFile(myPrompt, myFileName, &myFile, &myIsSelected, &myIsReplacing); |
myErr = myIsSelected ? noErr : userCanceledErr; |
if (myErr != noErr) |
goto bail; |
// create a movie file for the destination movie |
myErr = CreateMovieFile(&myFile, sigMoviePlayer, smCurrentScript, myFlags, &myResRefNum, &myMovie); |
if (myErr != noErr) |
goto bail; |
// create the movie track and media |
myTrack = NewMovieTrack(myMovie, FixRatio(kVideoTrackWidth, 1), FixRatio(kVideoTrackHeight, 1), kNoVolume); |
myErr = GetMoviesError(); |
if (myErr != noErr) |
goto bail; |
myMedia = NewTrackMedia(myTrack, VideoMediaType, kVideoTimeScale, NULL, 0); |
myErr = GetMoviesError(); |
if (myErr != noErr) |
goto bail; |
// create the media samples |
myErr = BeginMediaEdits(myMedia); |
if (myErr != noErr) |
goto bail; |
myErr = QTMM_AddVideoSamplesToMedia(myMedia, kVideoTrackWidth, kVideoTrackHeight); |
if (myErr != noErr) |
goto bail; |
myErr = EndMediaEdits(myMedia); |
if (myErr != noErr) |
goto bail; |
// add the media to the track |
myErr = InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), fixed1); |
if (myErr != noErr) |
goto bail; |
// add the movie atom to the movie file |
myErr = AddMovieResource(myMovie, myResRefNum, &myResID, NULL); |
bail: |
if (myResRefNum != 0) |
CloseMovieFile(myResRefNum); |
if (myMovie != NULL) |
DisposeMovie(myMovie); |
free(myPrompt); |
free(myFileName); |
return(myErr); |
} |
////////// |
// |
// QTMM_AddVideoSamplesToMedia |
// Add video media samples to the specified media. |
// |
////////// |
OSErr QTMM_AddVideoSamplesToMedia (Media theMedia, short theTrackWidth, short theTrackHeight) |
{ |
GWorldPtr myGWorld = NULL; |
PixMapHandle myPixMap = NULL; |
CodecType myCodecType = kJPEGCodecType; |
long myNumSample; |
long myMaxComprSize = 0L; |
Handle myComprDataHdl = NULL; |
Ptr myComprDataPtr = NULL; |
ImageDescriptionHandle myImageDesc = NULL; |
CGrafPtr mySavedPort = NULL; |
GDHandle mySavedDevice = NULL; |
Rect myRect; |
OSErr myErr = noErr; |
MacSetRect(&myRect, 0, 0, theTrackWidth, theTrackHeight); |
myErr = NewGWorld(&myGWorld, kPixelDepth, &myRect, NULL, NULL, (GWorldFlags)0); |
if (myErr != noErr) |
goto bail; |
myPixMap = GetGWorldPixMap(myGWorld); |
if (myPixMap == NULL) |
goto bail; |
LockPixels(myPixMap); |
myErr = GetMaxCompressionSize( myPixMap, |
&myRect, |
0, // let ICM choose depth |
codecNormalQuality, |
myCodecType, |
(CompressorComponent)anyCodec, |
&myMaxComprSize); |
if (myErr != noErr) |
goto bail; |
myComprDataHdl = NewHandle(myMaxComprSize); |
if (myComprDataHdl == NULL) |
goto bail; |
HLockHi(myComprDataHdl); |
#if TARGET_CPU_68K |
myComprDataPtr = StripAddress(*myComprDataHdl); |
#else |
myComprDataPtr = *myComprDataHdl; |
#endif |
myImageDesc = (ImageDescriptionHandle)NewHandle(4); |
if (myImageDesc == NULL) |
goto bail; |
GetGWorld(&mySavedPort, &mySavedDevice); |
SetGWorld(myGWorld, NULL); |
for (myNumSample = 1; myNumSample <= kNumVideoFrames; myNumSample++) { |
EraseRect(&myRect); |
QTMM_DrawFrame(theTrackWidth, theTrackHeight, myNumSample, myGWorld); |
myErr = CompressImage( myPixMap, |
&myRect, |
codecNormalQuality, |
myCodecType, |
myImageDesc, |
myComprDataPtr); |
if (myErr != noErr) |
goto bail; |
myErr = AddMediaSample( theMedia, |
myComprDataHdl, |
0, // no offset in data |
(**myImageDesc).dataSize, |
kVideoFrameDuration, // frame duration |
(SampleDescriptionHandle)myImageDesc, |
1, // one sample |
0, // self-contained samples |
NULL); |
if (myErr != noErr) |
goto bail; |
} |
bail: |
SetGWorld(mySavedPort, mySavedDevice); |
if (myImageDesc != NULL) |
DisposeHandle((Handle)myImageDesc); |
if (myComprDataHdl != NULL) |
DisposeHandle(myComprDataHdl); |
if (myGWorld != NULL) |
DisposeGWorld(myGWorld); |
return(myErr); |
} |
////////// |
// |
// QTMM_DrawFrame |
// Draw a frame of video. |
// |
////////// |
static void QTMM_DrawFrame (short theTrackWidth, short theTrackHeight, long theNumSample, GWorldPtr theGWorld) |
{ |
Handle myHandle = NULL; |
char myData[kPICTFileHeaderSize]; |
static PicHandle myPicture = NULL; |
static GWorldPtr myGWorld = NULL; |
static GraphicsImportComponent myImporter = NULL; |
Rect myRect; |
RGBColor myColor; |
ComponentResult myErr = noErr; |
MacSetRect(&myRect, 0, 0, theTrackWidth, theTrackHeight); |
if (myPicture == NULL) { |
myErr = NewGWorld(&myGWorld, kPixelDepth, &myRect, NULL, NULL, (GWorldFlags)0); |
if (myErr != noErr) |
goto bail; |
// read a picture from our resource file |
myPicture = GetPicture(kPictureID); |
if (myPicture == NULL) |
goto bail; |
// use Munger to prepend a 512-byte header onto the picture data; this converts the PICT |
// resource data into in-memory PICT file data (see Ice Floe 14 for an explanation of this) |
myHandle = (Handle)myPicture; |
Munger(myHandle, 0, NULL, 0, myData, kPICTFileHeaderSize); |
// get a graphics importer for the picture |
myErr = OpenADefaultComponent(GraphicsImporterComponentType, kQTFileTypePicture, &myImporter); |
if (myErr != noErr) |
goto bail; |
// configure the graphics importer |
myErr = GraphicsImportSetGWorld(myImporter, myGWorld, NULL); |
if (myErr != noErr) |
goto bail; |
myErr = GraphicsImportSetDataHandle(myImporter, myHandle); |
if (myErr != noErr) |
goto bail; |
myErr = GraphicsImportSetBoundsRect(myImporter, &myRect); |
if (myErr != noErr) |
goto bail; |
// draw the picture into the source GWorld |
myErr = GraphicsImportDraw(myImporter); |
if (myErr != noErr) |
goto bail; |
} |
// set the blend amount (0 = fully transparent; 0xffff = fully opaque) |
myColor.red = (theNumSample - 1) * (0xffff / kNumVideoFrames - 1); |
myColor.green = (theNumSample - 1) * (0xffff / kNumVideoFrames - 1); |
myColor.blue = (theNumSample - 1) * (0xffff / kNumVideoFrames - 1); |
OpColor(&myColor); |
// blend the picture (in the source GWorld) into the empty rectangle (in the destination GWorld) |
CopyBits((BitMapPtr)*GetGWorldPixMap(myGWorld), |
(BitMapPtr)*GetGWorldPixMap(theGWorld), |
&myRect, |
&myRect, |
blend, |
NULL); |
if (theNumSample == kNumVideoFrames) |
goto bail; |
return; |
bail: |
if (myHandle != NULL) |
DisposeHandle(myHandle); |
if (myPicture != NULL) |
ReleaseResource((Handle)myPicture); |
if (myImporter != NULL) |
CloseComponent(myImporter); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14