/***************************************************************************** CreateMovie -- Create a Quicktime Movie This command line program creates a valid Quicktime movie file and populates it with some generated visual data. Define the Movie: Create empty movie file and in-memory movie structures Step 1 - movie Step 2 - track Step 3 - track media Populate the Movie: Step 4 - Add image data to movie file Close the Movie: Step 5 - Update in-memory movie structures Step 6 - Append in-memory movie structures to end of file ('moov' atom) Step 7 - Move 'moov' atom to front of file and interleave samples (optional) Step 8 - Close movie file and discard in-memory movie structures Environment: OS X version 10.1.5 Creation: open Project Builder click file menu -- New Project click "Standard Tool" at the end of the list name=CreateMovie, directory -- pick one ^click on CreateMovie -- new Group give it the name "Frameworks" ^click on Frameworks -- choose add frameworks choose QuickTime.framework -- reference style = default ^click on Frameworks -- choose add frameworks choose Carbon.framework -- reference style = default ^click on main.c under the "Source" group choose "Show Info" change Reference Style from "Group Relative" to "Project Relative" Warnings: This example issues 3 warnings that indicate that it could not use Carbon.framework's precompiled header because 2 header files have different dates. These warnings can be ignored. Notes for Simplification: Check out the Carbon documentation on RGBBackColor to see how to change the fill background color. *****************************************************************************/ /* Include the Quicktime header. ============================= */ #include /* Define the function prototypes. =============================== */ OSErr add_video_samples_to_media (Media media, short sTrackWidth, short sTrackHeight, long lNumImages); void flatten_my_movie (Movie movie, char *pszFlatFilename); /* M A I N * */ int main (int argc, char *argv[]) { OSErr osErr; short sResId; short sResRefNum = 0; FSSpec fsSpec; Movie movie = nil; Track track = NULL; Media media = NULL; char *pszFilename = "../my.mov"; char *pszFlatFilename = "../myflat.mov"; EnterMovies (); /* Initialize Quicktime */ /* Load the FSSpec structure to describe the receiving file. For a description of this and related calls see http://developer.apple.com/quicktime/icefloe/dispatch004.html. ================================================================ */ osErr = NativePathNameToFSSpec (pszFilename, &fsSpec, 0); if (osErr && (osErr != fnfErr)) /* File-not-found error is ok */ { printf ("NativePathNameToFSSpec failed %d\n", osErr); goto bail; } /* Step 1: Create a new, empty movie file and a movie that references that file (CreateMovieFile). ======================================================================== */ osErr = CreateMovieFile ( &fsSpec, /* FSSpec specifier */ FOUR_CHAR_CODE('TVOD'), /* file creator type, TVOD = QT player*/ smCurrentScript, /* movie script system to use */ createMovieFileDeleteCurFile /* movie file creation flags */ | createMovieFileDontCreateResFile, &sResRefNum, /* returned file ref num to data fork */ &movie /* returned handle to open empty movie*/ /* that references the created file */ ); if (osErr) { printf ("CreateMovieFile failed %d\n", osErr); goto bail; } /* Step 2: Add a new track to that movie (NewMovieTrack). ======================================================= */ track = NewMovieTrack ( movie, /* the movie to add track to */ ((long) 160 << 16), /* width of track in pixels (Fixed) */ FixRatio (120, 1), /* height of track in pixels (Fixed) */ kNoVolume /* default volume level */ ); osErr = GetMoviesError (); if (osErr) { printf ("NewMovieTrack failed %d\n", osErr); goto bail; } /* Step 3: Add a new media to that track (NewTrackMedia). ======================================================= */ media = NewTrackMedia ( track, /* the track to add the media to */ VideoMediaType, /* media type, e.g. SoundMediaType */ 600, /* num media time units that elapse/sec*/ NULL, /* ptr to file that holds media sampls*/ 0 /* type of ptr to media samples */ ); osErr = GetMoviesError (); if (osErr) { printf ("NewTrackMedia failed %d\n", osErr); goto bail; } /* Step 4: Add media samples to the media. ======================================== */ BeginMediaEdits (media); /* Inform the Movie Toolbox that we */ /* want to change the media samples */ /* referenced by a track's media. */ /* This opens the media container */ /* and makes it ready to receive */ /* and/or remove sample data. */ add_video_samples_to_media (media, 160, 120, 16); EndMediaEdits (media); /* Inform the Movie Toolbox that they */ /* can close the media container. */ /* Step 5: Insert a reference into the track that specifies which of the media samples to play and when to start playing them. ====================================================================== */ InsertMediaIntoTrack ( track, /* the track to update. */ 0, /* time in track where the specified */ /* media samples should start playg */ /* using movie time scale. */ 0, /* time in media samples of the first */ /* sample to play using media time */ /* scale. */ GetMediaDuration (media), /* duration of media samples to play */ /* using media time scale. */ fixed1 /* rate at which to play the samples. */ ); /* Step 6: Append the movie atom to the movie file (AddMovieResource). ==================================================================== */ sResId = movieInDataForkResID; osErr = AddMovieResource ( movie, /* movie to create moov atom from */ sResRefNum, /* file to receive the moov atom */ &sResId, /* id num of movie resource (res fork)*/ pszFilename /* name of movie resource (res fork) */ ); if (osErr) { printf ("AddMovieResource failed %d\n", osErr); goto bail; } if (sResRefNum != 0) { CloseMovieFile (sResRefNum); /* close file CreateMovieFile opened */ sResRefNum = 0; } /* Step 7 (optional): Place the movie atom as the first atom in a new movie file, and interleave the media data (FlattenMovieData). =================================================================== */ flatten_my_movie (movie, pszFlatFilename); /* Step 8: Close the movie file that CreateMovieFile opened (if necessary) and dispose of the movie memory structures (DisposeMovie). ======================================================================== */ bail: if (sResRefNum != 0) CloseMovieFile (sResRefNum); if (movie != NULL) DisposeMovie (movie); ExitMovies (); /* Finalize Quicktime */ return (0); } /* A D D _ V I D E O _ S A M P L E S _ T O _ M E D I A * * * */ OSErr add_video_samples_to_media (Media media, short sTrackWidth, short sTrackHeight, long lNumImages) { OSErr osErr; Rect rect; RGBColor rgbColor; long lImageNumber; CodecType codecType; GWorldPtr pMovieGWorld = NULL; PixMapHandle pixMapHandle = NULL; Ptr pCompressedData = NULL; CGrafPtr pSavedPort = NULL; GDHandle hSavedDevice = NULL; Handle hCompressedData = NULL; long lMaxCompressionSize = 0L; ImageDescriptionHandle hImageDescription = NULL; /* Create a new offscreen graphics world that will hold the movie's drawing surface. draw_image() copies the image of IceFlow to this surface with varying amounts of transparency. ================================================================= */ MacSetRect (&rect, 0, 0, sTrackWidth, sTrackHeight); osErr = NewGWorld ( &pMovieGWorld, /* receives the new GWorld. */ 24, /* pixel depth in bits/pixel */ &rect, /* desired size of the GWorld. */ NULL, NULL, (GWorldFlags) 0 ); if (osErr != noErr) { printf ("NewGWorld 1 failed %d\n", osErr); goto bail; } /* Retrieve the pixel map associated with that graphics world and lock the pixel map in memory. GetMaxCompressionSize() and CompressImage() only operate on pixel maps, not graphics worlds. ===================================================================== */ pixMapHandle = GetGWorldPixMap (pMovieGWorld); if (pixMapHandle == NULL) { printf ("GetGWorldPixMap failed\n"); goto bail; } LockPixels (pixMapHandle); /* Get the maximum number of bytes required to hold an image having the specified characteristics compressed using the specified compressor. ==================================================================== */ codecType = kJPEGCodecType; osErr = GetMaxCompressionSize ( pixMapHandle, /* the pixel map to compress from. */ &rect, /* the image rectangle. */ 0, /* let ICM choose image bit depth. */ codecNormalQuality, /* compression quality specifier. */ codecType, /* desired compression type */ (CompressorComponent) anyCodec, /* codec specifier. */ &lMaxCompressionSize /* receives max bytes needed for cmp. */ ); if (osErr != noErr) { printf ("GetMaxCompressionSize failed %d\n", osErr); goto bail; } /* Allocate a buffer to hold the compressed image data by creating a new handle. ===================================================================== */ hCompressedData = NewHandle (lMaxCompressionSize); if (hCompressedData == NULL) { printf ("NewHandle(%ld) failed\n", lMaxCompressionSize); goto bail; } /* Lock the handle and then dereference it to obtain a pointer to the data buffer because CompressImage() wants us to pass it a pointer, not a handle. ======================================================================= */ HLockHi (hCompressedData); pCompressedData = *hCompressedData; /* Create an image description object in memory of minimum size to pass to CompressImage(). CompressImage() will resize the memory as necessary so create it small here. ==================================================================== */ hImageDescription = (ImageDescriptionHandle) NewHandle (4); if (hImageDescription == NULL) { printf ("NewHandle(4) failed\n"); goto bail; } /* Save the current GWorld and set the offscreen GWorld as current. ================================================================ */ GetGWorld (&pSavedPort, &hSavedDevice); SetGWorld (pMovieGWorld, NULL); /* Each cycle of this loop draws one image into the GWorld, compresses it, then adds it to the movie. =================================================================== */ for (lImageNumber = 1; lImageNumber <= lNumImages; lImageNumber++) { /* Clear the rectangle using a calculated color. ============================================= */ rgbColor.red = (0xffff / lNumImages - 1) * (lImageNumber - 1); rgbColor.green = (0xffff / lNumImages - 1) * (lImageNumber - 1); rgbColor.blue = (0xffff / lNumImages - 1) * (lImageNumber - 1); RGBBackColor (&rgbColor); EraseRect (&rect); /* Compress the pixel map that has just been drawn on. Also resize and fill in the image description. Resulting image size can be discovered by consulting the image description field dataSize. ================================================================ */ osErr = CompressImage ( pixMapHandle, /* the pixel map of the offscreen img */ &rect, /* portion of the image to compress */ codecNormalQuality, /* same compression quality as above */ codecType, /* same codec specifier as above */ hImageDescription, /* the created image description. */ pCompressedData /* ptr to bufr that receives cmp image*/ ); if (osErr != noErr) { printf ("CompressImage failed %d\n", osErr); goto bail; } /* Add the compressed image to the movie. ====================================== */ osErr = AddMediaSample ( media, /* the media to add the image to. */ hCompressedData, /* the compressed image to add. */ 0, /* byte offs into data to begin readg */ (**hImageDescription).dataSize,/* num bytes to be copied into media. */ 600 / 10, /* duration of the frame (media time) */ (SampleDescriptionHandle) hImageDescription, /* image desc cast to */ /* a sample description since both */ /* both structures start with same */ /* fields. */ 1, /* num samples in the data buffer. */ 0, /* default flags */ NULL /* ptr to receive media time in which */ /* the image was added. */ ); if (osErr != noErr) { printf ("AddMediaSample failed %d\n", osErr); goto bail; } } bail: SetGWorld (pSavedPort, hSavedDevice); if (hImageDescription != NULL) DisposeHandle ((Handle) hImageDescription); if (hCompressedData != NULL) DisposeHandle (hCompressedData); if (pMovieGWorld != NULL) DisposeGWorld (pMovieGWorld); return (osErr); } /* F L A T T E N _ M Y _ M O V I E * * Move the 'moov' atom to the front of the movie file and interleave the * media data (if it isn't already). This makes the movie a "fast start" * movie which will cause the QT player to start playing the movie before * all the data has arrived. * */ void flatten_my_movie (Movie movie, char *pszFlatFilename) { OSErr osErr; FSSpec fsSpec; Movie flatMovie; /* Define the file that will receive the flattened movie. ====================================================== */ osErr = NativePathNameToFSSpec (pszFlatFilename, &fsSpec, 0); if (osErr && (osErr != fnfErr)) { printf ("NativePathNameToFSSpec 2 failed %d\n", osErr); goto bail; } /* Flatten the movie: puts the 'moov' atom at the front of the file, compresses the entire atom for faster network loading, and interleaves the video and audio data so both tracks are available at the same time. ======================================================================= */ flatMovie = FlattenMovieData ( movie, flattenAddMovieToDataFork | flattenForceMovieResourceBeforeMovieData /* fast start */ | flattenCompressMovieResource,/* faster start... */ /* performs zlib compression on 'moov'*/ &fsSpec, FOUR_CHAR_CODE('TVOD'), smSystemScript, createMovieFileDeleteCurFile | createMovieFileDontCreateResFile ); osErr = GetMoviesError (); if (osErr) { printf ("FlattenMovieData failed %d\n", osErr); goto bail; } bail: if (flatMovie != NULL) DisposeMovie (flatMovie); } /*============================( End of Source )============================*/