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.
ConvertToMovie Jr.c
/* |
File: ConvertToMovie Jr.c |
Contains: Movie Recompression Routines. |
ConvertToMovie Jr. is an example application to recompress QuickTime |
movies and is based heavily on the venerable ConvertToMovieª. It shows |
how to use various parts of the Movie Toolbox, Image Compression Manager, |
and Standard Compression Component. |
This code is not intended to show coding style, user interface |
or graceful error handling. |
Written by: |
Copyright: Copyright © 1991-2001 by Apple Computer, Inc., All Rights Reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Change History (most recent first): |
11/05/2001 srk fully carbonized |
7/28/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
// |
// INCLUDES |
#ifndef __CONVERTTOMOVIEJR__ |
#include "ConvertToMovieJr.h" |
#endif |
// DEFINES |
#define BailNil(n) if (!n) goto bail; |
#define BailError(n) if (n) goto bail; |
#ifdef TARGET_API_MAC_CARBON |
#define kTypeListCount 2 |
#endif |
ConstStr255Param kPutFileName = "\pUntitled.mov"; |
ConstStr255Param kPutFilePrompt = "\pSave new movie file as:"; |
// FUNCTION PROTOTYPES |
pascal short |
DefaultsHookProc(DialogPtr theDialog, short itemHit, void *params, long refcon); |
// MAIN |
int main() |
{ |
short frameNum; |
OSErr result; |
short abort; |
short firstPass = true; |
Point where; |
OSType myTypeList[kTypeListCount] = {kQTFileTypeMovie, kQTFileTypeQuickTimeImage}; |
short movieRefNum = 0; |
FSSpec theFSSpec,dstFile; |
Boolean theIsSelected; |
Boolean theIsReplacing; |
ComponentInstance ci; |
Movie srcMovie = nil; |
Rect srcRect, portRect; |
GWorldPtr srcGWorld = nil; |
Movie dstMovie = nil; |
Track dstTrack; |
Media dstMedia; |
long frameCount; |
TimeValue curMovieTime; |
WindowRef progressWindow = nil; |
ImageSequence dstSeqID; |
ImageDescription **idh; |
SCTemporalSettings ts; |
SCDataRateSettings ds; |
GDHandle saveWinGDH; |
CGrafPtr saveWinPort; |
FlushEvents(0xffff,0); |
InitCursor(); |
//--------------------------------------------------------------------------------------------- |
// Initialize the Movie Toolbox. |
// Make sure you do this. It's very easy to forget about. |
//--------------------------------------------------------------------------------------------- |
result = EnterMovies(); |
BailError(result); |
//--------------------------------------------------------------------------------------------- |
// Open the Standard Compression component and adjust it to meet our needs. |
//--------------------------------------------------------------------------------------------- |
ci = OpenDefaultComponent(StandardCompressionType,StandardCompressionSubType); |
BailNil(ci); |
// Turn off best depth option in the compression dialog. |
// Because all of our buffering is done at 32-bits, regardless |
// of the depth of the source data, best depth isn't very useful |
// as it will always choose 32-bit. |
// |
// A more ambitious approach would be to loop through each of the |
// video sample descriptions in each of the video tracks looking |
// for the deepest depth, and using that for the best depth. |
// Better yet, we could find out which compressors were used |
// and set one of those as the default in the compression dialog. |
{ |
long flags; |
SCGetInfo(ci,scPreferenceFlagsType,&flags); |
flags &= ~scShowBestDepth; |
SCSetInfo(ci,scPreferenceFlagsType,&flags); |
} |
// Because we are recompressing a movie that may have a variable frame rate, |
// we want to allow the user to leave the frame rate text field blank. We |
// can then preserve the frame durations of the source movie. If they enter |
// a number we will resample the movie at a new frame rate. If we don't clear |
// this flag, the compression dialog will not allow zero in the frame rate field. |
// |
// NOTE: Setting this flag could have been done above when we cleared the |
// scShowBestDepth flag. It is done separately for clarity. |
{ |
long flags; |
SCGetInfo(ci,scPreferenceFlagsType,&flags); |
flags |= scAllowZeroFrameRate; |
SCSetInfo(ci,scPreferenceFlagsType,&flags); |
} |
//--------------------------------------------------------------------------------------------- |
// Ask for a source movie file using Standard Preview. |
// Use (-2,-2) to center on the best device. |
//--------------------------------------------------------------------------------------------- |
NextPass: |
{ |
result = GetOneFileWithPreview(kTypeListCount, myTypeList, &theFSSpec, NULL); |
if (result == userCanceledErr) |
goto bail; |
} |
//--------------------------------------------------------------------------------------------- |
// Open up the movie file and get a movie from it. |
//--------------------------------------------------------------------------------------------- |
{ |
short refnum; |
// Open a movie file using the FSSpec and create a movie from that file. |
result = OpenMovieFile(&theFSSpec, &refnum, 0); |
BailError(result); |
result = NewMovieFromFile(&srcMovie,refnum, nil, nil,newMovieActive, nil); |
BailError(result); |
// We're done with the movie file. |
CloseMovieFile(refnum); |
} |
//--------------------------------------------------------------------------------------------- |
// Count the number of video "frames" in the movie by stepping through all of the |
// video "interesting times", or in other words, the places where the movie displays |
// a new video sample. The time between these interesting times is not necessarily constant. |
//--------------------------------------------------------------------------------------------- |
{ |
OSType whichMediaType = VIDEO_TYPE; |
short flags = nextTimeMediaSample + nextTimeEdgeOK; |
TimeValue duration; |
TimeValue theTime = 0; |
frameCount = -1; |
while (theTime >= 0) |
{ |
frameCount++; |
GetMovieNextInterestingTime(srcMovie,flags,1,&whichMediaType,theTime,0,&theTime,&duration); |
// After the first interesting time, don't include the time we are currently at. |
flags = nextTimeMediaSample; |
} |
} |
//--------------------------------------------------------------------------------------------- |
// Get the bounding rectangle of the movie, create a 32-bit gworld with those dimensions, |
// and draw the movie poster picture into it. This gworld will be used for the test image |
// in the compression dialog and for rendering movie frames into. |
//--------------------------------------------------------------------------------------------- |
{ |
PicHandle ph = GetMoviePosterPict(srcMovie); |
CGrafPtr savePort; |
GDHandle saveDevice; |
result = GetMoviesError(); |
GetMovieBox(srcMovie,&srcRect); |
result = NewGWorld(&srcGWorld,32,&srcRect,nil,nil,0); |
BailError(result); |
if (ph) { |
GetGWorld(&savePort,&saveDevice); |
SetGWorld(srcGWorld,nil); |
EraseRect(&srcRect); |
DrawPicture(ph,&srcRect); |
KillPicture(ph); |
SetGWorld(savePort,saveDevice); |
// Use the gworld image of the movie poster frame as a compression dialog test image. |
// Pass nil for srcRect to use the entire image, and pass zero to use the default display method. |
#if TARGET_OS_WIN32 |
result = SCSetTestImagePixMap(ci,srcGWorld->portPixMap,nil,0); |
#else |
result = SCSetTestImagePixMap(ci,GetPortPixMap(srcGWorld),nil,0); |
#endif |
BailError(result); |
} |
} |
//--------------------------------------------------------------------------------------------- |
// Set compression dialog extend procs for the "Defaults" button. |
// We wait until now so we can pass the srcGWorld pixmap in as a refcon. |
//--------------------------------------------------------------------------------------------- |
{ |
SCExtendedProcs xprocs; |
// Because the compression dialog is movable modal, a filter proc |
// to handle update events for application windows is required. |
// Since our example application is completely modal, and we have |
// no other windows to update, we don't have a filter proc. |
xprocs.filterProc = nil; |
// Proc to handle custom button click. |
xprocs.hookProc = NewSCModalHookUPP(DefaultsHookProc); |
// Any information useful to the extended procs can be put |
// in the refcon. For an application that has globals, a refcon |
// of the current A5 is handy for getting at those globals in |
// the extended procs. In our case, we put the srcGWorld pixmap |
// in the refcon so we can set defaults on it in our hook proc. |
#if TARGET_OS_WIN32 |
xprocs.refcon = (long)srcGWorld->portPixMap; |
#else |
xprocs.refcon = (long)GetPortPixMap(srcGWorld); |
#endif |
// Set the custom button name. |
BlockMove("\pDefaults",xprocs.customName,9); |
// Tell the compression dialog about the extended procs. |
SCSetInfo(ci,scExtendedProcsType,&xprocs); |
} |
//--------------------------------------------------------------------------------------------- |
// Set up some default settings for the compression dialog if needed, and ask |
// for compression settings from the user. |
//--------------------------------------------------------------------------------------------- |
if (firstPass) { |
#if TARGET_OS_WIN32 |
result = SCDefaultPixMapSettings(ci,srcGWorld->portPixMap,true); |
#else |
result = SCDefaultPixMapSettings(ci,GetPortPixMap(srcGWorld),true); |
#endif |
BailError(result); |
} |
result = SCGetInfo(ci,scTemporalSettingsType,&ts); |
BailError(result); |
// The first time through, clear out the default frame rate chosen by Standard Compression. |
// We know that a frame rate of 0 means use the rate of the source movie. It probably should |
// have been smart enough to figure that out because we set the scAllowZeroFrameRate flag. |
// Oh well. |
if (firstPass) { |
ts.frameRate = 0; |
SCSetInfo(ci,scTemporalSettingsType,&ts); |
} |
// Get compression settings from the user. The first time through this loop, |
// we choose default compression settings for the test image we set above. |
// On subsequent passes, the settings previously chosen by the user will be the defaults. |
result = SCRequestSequenceSettings(ci); |
if (result == scUserCancelled) { |
// deal with user cancelling. |
} |
BailError(result); |
// Get a copy of the temporal settings the user entered. We'll need them for |
// some of our calculations. In a simpler application we'd never have to look at them. |
result = SCGetInfo(ci,scTemporalSettingsType,&ts); |
BailError(result); |
//--------------------------------------------------------------------------------------------- |
// Take the overall data rate value entered by the user and subtract out the |
// part of the data rate allocated to sound. We do this by looking at all of the |
// sound tracks in the source movie and using the one with the highest sample rate. |
// This number is then subtracted from the data rate leaving the amount of data per |
// second available to the compressed video data. This is obviously only an approximation |
// because the various sound tracks may or may not overlap, be stereo, be compressed, etc. |
// A more accurate approximation would be almost mandatory for a real QuickTime movie |
// creating/editing application. |
//--------------------------------------------------------------------------------------------- |
if (!SCGetInfo(ci,scDataRateSettingsType,&ds)) { |
if (ds.dataRate) { |
short i; |
short trackCount = GetMovieTrackCount(srcMovie); |
long maxSoundRate = 0; |
for (i = 1; i <= trackCount; i++) { |
OSType trackType; |
Track strack = GetMovieIndTrack(srcMovie,i); |
Media smedia = GetTrackMedia(strack); |
BailError(GetMoviesError()); |
GetMediaHandlerDescription(smedia,&trackType,0,0); |
if (trackType == SOUND_TYPE) { |
long rate; |
SampleDescriptionHandle desc = (SampleDescriptionHandle)NewHandle(sizeof(SampleDescription)); |
GetMediaSampleDescription(smedia,1,desc); |
if (GetMoviesError()) { |
DisposeHandle((Handle)desc); |
continue; |
} |
rate = (*(SoundDescriptionHandle)desc)->sampleRate >> 16; |
if (rate > maxSoundRate) |
maxSoundRate = rate; |
} |
} |
ds.dataRate -= maxSoundRate; |
} |
SCSetInfo(ci,scDataRateSettingsType,&ds); |
} |
//--------------------------------------------------------------------------------------------- |
// If the user said they want to resample the frame rate of the movie, |
// (by entering a non-zero value in the frame rate field) calculate |
// the number of frames and duration for the new movie. |
//--------------------------------------------------------------------------------------------- |
if (ts.frameRate) { |
long dur = GetMovieDuration(srcMovie); |
long timescale = GetMovieTimeScale(srcMovie); |
float f = (float)dur * ts.frameRate; |
frameCount = f / timescale / 65536; |
if (frameCount == 0) |
frameCount = 1; |
} |
//--------------------------------------------------------------------------------------------- |
// Ask the user for the name of the new movie file. |
// We'll be lazy here and just use "Untitled". A real app would |
// base it on the name of the source movie and check if the user |
// tried to enter a name the same as the source movie. |
// |
// Note we use the SCPositionDialog call to get a good position |
// point for the SFPutFile. -3999 is the resource I.D. for |
// SFPutFile dialog. |
//--------------------------------------------------------------------------------------------- |
where.h = where.v = -2; |
SCPositionDialog(ci,-3999,&where); |
result = PutFile (kPutFilePrompt, kPutFileName, &dstFile, &theIsSelected, &theIsReplacing); |
if (!theIsSelected) |
goto bail; // deal with user cancelling |
//--------------------------------------------------------------------------------------------- |
// Open a progress window to frames as they are compressed. Use Standard |
// Compression utility routines to position the window. |
//--------------------------------------------------------------------------------------------- |
{ |
Rect r = srcRect; |
where.h = where.v = -2; |
result = SCPositionRect(ci,&r,&where); |
GetGWorld(&saveWinPort,&saveWinGDH); |
progressWindow = NewCWindow(0,&r,dstFile.name,true,documentProc,(WindowPtr)-1,true,0); |
} |
//--------------------------------------------------------------------------------------------- |
// Create the new movie file and prepare it for edits. |
//--------------------------------------------------------------------------------------------- |
{ |
MatrixRecord matrix; |
// Using the FSSpec create a movie file for the destination movie. |
result = CreateMovieFile(&dstFile,'TVOD',0,createMovieFileDeleteCurFile,&movieRefNum,&dstMovie); |
BailError(result); |
// Create a new video movie track with the same dimensions as the entire source movie. |
dstTrack = NewMovieTrack(dstMovie, |
(long)(srcRect.right - srcRect.left) << 16, |
(long)(srcRect.bottom - srcRect.top) << 16,0); |
// Create a media for the new track with the same time scale as the |
// source movie. Because the time scales are the same, we don't have |
// to do any time scale conversions. |
dstMedia = NewTrackMedia(dstTrack,VIDEO_TYPE,GetMovieTimeScale(srcMovie),0,0); |
BailError(GetMoviesError()); |
// Copy the user data and settings from the source to the dest movie. |
// These settings include information like user data. |
CopyMovieSettings(srcMovie,dstMovie); |
// Set movie matrix to identity and clear the movie clip region |
// because conversion process transforms and composites all video |
// tracks into one untransformed video track. |
SetIdentityMatrix(&matrix); |
SetMovieMatrix(dstMovie,&matrix); |
SetMovieClipRgn(dstMovie,nil); |
} |
//--------------------------------------------------------------------------------------------- |
// Start a compression sequence using the parameters chosen by the user. |
// Pass nil for the source rect to use the entire image. An image description |
// handle will be returned in idh. Nil could be passed for that if we |
// didn't need it for our progress window display. We do not have dispose |
// the image description handle. It is disposed for use by SCCompressSequenceEnd. |
//--------------------------------------------------------------------------------------------- |
#if TARGET_OS_WIN32 |
result = SCCompressSequenceBegin(ci,srcGWorld->portPixMap,nil,&idh); |
#else |
result = SCCompressSequenceBegin(ci,GetPortPixMap(srcGWorld),nil,&idh); |
#endif |
BailError(result); |
// Clear out our image gworld and set movie to draw into it. |
SetGWorld(srcGWorld,nil); |
GetPortBounds(srcGWorld, &portRect); |
EraseRect(&portRect); |
SetMovieGWorld(srcMovie,srcGWorld,GetGWorldDevice(srcGWorld)); |
// Set current time value to begining of movie. |
curMovieTime = 0; |
// Loop through all of the interesting times we counted above. |
for (frameNum = 0; frameNum < frameCount; frameNum++) { |
short syncFlag; |
TimeValue duration; |
long dataSize; |
Handle compressedData; |
// Abort if the user clicked the mouse or pressed a key. |
{ |
EventRecord event; |
abort = false; |
WaitNextEvent(everyEvent,&event,0,NULL); |
switch (event.what) |
{ |
case mouseDown: |
{ |
WindowPtr myWindow = NULL; |
short myWindowPart; |
myWindowPart = FindWindow(event.where, &myWindow); |
// menu bar and window-related events: |
switch (myWindowPart) { |
case inGoAway: |
if (TrackGoAway(myWindow, event.where)) { |
DisposeWindow(myWindow); |
progressWindow = nil; |
abort = true; |
} |
break; |
} |
} |
break; |
case keyDown: |
case keyUp: |
abort = true; |
break; |
} |
} |
// Get the next frame of the source movie. |
{ |
// If we are resampling the movie, step to the next frame. |
if (ts.frameRate) { |
// This code could be much smarter about its calculations. |
// The srcMovie duration and dstMovie frame duration are both |
// constant and could be calculated once outside this loop |
long dur = GetMovieDuration(srcMovie); |
curMovieTime = frameNum*dur/(frameCount-1); |
duration = dur / frameCount; |
} else { |
short flags = nextTimeMediaSample; |
OSType whichMediaType = VIDEO_TYPE; |
// If this is the first frame, include the frame we are currently on. |
if (frameNum == 0) |
flags |= nextTimeEdgeOK; |
// If we are maintaining the frame durations of the source movie, |
// skip to the next interesting time and get the duration for that frame. |
GetMovieNextInterestingTime(srcMovie,flags,1,&whichMediaType,curMovieTime,0,&curMovieTime,&duration); |
} |
SetMovieTimeValue(srcMovie,curMovieTime); |
MoviesTask(srcMovie,0); |
MoviesTask(srcMovie,0); |
MoviesTask(srcMovie,0); |
} |
// If data rate constraining is being done, tell Standard Compression the |
// duration of the current frame in milliseconds. We only need to do this |
// if the frames have variable durations. |
{ |
SCDataRateSettings ds; |
if (!SCGetInfo(ci,scDataRateSettingsType,&ds)) { |
ds.frameDuration = duration * 1000 / GetMovieTimeScale(srcMovie); |
SCSetInfo(ci,scDataRateSettingsType,&ds); |
} |
} |
// Compress the frame. compressedData will hold a handle to the newly compressed |
// image data. dataSize is the size of the compressed data, which will usually be |
// different than the size of the compressedData handle. syncFlag is a value that |
// can be passed directly to AddMediaSample which indicates whether or not the frame |
// is a key frame. Note that we do not have to dispose of the compressedData handle. |
// It will be dispose for us when we call SCCompressSequenceEnd. |
#if TARGET_OS_WIN32 |
result = SCCompressSequenceFrame(ci,srcGWorld->portPixMap,&srcRect,&compressedData,&dataSize,&syncFlag); |
#else |
result = SCCompressSequenceFrame(ci,GetPortPixMap(srcGWorld),&srcRect,&compressedData,&dataSize,&syncFlag); |
#endif |
BailError(result); |
// Prepare for adding frames to the movie. |
// Make sure you do this. It's very easy to forget about. |
result = BeginMediaEdits(dstMedia); |
BailError(result); |
// Append the compressed image data to the media. |
result = AddMediaSample(dstMedia,compressedData,0,dataSize,duration, |
(SampleDescriptionHandle)idh,1,syncFlag,nil); |
BailError(result); |
// End changes to the media. |
result = EndMediaEdits(dstMedia); |
BailError(result); |
// Decompress the compressed frame into the progress window. |
if (progressWindow) { |
char hstate; |
// Set port to progress window. |
SetGWorld(GetWindowPort(progressWindow),saveWinGDH); |
// If this is the first frame, start up a decompression sequence. |
if (frameNum == 0) { |
result = DecompressSequenceBegin(&dstSeqID,idh,nil,nil,&srcRect,nil,ditherCopy, |
nil,0,codecNormalQuality,anyCodec); |
BailError(result); |
} |
// Save the locked state of the compressed data and then lock it. |
// We want it locked but standard compression may or may not. |
hstate = HGetState(compressedData); |
HLock(compressedData); |
// Decompress the frame to the progress window. |
result = DecompressSequenceFrame(dstSeqID,*compressedData,0,nil,nil); |
// Restore the locked state of the data handle. |
HSetState(compressedData,hstate); |
BailError(result); |
} |
} |
// Close the compression sequence. This will dispose of the image description |
// and compressed data handles allocated by SCCompressSequenceBegin. |
SCCompressSequenceEnd(ci); |
// Close the decompression sequence. Note that this is an Image Compression |
// Manager call, not Standard Compression. |
CDSequenceEnd(dstSeqID); |
// Copy all sound tracks from the source to dest movie. |
// |
// NOTE: We are not copying any other track types here. That means |
// text tracks, alternate tracks, etc. are not being copied. A real |
// application would give the user some options here. |
{ |
short i; |
short trackCount; |
// Get a count of all the tracks present in the source movie. |
trackCount = GetMovieTrackCount(srcMovie); |
// Loop through each of the tracks, looking for sound tracks. |
for (i = 1; i <= trackCount; i++) { |
OSType trackType; |
Track strack; |
Media smedia; |
// Get the next track and its media. |
strack = GetMovieIndTrack(srcMovie,i); |
smedia = GetTrackMedia(strack); |
BailError(GetMoviesError()); |
// Find out what type of media this track has. |
// We only care about sound. |
GetMediaHandlerDescription(smedia,&trackType,0,0); |
if (trackType == SOUND_TYPE) { |
Track dtrack; |
Media dmedia; |
// Create a new sound track in the destination movie. |
dtrack = NewMovieTrack(dstMovie,0,0,GetTrackVolume(strack)); |
BailError(GetMoviesError()); |
// Create a media for that sound track and prepare it for editing. |
dmedia = NewTrackMedia(dtrack,SOUND_TYPE,GetMediaTimeScale(smedia),0,0); |
BailError(GetMoviesError()); |
result = BeginMediaEdits(dmedia); |
BailError(GetMoviesError()); |
// Insert the new track into the dest movie starting at time |
// zero and lasting for the entire duration of the movie. |
InsertTrackSegment(strack,dtrack,0,GetTrackDuration(strack),0); |
BailError(GetMoviesError()); |
// We're done editing the media. |
EndMediaEdits(dmedia); |
} |
} |
} |
//--------------------------------------------------------------------------------------------- |
// Now that we're finished compressing video data, make that data part of our movie. |
//--------------------------------------------------------------------------------------------- |
if (dstTrack) { |
short resID = 128; |
// Insert the newly created media into the newly created track at |
// the begining of the track and lasting for the entire duration of |
// the media. The media rate is 1.0 for normal playback rate. |
InsertMediaIntoTrack(dstTrack,0,0,GetMediaDuration(dstMedia),fixed1); |
// Add the movie resource to the dst movie file. |
result = AddMovieResource(dstMovie,movieRefNum,&resID,"\pMovie 1"); |
BailError(result); |
//--------------------------------------------------------------------------------------------- |
// Next we flatten the movie. The movie file we just created has all of |
// the video data at the beginning and all of the sound data at the end. |
// This unnatural situation may cause the movie to play poorly. By flattening |
// the movie, we are reinterleaving all of the tracks in a way optimal for playback. |
// Because we cannot flatten a movie in place, we create a temp file, flatten to |
// that file and if successful, delete the first file and rename the temp file to |
// be the real file. For large files this takes some time. |
// |
// Once again, a real application would choose a temp file name that doesn't |
// possibly conflict with other file names, as well as delete the temp file |
// if the flatten fails. |
//--------------------------------------------------------------------------------------------- |
{ |
FSSpec theFSSpec; |
short resID = 128; |
dstFile.name[++dstFile.name[0]] = '@'; |
result = FSMakeFSSpec(dstFile.vRefNum,0,dstFile.name,&theFSSpec); |
if (result != fnfErr) |
BailError(result); |
FlattenMovie(dstMovie,0,&theFSSpec,'TVOD',-1,createMovieFileDeleteCurFile,&resID,"\p"); |
result = GetMoviesError(); |
CloseMovieFile(movieRefNum); |
DisposeMovie(dstMovie); |
dstTrack = nil; |
if (!result) { |
Str255 tempStr; |
BlockMove(dstFile.name,tempStr,256); |
tempStr[0]--; |
result = FSpDelete(&dstFile); |
result = FSpRename(&dstFile, tempStr); |
} |
} |
} |
// Throw away the progress window. |
if (progressWindow) { |
DisposeWindow(progressWindow); |
progressWindow = nil; |
} |
// Throw away the movie buffer gworld. |
if (srcGWorld) { |
DisposeGWorld(srcGWorld); |
srcGWorld = nil; |
// Just to be overly safe, we clear our the test image |
// because we just disposed the pixmap it depends on. |
SCSetTestImagePixMap(ci,nil,nil,0); |
} |
if (!abort) { |
firstPass = false; |
goto NextPass; |
} |
// Close the Standard Compression component. |
CloseComponent(ci); |
bail:; |
return 0; |
} |
// Handle clicks in the "Defaults" custom button. |
pascal short |
DefaultsHookProc(DialogPtr theDialog, short itemHit, void *params, long refcon) |
{ |
#pragma unused(theDialog) |
if (itemHit == scCustomItem) { |
SCTemporalSettings ts; |
// Set defaults for our test image (passed in refcon). |
SCDefaultPixMapSettings(params,(PixMapHandle)refcon,true); |
// Once again, don't use the default frame rate chosen |
// by Standard Compression. Clear it out to zero. |
SCGetInfo(params,scTemporalSettingsType,&ts); |
ts.frameRate = 0; |
SCSetInfo(params,scTemporalSettingsType,&ts); |
} |
return (itemHit); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14